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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSybren A. Stüvel <sybren@blender.org>2020-03-06 18:19:35 +0300
committerSybren A. Stüvel <sybren@blender.org>2020-03-06 18:19:45 +0300
commiteb522af4fec58876ac1b0a73ad9bcdae2d82d33f (patch)
tree485c6a1fb23b5be256757375e2157378d3a5c61b /source/blender/io/alembic/intern/abc_reader_mesh.cc
parentff60dd8b18ed00902e5bdfd36882072db7af8735 (diff)
Cleanup: move Alembic, AVI, Collada, and USD to `source/blender/io`
This moves the `alembic`, `avi`, `collada`, and `usd` modules into a common `io` directory. This also cleans up some `#include "../../{somedir}/{somefile}.h"` by adding `../../io/{somedir}` to `CMakeLists.txt` and then just using `#include "{somefile}.h"`. No functional changes.
Diffstat (limited to 'source/blender/io/alembic/intern/abc_reader_mesh.cc')
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.cc889
1 files changed, 889 insertions, 0 deletions
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc
new file mode 100644
index 00000000000..a4e412695c3
--- /dev/null
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc
@@ -0,0 +1,889 @@
+/*
+ * 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_reader_mesh.h"
+#include "abc_reader_transform.h"
+#include "abc_util.h"
+
+#include <algorithm>
+
+#include "MEM_guardedalloc.h"
+
+extern "C" {
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_math_geom.h"
+
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+}
+
+using Alembic::Abc::Int32ArraySamplePtr;
+using Alembic::Abc::P3fArraySamplePtr;
+
+using Alembic::AbcGeom::IFaceSet;
+using Alembic::AbcGeom::IFaceSetSchema;
+using Alembic::AbcGeom::IN3fGeomParam;
+using Alembic::AbcGeom::IObject;
+using Alembic::AbcGeom::IPolyMesh;
+using Alembic::AbcGeom::IPolyMeshSchema;
+using Alembic::AbcGeom::ISampleSelector;
+using Alembic::AbcGeom::ISubD;
+using Alembic::AbcGeom::ISubDSchema;
+using Alembic::AbcGeom::IV2fGeomParam;
+using Alembic::AbcGeom::kWrapExisting;
+using Alembic::AbcGeom::N3fArraySample;
+using Alembic::AbcGeom::N3fArraySamplePtr;
+using Alembic::AbcGeom::UInt32ArraySamplePtr;
+using Alembic::AbcGeom::V2fArraySamplePtr;
+
+/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
+
+/* Some helpers for mesh generation */
+namespace utils {
+
+static void build_mat_map(const Main *bmain, std::map<std::string, Material *> &mat_map)
+{
+ Material *material = static_cast<Material *>(bmain->materials.first);
+
+ for (; material; material = static_cast<Material *>(material->id.next)) {
+ mat_map[material->id.name + 2] = material;
+ }
+}
+
+static void assign_materials(Main *bmain,
+ Object *ob,
+ const std::map<std::string, int> &mat_index_map)
+{
+ bool can_assign = true;
+ std::map<std::string, int>::const_iterator it = mat_index_map.begin();
+
+ int matcount = 0;
+ for (; it != mat_index_map.end(); ++it, matcount++) {
+ if (!BKE_object_material_slot_add(bmain, ob)) {
+ can_assign = false;
+ break;
+ }
+ }
+
+ /* TODO(kevin): use global map? */
+ std::map<std::string, Material *> mat_map;
+ build_mat_map(bmain, mat_map);
+
+ std::map<std::string, Material *>::iterator mat_iter;
+
+ if (can_assign) {
+ it = mat_index_map.begin();
+
+ for (; it != mat_index_map.end(); ++it) {
+ std::string mat_name = it->first;
+ mat_iter = mat_map.find(mat_name.c_str());
+
+ Material *assigned_mat;
+
+ if (mat_iter == mat_map.end()) {
+ assigned_mat = BKE_material_add(bmain, mat_name.c_str());
+ mat_map[mat_name] = assigned_mat;
+ }
+ else {
+ assigned_mat = mat_iter->second;
+ }
+
+ BKE_object_material_assign(bmain, ob, assigned_mat, it->second, BKE_MAT_ASSIGN_OBDATA);
+ }
+ }
+}
+
+} /* namespace utils */
+
+struct AbcMeshData {
+ Int32ArraySamplePtr face_indices;
+ Int32ArraySamplePtr face_counts;
+
+ P3fArraySamplePtr positions;
+ P3fArraySamplePtr ceil_positions;
+
+ V2fArraySamplePtr uvs;
+ UInt32ArraySamplePtr uvs_indices;
+};
+
+static void read_mverts_interp(MVert *mverts,
+ const P3fArraySamplePtr &positions,
+ const P3fArraySamplePtr &ceil_positions,
+ const float weight)
+{
+ float tmp[3];
+ for (int i = 0; i < positions->size(); i++) {
+ MVert &mvert = mverts[i];
+ const Imath::V3f &floor_pos = (*positions)[i];
+ const Imath::V3f &ceil_pos = (*ceil_positions)[i];
+
+ interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), weight);
+ copy_zup_from_yup(mvert.co, tmp);
+
+ mvert.bweight = 0;
+ }
+}
+
+static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data)
+{
+ MVert *mverts = config.mvert;
+ const P3fArraySamplePtr &positions = mesh_data.positions;
+
+ if (config.weight != 0.0f && mesh_data.ceil_positions != NULL &&
+ mesh_data.ceil_positions->size() == positions->size()) {
+ read_mverts_interp(mverts, positions, mesh_data.ceil_positions, config.weight);
+ return;
+ }
+
+ read_mverts(mverts, positions, nullptr);
+}
+
+void read_mverts(MVert *mverts, const P3fArraySamplePtr positions, const N3fArraySamplePtr normals)
+{
+ for (int i = 0; i < positions->size(); i++) {
+ MVert &mvert = mverts[i];
+ Imath::V3f pos_in = (*positions)[i];
+
+ copy_zup_from_yup(mvert.co, pos_in.getValue());
+
+ mvert.bweight = 0;
+
+ if (normals) {
+ Imath::V3f nor_in = (*normals)[i];
+
+ short no[3];
+ normal_float_to_short_v3(no, nor_in.getValue());
+
+ copy_zup_from_yup(mvert.no, no);
+ }
+ }
+}
+
+static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data)
+{
+ MPoly *mpolys = config.mpoly;
+ MLoop *mloops = config.mloop;
+ MLoopUV *mloopuvs = config.mloopuv;
+
+ const Int32ArraySamplePtr &face_indices = mesh_data.face_indices;
+ const Int32ArraySamplePtr &face_counts = mesh_data.face_counts;
+ const V2fArraySamplePtr &uvs = mesh_data.uvs;
+ const size_t uvs_size = uvs == nullptr ? 0 : uvs->size();
+
+ const UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices;
+
+ const bool do_uvs = (mloopuvs && uvs && uvs_indices) &&
+ (uvs_indices->size() == face_indices->size());
+ unsigned int loop_index = 0;
+ unsigned int rev_loop_index = 0;
+ unsigned int uv_index = 0;
+
+ for (int i = 0; i < face_counts->size(); i++) {
+ const int face_size = (*face_counts)[i];
+
+ MPoly &poly = mpolys[i];
+ poly.loopstart = loop_index;
+ poly.totloop = face_size;
+
+ /* Polygons are always assumed to be smooth-shaded. If the Alembic mesh should be flat-shaded,
+ * this is encoded in custom loop normals. See T71246. */
+ poly.flag |= ME_SMOOTH;
+
+ /* NOTE: Alembic data is stored in the reverse order. */
+ rev_loop_index = loop_index + (face_size - 1);
+
+ for (int f = 0; f < face_size; f++, loop_index++, rev_loop_index--) {
+ MLoop &loop = mloops[rev_loop_index];
+ loop.v = (*face_indices)[loop_index];
+
+ if (do_uvs) {
+ MLoopUV &loopuv = mloopuvs[rev_loop_index];
+
+ uv_index = (*uvs_indices)[loop_index];
+
+ /* Some Alembic files are broken (or at least export UVs in a way we don't expect). */
+ if (uv_index >= uvs_size) {
+ continue;
+ }
+
+ loopuv.uv[0] = (*uvs)[uv_index][0];
+ loopuv.uv[1] = (*uvs)[uv_index][1];
+ }
+ }
+ }
+
+ BKE_mesh_calc_edges(config.mesh, false, false);
+}
+
+static void process_no_normals(CDStreamConfig &config)
+{
+ /* Absense of normals in the Alembic mesh is interpreted as 'smooth'. */
+ BKE_mesh_calc_normals(config.mesh);
+}
+
+static void process_loop_normals(CDStreamConfig &config, const N3fArraySamplePtr loop_normals_ptr)
+{
+ size_t loop_count = loop_normals_ptr->size();
+
+ if (loop_count == 0) {
+ process_no_normals(config);
+ return;
+ }
+
+ float(*lnors)[3] = static_cast<float(*)[3]>(
+ MEM_malloc_arrayN(loop_count, sizeof(float[3]), "ABC::FaceNormals"));
+
+ Mesh *mesh = config.mesh;
+ MPoly *mpoly = mesh->mpoly;
+ const N3fArraySample &loop_normals = *loop_normals_ptr;
+ int abc_index = 0;
+ for (int i = 0, e = mesh->totpoly; i < e; i++, mpoly++) {
+ /* As usual, ABC orders the loops in reverse. */
+ for (int j = mpoly->totloop - 1; j >= 0; j--, abc_index++) {
+ int blender_index = mpoly->loopstart + j;
+ copy_zup_from_yup(lnors[blender_index], loop_normals[abc_index].getValue());
+ }
+ }
+
+ mesh->flag |= ME_AUTOSMOOTH;
+ BKE_mesh_set_custom_normals(mesh, lnors);
+
+ MEM_freeN(lnors);
+}
+
+static void process_vertex_normals(CDStreamConfig &config,
+ const N3fArraySamplePtr vertex_normals_ptr)
+{
+ size_t normals_count = vertex_normals_ptr->size();
+ if (normals_count == 0) {
+ process_no_normals(config);
+ return;
+ }
+
+ float(*vnors)[3] = static_cast<float(*)[3]>(
+ MEM_malloc_arrayN(normals_count, sizeof(float[3]), "ABC::VertexNormals"));
+
+ const N3fArraySample &vertex_normals = *vertex_normals_ptr;
+ for (int index = 0; index < normals_count; index++) {
+ copy_zup_from_yup(vnors[index], vertex_normals[index].getValue());
+ }
+
+ config.mesh->flag |= ME_AUTOSMOOTH;
+ BKE_mesh_set_custom_normals_from_vertices(config.mesh, vnors);
+ MEM_freeN(vnors);
+}
+
+static void process_normals(CDStreamConfig &config,
+ const IN3fGeomParam &normals,
+ const ISampleSelector &selector)
+{
+ if (!normals.valid()) {
+ process_no_normals(config);
+ return;
+ }
+
+ IN3fGeomParam::Sample normsamp = normals.getExpandedValue(selector);
+ Alembic::AbcGeom::GeometryScope scope = normals.getScope();
+
+ switch (scope) {
+ case Alembic::AbcGeom::kFacevaryingScope: // 'Vertex Normals' in Houdini.
+ process_loop_normals(config, normsamp.getVals());
+ break;
+ case Alembic::AbcGeom::kVertexScope:
+ case Alembic::AbcGeom::kVaryingScope: // 'Point Normals' in Houdini.
+ process_vertex_normals(config, normsamp.getVals());
+ break;
+ case Alembic::AbcGeom::kConstantScope:
+ case Alembic::AbcGeom::kUniformScope:
+ case Alembic::AbcGeom::kUnknownScope:
+ process_no_normals(config);
+ break;
+ }
+}
+
+ABC_INLINE void read_uvs_params(CDStreamConfig &config,
+ AbcMeshData &abc_data,
+ const IV2fGeomParam &uv,
+ const ISampleSelector &selector)
+{
+ if (!uv.valid()) {
+ return;
+ }
+
+ IV2fGeomParam::Sample uvsamp;
+ uv.getIndexed(uvsamp, selector);
+
+ abc_data.uvs = uvsamp.getVals();
+ abc_data.uvs_indices = uvsamp.getIndices();
+
+ if (abc_data.uvs_indices->size() == config.totloop) {
+ std::string name = Alembic::Abc::GetSourceName(uv.getMetaData());
+
+ /* According to the convention, primary UVs should have had their name
+ * set using Alembic::Abc::SetSourceName, but you can't expect everyone
+ * to follow it! :) */
+ if (name.empty()) {
+ name = uv.getName();
+ }
+
+ void *cd_ptr = config.add_customdata_cb(config.mesh, name.c_str(), CD_MLOOPUV);
+ config.mloopuv = static_cast<MLoopUV *>(cd_ptr);
+ }
+}
+
+static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type)
+{
+ CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
+ void *cd_ptr;
+ CustomData *loopdata;
+ int numloops;
+
+ /* unsupported custom data type -- don't do anything. */
+ if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) {
+ return NULL;
+ }
+
+ loopdata = &mesh->ldata;
+ cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name);
+ if (cd_ptr != NULL) {
+ /* layer already exists, so just return it. */
+ return cd_ptr;
+ }
+
+ /* Create a new layer. */
+ numloops = mesh->totloop;
+ cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, NULL, numloops, name);
+ return cd_ptr;
+}
+
+static void get_weight_and_index(CDStreamConfig &config,
+ Alembic::AbcCoreAbstract::TimeSamplingPtr time_sampling,
+ size_t samples_number)
+{
+ Alembic::AbcGeom::index_t i0, i1;
+
+ config.weight = get_weight_and_index(config.time, time_sampling, samples_number, i0, i1);
+
+ config.index = i0;
+ config.ceil_index = i1;
+}
+
+static void read_mesh_sample(const std::string &iobject_full_name,
+ ImportSettings *settings,
+ const IPolyMeshSchema &schema,
+ const ISampleSelector &selector,
+ CDStreamConfig &config)
+{
+ const IPolyMeshSchema::Sample sample = schema.getValue(selector);
+
+ AbcMeshData abc_mesh_data;
+ abc_mesh_data.face_counts = sample.getFaceCounts();
+ abc_mesh_data.face_indices = sample.getFaceIndices();
+ abc_mesh_data.positions = sample.getPositions();
+
+ get_weight_and_index(config, schema.getTimeSampling(), schema.getNumSamples());
+
+ if (config.weight != 0.0f) {
+ Alembic::AbcGeom::IPolyMeshSchema::Sample ceil_sample;
+ schema.get(ceil_sample, Alembic::Abc::ISampleSelector(config.ceil_index));
+ abc_mesh_data.ceil_positions = ceil_sample.getPositions();
+ }
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
+ read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
+ }
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
+ read_mverts(config, abc_mesh_data);
+ }
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
+ read_mpolys(config, abc_mesh_data);
+ process_normals(config, schema.getNormalsParam(), selector);
+ }
+
+ if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
+ read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector);
+ }
+}
+
+CDStreamConfig get_config(Mesh *mesh)
+{
+ CDStreamConfig config;
+
+ BLI_assert(mesh->mvert || mesh->totvert == 0);
+
+ config.mesh = mesh;
+ config.mvert = mesh->mvert;
+ config.mloop = mesh->mloop;
+ config.mpoly = mesh->mpoly;
+ config.totloop = mesh->totloop;
+ config.totpoly = mesh->totpoly;
+ config.loopdata = &mesh->ldata;
+ config.add_customdata_cb = add_customdata_cb;
+
+ return config;
+}
+
+/* ************************************************************************** */
+
+AbcMeshReader::AbcMeshReader(const IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ m_settings->read_flag |= MOD_MESHSEQ_READ_ALL;
+
+ IPolyMesh ipoly_mesh(m_iobject, kWrapExisting);
+ m_schema = ipoly_mesh.getSchema();
+
+ get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time);
+}
+
+bool AbcMeshReader::valid() const
+{
+ return m_schema.valid();
+}
+
+void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
+{
+ Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
+
+ m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
+ m_object->data = mesh;
+
+ Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, NULL);
+ if (read_mesh != mesh) {
+ /* XXX fixme after 2.80; mesh->flag isn't copied by BKE_mesh_nomain_to_mesh() */
+ /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */
+ short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH);
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true);
+ mesh->flag |= autosmooth;
+ }
+
+ if (m_settings->validate_meshes) {
+ BKE_mesh_validate(mesh, false, false);
+ }
+
+ readFaceSetsSample(bmain, mesh, sample_sel);
+
+ if (has_animations(m_schema, m_settings)) {
+ addCacheModifier();
+ }
+}
+
+bool AbcMeshReader::accepts_object_type(
+ const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
+ const Object *const ob,
+ const char **err_str) const
+{
+ if (!Alembic::AbcGeom::IPolyMesh::matches(alembic_header)) {
+ *err_str =
+ "Object type mismatch, Alembic object path pointed to PolyMesh when importing, but not "
+ "any more.";
+ return false;
+ }
+
+ if (ob->type != OB_MESH) {
+ *err_str = "Object type mismatch, Alembic object path points to PolyMesh.";
+ return false;
+ }
+
+ return true;
+}
+
+bool AbcMeshReader::topology_changed(Mesh *existing_mesh, const ISampleSelector &sample_sel)
+{
+ IPolyMeshSchema::Sample sample;
+ try {
+ sample = m_schema.getValue(sample_sel);
+ }
+ catch (Alembic::Util::Exception &ex) {
+ printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n",
+ m_iobject.getFullName().c_str(),
+ m_schema.getName().c_str(),
+ sample_sel.getRequestedTime(),
+ ex.what());
+ // A similar error in read_mesh() would just return existing_mesh.
+ return false;
+ }
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+ const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+ return positions->size() != existing_mesh->totvert ||
+ face_counts->size() != existing_mesh->totpoly ||
+ face_indices->size() != existing_mesh->totloop;
+}
+
+Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh,
+ const ISampleSelector &sample_sel,
+ int read_flag,
+ const char **err_str)
+{
+ IPolyMeshSchema::Sample sample;
+ try {
+ sample = m_schema.getValue(sample_sel);
+ }
+ catch (Alembic::Util::Exception &ex) {
+ if (err_str != nullptr) {
+ *err_str = "Error reading mesh sample; more detail on the console";
+ }
+ printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n",
+ m_iobject.getFullName().c_str(),
+ m_schema.getName().c_str(),
+ sample_sel.getRequestedTime(),
+ ex.what());
+ return existing_mesh;
+ }
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+ const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+ Mesh *new_mesh = NULL;
+
+ /* Only read point data when streaming meshes, unless we need to create new ones. */
+ ImportSettings settings;
+ settings.read_flag |= read_flag;
+
+ if (topology_changed(existing_mesh, sample_sel)) {
+ new_mesh = BKE_mesh_new_nomain_from_template(
+ existing_mesh, positions->size(), 0, 0, face_indices->size(), face_counts->size());
+
+ settings.read_flag |= MOD_MESHSEQ_READ_ALL;
+ }
+ else {
+ /* If the face count changed (e.g. by triangulation), only read points.
+ * This prevents crash from T49813.
+ * TODO(kevin): perhaps find a better way to do this? */
+ if (face_counts->size() != existing_mesh->totpoly ||
+ face_indices->size() != existing_mesh->totloop) {
+ settings.read_flag = MOD_MESHSEQ_READ_VERT;
+
+ if (err_str) {
+ *err_str =
+ "Topology has changed, perhaps by triangulating the"
+ " mesh. Only vertices will be read!";
+ }
+ }
+ }
+
+ CDStreamConfig config = get_config(new_mesh ? new_mesh : existing_mesh);
+ config.time = sample_sel.getRequestedTime();
+
+ read_mesh_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config);
+
+ if (new_mesh) {
+ /* Here we assume that the number of materials doesn't change, i.e. that
+ * the material slots that were created when the object was loaded from
+ * Alembic are still valid now. */
+ size_t num_polys = new_mesh->totpoly;
+ if (num_polys > 0) {
+ std::map<std::string, int> mat_map;
+ assign_facesets_to_mpoly(sample_sel, new_mesh->mpoly, num_polys, mat_map);
+ }
+
+ return new_mesh;
+ }
+
+ return existing_mesh;
+}
+
+void AbcMeshReader::assign_facesets_to_mpoly(const ISampleSelector &sample_sel,
+ MPoly *mpoly,
+ int totpoly,
+ std::map<std::string, int> &r_mat_map)
+{
+ std::vector<std::string> face_sets;
+ m_schema.getFaceSetNames(face_sets);
+
+ if (face_sets.empty()) {
+ return;
+ }
+
+ int current_mat = 0;
+
+ for (int i = 0; i < face_sets.size(); i++) {
+ const std::string &grp_name = face_sets[i];
+
+ if (r_mat_map.find(grp_name) == r_mat_map.end()) {
+ r_mat_map[grp_name] = 1 + current_mat++;
+ }
+
+ const int assigned_mat = r_mat_map[grp_name];
+
+ const IFaceSet faceset = m_schema.getFaceSet(grp_name);
+
+ if (!faceset.valid()) {
+ std::cerr << " Face set " << grp_name << " invalid for " << m_object_name << "\n";
+ continue;
+ }
+
+ const IFaceSetSchema face_schem = faceset.getSchema();
+ const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
+ const Int32ArraySamplePtr group_faces = face_sample.getFaces();
+ const size_t num_group_faces = group_faces->size();
+
+ for (size_t l = 0; l < num_group_faces; l++) {
+ size_t pos = (*group_faces)[l];
+
+ if (pos >= totpoly) {
+ std::cerr << "Faceset overflow on " << faceset.getName() << '\n';
+ break;
+ }
+
+ MPoly &poly = mpoly[pos];
+ poly.mat_nr = assigned_mat - 1;
+ }
+ }
+}
+
+void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const ISampleSelector &sample_sel)
+{
+ std::map<std::string, int> mat_map;
+ assign_facesets_to_mpoly(sample_sel, mesh->mpoly, mesh->totpoly, mat_map);
+ utils::assign_materials(bmain, m_object, mat_map);
+}
+
+/* ************************************************************************** */
+
+ABC_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2)
+{
+ for (int i = 0, e = totedge; i < e; i++) {
+ MEdge &edge = edges[i];
+
+ if (edge.v1 == v1 && edge.v2 == v2) {
+ return &edge;
+ }
+ }
+
+ return NULL;
+}
+
+static void read_subd_sample(const std::string &iobject_full_name,
+ ImportSettings *settings,
+ const ISubDSchema &schema,
+ const ISampleSelector &selector,
+ CDStreamConfig &config)
+{
+ const ISubDSchema::Sample sample = schema.getValue(selector);
+
+ AbcMeshData abc_mesh_data;
+ abc_mesh_data.face_counts = sample.getFaceCounts();
+ abc_mesh_data.face_indices = sample.getFaceIndices();
+ abc_mesh_data.positions = sample.getPositions();
+
+ get_weight_and_index(config, schema.getTimeSampling(), schema.getNumSamples());
+
+ if (config.weight != 0.0f) {
+ Alembic::AbcGeom::ISubDSchema::Sample ceil_sample;
+ schema.get(ceil_sample, Alembic::Abc::ISampleSelector(config.ceil_index));
+ abc_mesh_data.ceil_positions = ceil_sample.getPositions();
+ }
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
+ read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
+ }
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
+ read_mverts(config, abc_mesh_data);
+ }
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
+ /* Alembic's 'SubD' scheme is used to store subdivision surfaces, i.e. the pre-subdivision
+ * mesh. Currently we don't add a subdivision modifier when we load such data. This code is
+ * assuming that the subdivided surface should be smooth. */
+ read_mpolys(config, abc_mesh_data);
+ process_no_normals(config);
+ }
+
+ if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
+ read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector);
+ }
+}
+
+/* ************************************************************************** */
+
+AbcSubDReader::AbcSubDReader(const IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ m_settings->read_flag |= MOD_MESHSEQ_READ_ALL;
+
+ ISubD isubd_mesh(m_iobject, kWrapExisting);
+ m_schema = isubd_mesh.getSchema();
+
+ get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time);
+}
+
+bool AbcSubDReader::valid() const
+{
+ return m_schema.valid();
+}
+
+bool AbcSubDReader::accepts_object_type(
+ const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
+ const Object *const ob,
+ const char **err_str) const
+{
+ if (!Alembic::AbcGeom::ISubD::matches(alembic_header)) {
+ *err_str =
+ "Object type mismatch, Alembic object path pointed to SubD when importing, but not any "
+ "more.";
+ return false;
+ }
+
+ if (ob->type != OB_MESH) {
+ *err_str = "Object type mismatch, Alembic object path points to SubD.";
+ return false;
+ }
+
+ return true;
+}
+
+void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
+{
+ Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
+
+ m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
+ m_object->data = mesh;
+
+ Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, NULL);
+ if (read_mesh != mesh) {
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true);
+ }
+
+ ISubDSchema::Sample sample;
+ try {
+ sample = m_schema.getValue(sample_sel);
+ }
+ catch (Alembic::Util::Exception &ex) {
+ printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n",
+ m_iobject.getFullName().c_str(),
+ m_schema.getName().c_str(),
+ sample_sel.getRequestedTime(),
+ ex.what());
+ return;
+ }
+
+ Int32ArraySamplePtr indices = sample.getCreaseIndices();
+ Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses();
+
+ if (indices && sharpnesses) {
+ MEdge *edges = mesh->medge;
+ int totedge = mesh->totedge;
+
+ for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, s++) {
+ int v1 = (*indices)[i];
+ int v2 = (*indices)[i + 1];
+
+ if (v2 < v1) {
+ /* It appears to be common to store edges with the smallest index first, in which case this
+ * prevents us from doing the second search below. */
+ std::swap(v1, v2);
+ }
+
+ MEdge *edge = find_edge(edges, totedge, v1, v2);
+ if (edge == NULL) {
+ edge = find_edge(edges, totedge, v2, v1);
+ }
+
+ if (edge) {
+ edge->crease = unit_float_to_uchar_clamp((*sharpnesses)[s]);
+ }
+ }
+
+ mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE;
+ }
+
+ if (m_settings->validate_meshes) {
+ BKE_mesh_validate(mesh, false, false);
+ }
+
+ if (has_animations(m_schema, m_settings)) {
+ addCacheModifier();
+ }
+}
+
+Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh,
+ const ISampleSelector &sample_sel,
+ int read_flag,
+ const char **err_str)
+{
+ ISubDSchema::Sample sample;
+ try {
+ sample = m_schema.getValue(sample_sel);
+ }
+ catch (Alembic::Util::Exception &ex) {
+ if (err_str != nullptr) {
+ *err_str = "Error reading mesh sample; more detail on the console";
+ }
+ printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n",
+ m_iobject.getFullName().c_str(),
+ m_schema.getName().c_str(),
+ sample_sel.getRequestedTime(),
+ ex.what());
+ return existing_mesh;
+ }
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+ const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+ Mesh *new_mesh = NULL;
+
+ ImportSettings settings;
+ settings.read_flag |= read_flag;
+
+ if (existing_mesh->totvert != positions->size()) {
+ new_mesh = BKE_mesh_new_nomain_from_template(
+ existing_mesh, positions->size(), 0, 0, face_indices->size(), face_counts->size());
+
+ settings.read_flag |= MOD_MESHSEQ_READ_ALL;
+ }
+ else {
+ /* If the face count changed (e.g. by triangulation), only read points.
+ * This prevents crash from T49813.
+ * TODO(kevin): perhaps find a better way to do this? */
+ if (face_counts->size() != existing_mesh->totpoly ||
+ face_indices->size() != existing_mesh->totloop) {
+ settings.read_flag = MOD_MESHSEQ_READ_VERT;
+
+ if (err_str) {
+ *err_str =
+ "Topology has changed, perhaps by triangulating the"
+ " mesh. Only vertices will be read!";
+ }
+ }
+ }
+
+ /* Only read point data when streaming meshes, unless we need to create new ones. */
+ CDStreamConfig config = get_config(new_mesh ? new_mesh : existing_mesh);
+ config.time = sample_sel.getRequestedTime();
+ read_subd_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config);
+
+ return config.mesh;
+}