This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/alembic/intern/abc_exporter.cc
Sybren A. Stüvel 0829bd7b66 Alembic export: avoid BLI_assert() failure when object is not in depsgraph
When the object we iterate over is not part of the depsgraph, we cannot
get the evaluated copy to export. This workaround is temporary to avoid
a BLI_assert() failure getting the evaluated mesh of this object.

This will be handled more elegantly in the new AbstractHierarchyIterator
that I'm working on, but that requires a bigger change than we should
allow this close to the 2.80 release candidate.

This fixes a problem described in T58686.
2019-07-10 09:56:27 +02:00

676 lines
19 KiB
C++

/*
* 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_archive.h"
#include "abc_camera.h"
#include "abc_curves.h"
#include "abc_hair.h"
#include "abc_mball.h"
#include "abc_mesh.h"
#include "abc_nurbs.h"
#include "abc_points.h"
#include "abc_transform.h"
#include "abc_util.h"
extern "C" {
#include "DNA_camera_types.h"
#include "DNA_curve_types.h"
#include "DNA_meta_types.h"
#include "DNA_mesh_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_anim.h"
#include "BKE_global.h"
#include "BKE_idprop.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;
/* ************************************************************************** */
ExportSettings::ExportSettings()
: scene(NULL),
view_layer(NULL),
depsgraph(NULL),
logger(),
selected_only(false),
visible_layers_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),
export_ogawa(true),
pack_uv(false),
triangulate(false),
quad_method(0),
ngon_method(0),
do_convert_axis(false)
{
}
static bool object_is_smoke_sim(Object *ob)
{
ModifierData *md = modifiers_findByType(ob, eModifierType_Smoke);
if (md) {
SmokeModifierData *smd = reinterpret_cast<SmokeModifierData *>(md);
return (smd->type == MOD_SMOKE_TYPE_DOMAIN);
}
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 ob: 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 dupligroup).
* 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_layers_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);
Alembic::Abc::TimeSamplingType ts(
static_cast<uint32_t>(samples.size()),
1.0 / m_settings.scene->r.frs_sec); /* TODO(Sybren): shouldn't we use the FPS macro here? */
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()(float &progress, bool &was_canceled)
{
std::string scene_name;
if (m_bmain->name[0] != '\0') {
char scene_file_name[FILE_MAX];
BLI_strncpy(scene_file_name, m_bmain->name, FILE_MAX);
scene_name = scene_file_name;
}
else {
scene_name = "untitled";
}
Scene *scene = m_settings.scene;
const double fps = FPS;
char buf[16];
snprintf(buf, 15, "%f", fps);
const std::string str_fps = buf;
Alembic::AbcCoreAbstract::MetaData md;
md.set("FramesPerTimeUnit", str_fps);
m_writer = new ArchiveWriter(m_filename, scene_name.c_str(), m_settings.export_ogawa, md);
/* 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);
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) {
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);
}