Alembic: new exporter based on the USD exporter structure
The Alembic exporter has been restructured by leverages the `AbstractHierarchyIterator` introduced by the USD exporter. The produced Alembic files have not changed much (details below), as the Alembic writing code has simply been moved from the old exporter to the new. How the export hierarchy is handled changed a lot, though, and also the way in which transforms are computed. As a result, T71395 is fixed. Differences between the old and new exporter, in terms of the produced Alembic file: - Duplicated objects now have a unique numerical suffix. - Matrices are computed differently, namely by simply computing the evaluated transform of the object relative to the evaluated transform of its export-parent. This fixes {T71395}, but otherwise should produce the same result as before (but with simpler code). Compared to the old Alembic exporter, Subdivision modifiers are now disabled in a cleaner, more efficient way (they are disabled when exporting with the "Apply Subdivisions" option is unchecked). Previously the exporter would move to a new frame, disable the modifier, evaluate the object, and enable the modifier again. This is now done before exporting starts, and modifiers are only restored when exporting ends. Some issues with the old Alembic exporter that have NOT been fixed in this patch: - Exporting NURBS patches and curves (see T49114 for example). - Exporting flattened hierarchy in combination with dupli-objects. This seems to be broken in the old Alembic exporter as well, but nobody reported this yet. Differential Revision: https://developer.blender.org/D7664 Reviewed By: Sergey
This commit is contained in:
@@ -30,6 +30,7 @@ set(INC
|
|||||||
../../makesdna
|
../../makesdna
|
||||||
../../makesrna
|
../../makesrna
|
||||||
../../windowmanager
|
../../windowmanager
|
||||||
|
../../../../intern/clog
|
||||||
../../../../intern/guardedalloc
|
../../../../intern/guardedalloc
|
||||||
../../../../intern/utfconv
|
../../../../intern/utfconv
|
||||||
)
|
)
|
||||||
@@ -54,16 +55,17 @@ set(SRC
|
|||||||
intern/abc_util.cc
|
intern/abc_util.cc
|
||||||
intern/alembic_capi.cc
|
intern/alembic_capi.cc
|
||||||
|
|
||||||
|
exporter/abc_archive.cc
|
||||||
exporter/abc_export_capi.cc
|
exporter/abc_export_capi.cc
|
||||||
exporter/abc_exporter.cc
|
exporter/abc_hierarchy_iterator.cc
|
||||||
exporter/abc_writer_archive.cc
|
exporter/abc_subdiv_disabler.cc
|
||||||
|
exporter/abc_writer_abstract.cc
|
||||||
exporter/abc_writer_camera.cc
|
exporter/abc_writer_camera.cc
|
||||||
exporter/abc_writer_curves.cc
|
exporter/abc_writer_curves.cc
|
||||||
exporter/abc_writer_hair.cc
|
exporter/abc_writer_hair.cc
|
||||||
exporter/abc_writer_mball.cc
|
|
||||||
exporter/abc_writer_mesh.cc
|
exporter/abc_writer_mesh.cc
|
||||||
|
exporter/abc_writer_mball.cc
|
||||||
exporter/abc_writer_nurbs.cc
|
exporter/abc_writer_nurbs.cc
|
||||||
exporter/abc_writer_object.cc
|
|
||||||
exporter/abc_writer_points.cc
|
exporter/abc_writer_points.cc
|
||||||
exporter/abc_writer_transform.cc
|
exporter/abc_writer_transform.cc
|
||||||
|
|
||||||
@@ -80,15 +82,16 @@ set(SRC
|
|||||||
intern/abc_reader_transform.h
|
intern/abc_reader_transform.h
|
||||||
intern/abc_util.h
|
intern/abc_util.h
|
||||||
|
|
||||||
exporter/abc_exporter.h
|
exporter/abc_archive.h
|
||||||
exporter/abc_writer_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_camera.h
|
||||||
exporter/abc_writer_curves.h
|
exporter/abc_writer_curves.h
|
||||||
exporter/abc_writer_hair.h
|
exporter/abc_writer_hair.h
|
||||||
exporter/abc_writer_mball.h
|
|
||||||
exporter/abc_writer_mesh.h
|
exporter/abc_writer_mesh.h
|
||||||
|
exporter/abc_writer_mball.h
|
||||||
exporter/abc_writer_nurbs.h
|
exporter/abc_writer_nurbs.h
|
||||||
exporter/abc_writer_object.h
|
|
||||||
exporter/abc_writer_points.h
|
exporter/abc_writer_points.h
|
||||||
exporter/abc_writer_transform.h
|
exporter/abc_writer_transform.h
|
||||||
)
|
)
|
||||||
|
255
source/blender/io/alembic/exporter/abc_archive.cc
Normal file
255
source/blender/io/alembic/exporter/abc_archive.cc
Normal file
@@ -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 ¶ms,
|
||||||
|
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 ¶ms,
|
||||||
|
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 ¶ms,
|
||||||
|
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
|
87
source/blender/io/alembic/exporter/abc_archive.h
Normal file
87
source/blender/io/alembic/exporter/abc_archive.h
Normal file
@@ -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
|
@@ -18,18 +18,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ABC_alembic.h"
|
#include "ABC_alembic.h"
|
||||||
#include "abc_writer_camera.h"
|
#include "abc_archive.h"
|
||||||
#include "abc_writer_curves.h"
|
#include "abc_hierarchy_iterator.h"
|
||||||
#include "abc_writer_hair.h"
|
#include "abc_subdiv_disabler.h"
|
||||||
#include "abc_writer_mesh.h"
|
|
||||||
#include "abc_writer_nurbs.h"
|
|
||||||
#include "abc_writer_points.h"
|
|
||||||
#include "abc_writer_transform.h"
|
|
||||||
|
|
||||||
#include "MEM_guardedalloc.h"
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
#include "DEG_depsgraph.h"
|
#include "DEG_depsgraph.h"
|
||||||
#include "DEG_depsgraph_build.h"
|
#include "DEG_depsgraph_build.h"
|
||||||
|
#include "DEG_depsgraph_query.h"
|
||||||
|
|
||||||
#include "DNA_modifier_types.h"
|
#include "DNA_modifier_types.h"
|
||||||
#include "DNA_scene_types.h"
|
#include "DNA_scene_types.h"
|
||||||
@@ -47,179 +44,163 @@
|
|||||||
#include "WM_api.h"
|
#include "WM_api.h"
|
||||||
#include "WM_types.h"
|
#include "WM_types.h"
|
||||||
|
|
||||||
using namespace blender::io::alembic;
|
#include "CLG_log.h"
|
||||||
|
static CLG_LogRef LOG = {"io.alembic"};
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
struct ExportJobData {
|
struct ExportJobData {
|
||||||
ViewLayer *view_layer;
|
|
||||||
Main *bmain;
|
Main *bmain;
|
||||||
|
Depsgraph *depsgraph;
|
||||||
wmWindowManager *wm;
|
wmWindowManager *wm;
|
||||||
|
|
||||||
char filename[1024];
|
char filename[FILE_MAX];
|
||||||
ExportSettings settings;
|
AlembicExportParams params;
|
||||||
|
|
||||||
short *stop;
|
|
||||||
short *do_update;
|
|
||||||
float *progress;
|
|
||||||
|
|
||||||
bool was_canceled;
|
bool was_canceled;
|
||||||
bool export_ok;
|
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)
|
static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
|
||||||
{
|
{
|
||||||
ExportJobData *data = static_cast<ExportJobData *>(customdata);
|
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;
|
G.is_rendering = true;
|
||||||
WM_set_locked_interface(data->wm, true);
|
WM_set_locked_interface(data->wm, true);
|
||||||
G.is_break = false;
|
G.is_break = false;
|
||||||
|
|
||||||
DEG_graph_build_from_view_layer(
|
*progress = 0.0f;
|
||||||
data->settings.depsgraph, data->bmain, data->settings.scene, data->view_layer);
|
*do_update = true;
|
||||||
BKE_scene_graph_update_tagged(data->settings.depsgraph, data->bmain);
|
|
||||||
|
|
||||||
try {
|
build_depsgraph(data->depsgraph, data->bmain);
|
||||||
AbcExporter exporter(data->bmain, data->filename, data->settings);
|
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 */
|
// For restoring the current frame after exporting animation is done.
|
||||||
const int orig_frame = CFRA;
|
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;
|
// Create the Alembic archive.
|
||||||
exporter(do_update, progress, &data->was_canceled);
|
ABCArchive abc_archive(data->bmain, scene, data->params, std::string(data->filename));
|
||||||
|
|
||||||
if (CFRA != orig_frame) {
|
ABCHierarchyIterator iter(data->depsgraph, &abc_archive, data->params);
|
||||||
CFRA = orig_frame;
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If we're not animating, a single iteration over all objects is enough.
|
||||||
|
iter.iterate_and_write();
|
||||||
|
}
|
||||||
|
|
||||||
data->export_ok = !data->was_canceled;
|
iter.release_writers();
|
||||||
}
|
|
||||||
catch (const std::exception &e) {
|
// Finish up by going back to the keyframe that was current before we started.
|
||||||
ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n';
|
if (CFRA != orig_frame) {
|
||||||
}
|
CFRA = orig_frame;
|
||||||
catch (...) {
|
BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain);
|
||||||
ABC_LOG(data->settings.logger) << "Abc Export: unknown error...\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data->export_ok = !data->was_canceled;
|
||||||
|
|
||||||
|
*progress = 1.0f;
|
||||||
|
*do_update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void export_endjob(void *customdata)
|
static void export_endjob(void *customdata)
|
||||||
{
|
{
|
||||||
ExportJobData *data = static_cast<ExportJobData *>(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)) {
|
if (data->was_canceled && BLI_exists(data->filename)) {
|
||||||
BLI_delete(data->filename, false, false);
|
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;
|
G.is_rendering = false;
|
||||||
WM_set_locked_interface(data->wm, false);
|
WM_set_locked_interface(data->wm, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ABC_export(struct Scene *scene,
|
} // namespace alembic
|
||||||
struct bContext *C,
|
} // namespace io
|
||||||
|
} // namespace blender
|
||||||
|
|
||||||
|
bool ABC_export(Scene *scene,
|
||||||
|
bContext *C,
|
||||||
const char *filepath,
|
const char *filepath,
|
||||||
const struct AlembicExportParams *params,
|
const AlembicExportParams *params,
|
||||||
bool as_background_job)
|
bool as_background_job)
|
||||||
{
|
{
|
||||||
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||||
|
|
||||||
ExportJobData *job = static_cast<ExportJobData *>(
|
ExportJobData *job = static_cast<ExportJobData *>(
|
||||||
MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
|
MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
|
||||||
|
|
||||||
job->view_layer = CTX_data_view_layer(C);
|
|
||||||
job->bmain = CTX_data_main(C);
|
job->bmain = CTX_data_main(C);
|
||||||
job->wm = CTX_wm_manager(C);
|
job->wm = CTX_wm_manager(C);
|
||||||
job->export_ok = false;
|
job->export_ok = false;
|
||||||
BLI_strncpy(job->filename, filepath, 1024);
|
BLI_strncpy(job->filename, filepath, sizeof(job->filename));
|
||||||
|
|
||||||
/* Alright, alright, alright....
|
job->depsgraph = DEG_graph_new(
|
||||||
*
|
job->bmain, scene, view_layer, DAG_EVAL_RENDER /* TODO(Sybren): params->evaluation_mode */);
|
||||||
* ExportJobData contains an ExportSettings containing a SimpleLogger.
|
job->params = *params;
|
||||||
*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool export_ok = false;
|
bool export_ok = false;
|
||||||
if (as_background_job) {
|
if (as_background_job) {
|
||||||
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
|
wmJob *wm_job = WM_jobs_get(
|
||||||
CTX_wm_window(C),
|
job->wm, CTX_wm_window(C), scene, "Alembic Export", WM_JOB_PROGRESS, WM_JOB_TYPE_ALEMBIC);
|
||||||
job->settings.scene,
|
|
||||||
"Alembic Export",
|
|
||||||
WM_JOB_PROGRESS,
|
|
||||||
WM_JOB_TYPE_ALEMBIC);
|
|
||||||
|
|
||||||
/* setup job */
|
/* setup job */
|
||||||
WM_jobs_customdata_set(wm_job, job, MEM_freeN);
|
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_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);
|
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;
|
short stop = 0, do_update = 0;
|
||||||
float progress = 0.f;
|
float progress = 0.f;
|
||||||
|
|
||||||
export_startjob(job, &stop, &do_update, &progress);
|
blender::io::alembic::export_startjob(job, &stop, &do_update, &progress);
|
||||||
export_endjob(job);
|
blender::io::alembic::export_endjob(job);
|
||||||
export_ok = job->export_ok;
|
export_ok = job->export_ok;
|
||||||
|
|
||||||
MEM_freeN(job);
|
MEM_freeN(job);
|
||||||
|
@@ -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
|
|
@@ -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
|
|
261
source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
Normal file
261
source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
Normal file
@@ -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 ¶ms)
|
||||||
|
: 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 = ¶ms_;
|
||||||
|
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
|
90
source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
Normal file
90
source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
Normal file
@@ -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 ¶ms_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ABCHierarchyIterator(Depsgraph *depsgraph,
|
||||||
|
ABCArchive *abc_archive_,
|
||||||
|
const AlembicExportParams ¶ms);
|
||||||
|
|
||||||
|
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
|
107
source/blender/io/alembic/exporter/abc_subdiv_disabler.cc
Normal file
107
source/blender/io/alembic/exporter/abc_subdiv_disabler.cc
Normal file
@@ -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
|
@@ -13,40 +13,41 @@
|
|||||||
* along with this program; if not, write to the Free Software Foundation,
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* 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.
|
* All rights reserved.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
/** \file
|
#include <set>
|
||||||
* \ingroup balembic
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Alembic/Abc/All.h>
|
struct Depsgraph;
|
||||||
#include <Alembic/AbcCoreOgawa/All.h>
|
struct ModifierData;
|
||||||
|
struct Object;
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
struct Main;
|
|
||||||
struct Scene;
|
struct Scene;
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
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
|
* Temporarily all subdivision modifiers on mesh objects.
|
||||||
* the stream objects remain valid as long as the archives are open.
|
* 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 SubdivModifierDisabler final {
|
||||||
class ArchiveWriter {
|
private:
|
||||||
std::ofstream m_outfile;
|
Depsgraph *depsgraph_;
|
||||||
Alembic::Abc::OArchive m_archive;
|
std::set<ModifierData *> disabled_modifiers_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ArchiveWriter(const char *filename, const std::string &abc_scene_name, const Scene *scene);
|
explicit SubdivModifierDisabler(Depsgraph *depsgraph);
|
||||||
|
~SubdivModifierDisabler();
|
||||||
|
|
||||||
Alembic::Abc::OArchive &archive();
|
void disable_modifiers();
|
||||||
|
|
||||||
|
static ModifierData *get_subdiv_modifier(Scene *scene, Object *ob);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace alembic
|
} // namespace alembic
|
101
source/blender/io/alembic/exporter/abc_writer_abstract.cc
Normal file
101
source/blender/io/alembic/exporter/abc_writer_abstract.cc
Normal file
@@ -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
|
77
source/blender/io/alembic/exporter/abc_writer_abstract.h
Normal file
77
source/blender/io/alembic/exporter/abc_writer_abstract.h
Normal file
@@ -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
|
@@ -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
|
|
@@ -19,67 +19,90 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_camera.h"
|
#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_camera_types.h"
|
||||||
#include "DNA_object_types.h"
|
#include "DNA_scene_types.h"
|
||||||
|
|
||||||
using Alembic::AbcGeom::OCamera;
|
#include "CLG_log.h"
|
||||||
using Alembic::AbcGeom::OFloatProperty;
|
static CLG_LogRef LOG = {"io.alembic"};
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
AbcCameraWriter::AbcCameraWriter(Object *ob,
|
using Alembic::AbcGeom::CameraSample;
|
||||||
AbcTransformWriter *parent,
|
using Alembic::AbcGeom::OCamera;
|
||||||
uint32_t time_sampling,
|
using Alembic::AbcGeom::OFloatProperty;
|
||||||
ExportSettings &settings)
|
|
||||||
: AbcObjectWriter(ob, time_sampling, settings, parent)
|
|
||||||
{
|
|
||||||
OCamera camera(parent->alembicXform(), m_name, m_time_sampling);
|
|
||||||
m_camera_schema = camera.getSchema();
|
|
||||||
|
|
||||||
m_custom_data_container = m_camera_schema.getUserProperties();
|
ABCCameraWriter::ABCCameraWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
|
||||||
m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling);
|
{
|
||||||
m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbcCameraWriter::do_write()
|
bool ABCCameraWriter::is_supported(const HierarchyContext *context) const
|
||||||
{
|
{
|
||||||
Camera *cam = static_cast<Camera *>(m_object->data);
|
Camera *camera = static_cast<Camera *>(context->object->data);
|
||||||
|
return camera->type == CAM_PERSP;
|
||||||
|
}
|
||||||
|
|
||||||
m_stereo_distance.set(cam->stereo.convergence_distance);
|
void ABCCameraWriter::create_alembic_objects(const HierarchyContext * /*context*/)
|
||||||
m_eye_separation.set(cam->stereo.interocular_distance);
|
{
|
||||||
|
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_);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Alembic::Abc::OObject ABCCameraWriter::get_alembic_object() const
|
||||||
|
{
|
||||||
|
return abc_camera_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABCCameraWriter::do_write(HierarchyContext &context)
|
||||||
|
{
|
||||||
|
Camera *cam = static_cast<Camera *>(context.object->data);
|
||||||
|
|
||||||
|
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_x = cam->sensor_x / 10.0;
|
||||||
const double apperture_y = cam->sensor_y / 10.0;
|
const double apperture_y = cam->sensor_y / 10.0;
|
||||||
const double film_aspect = apperture_x / apperture_y;
|
const double film_aspect = apperture_x / apperture_y;
|
||||||
|
|
||||||
m_camera_sample.setFocalLength(cam->lens);
|
CameraSample camera_sample;
|
||||||
m_camera_sample.setHorizontalAperture(apperture_x);
|
camera_sample.setFocalLength(cam->lens);
|
||||||
m_camera_sample.setVerticalAperture(apperture_y);
|
camera_sample.setHorizontalAperture(apperture_x);
|
||||||
m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx);
|
camera_sample.setVerticalAperture(apperture_y);
|
||||||
m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect);
|
camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx);
|
||||||
m_camera_sample.setNearClippingPlane(cam->clip_start);
|
camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect);
|
||||||
m_camera_sample.setFarClippingPlane(cam->clip_end);
|
camera_sample.setNearClippingPlane(cam->clip_start);
|
||||||
|
camera_sample.setFarClippingPlane(cam->clip_end);
|
||||||
|
|
||||||
if (cam->dof.focus_object) {
|
if (cam->dof.focus_object) {
|
||||||
Imath::V3f v(m_object->loc[0] - cam->dof.focus_object->loc[0],
|
Imath::V3f v(context.object->loc[0] - cam->dof.focus_object->loc[0],
|
||||||
m_object->loc[1] - cam->dof.focus_object->loc[1],
|
context.object->loc[1] - cam->dof.focus_object->loc[1],
|
||||||
m_object->loc[2] - cam->dof.focus_object->loc[2]);
|
context.object->loc[2] - cam->dof.focus_object->loc[2]);
|
||||||
m_camera_sample.setFocusDistance(v.length());
|
camera_sample.setFocusDistance(v.length());
|
||||||
}
|
}
|
||||||
else {
|
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
|
/* Blender camera does not have an fstop param, so try to find a custom prop
|
||||||
* instead. */
|
* instead. */
|
||||||
m_camera_sample.setFStop(cam->dof.aperture_fstop);
|
camera_sample.setFStop(cam->dof.aperture_fstop);
|
||||||
|
|
||||||
m_camera_sample.setLensSqueezeRatio(1.0);
|
camera_sample.setLensSqueezeRatio(1.0);
|
||||||
m_camera_schema.set(m_camera_sample);
|
abc_camera_schema_.set(camera_sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace alembic
|
} // namespace alembic
|
||||||
|
@@ -19,29 +19,32 @@
|
|||||||
* \ingroup balembic
|
* \ingroup balembic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_object.h"
|
#include "abc_writer_abstract.h"
|
||||||
|
|
||||||
/* ************************************************************************** */
|
#include <Alembic/AbcGeom/OCamera.h>
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
class AbcCameraWriter : public AbcObjectWriter {
|
class ABCCameraWriter : public ABCAbstractWriter {
|
||||||
Alembic::AbcGeom::OCameraSchema m_camera_schema;
|
private:
|
||||||
Alembic::AbcGeom::CameraSample m_camera_sample;
|
Alembic::AbcGeom::OCamera abc_camera_;
|
||||||
Alembic::AbcGeom::OCompoundProperty m_custom_data_container;
|
Alembic::AbcGeom::OCameraSchema abc_camera_schema_;
|
||||||
Alembic::AbcGeom::OFloatProperty m_stereo_distance;
|
|
||||||
Alembic::AbcGeom::OFloatProperty m_eye_separation;
|
Alembic::AbcGeom::OCompoundProperty abc_custom_data_container_;
|
||||||
|
Alembic::AbcGeom::OFloatProperty abc_stereo_distance_;
|
||||||
|
Alembic::AbcGeom::OFloatProperty abc_eye_separation_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AbcCameraWriter(Object *ob,
|
explicit ABCCameraWriter(const ABCWriterConstructorArgs &args);
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings);
|
|
||||||
|
|
||||||
private:
|
virtual void create_alembic_objects(const HierarchyContext *context) override;
|
||||||
virtual void do_write();
|
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
|
} // namespace alembic
|
||||||
|
@@ -22,9 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_curves.h"
|
#include "abc_writer_curves.h"
|
||||||
#include "abc_writer_transform.h"
|
|
||||||
#include "intern/abc_axis_conversion.h"
|
#include "intern/abc_axis_conversion.h"
|
||||||
#include "intern/abc_reader_curves.h"
|
|
||||||
|
|
||||||
#include "DNA_curve_types.h"
|
#include "DNA_curve_types.h"
|
||||||
#include "DNA_object_types.h"
|
#include "DNA_object_types.h"
|
||||||
@@ -33,6 +31,9 @@
|
|||||||
#include "BKE_mesh.h"
|
#include "BKE_mesh.h"
|
||||||
#include "BKE_object.h"
|
#include "BKE_object.h"
|
||||||
|
|
||||||
|
#include "CLG_log.h"
|
||||||
|
static CLG_LogRef LOG = {"io.alembic"};
|
||||||
|
|
||||||
using Alembic::AbcGeom::OCompoundProperty;
|
using Alembic::AbcGeom::OCompoundProperty;
|
||||||
using Alembic::AbcGeom::OCurves;
|
using Alembic::AbcGeom::OCurves;
|
||||||
using Alembic::AbcGeom::OCurvesSchema;
|
using Alembic::AbcGeom::OCurvesSchema;
|
||||||
@@ -44,24 +45,32 @@ namespace blender {
|
|||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
AbcCurveWriter::AbcCurveWriter(Object *ob,
|
const std::string ABC_CURVE_RESOLUTION_U_PROPNAME("blender:resolution");
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings)
|
|
||||||
: AbcObjectWriter(ob, time_sampling, settings, parent)
|
|
||||||
{
|
|
||||||
OCurves curves(parent->alembicXform(), m_name, m_time_sampling);
|
|
||||||
m_schema = curves.getSchema();
|
|
||||||
|
|
||||||
Curve *cu = static_cast<Curve *>(m_object->data);
|
ABCCurveWriter::ABCCurveWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
|
||||||
OCompoundProperty user_props = m_schema.getUserProperties();
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABCCurveWriter::create_alembic_objects(const HierarchyContext *context)
|
||||||
|
{
|
||||||
|
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 *>(context->object->data);
|
||||||
|
OCompoundProperty user_props = abc_curve_schema_.getUserProperties();
|
||||||
OInt16Property user_prop_resolu(user_props, ABC_CURVE_RESOLUTION_U_PROPNAME);
|
OInt16Property user_prop_resolu(user_props, ABC_CURVE_RESOLUTION_U_PROPNAME);
|
||||||
user_prop_resolu.set(cu->resolu);
|
user_prop_resolu.set(cu->resolu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbcCurveWriter::do_write()
|
const Alembic::Abc::OObject ABCCurveWriter::get_alembic_object() const
|
||||||
{
|
{
|
||||||
Curve *curve = static_cast<Curve *>(m_object->data);
|
return abc_curve_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABCCurveWriter::do_write(HierarchyContext &context)
|
||||||
|
{
|
||||||
|
Curve *curve = static_cast<Curve *>(context.object->data);
|
||||||
|
|
||||||
std::vector<Imath::V3f> verts;
|
std::vector<Imath::V3f> verts;
|
||||||
std::vector<int32_t> vert_counts;
|
std::vector<int32_t> vert_counts;
|
||||||
@@ -152,35 +161,31 @@ void AbcCurveWriter::do_write()
|
|||||||
Alembic::AbcGeom::OFloatGeomParam::Sample width_sample;
|
Alembic::AbcGeom::OFloatGeomParam::Sample width_sample;
|
||||||
width_sample.setVals(widths);
|
width_sample.setVals(widths);
|
||||||
|
|
||||||
m_sample = OCurvesSchema::Sample(verts,
|
OCurvesSchema::Sample sample(verts,
|
||||||
vert_counts,
|
vert_counts,
|
||||||
curve_type,
|
curve_type,
|
||||||
periodicity,
|
periodicity,
|
||||||
width_sample,
|
width_sample,
|
||||||
OV2fGeomParam::Sample(), /* UVs */
|
OV2fGeomParam::Sample(), /* UVs */
|
||||||
ON3fGeomParam::Sample(), /* normals */
|
ON3fGeomParam::Sample(), /* normals */
|
||||||
curve_basis,
|
curve_basis,
|
||||||
weights,
|
weights,
|
||||||
orders,
|
orders,
|
||||||
knots);
|
knots);
|
||||||
|
|
||||||
m_sample.setSelfBounds(bounds());
|
update_bounding_box(context.object);
|
||||||
m_schema.set(m_sample);
|
sample.setSelfBounds(bounding_box_);
|
||||||
|
abc_curve_schema_.set(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
AbcCurveMeshWriter::AbcCurveMeshWriter(Object *ob,
|
ABCCurveMeshWriter::ABCCurveMeshWriter(const ABCWriterConstructorArgs &args)
|
||||||
AbcTransformWriter *parent,
|
: ABCGenericMeshWriter(args)
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings)
|
|
||||||
: AbcGenericMeshWriter(ob, parent, time_sampling, settings)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/,
|
Mesh *ABCCurveMeshWriter::get_export_mesh(Object *object_eval, bool &r_needsfree)
|
||||||
Object *ob_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) {
|
if (mesh_eval != NULL) {
|
||||||
/* Mesh_eval only exists when generative modifiers are in use. */
|
/* Mesh_eval only exists when generative modifiers are in use. */
|
||||||
r_needsfree = false;
|
r_needsfree = false;
|
||||||
@@ -188,7 +193,7 @@ Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/,
|
|||||||
}
|
}
|
||||||
|
|
||||||
r_needsfree = true;
|
r_needsfree = true;
|
||||||
return BKE_mesh_new_nomain_from_curve(ob_eval);
|
return BKE_mesh_new_nomain_from_curve(object_eval);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace alembic
|
} // namespace alembic
|
||||||
|
@@ -22,36 +22,38 @@
|
|||||||
* \ingroup balembic
|
* \ingroup balembic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "abc_writer_abstract.h"
|
||||||
#include "abc_writer_mesh.h"
|
#include "abc_writer_mesh.h"
|
||||||
#include "abc_writer_object.h"
|
|
||||||
|
#include <Alembic/AbcGeom/OCurves.h>
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
class AbcCurveWriter : public AbcObjectWriter {
|
extern const std::string ABC_CURVE_RESOLUTION_U_PROPNAME;
|
||||||
Alembic::AbcGeom::OCurvesSchema m_schema;
|
|
||||||
Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
|
class ABCCurveWriter : public ABCAbstractWriter {
|
||||||
|
private:
|
||||||
|
Alembic::AbcGeom::OCurves abc_curve_;
|
||||||
|
Alembic::AbcGeom::OCurvesSchema abc_curve_schema_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AbcCurveWriter(Object *ob,
|
explicit ABCCurveWriter(const ABCWriterConstructorArgs &args);
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
virtual void create_alembic_objects(const HierarchyContext *context) override;
|
||||||
ExportSettings &settings);
|
virtual const Alembic::Abc::OObject get_alembic_object() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void do_write();
|
virtual void do_write(HierarchyContext &context) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AbcCurveMeshWriter : public AbcGenericMeshWriter {
|
class ABCCurveMeshWriter : public ABCGenericMeshWriter {
|
||||||
public:
|
public:
|
||||||
AbcCurveMeshWriter(Object *ob,
|
ABCCurveMeshWriter(const ABCWriterConstructorArgs &args);
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings);
|
|
||||||
|
|
||||||
protected:
|
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
|
} // namespace alembic
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_hair.h"
|
#include "abc_writer_hair.h"
|
||||||
#include "abc_writer_transform.h"
|
|
||||||
#include "intern/abc_axis_conversion.h"
|
#include "intern/abc_axis_conversion.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -35,40 +34,46 @@
|
|||||||
#include "BKE_mesh_runtime.h"
|
#include "BKE_mesh_runtime.h"
|
||||||
#include "BKE_particle.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::OCurves;
|
||||||
using Alembic::AbcGeom::OCurvesSchema;
|
using Alembic::AbcGeom::OCurvesSchema;
|
||||||
using Alembic::AbcGeom::ON3fGeomParam;
|
using Alembic::AbcGeom::ON3fGeomParam;
|
||||||
using Alembic::AbcGeom::OV2fGeomParam;
|
using Alembic::AbcGeom::OV2fGeomParam;
|
||||||
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
AbcHairWriter::AbcHairWriter(Object *ob,
|
ABCHairWriter::ABCHairWriter(const ABCWriterConstructorArgs &args)
|
||||||
AbcTransformWriter *parent,
|
: ABCAbstractWriter(args), uv_warning_shown_(false)
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings,
|
|
||||||
ParticleSystem *psys)
|
|
||||||
: AbcObjectWriter(ob, time_sampling, settings, parent), m_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::do_write()
|
void ABCHairWriter::create_alembic_objects(const HierarchyContext * /*context*/)
|
||||||
{
|
{
|
||||||
if (!m_psys) {
|
CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
|
||||||
return;
|
abc_curves_ = OCurves(args_.abc_parent, args_.abc_name, timesample_index_);
|
||||||
}
|
abc_curves_schema_ = abc_curves_.getSchema();
|
||||||
Mesh *mesh = mesh_get_eval_final(
|
}
|
||||||
m_settings.depsgraph, m_settings.scene, m_object, &CD_MASK_MESH);
|
|
||||||
|
const Alembic::Abc::OObject ABCHairWriter::get_alembic_object() const
|
||||||
|
{
|
||||||
|
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);
|
BKE_mesh_tessface_ensure(mesh);
|
||||||
|
|
||||||
std::vector<Imath::V3f> verts;
|
std::vector<Imath::V3f> verts;
|
||||||
@@ -76,44 +81,45 @@ void AbcHairWriter::do_write()
|
|||||||
std::vector<Imath::V2f> uv_values;
|
std::vector<Imath::V2f> uv_values;
|
||||||
std::vector<Imath::V3f> norm_values;
|
std::vector<Imath::V3f> norm_values;
|
||||||
|
|
||||||
if (m_psys->pathcache) {
|
ParticleSystem *psys = context.particle_system;
|
||||||
ParticleSettings *part = m_psys->part;
|
if (psys->pathcache) {
|
||||||
bool export_children = m_settings.export_child_hairs && m_psys->childcache &&
|
ParticleSettings *part = psys->part;
|
||||||
part->childtype != 0;
|
bool export_children = psys->childcache && part->childtype != 0;
|
||||||
|
|
||||||
if (!export_children || part->draw & PART_DRAW_PARENT) {
|
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) {
|
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);
|
Alembic::Abc::P3fArraySample iPos(verts);
|
||||||
m_sample = OCurvesSchema::Sample(iPos, hvertices);
|
OCurvesSchema::Sample sample(iPos, hvertices);
|
||||||
m_sample.setBasis(Alembic::AbcGeom::kNoBasis);
|
sample.setBasis(Alembic::AbcGeom::kNoBasis);
|
||||||
m_sample.setType(Alembic::AbcGeom::kLinear);
|
sample.setType(Alembic::AbcGeom::kLinear);
|
||||||
m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic);
|
sample.setWrap(Alembic::AbcGeom::kNonPeriodic);
|
||||||
|
|
||||||
if (!uv_values.empty()) {
|
if (!uv_values.empty()) {
|
||||||
OV2fGeomParam::Sample uv_smp;
|
OV2fGeomParam::Sample uv_smp;
|
||||||
uv_smp.setVals(uv_values);
|
uv_smp.setVals(uv_values);
|
||||||
m_sample.setUVs(uv_smp);
|
sample.setUVs(uv_smp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!norm_values.empty()) {
|
if (!norm_values.empty()) {
|
||||||
ON3fGeomParam::Sample norm_smp;
|
ON3fGeomParam::Sample norm_smp;
|
||||||
norm_smp.setVals(norm_values);
|
norm_smp.setVals(norm_values);
|
||||||
m_sample.setNormals(norm_smp);
|
sample.setNormals(norm_smp);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sample.setSelfBounds(bounds());
|
update_bounding_box(context.object);
|
||||||
m_schema.set(m_sample);
|
sample.setSelfBounds(bounding_box_);
|
||||||
|
abc_curves_schema_.set(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbcHairWriter::write_hair_sample(Mesh *mesh,
|
void ABCHairWriter::write_hair_sample(const HierarchyContext &context,
|
||||||
ParticleSettings *part,
|
Mesh *mesh,
|
||||||
std::vector<Imath::V3f> &verts,
|
std::vector<Imath::V3f> &verts,
|
||||||
std::vector<Imath::V3f> &norm_values,
|
std::vector<Imath::V3f> &norm_values,
|
||||||
std::vector<Imath::V2f> &uv_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. */
|
/* Get untransformed vertices, there's a xform under the hair. */
|
||||||
float inv_mat[4][4];
|
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;
|
MTFace *mtface = mesh->mtface;
|
||||||
MFace *mface = mesh->mface;
|
MFace *mface = mesh->mface;
|
||||||
MVert *mverts = mesh->mvert;
|
MVert *mverts = mesh->mvert;
|
||||||
|
|
||||||
if ((!mtface || !mface) && !m_uv_warning_shown) {
|
if ((!mtface || !mface) && !uv_warning_shown_) {
|
||||||
std::fprintf(stderr,
|
std::fprintf(stderr,
|
||||||
"Warning, no UV set found for underlying geometry of %s.\n",
|
"Warning, no UV set found for underlying geometry of %s.\n",
|
||||||
m_object->id.name + 2);
|
context.object->id.name + 2);
|
||||||
m_uv_warning_shown = true;
|
uv_warning_shown_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ParticleData *pa = m_psys->particles;
|
ParticleSystem *psys = context.particle_system;
|
||||||
|
ParticleSettings *part = psys->part;
|
||||||
|
ParticleData *pa = psys->particles;
|
||||||
int k;
|
int k;
|
||||||
|
|
||||||
ParticleCacheKey **cache = m_psys->pathcache;
|
ParticleCacheKey **cache = psys->pathcache;
|
||||||
ParticleCacheKey *path;
|
ParticleCacheKey *path;
|
||||||
float normal[3];
|
float normal[3];
|
||||||
Imath::V3f tmp_nor;
|
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 */
|
/* underlying info for faces-only emission */
|
||||||
path = cache[p];
|
path = cache[p];
|
||||||
|
|
||||||
@@ -224,8 +232,8 @@ void AbcHairWriter::write_hair_sample(Mesh *mesh,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbcHairWriter::write_hair_child_sample(Mesh *mesh,
|
void ABCHairWriter::write_hair_child_sample(const HierarchyContext &context,
|
||||||
ParticleSettings *part,
|
Mesh *mesh,
|
||||||
std::vector<Imath::V3f> &verts,
|
std::vector<Imath::V3f> &verts,
|
||||||
std::vector<Imath::V3f> &norm_values,
|
std::vector<Imath::V3f> &norm_values,
|
||||||
std::vector<Imath::V2f> &uv_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. */
|
/* Get untransformed vertices, there's a xform under the hair. */
|
||||||
float inv_mat[4][4];
|
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;
|
MTFace *mtface = mesh->mtface;
|
||||||
MVert *mverts = mesh->mvert;
|
MVert *mverts = mesh->mvert;
|
||||||
|
|
||||||
ParticleCacheKey **cache = m_psys->childcache;
|
ParticleSystem *psys = context.particle_system;
|
||||||
|
ParticleSettings *part = psys->part;
|
||||||
|
ParticleCacheKey **cache = psys->childcache;
|
||||||
ParticleCacheKey *path;
|
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];
|
path = cache[p];
|
||||||
|
|
||||||
if (part->from == PART_FROM_FACE && part->childtype != PART_CHILD_PARTICLES && mtface) {
|
if (part->from == PART_FROM_FACE && part->childtype != PART_CHILD_PARTICLES && mtface) {
|
||||||
const int num = pc->num;
|
const int num = pc->num;
|
||||||
if (num < 0) {
|
if (num < 0) {
|
||||||
ABC_LOG(m_settings.logger)
|
CLOG_WARN(
|
||||||
<< "Warning, child particle of hair system " << m_psys->name
|
&LOG,
|
||||||
<< " has unknown face index of geometry of " << (m_object->id.name + 2)
|
"Child particle of hair system %s has unknown face index of geometry of %s, skipping "
|
||||||
<< ", skipping child hair." << std::endl;
|
"child hair.",
|
||||||
|
psys->name,
|
||||||
|
context.object->id.name + 2);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,9 +19,10 @@
|
|||||||
* \ingroup balembic
|
* \ingroup balembic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_object.h"
|
#include "abc_writer_abstract.h"
|
||||||
|
#include <Alembic/AbcGeom/OCurves.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
struct Mesh;
|
|
||||||
struct ParticleSettings;
|
struct ParticleSettings;
|
||||||
struct ParticleSystem;
|
struct ParticleSystem;
|
||||||
|
|
||||||
@@ -29,33 +30,33 @@ namespace blender {
|
|||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
class AbcHairWriter : public AbcObjectWriter {
|
class ABCHairWriter : public ABCAbstractWriter {
|
||||||
ParticleSystem *m_psys;
|
private:
|
||||||
|
Alembic::AbcGeom::OCurves abc_curves_;
|
||||||
|
Alembic::AbcGeom::OCurvesSchema abc_curves_schema_;
|
||||||
|
|
||||||
Alembic::AbcGeom::OCurvesSchema m_schema;
|
bool uv_warning_shown_;
|
||||||
Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
|
|
||||||
|
|
||||||
bool m_uv_warning_shown;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AbcHairWriter(Object *ob,
|
explicit ABCHairWriter(const ABCWriterConstructorArgs &args);
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
virtual void create_alembic_objects(const HierarchyContext *context) override;
|
||||||
ExportSettings &settings,
|
virtual const Alembic::Abc::OObject get_alembic_object() const override;
|
||||||
ParticleSystem *psys);
|
|
||||||
|
protected:
|
||||||
|
virtual void do_write(HierarchyContext &context) override;
|
||||||
|
virtual bool check_is_animated(const HierarchyContext &context) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void do_write();
|
void write_hair_sample(const HierarchyContext &context,
|
||||||
|
struct Mesh *mesh,
|
||||||
void write_hair_sample(struct Mesh *mesh,
|
|
||||||
ParticleSettings *part,
|
|
||||||
std::vector<Imath::V3f> &verts,
|
std::vector<Imath::V3f> &verts,
|
||||||
std::vector<Imath::V3f> &norm_values,
|
std::vector<Imath::V3f> &norm_values,
|
||||||
std::vector<Imath::V2f> &uv_values,
|
std::vector<Imath::V2f> &uv_values,
|
||||||
std::vector<int32_t> &hvertices);
|
std::vector<int32_t> &hvertices);
|
||||||
|
|
||||||
void write_hair_child_sample(struct Mesh *mesh,
|
void write_hair_child_sample(const HierarchyContext &context,
|
||||||
ParticleSettings *part,
|
struct Mesh *mesh,
|
||||||
std::vector<Imath::V3f> &verts,
|
std::vector<Imath::V3f> &verts,
|
||||||
std::vector<Imath::V3f> &norm_values,
|
std::vector<Imath::V3f> &norm_values,
|
||||||
std::vector<Imath::V2f> &uv_values,
|
std::vector<Imath::V2f> &uv_values,
|
||||||
|
@@ -19,11 +19,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_mball.h"
|
#include "abc_writer_mball.h"
|
||||||
#include "abc_writer_mesh.h"
|
#include "abc_hierarchy_iterator.h"
|
||||||
|
|
||||||
#include "DNA_mesh_types.h"
|
#include "BLI_assert.h"
|
||||||
#include "DNA_meta_types.h"
|
|
||||||
#include "DNA_object_types.h"
|
|
||||||
|
|
||||||
#include "BKE_displist.h"
|
#include "BKE_displist.h"
|
||||||
#include "BKE_lib_id.h"
|
#include "BKE_lib_id.h"
|
||||||
@@ -31,68 +29,57 @@
|
|||||||
#include "BKE_mesh.h"
|
#include "BKE_mesh.h"
|
||||||
#include "BKE_object.h"
|
#include "BKE_object.h"
|
||||||
|
|
||||||
#include "BLI_utildefines.h"
|
#include "DNA_mesh_types.h"
|
||||||
|
#include "DNA_meta_types.h"
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
AbcMBallWriter::AbcMBallWriter(Main *bmain,
|
ABCMetaballWriter::ABCMetaballWriter(const ABCWriterConstructorArgs &args)
|
||||||
Object *ob,
|
: ABCGenericMeshWriter(args)
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings)
|
|
||||||
: AbcGenericMeshWriter(ob, parent, time_sampling, settings), m_bmain(bmain)
|
|
||||||
{
|
|
||||||
m_is_animated = isAnimated();
|
|
||||||
}
|
|
||||||
|
|
||||||
AbcMBallWriter::~AbcMBallWriter()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AbcMBallWriter::isAnimated() const
|
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 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;
|
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);
|
/* Metaballs should be exported to subdivision surfaces, if the export options allow. */
|
||||||
if (mesh_eval != NULL) {
|
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. */
|
/* Mesh_eval only exists when generative modifiers are in use. */
|
||||||
r_needsfree = false;
|
r_needsfree = false;
|
||||||
return mesh_eval;
|
return mesh_eval;
|
||||||
}
|
}
|
||||||
r_needsfree = true;
|
r_needsfree = true;
|
||||||
|
return BKE_mesh_new_from_object(args_.depsgraph, object_eval, false);
|
||||||
/* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
Object *basis_ob = BKE_mball_basis_find(scene, ob);
|
||||||
return ob == basis_ob;
|
return ob == basis_ob;
|
||||||
|
@@ -20,39 +20,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_mesh.h"
|
#include "abc_writer_mesh.h"
|
||||||
#include "abc_writer_object.h"
|
|
||||||
|
|
||||||
struct Main;
|
|
||||||
struct Object;
|
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
/* AbcMBallWriter converts the metaballs to meshes at every frame,
|
class ABCMetaballWriter : public ABCGenericMeshWriter {
|
||||||
* 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;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AbcMBallWriter(Main *bmain,
|
explicit ABCMetaballWriter(const ABCWriterConstructorArgs &args);
|
||||||
Object *ob,
|
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings);
|
|
||||||
|
|
||||||
~AbcMBallWriter();
|
|
||||||
|
|
||||||
static bool isBasisBall(Scene *scene, Object *ob);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override;
|
virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override;
|
||||||
void freeEvaluatedMesh(struct Mesh *mesh) 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:
|
private:
|
||||||
bool isAnimated() const override;
|
bool is_basis_ball(Scene *scene, Object *ob) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace alembic
|
} // namespace alembic
|
||||||
|
@@ -19,29 +19,37 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_mesh.h"
|
#include "abc_writer_mesh.h"
|
||||||
#include "abc_writer_transform.h"
|
#include "abc_hierarchy_iterator.h"
|
||||||
#include "intern/abc_axis_conversion.h"
|
#include "intern/abc_axis_conversion.h"
|
||||||
|
|
||||||
#include "DNA_material_types.h"
|
#include "BLI_assert.h"
|
||||||
#include "DNA_mesh_types.h"
|
#include "BLI_math_vector.h"
|
||||||
#include "DNA_meshdata_types.h"
|
|
||||||
#include "DNA_object_fluidsim_types.h"
|
|
||||||
|
|
||||||
#include "BKE_anim_data.h"
|
#include "BKE_customdata.h"
|
||||||
#include "BKE_key.h"
|
|
||||||
#include "BKE_lib_id.h"
|
#include "BKE_lib_id.h"
|
||||||
#include "BKE_material.h"
|
#include "BKE_material.h"
|
||||||
#include "BKE_mesh.h"
|
#include "BKE_mesh.h"
|
||||||
#include "BKE_mesh_runtime.h"
|
|
||||||
#include "BKE_modifier.h"
|
#include "BKE_modifier.h"
|
||||||
|
#include "BKE_object.h"
|
||||||
|
|
||||||
#include "bmesh.h"
|
#include "bmesh.h"
|
||||||
#include "bmesh_tools.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::FloatArraySample;
|
||||||
using Alembic::Abc::Int32ArraySample;
|
using Alembic::Abc::Int32ArraySample;
|
||||||
|
using Alembic::Abc::OObject;
|
||||||
using Alembic::Abc::V2fArraySample;
|
using Alembic::Abc::V2fArraySample;
|
||||||
using Alembic::Abc::V3fArraySample;
|
using Alembic::Abc::V3fArraySample;
|
||||||
|
|
||||||
@@ -64,6 +72,394 @@ namespace alembic {
|
|||||||
|
|
||||||
/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
|
/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
|
||||||
|
|
||||||
|
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);
|
||||||
|
static void get_creases(struct Mesh *mesh,
|
||||||
|
std::vector<int32_t> &indices,
|
||||||
|
std::vector<int32_t> &lengths,
|
||||||
|
std::vector<float> &sharpnesses);
|
||||||
|
static void get_loop_normals(struct Mesh *mesh,
|
||||||
|
std::vector<Imath::V3f> &normals,
|
||||||
|
bool has_flat_shaded_poly);
|
||||||
|
|
||||||
|
ABCGenericMeshWriter::ABCGenericMeshWriter(const ABCWriterConstructorArgs &args)
|
||||||
|
: ABCAbstractWriter(args), is_subd_(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
OCompoundProperty typeContainer = abc_poly_mesh_.getSchema().getUserProperties();
|
||||||
|
OBoolProperty type(typeContainer, "meshtype");
|
||||||
|
type.set(subsurf_modifier_ == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene *scene_eval = DEG_get_evaluated_scene(args_.depsgraph);
|
||||||
|
liquid_sim_modifier_ = get_liquid_sim_modifier(scene_eval, context->object);
|
||||||
|
}
|
||||||
|
|
||||||
|
ABCGenericMeshWriter::~ABCGenericMeshWriter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const Alembic::Abc::OObject ABCGenericMeshWriter::get_alembic_object() const
|
||||||
|
{
|
||||||
|
if (is_subd_) {
|
||||||
|
return abc_subdiv_;
|
||||||
|
}
|
||||||
|
return abc_poly_mesh_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ABCGenericMeshWriter::export_as_subdivision_surface(Object *ob_eval) const
|
||||||
|
{
|
||||||
|
ModifierData *md = static_cast<ModifierData *>(ob_eval->modifiers.last);
|
||||||
|
|
||||||
|
for (; md; md = md->prev) {
|
||||||
|
/* 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 false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModifierData *ABCGenericMeshWriter::get_liquid_sim_modifier(Scene *scene, Object *ob)
|
||||||
|
{
|
||||||
|
ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluidsim);
|
||||||
|
|
||||||
|
if (md && (BKE_modifier_is_enabled(scene, md, eModifierMode_Render))) {
|
||||||
|
FluidsimModifierData *fsmd = reinterpret_cast<FluidsimModifierData *>(md);
|
||||||
|
|
||||||
|
if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) {
|
||||||
|
return md;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ABCGenericMeshWriter::is_supported(const HierarchyContext *context) const
|
||||||
|
{
|
||||||
|
Object *object = context->object;
|
||||||
|
bool is_dupli = context->duplicator != nullptr;
|
||||||
|
int base_flag;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int visibility = BKE_object_visibility(
|
||||||
|
object, DAG_EVAL_RENDER /* TODO(Sybren): add evaluation mode to export options? */);
|
||||||
|
|
||||||
|
if (is_dupli) {
|
||||||
|
object->base_flag = base_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (visibility & OB_VISIBLE_SELF) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABCGenericMeshWriter::do_write(HierarchyContext &context)
|
||||||
|
{
|
||||||
|
Object *object = context.object;
|
||||||
|
bool needsfree = false;
|
||||||
|
|
||||||
|
Mesh *mesh = get_export_mesh(object, needsfree);
|
||||||
|
|
||||||
|
if (mesh == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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, nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
|
Mesh *triangulated_mesh = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
|
||||||
|
BM_mesh_free(bm);
|
||||||
|
|
||||||
|
if (needsfree) {
|
||||||
|
free_export_mesh(mesh);
|
||||||
|
}
|
||||||
|
mesh = triangulated_mesh;
|
||||||
|
needsfree = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (is_subd_) {
|
||||||
|
write_subd(context, mesh);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
write_mesh(context, mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsfree) {
|
||||||
|
free_export_mesh(mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
if (needsfree) {
|
||||||
|
free_export_mesh(mesh);
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABCGenericMeshWriter::free_export_mesh(Mesh *mesh)
|
||||||
|
{
|
||||||
|
BKE_id_free(nullptr, mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
|
||||||
|
{
|
||||||
|
std::vector<Imath::V3f> points, normals;
|
||||||
|
std::vector<int32_t> poly_verts, loop_counts;
|
||||||
|
std::vector<Imath::V3f> velocities;
|
||||||
|
bool has_flat_shaded_poly = false;
|
||||||
|
|
||||||
|
get_vertices(mesh, points);
|
||||||
|
get_topology(mesh, poly_verts, loop_counts, has_flat_shaded_poly);
|
||||||
|
|
||||||
|
if (!frame_has_been_written_ && args_.export_params->face_sets) {
|
||||||
|
write_face_sets(context.object, mesh, abc_poly_mesh_schema_);
|
||||||
|
}
|
||||||
|
|
||||||
|
OPolyMeshSchema::Sample mesh_sample = OPolyMeshSchema::Sample(
|
||||||
|
V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts));
|
||||||
|
|
||||||
|
UVSample uvs_and_indices;
|
||||||
|
|
||||||
|
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(uvs_and_indices.uvs));
|
||||||
|
uv_sample.setIndices(UInt32ArraySample(uvs_and_indices.indices));
|
||||||
|
uv_sample.setScope(kFacevaryingScope);
|
||||||
|
|
||||||
|
abc_poly_mesh_schema_.setUVSourceName(name);
|
||||||
|
mesh_sample.setUVs(uv_sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_custom_data(
|
||||||
|
abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args_.export_params->normals) {
|
||||||
|
get_loop_normals(mesh, normals, has_flat_shaded_poly);
|
||||||
|
|
||||||
|
ON3fGeomParam::Sample normals_sample;
|
||||||
|
if (!normals.empty()) {
|
||||||
|
normals_sample.setScope(kFacevaryingScope);
|
||||||
|
normals_sample.setVals(V3fArraySample(normals));
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh_sample.setNormals(normals_sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (liquid_sim_modifier_ != nullptr) {
|
||||||
|
get_velocities(mesh, velocities);
|
||||||
|
mesh_sample.setVelocities(V3fArraySample(velocities));
|
||||||
|
}
|
||||||
|
|
||||||
|
update_bounding_box(context.object);
|
||||||
|
mesh_sample.setSelfBounds(bounding_box_);
|
||||||
|
|
||||||
|
abc_poly_mesh_schema_.set(mesh_sample);
|
||||||
|
|
||||||
|
write_arb_geo_params(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *mesh)
|
||||||
|
{
|
||||||
|
std::vector<float> crease_sharpness;
|
||||||
|
std::vector<Imath::V3f> points;
|
||||||
|
std::vector<int32_t> poly_verts, loop_counts;
|
||||||
|
std::vector<int32_t> crease_indices, crease_lengths;
|
||||||
|
bool has_flat_poly = false;
|
||||||
|
|
||||||
|
get_vertices(mesh, points);
|
||||||
|
get_topology(mesh, poly_verts, loop_counts, has_flat_poly);
|
||||||
|
get_creases(mesh, crease_indices, crease_lengths, crease_sharpness);
|
||||||
|
|
||||||
|
if (!frame_has_been_written_ && args_.export_params->face_sets) {
|
||||||
|
write_face_sets(context.object, mesh, abc_subdiv_schema_);
|
||||||
|
}
|
||||||
|
|
||||||
|
OSubDSchema::Sample subdiv_sample = OSubDSchema::Sample(
|
||||||
|
V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts));
|
||||||
|
|
||||||
|
UVSample sample;
|
||||||
|
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()) {
|
||||||
|
OV2fGeomParam::Sample uv_sample;
|
||||||
|
uv_sample.setVals(V2fArraySample(sample.uvs));
|
||||||
|
uv_sample.setIndices(UInt32ArraySample(sample.indices));
|
||||||
|
uv_sample.setScope(kFacevaryingScope);
|
||||||
|
|
||||||
|
abc_subdiv_schema_.setUVSourceName(name);
|
||||||
|
subdiv_sample.setUVs(uv_sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_custom_data(
|
||||||
|
abc_subdiv_schema_.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!crease_indices.empty()) {
|
||||||
|
subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices));
|
||||||
|
subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths));
|
||||||
|
subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness));
|
||||||
|
}
|
||||||
|
|
||||||
|
update_bounding_box(context.object);
|
||||||
|
subdiv_sample.setSelfBounds(bounding_box_);
|
||||||
|
abc_subdiv_schema_.set(subdiv_sample);
|
||||||
|
|
||||||
|
write_arb_geo_params(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
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) {
|
||||||
|
OFaceSet face_set = schema.createFaceSet(it->first);
|
||||||
|
OFaceSetSchema::Sample samp;
|
||||||
|
samp.setFaces(Int32ArraySample(it->second));
|
||||||
|
face_set.getSchema().set(samp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABCGenericMeshWriter::write_arb_geo_params(struct Mesh *me)
|
||||||
|
{
|
||||||
|
if (liquid_sim_modifier_ != nullptr) {
|
||||||
|
/* We don't need anything more for liquid meshes. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame_has_been_written_ || !args_.export_params->vcolors) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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::get_velocities(struct Mesh *mesh, std::vector<Imath::V3f> &vels)
|
||||||
|
{
|
||||||
|
const int totverts = mesh->totvert;
|
||||||
|
|
||||||
|
vels.clear();
|
||||||
|
vels.resize(totverts);
|
||||||
|
|
||||||
|
FluidsimModifierData *fmd = reinterpret_cast<FluidsimModifierData *>(liquid_sim_modifier_);
|
||||||
|
FluidsimSettings *fss = fmd->fss;
|
||||||
|
|
||||||
|
if (fss->meshVelocities) {
|
||||||
|
float *mesh_vels = reinterpret_cast<float *>(fss->meshVelocities);
|
||||||
|
|
||||||
|
for (int i = 0; i < totverts; i++) {
|
||||||
|
copy_yup_from_zup(vels[i].getValue(), mesh_vels);
|
||||||
|
mesh_vels += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
for (int i = 0; i < num_poly; i++) {
|
||||||
|
MPoly ¤t_poly = polygons[i];
|
||||||
|
short mnr = current_poly.mat_nr;
|
||||||
|
|
||||||
|
Material *mat = BKE_object_material_get(object, mnr + 1);
|
||||||
|
|
||||||
|
if (!mat) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name = args_.hierarchy_iterator->get_id_name(&mat->id);
|
||||||
|
|
||||||
|
if (geo_groups.find(name) == geo_groups.end()) {
|
||||||
|
std::vector<int32_t> faceArray;
|
||||||
|
geo_groups[name] = faceArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
geo_groups[name].push_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (geo_groups.size() == 0) {
|
||||||
|
Material *mat = BKE_object_material_get(object, 1);
|
||||||
|
|
||||||
|
std::string name = (mat) ? args_.hierarchy_iterator->get_id_name(&mat->id) : "default";
|
||||||
|
|
||||||
|
std::vector<int32_t> faceArray;
|
||||||
|
|
||||||
|
for (int i = 0, e = mesh->totface; i < e; i++) {
|
||||||
|
faceArray.push_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
geo_groups[name] = faceArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
|
||||||
|
|
||||||
static void get_vertices(struct Mesh *mesh, std::vector<Imath::V3f> &points)
|
static void get_vertices(struct Mesh *mesh, std::vector<Imath::V3f> &points)
|
||||||
{
|
{
|
||||||
points.clear();
|
points.clear();
|
||||||
@@ -148,7 +544,7 @@ static void get_loop_normals(struct Mesh *mesh,
|
|||||||
|
|
||||||
BKE_mesh_calc_normals_split(mesh);
|
BKE_mesh_calc_normals_split(mesh);
|
||||||
const float(*lnors)[3] = static_cast<float(*)[3]>(CustomData_get_layer(&mesh->ldata, CD_NORMAL));
|
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");
|
BLI_assert(lnors != nullptr || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL");
|
||||||
|
|
||||||
normals.resize(mesh->totloop);
|
normals.resize(mesh->totloop);
|
||||||
|
|
||||||
@@ -163,435 +559,13 @@ static void get_loop_normals(struct Mesh *mesh,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* *************** Modifiers *************** */
|
ABCMeshWriter::ABCMeshWriter(const ABCWriterConstructorArgs &args) : ABCGenericMeshWriter(args)
|
||||||
|
|
||||||
/* 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)
|
|
||||||
{
|
|
||||||
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 NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob)
|
|
||||||
{
|
|
||||||
ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluidsim);
|
|
||||||
|
|
||||||
if (md && (BKE_modifier_is_enabled(scene, md, eModifierMode_Render))) {
|
|
||||||
FluidsimModifierData *fsmd = reinterpret_cast<FluidsimModifierData *>(md);
|
|
||||||
|
|
||||||
if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) {
|
|
||||||
return md;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
AbcGenericMeshWriter::AbcGenericMeshWriter(Object *ob,
|
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings)
|
|
||||||
: AbcObjectWriter(ob, time_sampling, settings, parent)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_settings.apply_subdiv) {
|
|
||||||
m_subsurf_mod = get_subsurf_modifier(m_settings.scene, m_object);
|
|
||||||
m_is_subd = (m_subsurf_mod != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_is_liquid = (get_liquid_sim_modifier(m_settings.scene, m_object) != NULL);
|
|
||||||
|
|
||||||
while (parent->alembicXform().getChildHeader(m_name)) {
|
|
||||||
m_name.append("_");
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AbcGenericMeshWriter::~AbcGenericMeshWriter()
|
|
||||||
{
|
|
||||||
if (m_subsurf_mod) {
|
|
||||||
m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Test modifiers. */
|
|
||||||
ModifierData *md = static_cast<ModifierData *>(m_object->modifiers.first);
|
|
||||||
while (md) {
|
|
||||||
|
|
||||||
if (md->type != eModifierType_Subsurf) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
md = md->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbcGenericMeshWriter::setIsAnimated(bool is_animated)
|
|
||||||
{
|
|
||||||
m_is_animated = is_animated;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbcGenericMeshWriter::do_write()
|
|
||||||
{
|
|
||||||
/* We have already stored a sample for this object. */
|
|
||||||
if (!m_first_frame && !m_is_animated) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool needsfree;
|
|
||||||
struct Mesh *mesh = getFinalMesh(needsfree);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) {
|
|
||||||
writeSubD(mesh);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
writeMesh(mesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsfree) {
|
|
||||||
freeEvaluatedMesh(mesh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
if (needsfree) {
|
|
||||||
freeEvaluatedMesh(mesh);
|
|
||||||
}
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbcGenericMeshWriter::freeEvaluatedMesh(struct Mesh *mesh)
|
|
||||||
{
|
|
||||||
BKE_id_free(NULL, mesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh)
|
|
||||||
{
|
|
||||||
std::vector<Imath::V3f> points, normals;
|
|
||||||
std::vector<int32_t> poly_verts, loop_counts;
|
|
||||||
std::vector<Imath::V3f> velocities;
|
|
||||||
bool has_flat_shaded_poly = false;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_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);
|
|
||||||
|
|
||||||
if (!sample.indices.empty() && !sample.uvs.empty()) {
|
|
||||||
OV2fGeomParam::Sample uv_sample;
|
|
||||||
uv_sample.setVals(V2fArraySample(sample.uvs));
|
|
||||||
uv_sample.setIndices(UInt32ArraySample(sample.indices));
|
|
||||||
uv_sample.setScope(kFacevaryingScope);
|
|
||||||
|
|
||||||
m_mesh_schema.setUVSourceName(name);
|
|
||||||
m_mesh_sample.setUVs(uv_sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
write_custom_data(
|
|
||||||
m_mesh_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_settings.export_normals) {
|
|
||||||
get_loop_normals(mesh, normals, has_flat_shaded_poly);
|
|
||||||
|
|
||||||
ON3fGeomParam::Sample normals_sample;
|
|
||||||
if (!normals.empty()) {
|
|
||||||
normals_sample.setScope(kFacevaryingScope);
|
|
||||||
normals_sample.setVals(V3fArraySample(normals));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_mesh_sample.setNormals(normals_sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_is_liquid) {
|
|
||||||
getVelocities(mesh, velocities);
|
|
||||||
m_mesh_sample.setVelocities(V3fArraySample(velocities));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_mesh_sample.setSelfBounds(bounds());
|
|
||||||
|
|
||||||
m_mesh_schema.set(m_mesh_sample);
|
|
||||||
|
|
||||||
writeArbGeoParams(mesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh)
|
|
||||||
{
|
|
||||||
std::vector<float> crease_sharpness;
|
|
||||||
std::vector<Imath::V3f> points;
|
|
||||||
std::vector<int32_t> poly_verts, loop_counts;
|
|
||||||
std::vector<int32_t> crease_indices, crease_lengths;
|
|
||||||
bool has_flat_poly = false;
|
|
||||||
|
|
||||||
get_vertices(mesh, points);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_subdiv_sample = OSubDSchema::Sample(
|
|
||||||
V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts));
|
|
||||||
|
|
||||||
UVSample sample;
|
|
||||||
if (m_first_frame && m_settings.export_uvs) {
|
|
||||||
const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata);
|
|
||||||
|
|
||||||
if (!sample.indices.empty() && !sample.uvs.empty()) {
|
|
||||||
OV2fGeomParam::Sample uv_sample;
|
|
||||||
uv_sample.setVals(V2fArraySample(sample.uvs));
|
|
||||||
uv_sample.setIndices(UInt32ArraySample(sample.indices));
|
|
||||||
uv_sample.setScope(kFacevaryingScope);
|
|
||||||
|
|
||||||
m_subdiv_schema.setUVSourceName(name);
|
|
||||||
m_subdiv_sample.setUVs(uv_sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
write_custom_data(
|
|
||||||
m_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));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_subdiv_sample.setSelfBounds(bounds());
|
|
||||||
m_subdiv_schema.set(m_subdiv_sample);
|
|
||||||
|
|
||||||
writeArbGeoParams(mesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Schema> void AbcGenericMeshWriter::writeFaceSets(struct Mesh *me, Schema &schema)
|
|
||||||
{
|
|
||||||
std::map<std::string, std::vector<int32_t>> geo_groups;
|
|
||||||
getGeoGroups(me, geo_groups);
|
|
||||||
|
|
||||||
std::map<std::string, std::vector<int32_t>>::iterator it;
|
|
||||||
for (it = geo_groups.begin(); it != geo_groups.end(); ++it) {
|
|
||||||
OFaceSet face_set = schema.createFaceSet(it->first);
|
|
||||||
OFaceSetSchema::Sample samp;
|
|
||||||
samp.setFaces(Int32ArraySample(it->second));
|
|
||||||
face_set.getSchema().set(samp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Mesh *AbcGenericMeshWriter::getFinalMesh(bool &r_needsfree)
|
|
||||||
{
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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. */
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbcGenericMeshWriter::getVelocities(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);
|
|
||||||
FluidsimSettings *fss = fmd->fss;
|
|
||||||
|
|
||||||
if (fss->meshVelocities) {
|
|
||||||
float *mesh_vels = reinterpret_cast<float *>(fss->meshVelocities);
|
|
||||||
|
|
||||||
for (int i = 0; i < totverts; i++) {
|
|
||||||
copy_yup_from_zup(vels[i].getValue(), mesh_vels);
|
|
||||||
mesh_vels += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh,
|
|
||||||
std::map<std::string, std::vector<int32_t>> &geo_groups)
|
|
||||||
{
|
|
||||||
const int num_poly = mesh->totpoly;
|
|
||||||
MPoly *polygons = mesh->mpoly;
|
|
||||||
|
|
||||||
for (int i = 0; i < num_poly; i++) {
|
|
||||||
MPoly ¤t_poly = polygons[i];
|
|
||||||
short mnr = current_poly.mat_nr;
|
|
||||||
|
|
||||||
Material *mat = BKE_object_material_get(m_object, mnr + 1);
|
|
||||||
|
|
||||||
if (!mat) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name = get_id_name(&mat->id);
|
|
||||||
|
|
||||||
if (geo_groups.find(name) == geo_groups.end()) {
|
|
||||||
std::vector<int32_t> faceArray;
|
|
||||||
geo_groups[name] = faceArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
geo_groups[name].push_back(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (geo_groups.size() == 0) {
|
|
||||||
Material *mat = BKE_object_material_get(m_object, 1);
|
|
||||||
|
|
||||||
std::string name = (mat) ? get_id_name(&mat->id) : "default";
|
|
||||||
|
|
||||||
std::vector<int32_t> faceArray;
|
|
||||||
|
|
||||||
for (int i = 0, e = mesh->totface; i < e; i++) {
|
|
||||||
faceArray.push_back(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
geo_groups[name] = faceArray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AbcMeshWriter::AbcMeshWriter(Object *ob,
|
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings)
|
|
||||||
: AbcGenericMeshWriter(ob, parent, time_sampling, settings)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
AbcMeshWriter::~AbcMeshWriter()
|
Mesh *ABCMeshWriter::get_export_mesh(Object *object_eval, bool & /*r_needsfree*/)
|
||||||
{
|
{
|
||||||
}
|
return BKE_object_get_evaluated_mesh(object_eval);
|
||||||
|
|
||||||
Mesh *AbcMeshWriter::getEvaluatedMesh(Scene *scene_eval,
|
|
||||||
Object *ob_eval,
|
|
||||||
bool &UNUSED(r_needsfree))
|
|
||||||
{
|
|
||||||
return mesh_get_eval_final(m_settings.depsgraph, scene_eval, ob_eval, &CD_MASK_MESH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace alembic
|
} // namespace alembic
|
||||||
|
@@ -19,75 +19,75 @@
|
|||||||
* \ingroup balembic
|
* \ingroup balembic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_object.h"
|
#include "abc_writer_abstract.h"
|
||||||
#include "intern/abc_customdata.h"
|
#include "intern/abc_customdata.h"
|
||||||
|
|
||||||
struct Mesh;
|
#include <Alembic/AbcGeom/OPolyMesh.h>
|
||||||
|
#include <Alembic/AbcGeom/OSubD.h>
|
||||||
|
|
||||||
struct ModifierData;
|
struct ModifierData;
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
/* Writer for Alembic meshes. Does not assume the object is a mesh object. */
|
/* Writer for Alembic geometry. Does not assume the object is a mesh object. */
|
||||||
class AbcGenericMeshWriter : public AbcObjectWriter {
|
class ABCGenericMeshWriter : public ABCAbstractWriter {
|
||||||
protected:
|
private:
|
||||||
Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema;
|
/* Either polymesh or subd is used, depending on is_subd_.
|
||||||
Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample;
|
* 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::AbcGeom::OSubDSchema m_subdiv_schema;
|
Alembic::AbcGeom::OSubD abc_subdiv_;
|
||||||
Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample;
|
Alembic::AbcGeom::OSubDSchema abc_subdiv_schema_;
|
||||||
|
|
||||||
Alembic::Abc::OArrayProperty m_mat_indices;
|
/* 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
|
||||||
bool m_is_animated;
|
* exported object. */
|
||||||
ModifierData *m_subsurf_mod;
|
bool is_subd_;
|
||||||
|
ModifierData *subsurf_modifier_;
|
||||||
|
ModifierData *liquid_sim_modifier_;
|
||||||
|
|
||||||
CDStreamConfig m_custom_data_config;
|
CDStreamConfig m_custom_data_config;
|
||||||
|
|
||||||
bool m_is_liquid;
|
|
||||||
bool m_is_subd;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AbcGenericMeshWriter(Object *ob,
|
explicit ABCGenericMeshWriter(const ABCWriterConstructorArgs &args);
|
||||||
AbcTransformWriter *parent,
|
virtual ~ABCGenericMeshWriter();
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings);
|
|
||||||
|
|
||||||
~AbcGenericMeshWriter();
|
virtual void create_alembic_objects(const HierarchyContext *context) override;
|
||||||
void setIsAnimated(bool is_animated);
|
virtual const Alembic::Abc::OObject get_alembic_object() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void do_write();
|
virtual bool is_supported(const HierarchyContext *context) const override;
|
||||||
virtual bool isAnimated() const;
|
virtual void do_write(HierarchyContext &context) override;
|
||||||
virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) = 0;
|
|
||||||
virtual void freeEvaluatedMesh(struct Mesh *mesh);
|
|
||||||
|
|
||||||
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);
|
virtual bool export_as_subdivision_surface(Object *ob_eval) const;
|
||||||
void writeSubD(struct Mesh *mesh);
|
|
||||||
|
|
||||||
void writeArbGeoParams(struct Mesh *mesh);
|
private:
|
||||||
void getGeoGroups(struct Mesh *mesh, std::map<std::string, std::vector<int32_t>> &geoGroups);
|
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 */
|
ModifierData *get_liquid_sim_modifier(Scene *scene_eval, Object *ob_eval);
|
||||||
void getVelocities(struct Mesh *mesh, std::vector<Imath::V3f> &vels);
|
|
||||||
|
|
||||||
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:
|
public:
|
||||||
AbcMeshWriter(Object *ob,
|
ABCMeshWriter(const ABCWriterConstructorArgs &args);
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings);
|
|
||||||
|
|
||||||
~AbcMeshWriter();
|
|
||||||
|
|
||||||
protected:
|
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
|
} // namespace alembic
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_nurbs.h"
|
#include "abc_writer_nurbs.h"
|
||||||
#include "abc_writer_transform.h"
|
|
||||||
#include "intern/abc_axis_conversion.h"
|
#include "intern/abc_axis_conversion.h"
|
||||||
|
|
||||||
#include "DNA_curve_types.h"
|
#include "DNA_curve_types.h"
|
||||||
@@ -29,52 +28,70 @@
|
|||||||
|
|
||||||
#include "BKE_curve.h"
|
#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::FloatArraySample;
|
||||||
using Alembic::AbcGeom::OBoolProperty;
|
using Alembic::AbcGeom::OBoolProperty;
|
||||||
using Alembic::AbcGeom::OCompoundProperty;
|
using Alembic::AbcGeom::OCompoundProperty;
|
||||||
using Alembic::AbcGeom::ONuPatch;
|
using Alembic::AbcGeom::ONuPatch;
|
||||||
using Alembic::AbcGeom::ONuPatchSchema;
|
using Alembic::AbcGeom::ONuPatchSchema;
|
||||||
|
|
||||||
namespace blender {
|
ABCNurbsWriter::ABCNurbsWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
|
||||||
namespace io {
|
|
||||||
namespace alembic {
|
|
||||||
|
|
||||||
AbcNurbsWriter::AbcNurbsWriter(Object *ob,
|
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings)
|
|
||||||
: AbcObjectWriter(ob, time_sampling, settings, parent)
|
|
||||||
{
|
{
|
||||||
m_is_animated = isAnimated();
|
}
|
||||||
|
|
||||||
/* if the object is static, use the default static time sampling */
|
void ABCNurbsWriter::create_alembic_objects(const HierarchyContext *context)
|
||||||
if (!m_is_animated) {
|
{
|
||||||
m_time_sampling = 0;
|
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();
|
||||||
|
|
||||||
Curve *curve = static_cast<Curve *>(m_object->data);
|
for (size_t i = 0; i < num_nurbs; i++) {
|
||||||
size_t numNurbs = BLI_listbase_count(&curve->nurb);
|
std::stringstream patch_name_stream;
|
||||||
|
patch_name_stream << args_.abc_name << '_' << i;
|
||||||
|
|
||||||
for (size_t i = 0; i < numNurbs; i++) {
|
while (abc_parent.getChildHeader(patch_name_stream.str())) {
|
||||||
std::stringstream str;
|
patch_name_stream << "_";
|
||||||
str << m_name << '_' << i;
|
|
||||||
|
|
||||||
while (parent->alembicXform().getChildHeader(str.str())) {
|
|
||||||
str << "_";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling);
|
std::string patch_name = patch_name_stream.str();
|
||||||
m_nurbs_schema.push_back(nurbs.getSchema());
|
CLOG_INFO(&LOG, 2, "exporting %s/%s", abc_parent_path, patch_name.c_str());
|
||||||
|
|
||||||
|
ONuPatch nurbs(abc_parent, patch_name.c_str(), timesample_index_);
|
||||||
|
abc_nurbs_.push_back(nurbs);
|
||||||
|
abc_nurbs_schemas_.push_back(nurbs.getSchema());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AbcNurbsWriter::isAnimated() const
|
const OObject ABCNurbsWriter::get_alembic_object() const
|
||||||
{
|
{
|
||||||
/* check if object has shape keys */
|
if (abc_nurbs_.empty()) {
|
||||||
Curve *cu = static_cast<Curve *>(m_object->data);
|
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::check_is_animated(const HierarchyContext &context) const
|
||||||
|
{
|
||||||
|
/* Check if object has shape keys. */
|
||||||
|
Curve *cu = static_cast<Curve *>(context.object->data);
|
||||||
return (cu->key != NULL);
|
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)
|
static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots)
|
||||||
{
|
{
|
||||||
if (num_knots <= 1) {
|
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]);
|
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. */
|
Curve *curve = static_cast<Curve *>(context.object->data);
|
||||||
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);
|
|
||||||
ListBase *nulb;
|
ListBase *nulb;
|
||||||
|
|
||||||
if (m_object->runtime.curve_cache->deformed_nurbs.first != NULL) {
|
if (context.object->runtime.curve_cache->deformed_nurbs.first != NULL) {
|
||||||
nulb = &m_object->runtime.curve_cache->deformed_nurbs;
|
nulb = &context.object->runtime.curve_cache->deformed_nurbs;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
nulb = BKE_curve_nurbs_get(curve);
|
nulb = BKE_curve_nurbs_get(curve);
|
||||||
@@ -147,7 +155,7 @@ void AbcNurbsWriter::do_write()
|
|||||||
|
|
||||||
/* TODO(kevin): to accommodate other software we should duplicate control
|
/* TODO(kevin): to accommodate other software we should duplicate control
|
||||||
* points to indicate that a NURBS is cyclic. */
|
* 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) {
|
if ((nu->flagu & CU_NURB_ENDPOINT) != 0) {
|
||||||
OBoolProperty prop(user_props, "endpoint_u");
|
OBoolProperty prop(user_props, "endpoint_u");
|
||||||
@@ -169,7 +177,7 @@ void AbcNurbsWriter::do_write()
|
|||||||
prop.set(true);
|
prop.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_nurbs_schema[count].set(sample);
|
abc_nurbs_schemas_[count].set(sample);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,26 +19,37 @@
|
|||||||
* \ingroup balembic
|
* \ingroup balembic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_object.h"
|
#include "abc_writer_abstract.h"
|
||||||
|
#include "abc_writer_mesh.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
class AbcNurbsWriter : public AbcObjectWriter {
|
class ABCNurbsWriter : public ABCAbstractWriter {
|
||||||
std::vector<Alembic::AbcGeom::ONuPatchSchema> m_nurbs_schema;
|
private:
|
||||||
bool m_is_animated;
|
std::vector<Alembic::AbcGeom::ONuPatch> abc_nurbs_;
|
||||||
|
std::vector<Alembic::AbcGeom::ONuPatchSchema> abc_nurbs_schemas_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AbcNurbsWriter(Object *ob,
|
explicit ABCNurbsWriter(const ABCWriterConstructorArgs &args);
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings);
|
|
||||||
|
|
||||||
private:
|
virtual void create_alembic_objects(const HierarchyContext *context) override;
|
||||||
virtual void do_write();
|
virtual const Alembic::Abc::OObject get_alembic_object() const override;
|
||||||
|
|
||||||
bool isAnimated() const;
|
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);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace alembic
|
} // namespace alembic
|
||||||
|
@@ -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
|
|
@@ -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
|
|
@@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_points.h"
|
#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_object_types.h"
|
||||||
#include "DNA_particle_types.h"
|
#include "DNA_particle_types.h"
|
||||||
@@ -36,81 +33,102 @@
|
|||||||
|
|
||||||
#include "DEG_depsgraph_query.h"
|
#include "DEG_depsgraph_query.h"
|
||||||
|
|
||||||
using Alembic::AbcGeom::kVertexScope;
|
#include "CLG_log.h"
|
||||||
using Alembic::AbcGeom::OPoints;
|
static CLG_LogRef LOG = {"io.alembic"};
|
||||||
using Alembic::AbcGeom::OPointsSchema;
|
|
||||||
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
AbcPointsWriter::AbcPointsWriter(Object *ob,
|
using Alembic::AbcGeom::kVertexScope;
|
||||||
AbcTransformWriter *parent,
|
using Alembic::AbcGeom::OPoints;
|
||||||
uint32_t time_sampling,
|
using Alembic::AbcGeom::OPointsSchema;
|
||||||
ExportSettings &settings,
|
|
||||||
ParticleSystem *psys)
|
|
||||||
: AbcObjectWriter(ob, time_sampling, settings, parent)
|
|
||||||
{
|
|
||||||
m_psys = psys;
|
|
||||||
|
|
||||||
std::string psys_name = get_valid_abc_name(psys->name);
|
ABCPointsWriter::ABCPointsWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
|
||||||
OPoints points(parent->alembicXform(), psys_name, m_time_sampling);
|
{
|
||||||
m_schema = points.getSchema();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbcPointsWriter::do_write()
|
void ABCPointsWriter::create_alembic_objects(const HierarchyContext * /*context*/)
|
||||||
{
|
{
|
||||||
if (!m_psys) {
|
CLOG_INFO(&LOG, 2, "exporting OPoints %s", args_.abc_path.c_str());
|
||||||
return;
|
abc_points_ = OPoints(args_.abc_parent, args_.abc_name, timesample_index_);
|
||||||
}
|
abc_points_schema_ = abc_points_.getSchema();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Alembic::Abc::OObject ABCPointsWriter::get_alembic_object() const
|
||||||
|
{
|
||||||
|
return abc_points_;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ABCPointsWriter::check_is_animated(const HierarchyContext & /*context*/) const
|
||||||
|
{
|
||||||
|
/* 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> points;
|
||||||
std::vector<Imath::V3f> velocities;
|
std::vector<Imath::V3f> velocities;
|
||||||
std::vector<float> widths;
|
std::vector<float> widths;
|
||||||
std::vector<uint64_t> ids;
|
std::vector<uint64_t> ids;
|
||||||
|
|
||||||
|
ParticleSystem *psys = context.particle_system;
|
||||||
ParticleKey state;
|
ParticleKey state;
|
||||||
|
|
||||||
ParticleSimulationData sim;
|
ParticleSimulationData sim;
|
||||||
sim.depsgraph = m_settings.depsgraph;
|
sim.depsgraph = args_.depsgraph;
|
||||||
sim.scene = m_settings.scene;
|
sim.scene = DEG_get_evaluated_scene(args_.depsgraph);
|
||||||
sim.ob = m_object;
|
sim.ob = context.object;
|
||||||
sim.psys = m_psys;
|
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;
|
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];
|
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;
|
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) {
|
if (psys_get_particle_state(&sim, p, &state, 0) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* location */
|
/* location */
|
||||||
mul_v3_m4v3(pos, m_object->imat, state.co);
|
mul_v3_m4v3(pos, context.object->imat, state.co);
|
||||||
|
|
||||||
/* velocity */
|
/* 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. */
|
/* Convert Z-up to Y-up. */
|
||||||
points.push_back(Imath::V3f(pos[0], pos[2], -pos[1]));
|
points.push_back(Imath::V3f(pos[0], pos[2], -pos[1]));
|
||||||
velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[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++);
|
ids.push_back(index++);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_psys->lattice_deform_data) {
|
if (psys->lattice_deform_data) {
|
||||||
BKE_lattice_deform_data_destroy(m_psys->lattice_deform_data);
|
BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
|
||||||
m_psys->lattice_deform_data = NULL;
|
psys->lattice_deform_data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Alembic::Abc::P3fArraySample psample(points);
|
Alembic::Abc::P3fArraySample psample(points);
|
||||||
@@ -119,10 +137,10 @@ void AbcPointsWriter::do_write()
|
|||||||
Alembic::Abc::FloatArraySample wsample_array(widths);
|
Alembic::Abc::FloatArraySample wsample_array(widths);
|
||||||
Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope);
|
Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope);
|
||||||
|
|
||||||
m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample);
|
OPointsSchema::Sample sample(psample, idsample, vsample, wsample);
|
||||||
m_sample.setSelfBounds(bounds());
|
update_bounding_box(context.object);
|
||||||
|
sample.setSelfBounds(bounding_box_);
|
||||||
m_schema.set(m_sample);
|
abc_points_schema_.set(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace alembic
|
} // namespace alembic
|
||||||
|
@@ -22,30 +22,29 @@
|
|||||||
* \ingroup balembic
|
* \ingroup balembic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_object.h"
|
#include "abc_writer_abstract.h"
|
||||||
#include "intern/abc_customdata.h"
|
|
||||||
|
|
||||||
struct ParticleSystem;
|
#include <Alembic/AbcGeom/OPoints.h>
|
||||||
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
class AbcPointsWriter : public AbcObjectWriter {
|
class ABCPointsWriter : public ABCAbstractWriter {
|
||||||
Alembic::AbcGeom::OPointsSchema m_schema;
|
Alembic::AbcGeom::OPoints abc_points_;
|
||||||
Alembic::AbcGeom::OPointsSchema::Sample m_sample;
|
Alembic::AbcGeom::OPointsSchema abc_points_schema_;
|
||||||
ParticleSystem *m_psys;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AbcPointsWriter(Object *ob,
|
explicit ABCPointsWriter(const ABCWriterConstructorArgs &args);
|
||||||
AbcTransformWriter *parent,
|
|
||||||
uint32_t time_sampling,
|
|
||||||
ExportSettings &settings,
|
|
||||||
ParticleSystem *psys);
|
|
||||||
|
|
||||||
void do_write();
|
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;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool check_is_animated(const HierarchyContext &context) const override;
|
||||||
|
virtual void do_write(HierarchyContext &context) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace alembic
|
} // namespace alembic
|
||||||
|
@@ -19,112 +19,95 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "abc_writer_transform.h"
|
#include "abc_writer_transform.h"
|
||||||
|
#include "abc_hierarchy_iterator.h"
|
||||||
#include "intern/abc_axis_conversion.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"
|
#include "CLG_log.h"
|
||||||
|
static CLG_LogRef LOG = {"io.alembic"};
|
||||||
using Alembic::AbcGeom::OObject;
|
|
||||||
using Alembic::AbcGeom::OXform;
|
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
AbcTransformWriter::AbcTransformWriter(Object *ob,
|
using Alembic::Abc::OObject;
|
||||||
const OObject &abc_parent,
|
using Alembic::AbcGeom::OXform;
|
||||||
AbcTransformWriter *parent,
|
using Alembic::AbcGeom::OXformSchema;
|
||||||
unsigned int time_sampling,
|
using Alembic::AbcGeom::XformSample;
|
||||||
ExportSettings &settings)
|
|
||||||
: AbcObjectWriter(ob, time_sampling, settings, parent), m_proxy_from(NULL)
|
ABCTransformWriter::ABCTransformWriter(const ABCWriterConstructorArgs &args)
|
||||||
|
: ABCAbstractWriter(args)
|
||||||
{
|
{
|
||||||
m_is_animated = hasAnimation(m_object);
|
timesample_index_ = args_.abc_archive->time_sampling_index_transforms();
|
||||||
|
|
||||||
if (!m_is_animated) {
|
|
||||||
time_sampling = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling);
|
|
||||||
m_schema = m_xform.getSchema();
|
|
||||||
|
|
||||||
/* Blender objects can't have a parent without inheriting the transform. */
|
|
||||||
m_inherits_xform = parent != NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbcTransformWriter::do_write()
|
void ABCTransformWriter::create_alembic_objects(const HierarchyContext * /*context*/)
|
||||||
{
|
{
|
||||||
Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object);
|
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) {
|
void ABCTransformWriter::do_write(HierarchyContext &context)
|
||||||
m_visibility = Alembic::AbcGeom::CreateVisibilityProperty(
|
{
|
||||||
m_xform, m_xform.getSchema().getTimeSampling());
|
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);
|
||||||
|
|
||||||
m_visibility.set(!(ob_eval->restrictflag & OB_RESTRICT_VIEWPORT));
|
// After this, parent_relative_matrix uses Y=up.
|
||||||
|
copy_m44_axis_swap(parent_relative_matrix, parent_relative_matrix, ABC_YUP_FROM_ZUP);
|
||||||
if (!m_first_frame && !m_is_animated) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float yup_mat[4][4];
|
|
||||||
create_transform_matrix(
|
|
||||||
ob_eval, yup_mat, m_inherits_xform ? ABC_MATRIX_LOCAL : ABC_MATRIX_WORLD, m_proxy_from);
|
|
||||||
|
|
||||||
/* If the parent is a camera, undo its to-Maya rotation (see below). */
|
/* If the parent is a camera, undo its to-Maya rotation (see below). */
|
||||||
bool is_root_object = !m_inherits_xform || ob_eval->parent == nullptr;
|
bool is_root_object = context.export_parent == nullptr;
|
||||||
if (!is_root_object && ob_eval->parent->type == OB_CAMERA) {
|
if (!is_root_object && context.export_parent->type == OB_CAMERA) {
|
||||||
float rot_mat[4][4];
|
float rot_mat[4][4];
|
||||||
axis_angle_to_mat4_single(rot_mat, 'X', M_PI_2);
|
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 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];
|
float rot_mat[4][4];
|
||||||
axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2);
|
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) {
|
if (is_root_object) {
|
||||||
/* Only apply scaling to root objects, parenting will propagate it. */
|
/* Only apply scaling to root objects, parenting will propagate it. */
|
||||||
float scale_mat[4][4];
|
float scale_mat[4][4];
|
||||||
scale_m4_fl(scale_mat, m_settings.global_scale);
|
scale_m4_fl(scale_mat, args_.export_params->global_scale);
|
||||||
scale_mat[3][3] = m_settings.global_scale; /* also scale translation */
|
scale_mat[3][3] = args_.export_params->global_scale; /* also scale translation */
|
||||||
mul_m4_m4m4(yup_mat, yup_mat, scale_mat);
|
mul_m4_m4m4(parent_relative_matrix, parent_relative_matrix, scale_mat);
|
||||||
yup_mat[3][3] /= m_settings.global_scale; /* normalise the homogeneous component */
|
parent_relative_matrix[3][3] /=
|
||||||
|
args_.export_params->global_scale; /* normalise the homogeneous component */
|
||||||
}
|
}
|
||||||
|
|
||||||
m_matrix = convert_matrix_datatype(yup_mat);
|
XformSample xform_sample;
|
||||||
m_sample.setMatrix(m_matrix);
|
xform_sample.setMatrix(convert_matrix_datatype(parent_relative_matrix));
|
||||||
|
xform_sample.setInheritsXforms(true);
|
||||||
/* Always export as "inherits transform", as this is the only way in which Blender works. The
|
abc_xform_schema_.set(xform_sample);
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Imath::Box3d AbcTransformWriter::bounds()
|
const OObject ABCTransformWriter::get_alembic_object() const
|
||||||
{
|
{
|
||||||
Imath::Box3d bounds;
|
return abc_xform_;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < m_children.size(); i++) {
|
bool ABCTransformWriter::check_is_animated(const HierarchyContext &context) const
|
||||||
Imath::Box3d box(m_children[i]->bounds());
|
{
|
||||||
bounds.extendBy(box);
|
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);
|
||||||
return Imath::transform(bounds, m_matrix);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AbcTransformWriter::hasAnimation(Object * /*ob*/) const
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace alembic
|
} // namespace alembic
|
||||||
|
@@ -19,44 +19,27 @@
|
|||||||
* \ingroup balembic
|
* \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 blender {
|
||||||
namespace io {
|
namespace io {
|
||||||
namespace alembic {
|
namespace alembic {
|
||||||
|
|
||||||
class AbcTransformWriter : public AbcObjectWriter {
|
class ABCTransformWriter : public ABCAbstractWriter {
|
||||||
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;
|
|
||||||
|
|
||||||
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:
|
private:
|
||||||
virtual void do_write();
|
Alembic::AbcGeom::OXform abc_xform_;
|
||||||
|
Alembic::AbcGeom::OXformSchema abc_xform_schema_;
|
||||||
|
|
||||||
bool hasAnimation(Object *ob) const;
|
public:
|
||||||
|
explicit ABCTransformWriter(const ABCWriterConstructorArgs &args);
|
||||||
|
virtual void create_alembic_objects(const HierarchyContext *context) override;
|
||||||
|
|
||||||
|
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
|
} // namespace alembic
|
||||||
|
@@ -70,36 +70,19 @@
|
|||||||
#include "WM_api.h"
|
#include "WM_api.h"
|
||||||
#include "WM_types.h"
|
#include "WM_types.h"
|
||||||
|
|
||||||
using Alembic::Abc::Int32ArraySamplePtr;
|
|
||||||
using Alembic::Abc::ObjectHeader;
|
using Alembic::Abc::ObjectHeader;
|
||||||
|
|
||||||
using Alembic::AbcGeom::kWrapExisting;
|
|
||||||
using Alembic::AbcGeom::MetaData;
|
|
||||||
using Alembic::AbcGeom::P3fArraySamplePtr;
|
|
||||||
|
|
||||||
using Alembic::AbcGeom::ICamera;
|
using Alembic::AbcGeom::ICamera;
|
||||||
using Alembic::AbcGeom::ICompoundProperty;
|
|
||||||
using Alembic::AbcGeom::ICurves;
|
using Alembic::AbcGeom::ICurves;
|
||||||
using Alembic::AbcGeom::ICurvesSchema;
|
|
||||||
using Alembic::AbcGeom::IFaceSet;
|
using Alembic::AbcGeom::IFaceSet;
|
||||||
using Alembic::AbcGeom::ILight;
|
using Alembic::AbcGeom::ILight;
|
||||||
using Alembic::AbcGeom::IN3fArrayProperty;
|
|
||||||
using Alembic::AbcGeom::IN3fGeomParam;
|
|
||||||
using Alembic::AbcGeom::INuPatch;
|
using Alembic::AbcGeom::INuPatch;
|
||||||
using Alembic::AbcGeom::IObject;
|
using Alembic::AbcGeom::IObject;
|
||||||
using Alembic::AbcGeom::IPoints;
|
using Alembic::AbcGeom::IPoints;
|
||||||
using Alembic::AbcGeom::IPointsSchema;
|
|
||||||
using Alembic::AbcGeom::IPolyMesh;
|
using Alembic::AbcGeom::IPolyMesh;
|
||||||
using Alembic::AbcGeom::IPolyMeshSchema;
|
|
||||||
using Alembic::AbcGeom::ISampleSelector;
|
using Alembic::AbcGeom::ISampleSelector;
|
||||||
using Alembic::AbcGeom::ISubD;
|
using Alembic::AbcGeom::ISubD;
|
||||||
using Alembic::AbcGeom::IV2fGeomParam;
|
|
||||||
using Alembic::AbcGeom::IXform;
|
using Alembic::AbcGeom::IXform;
|
||||||
using Alembic::AbcGeom::IXformSchema;
|
using Alembic::AbcGeom::MetaData;
|
||||||
using Alembic::AbcGeom::N3fArraySamplePtr;
|
|
||||||
using Alembic::AbcGeom::V3fArraySamplePtr;
|
|
||||||
using Alembic::AbcGeom::XformSample;
|
|
||||||
|
|
||||||
using Alembic::AbcMaterial::IMaterial;
|
using Alembic::AbcMaterial::IMaterial;
|
||||||
|
|
||||||
using namespace blender::io::alembic;
|
using namespace blender::io::alembic;
|
||||||
|
@@ -197,8 +197,8 @@ class AbstractHierarchyIterator {
|
|||||||
|
|
||||||
/* Iterate over the depsgraph, create writers, and tell the writers to write.
|
/* 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
|
* Main entry point for the AbstractHierarchyIterator, must be called for every to-be-exported
|
||||||
* frame. */
|
* (sub)frame. */
|
||||||
void iterate_and_write();
|
virtual void iterate_and_write();
|
||||||
|
|
||||||
/* Release all writers. Call after all frames have been exported. */
|
/* Release all writers. Call after all frames have been exported. */
|
||||||
void release_writers();
|
void release_writers();
|
||||||
|
@@ -24,6 +24,8 @@ set(INC
|
|||||||
../../../source/blender/blenlib
|
../../../source/blender/blenlib
|
||||||
../../../source/blender/blenkernel
|
../../../source/blender/blenkernel
|
||||||
../../../source/blender/io/alembic
|
../../../source/blender/io/alembic
|
||||||
|
../../../source/blender/io/common
|
||||||
|
../../../source/blender/io/usd/intern
|
||||||
../../../source/blender/makesdna
|
../../../source/blender/makesdna
|
||||||
../../../source/blender/depsgraph
|
../../../source/blender/depsgraph
|
||||||
${ALEMBIC_INCLUDE_DIRS}
|
${ALEMBIC_INCLUDE_DIRS}
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
#include "testing/testing.h"
|
#include "testing/testing.h"
|
||||||
|
|
||||||
// Keep first since utildefines defines AT which conflicts with STL
|
// 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"
|
#include "intern/abc_util.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "BKE_main.h"
|
#include "BKE_main.h"
|
||||||
|
#include "BLI_fileops.h"
|
||||||
#include "BLI_math.h"
|
#include "BLI_math.h"
|
||||||
#include "BLI_utildefines.h"
|
#include "BLI_utildefines.h"
|
||||||
#include "DNA_scene_types.h"
|
#include "DNA_scene_types.h"
|
||||||
@@ -13,140 +14,150 @@ extern "C" {
|
|||||||
|
|
||||||
#include "DEG_depsgraph.h"
|
#include "DEG_depsgraph.h"
|
||||||
|
|
||||||
using namespace blender::io::alembic;
|
namespace blender {
|
||||||
|
namespace io {
|
||||||
class TestableAbcExporter : public AbcExporter {
|
namespace alembic {
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class AlembicExportTest : public testing::Test {
|
class AlembicExportTest : public testing::Test {
|
||||||
protected:
|
protected:
|
||||||
ExportSettings settings;
|
ABCArchive *abc_archive;
|
||||||
|
|
||||||
|
AlembicExportParams params;
|
||||||
Scene scene;
|
Scene scene;
|
||||||
Depsgraph *depsgraph;
|
Depsgraph *depsgraph;
|
||||||
TestableAbcExporter *exporter;
|
|
||||||
Main *bmain;
|
Main *bmain;
|
||||||
|
|
||||||
virtual void SetUp()
|
virtual void SetUp()
|
||||||
{
|
{
|
||||||
settings.frame_start = 31.0;
|
abc_archive = nullptr;
|
||||||
settings.frame_end = 223.0;
|
|
||||||
|
|
||||||
/* Fake a 25 FPS scene with a nonzero base (because that's sometimes forgotten) */
|
/* Fake a 25 FPS scene with a nonzero base (because that's sometimes forgotten) */
|
||||||
scene.r.frs_sec = 50;
|
scene.r.frs_sec = 50;
|
||||||
scene.r.frs_sec_base = 2;
|
scene.r.frs_sec_base = 2;
|
||||||
|
strcpy(scene.id.name, "SCTestScene");
|
||||||
|
|
||||||
bmain = BKE_main_new();
|
bmain = BKE_main_new();
|
||||||
|
|
||||||
/* TODO(sergey): Pass scene layer somehow? */
|
/* TODO(sergey): Pass scene layer somehow? */
|
||||||
ViewLayer *view_layer = (ViewLayer *)scene.view_layers.first;
|
ViewLayer *view_layer = (ViewLayer *)scene.view_layers.first;
|
||||||
settings.depsgraph = depsgraph = DEG_graph_new(bmain, &scene, view_layer, DAG_EVAL_VIEWPORT);
|
depsgraph = DEG_graph_new(bmain, &scene, view_layer, DAG_EVAL_RENDER);
|
||||||
|
|
||||||
settings.scene = &scene;
|
|
||||||
settings.view_layer = view_layer;
|
|
||||||
|
|
||||||
exporter = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void TearDown()
|
virtual void TearDown()
|
||||||
{
|
{
|
||||||
BKE_main_free(bmain);
|
BKE_main_free(bmain);
|
||||||
DEG_graph_free(depsgraph);
|
DEG_graph_free(depsgraph);
|
||||||
delete exporter;
|
deleteArchive();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call after setting up the settings.
|
// Call after setting up the parameters.
|
||||||
void createExporter()
|
void createArchive()
|
||||||
{
|
{
|
||||||
exporter = new TestableAbcExporter(bmain, "somefile.abc", settings);
|
if (abc_archive != nullptr) {
|
||||||
|
deleteArchive();
|
||||||
|
}
|
||||||
|
abc_archive = new ABCArchive(bmain, &scene, params, "somefile.abc");
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteArchive()
|
||||||
|
{
|
||||||
|
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;
|
/* Test 5 samples per frame, for 2 frames. */
|
||||||
settings.shutter_close = 1.0;
|
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);
|
||||||
|
|
||||||
createExporter();
|
for (double frame : frames) {
|
||||||
std::vector<double> samples;
|
EXPECT_TRUE(abc_archive->is_xform_frame(frame));
|
||||||
|
EXPECT_TRUE(abc_archive->is_shape_frame(frame));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* test 5 samples per frame */
|
TEST_F(AlembicExportTest, TimeSamplesFullShutterDifferent)
|
||||||
exporter->getShutterSamples(5, true, samples);
|
{
|
||||||
EXPECT_EQ(5, samples.size());
|
/* Test 3 samples per frame for transforms, and 2 per frame for shapes, for 2 frames. */
|
||||||
EXPECT_NEAR(1.240, samples[0], 1e-5f);
|
params.shutter_open = 0.0;
|
||||||
EXPECT_NEAR(1.248, samples[1], 1e-5f);
|
params.shutter_close = 1.0;
|
||||||
EXPECT_NEAR(1.256, samples[2], 1e-5f);
|
params.frame_start = 31.0;
|
||||||
EXPECT_NEAR(1.264, samples[3], 1e-5f);
|
params.frame_end = 32.0;
|
||||||
EXPECT_NEAR(1.272, samples[4], 1e-5f);
|
params.frame_samples_xform = 3;
|
||||||
|
params.frame_samples_shape = 2;
|
||||||
/* test same, but using frame number offset instead of time */
|
createArchive();
|
||||||
exporter->getShutterSamples(5, false, samples);
|
std::vector<double> frames(abc_archive->frames_begin(), abc_archive->frames_end());
|
||||||
EXPECT_EQ(5, samples.size());
|
EXPECT_EQ(8, frames.size());
|
||||||
EXPECT_NEAR(0.0, samples[0], 1e-5f);
|
EXPECT_NEAR(31.0, frames[0], 1e-5); // transform + shape
|
||||||
EXPECT_NEAR(0.2, samples[1], 1e-5f);
|
EXPECT_TRUE(abc_archive->is_xform_frame(frames[0]));
|
||||||
EXPECT_NEAR(0.4, samples[2], 1e-5f);
|
EXPECT_TRUE(abc_archive->is_shape_frame(frames[0]));
|
||||||
EXPECT_NEAR(0.6, samples[3], 1e-5f);
|
EXPECT_NEAR(31.33333, frames[1], 1e-5); // transform
|
||||||
EXPECT_NEAR(0.8, samples[4], 1e-5f);
|
EXPECT_TRUE(abc_archive->is_xform_frame(frames[1]));
|
||||||
|
EXPECT_FALSE(abc_archive->is_shape_frame(frames[1]));
|
||||||
/* use the same setup to test getFrameSet() */
|
EXPECT_NEAR(31.5, frames[2], 1e-5); // shape
|
||||||
std::set<double> frames;
|
EXPECT_FALSE(abc_archive->is_xform_frame(frames[2]));
|
||||||
exporter->getFrameSet(5, frames);
|
EXPECT_TRUE(abc_archive->is_shape_frame(frames[2]));
|
||||||
EXPECT_EQ(965, frames.size());
|
EXPECT_NEAR(31.66666, frames[3], 1e-5); // transform
|
||||||
EXPECT_EQ(1, frames.count(31.0));
|
EXPECT_TRUE(abc_archive->is_xform_frame(frames[3]));
|
||||||
EXPECT_EQ(1, frames.count(31.2));
|
EXPECT_FALSE(abc_archive->is_shape_frame(frames[3]));
|
||||||
EXPECT_EQ(1, frames.count(31.4));
|
EXPECT_NEAR(32.0, frames[4], 1e-5); // transform + shape
|
||||||
EXPECT_EQ(1, frames.count(31.6));
|
EXPECT_TRUE(abc_archive->is_xform_frame(frames[4]));
|
||||||
EXPECT_EQ(1, frames.count(31.8));
|
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)
|
TEST_F(AlembicExportTest, TimeSamples180degShutter)
|
||||||
{
|
{
|
||||||
settings.shutter_open = -0.25;
|
/* Test 5 samples per frame, for 2 frames. */
|
||||||
settings.shutter_close = 0.25;
|
params.shutter_open = -0.25;
|
||||||
|
params.shutter_close = 0.25;
|
||||||
createExporter();
|
params.frame_start = 31.0;
|
||||||
std::vector<double> samples;
|
params.frame_end = 32.0;
|
||||||
|
params.frame_samples_xform = params.frame_samples_shape = 5;
|
||||||
/* test 5 samples per frame */
|
createArchive();
|
||||||
exporter->getShutterSamples(5, true, samples);
|
std::vector<double> frames(abc_archive->frames_begin(), abc_archive->frames_end());
|
||||||
EXPECT_EQ(5, samples.size());
|
EXPECT_EQ(10, frames.size());
|
||||||
EXPECT_NEAR(1.230, samples[0], 1e-5f);
|
EXPECT_NEAR(31 - 0.25, frames[0], 1e-5);
|
||||||
EXPECT_NEAR(1.234, samples[1], 1e-5f);
|
EXPECT_NEAR(31 - 0.15, frames[1], 1e-5);
|
||||||
EXPECT_NEAR(1.238, samples[2], 1e-5f);
|
EXPECT_NEAR(31 - 0.05, frames[2], 1e-5);
|
||||||
EXPECT_NEAR(1.242, samples[3], 1e-5f);
|
EXPECT_NEAR(31 + 0.05, frames[3], 1e-5);
|
||||||
EXPECT_NEAR(1.246, samples[4], 1e-5f);
|
EXPECT_NEAR(31 + 0.15, frames[4], 1e-5);
|
||||||
|
EXPECT_NEAR(32 - 0.25, frames[5], 1e-5);
|
||||||
/* test same, but using frame number offset instead of time */
|
EXPECT_NEAR(32 - 0.15, frames[6], 1e-5);
|
||||||
exporter->getShutterSamples(5, false, samples);
|
EXPECT_NEAR(32 - 0.05, frames[7], 1e-5);
|
||||||
EXPECT_EQ(5, samples.size());
|
EXPECT_NEAR(32 + 0.05, frames[8], 1e-5);
|
||||||
EXPECT_NEAR(-0.25, samples[0], 1e-5f);
|
EXPECT_NEAR(32 + 0.15, frames[9], 1e-5);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace alembic
|
||||||
|
} // namespace io
|
||||||
|
} // namespace blender
|
||||||
|
@@ -8,7 +8,9 @@ extern "C" {
|
|||||||
#include "BLI_utildefines.h"
|
#include "BLI_utildefines.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace blender::io::alembic;
|
namespace blender {
|
||||||
|
namespace io {
|
||||||
|
namespace alembic {
|
||||||
|
|
||||||
TEST(abc_matrix, CreateRotationMatrixY_YfromZ)
|
TEST(abc_matrix, CreateRotationMatrixY_YfromZ)
|
||||||
{
|
{
|
||||||
@@ -286,3 +288,7 @@ TEST(abc_matrix, CopyM44AxisSwapWithScale_gimbal_ZfromY)
|
|||||||
|
|
||||||
EXPECT_M4_NEAR(expect, result, 1e-5f);
|
EXPECT_M4_NEAR(expect, result, 1e-5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace alembic
|
||||||
|
} // namespace io
|
||||||
|
} // namespace blender
|
||||||
|
@@ -214,7 +214,7 @@ class DupliGroupExportTest(AbstractAlembicTest):
|
|||||||
self.run_blender('dupligroup-scene.blend', script)
|
self.run_blender('dupligroup-scene.blend', script)
|
||||||
|
|
||||||
# Now check the resulting Alembic file.
|
# 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.assertEqual(1, xform['.inherits'])
|
||||||
self.assertAlmostEqualFloatArray(
|
self.assertAlmostEqualFloatArray(
|
||||||
xform['.vals'],
|
xform['.vals'],
|
||||||
@@ -232,7 +232,7 @@ class DupliGroupExportTest(AbstractAlembicTest):
|
|||||||
self.run_blender('dupligroup-scene.blend', script)
|
self.run_blender('dupligroup-scene.blend', script)
|
||||||
|
|
||||||
# Now check the resulting Alembic file.
|
# 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.assertEqual(1, xform['.inherits'])
|
||||||
|
|
||||||
self.assertAlmostEqualFloatArray(
|
self.assertAlmostEqualFloatArray(
|
||||||
|
Reference in New Issue
Block a user