/* * 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 #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(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 &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 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(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 &frames) { frames.clear(); std::vector 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 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 xform_frames; getFrameSet(m_settings.frame_samples_xform, xform_frames); std::set shape_frames; getFrameSet(m_settings.frame_samples_shape, shape_frames); /* Merge all frames needed. */ std::set frames(xform_frames); frames.insert(shape_frames.begin(), shape_frames.end()); /* Export all frames. */ std::set::const_iterator begin = frames.begin(); std::set::const_iterator end = frames.end(); const float size = static_cast(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(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(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(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(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(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(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(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(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(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(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::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(t); m_settings.scene->r.subframe = static_cast(t) - m_settings.scene->r.cfra; BKE_scene_graph_update_for_newframe(m_settings.depsgraph, bmain); }