2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
20 * ***** END GPL LICENSE BLOCK *****
27 #include "abc_transform.h"
31 #include "DNA_material_types.h"
32 #include "DNA_mesh_types.h"
33 #include "DNA_modifier_types.h"
34 #include "DNA_object_fluidsim.h"
35 #include "DNA_object_types.h"
37 #include "BLI_math_geom.h"
38 #include "BLI_string.h"
40 #include "BKE_cdderivedmesh.h"
42 #include "BKE_material.h"
44 #include "BKE_modifier.h"
45 #include "BKE_object.h"
53 #include "bmesh_tools.h"
56 using Alembic::Abc::FloatArraySample;
57 using Alembic::Abc::ICompoundProperty;
58 using Alembic::Abc::Int32ArraySample;
59 using Alembic::Abc::Int32ArraySamplePtr;
60 using Alembic::Abc::P3fArraySamplePtr;
61 using Alembic::Abc::V2fArraySample;
62 using Alembic::Abc::V3fArraySample;
63 using Alembic::Abc::C4fArraySample;
65 using Alembic::AbcGeom::IFaceSet;
66 using Alembic::AbcGeom::IFaceSetSchema;
67 using Alembic::AbcGeom::IObject;
68 using Alembic::AbcGeom::IPolyMesh;
69 using Alembic::AbcGeom::IPolyMeshSchema;
70 using Alembic::AbcGeom::ISampleSelector;
71 using Alembic::AbcGeom::ISubD;
72 using Alembic::AbcGeom::ISubDSchema;
73 using Alembic::AbcGeom::IV2fGeomParam;
75 using Alembic::AbcGeom::OArrayProperty;
76 using Alembic::AbcGeom::OBoolProperty;
77 using Alembic::AbcGeom::OC3fArrayProperty;
78 using Alembic::AbcGeom::OC3fGeomParam;
79 using Alembic::AbcGeom::OC4fGeomParam;
80 using Alembic::AbcGeom::OCompoundProperty;
81 using Alembic::AbcGeom::OFaceSet;
82 using Alembic::AbcGeom::OFaceSetSchema;
83 using Alembic::AbcGeom::OFloatGeomParam;
84 using Alembic::AbcGeom::OInt32GeomParam;
85 using Alembic::AbcGeom::ON3fArrayProperty;
86 using Alembic::AbcGeom::ON3fGeomParam;
87 using Alembic::AbcGeom::OPolyMesh;
88 using Alembic::AbcGeom::OPolyMeshSchema;
89 using Alembic::AbcGeom::OSubD;
90 using Alembic::AbcGeom::OSubDSchema;
91 using Alembic::AbcGeom::OV2fGeomParam;
92 using Alembic::AbcGeom::OV3fGeomParam;
94 using Alembic::AbcGeom::kFacevaryingScope;
95 using Alembic::AbcGeom::kVaryingScope;
96 using Alembic::AbcGeom::kVertexScope;
97 using Alembic::AbcGeom::kWrapExisting;
98 using Alembic::AbcGeom::UInt32ArraySample;
99 using Alembic::AbcGeom::N3fArraySamplePtr;
100 using Alembic::AbcGeom::IN3fGeomParam;
102 /* ************************************************************************** */
104 /* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
106 static void get_vertices(DerivedMesh *dm, std::vector<Imath::V3f> &points)
109 points.resize(dm->getNumVerts(dm));
111 MVert *verts = dm->getVertArray(dm);
113 for (int i = 0, e = dm->getNumVerts(dm); i < e; ++i) {
114 copy_yup_from_zup(points[i].getValue(), verts[i].co);
118 static void get_topology(DerivedMesh *dm,
119 std::vector<int32_t> &poly_verts,
120 std::vector<int32_t> &loop_counts,
123 const int num_poly = dm->getNumPolys(dm);
124 const int num_loops = dm->getNumLoops(dm);
125 MLoop *mloop = dm->getLoopArray(dm);
126 MPoly *mpoly = dm->getPolyArray(dm);
130 poly_verts.reserve(num_loops);
131 loop_counts.reserve(num_poly);
133 /* NOTE: data needs to be written in the reverse order. */
134 for (int i = 0; i < num_poly; ++i) {
135 MPoly &poly = mpoly[i];
136 loop_counts.push_back(poly.totloop);
138 smooth_normal |= ((poly.flag & ME_SMOOTH) != 0);
140 MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1);
142 for (int j = 0; j < poly.totloop; ++j, --loop) {
143 poly_verts.push_back(loop->v);
148 static void get_creases(DerivedMesh *dm,
149 std::vector<int32_t> &indices,
150 std::vector<int32_t> &lengths,
151 std::vector<float> &sharpnesses)
153 const float factor = 1.0f / 255.0f;
159 MEdge *edge = dm->getEdgeArray(dm);
161 for (int i = 0, e = dm->getNumEdges(dm); i < e; ++i) {
162 const float sharpness = static_cast<float>(edge[i].crease) * factor;
164 if (sharpness != 0.0f) {
165 indices.push_back(edge[i].v1);
166 indices.push_back(edge[i].v2);
167 sharpnesses.push_back(sharpness);
171 lengths.resize(sharpnesses.size(), 2);
174 static void get_vertex_normals(DerivedMesh *dm, std::vector<Imath::V3f> &normals)
177 normals.resize(dm->getNumVerts(dm));
179 MVert *verts = dm->getVertArray(dm);
182 for (int i = 0, e = dm->getNumVerts(dm); i < e; ++i) {
183 normal_short_to_float_v3(no, verts[i].no);
184 copy_yup_from_zup(normals[i].getValue(), no);
188 static void get_loop_normals(DerivedMesh *dm, std::vector<Imath::V3f> &normals)
190 MPoly *mpoly = dm->getPolyArray(dm);
193 MLoop *mloop = dm->getLoopArray(dm);
196 MVert *verts = dm->getVertArray(dm);
198 const float (*lnors)[3] = static_cast<float(*)[3]>(dm->getLoopDataArray(dm, CD_NORMAL));
201 normals.resize(dm->getNumLoops(dm));
203 unsigned loop_index = 0;
205 /* NOTE: data needs to be written in the reverse order. */
208 for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i, ++mp) {
209 ml = mloop + mp->loopstart + (mp->totloop - 1);
211 for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
212 const int index = ml->v;
213 copy_yup_from_zup(normals[loop_index].getValue(), lnors[index]);
220 for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i, ++mp) {
221 ml = mloop + mp->loopstart + (mp->totloop - 1);
223 /* Flat shaded, use common normal for all verts. */
224 if ((mp->flag & ME_SMOOTH) == 0) {
225 BKE_mesh_calc_poly_normal(mp, ml - (mp->totloop - 1), verts, no);
227 for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
228 copy_yup_from_zup(normals[loop_index].getValue(), no);
232 /* Smooth shaded, use individual vert normals. */
233 for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
234 normal_short_to_float_v3(no, verts[ml->v].no);
235 copy_yup_from_zup(normals[loop_index].getValue(), no);
242 /* *************** Modifiers *************** */
244 /* check if the mesh is a subsurf, ignoring disabled modifiers and
245 * displace if it's after subsurf. */
246 static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob)
248 ModifierData *md = static_cast<ModifierData *>(ob->modifiers.last);
250 for (; md; md = md->prev) {
251 if (!modifier_isEnabled(scene, md, eModifierMode_Render)) {
255 if (md->type == eModifierType_Subsurf) {
256 SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData*>(md);
258 if (smd->subdivType == ME_CC_SUBSURF) {
263 /* mesh is not a subsurf. break */
264 if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) {
272 static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob)
274 ModifierData *md = modifiers_findByType(ob, eModifierType_Fluidsim);
276 if (md && (modifier_isEnabled(scene, md, eModifierMode_Render))) {
277 FluidsimModifierData *fsmd = reinterpret_cast<FluidsimModifierData *>(md);
279 if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) {
287 /* ************************************************************************** */
289 AbcMeshWriter::AbcMeshWriter(Scene *scene,
291 AbcTransformWriter *parent,
292 uint32_t time_sampling,
293 ExportSettings &settings)
294 : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
296 m_is_animated = isAnimated();
297 m_subsurf_mod = NULL;
300 /* If the object is static, use the default static time sampling. */
301 if (!m_is_animated) {
305 if (!m_settings.apply_subdiv) {
306 m_subsurf_mod = get_subsurf_modifier(m_scene, m_object);
307 m_is_subd = (m_subsurf_mod != NULL);
310 m_is_liquid = (get_liquid_sim_modifier(m_scene, m_object) != NULL);
312 while (parent->alembicXform().getChildHeader(m_name)) {
316 if (m_settings.use_subdiv_schema && m_is_subd) {
317 OSubD subd(parent->alembicXform(), m_name, m_time_sampling);
318 m_subdiv_schema = subd.getSchema();
321 OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling);
322 m_mesh_schema = mesh.getSchema();
324 OCompoundProperty typeContainer = m_mesh_schema.getUserProperties();
325 OBoolProperty type(typeContainer, "meshtype");
330 AbcMeshWriter::~AbcMeshWriter()
333 m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
337 bool AbcMeshWriter::isAnimated() const
339 /* Check if object has shape keys. */
340 Mesh *me = static_cast<Mesh *>(m_object->data);
346 /* Test modifiers. */
347 ModifierData *md = static_cast<ModifierData *>(m_object->modifiers.first);
350 if (md->type != eModifierType_Subsurf) {
357 return me->adt != NULL;
360 void AbcMeshWriter::do_write()
362 /* We have already stored a sample for this object. */
363 if (!m_first_frame && !m_is_animated)
366 DerivedMesh *dm = getFinalMesh();
369 if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) {
384 void AbcMeshWriter::writeMesh(DerivedMesh *dm)
386 std::vector<Imath::V3f> points, normals;
387 std::vector<int32_t> poly_verts, loop_counts;
389 bool smooth_normal = false;
391 get_vertices(dm, points);
392 get_topology(dm, poly_verts, loop_counts, smooth_normal);
394 if (m_first_frame && m_settings.export_face_sets) {
395 writeFaceSets(dm, m_mesh_schema);
398 m_mesh_sample = OPolyMeshSchema::Sample(V3fArraySample(points),
399 Int32ArraySample(poly_verts),
400 Int32ArraySample(loop_counts));
403 if (m_first_frame && m_settings.export_uvs) {
404 const char *name = get_uv_sample(sample, m_custom_data_config, &dm->loopData);
406 if (!sample.indices.empty() && !sample.uvs.empty()) {
407 OV2fGeomParam::Sample uv_sample;
408 uv_sample.setVals(V2fArraySample(sample.uvs));
409 uv_sample.setIndices(UInt32ArraySample(sample.indices));
410 uv_sample.setScope(kFacevaryingScope);
412 m_mesh_schema.setUVSourceName(name);
413 m_mesh_sample.setUVs(uv_sample);
416 write_custom_data(m_mesh_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPUV);
419 if (m_settings.export_normals) {
421 get_loop_normals(dm, normals);
424 get_vertex_normals(dm, normals);
427 ON3fGeomParam::Sample normals_sample;
428 if (!normals.empty()) {
429 normals_sample.setScope((smooth_normal) ? kFacevaryingScope : kVertexScope);
430 normals_sample.setVals(V3fArraySample(normals));
433 m_mesh_sample.setNormals(normals_sample);
437 std::vector<Imath::V3f> velocities;
438 getVelocities(dm, velocities);
440 m_mesh_sample.setVelocities(V3fArraySample(velocities));
443 m_mesh_sample.setSelfBounds(bounds());
445 m_mesh_schema.set(m_mesh_sample);
447 writeArbGeoParams(dm);
450 void AbcMeshWriter::writeSubD(DerivedMesh *dm)
452 std::vector<float> crease_sharpness;
453 std::vector<Imath::V3f> points;
454 std::vector<int32_t> poly_verts, loop_counts;
455 std::vector<int32_t> crease_indices, crease_lengths;
457 bool smooth_normal = false;
459 get_vertices(dm, points);
460 get_topology(dm, poly_verts, loop_counts, smooth_normal);
461 get_creases(dm, crease_indices, crease_lengths, crease_sharpness);
463 if (m_first_frame && m_settings.export_face_sets) {
464 writeFaceSets(dm, m_subdiv_schema);
467 m_subdiv_sample = OSubDSchema::Sample(V3fArraySample(points),
468 Int32ArraySample(poly_verts),
469 Int32ArraySample(loop_counts));
472 if (m_first_frame && m_settings.export_uvs) {
473 const char *name = get_uv_sample(sample, m_custom_data_config, &dm->loopData);
475 if (!sample.indices.empty() && !sample.uvs.empty()) {
476 OV2fGeomParam::Sample uv_sample;
477 uv_sample.setVals(V2fArraySample(sample.uvs));
478 uv_sample.setIndices(UInt32ArraySample(sample.indices));
479 uv_sample.setScope(kFacevaryingScope);
481 m_subdiv_schema.setUVSourceName(name);
482 m_subdiv_sample.setUVs(uv_sample);
485 write_custom_data(m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPUV);
488 if (!crease_indices.empty()) {
489 m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices));
490 m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths));
491 m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness));
494 m_subdiv_sample.setSelfBounds(bounds());
495 m_subdiv_schema.set(m_subdiv_sample);
497 writeArbGeoParams(dm);
500 template <typename Schema>
501 void AbcMeshWriter::writeFaceSets(DerivedMesh *dm, Schema &schema)
503 std::map< std::string, std::vector<int32_t> > geo_groups;
504 getGeoGroups(dm, geo_groups);
506 std::map< std::string, std::vector<int32_t> >::iterator it;
507 for (it = geo_groups.begin(); it != geo_groups.end(); ++it) {
508 OFaceSet face_set = schema.createFaceSet(it->first);
509 OFaceSetSchema::Sample samp;
510 samp.setFaces(Int32ArraySample(it->second));
511 face_set.getSchema().set(samp);
515 DerivedMesh *AbcMeshWriter::getFinalMesh()
517 /* We don't want subdivided mesh data */
519 m_subsurf_mod->mode |= eModifierMode_DisableTemporary;
522 DerivedMesh *dm = mesh_create_derived_render(m_scene, m_object, CD_MASK_MESH);
525 m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
528 if (m_settings.triangulate) {
529 const bool tag_only = false;
530 const int quad_method = m_settings.quad_method;
531 const int ngon_method = m_settings.ngon_method;
533 BMesh *bm = DM_to_bmesh(dm, true);
535 BM_mesh_triangulate(bm, quad_method, ngon_method, tag_only, NULL, NULL, NULL);
537 DerivedMesh *result = CDDM_from_bmesh(bm, false);
545 m_custom_data_config.pack_uvs = m_settings.pack_uv;
546 m_custom_data_config.mpoly = dm->getPolyArray(dm);
547 m_custom_data_config.mloop = dm->getLoopArray(dm);
548 m_custom_data_config.totpoly = dm->getNumPolys(dm);
549 m_custom_data_config.totloop = dm->getNumLoops(dm);
550 m_custom_data_config.totvert = dm->getNumVerts(dm);
555 void AbcMeshWriter::freeMesh(DerivedMesh *dm)
560 void AbcMeshWriter::writeArbGeoParams(DerivedMesh *dm)
563 /* We don't need anything more for liquid meshes. */
567 if (m_first_frame && m_settings.export_vcols) {
568 if (m_subdiv_schema.valid()) {
569 write_custom_data(m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPCOL);
572 write_custom_data(m_mesh_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPCOL);
577 void AbcMeshWriter::getVelocities(DerivedMesh *dm, std::vector<Imath::V3f> &vels)
579 const int totverts = dm->getNumVerts(dm);
582 vels.resize(totverts);
584 ModifierData *md = get_liquid_sim_modifier(m_scene, m_object);
585 FluidsimModifierData *fmd = reinterpret_cast<FluidsimModifierData *>(md);
586 FluidsimSettings *fss = fmd->fss;
588 if (fss->meshVelocities) {
589 float *mesh_vels = reinterpret_cast<float *>(fss->meshVelocities);
591 for (int i = 0; i < totverts; ++i) {
592 copy_yup_from_zup(vels[i].getValue(), mesh_vels);
597 std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f));
601 void AbcMeshWriter::getGeoGroups(
603 std::map<std::string, std::vector<int32_t> > &geo_groups)
605 const int num_poly = dm->getNumPolys(dm);
606 MPoly *polygons = dm->getPolyArray(dm);
608 for (int i = 0; i < num_poly; ++i) {
609 MPoly ¤t_poly = polygons[i];
610 short mnr = current_poly.mat_nr;
612 Material *mat = give_current_material(m_object, mnr + 1);
618 std::string name = get_id_name(&mat->id);
620 if (geo_groups.find(name) == geo_groups.end()) {
621 std::vector<int32_t> faceArray;
622 geo_groups[name] = faceArray;
625 geo_groups[name].push_back(i);
628 if (geo_groups.size() == 0) {
629 Material *mat = give_current_material(m_object, 1);
631 std::string name = (mat) ? get_id_name(&mat->id) : "default";
633 std::vector<int32_t> faceArray;
635 for (int i = 0, e = dm->getNumTessFaces(dm); i < e; ++i) {
636 faceArray.push_back(i);
639 geo_groups[name] = faceArray;
643 /* ************************************************************************** */
645 /* Some helpers for mesh generation */
648 static void build_mat_map(const Main *bmain, std::map<std::string, Material *> &mat_map)
650 Material *material = static_cast<Material *>(bmain->mat.first);
652 for (; material; material = static_cast<Material *>(material->id.next)) {
653 mat_map[material->id.name + 2] = material;
657 static void assign_materials(Main *bmain, Object *ob, const std::map<std::string, int> &mat_index_map)
659 bool can_assign = true;
660 std::map<std::string, int>::const_iterator it = mat_index_map.begin();
663 for (; it != mat_index_map.end(); ++it, ++matcount) {
664 if (!BKE_object_material_slot_add(ob)) {
670 /* TODO(kevin): use global map? */
671 std::map<std::string, Material *> mat_map;
672 build_mat_map(bmain, mat_map);
674 std::map<std::string, Material *>::iterator mat_iter;
677 it = mat_index_map.begin();
679 for (; it != mat_index_map.end(); ++it) {
680 std::string mat_name = it->first;
681 mat_iter = mat_map.find(mat_name.c_str());
683 Material *assigned_name;
685 if (mat_iter == mat_map.end()) {
686 assigned_name = BKE_material_add(bmain, mat_name.c_str());
687 mat_map[mat_name] = assigned_name;
690 assigned_name = mat_iter->second;
693 assign_material(ob, assigned_name, it->second, BKE_MAT_ASSIGN_OBDATA);
698 } /* namespace utils */
700 /* ************************************************************************** */
702 using Alembic::AbcGeom::UInt32ArraySamplePtr;
703 using Alembic::AbcGeom::V2fArraySamplePtr;
706 Int32ArraySamplePtr face_indices;
707 Int32ArraySamplePtr face_counts;
709 P3fArraySamplePtr positions;
710 P3fArraySamplePtr ceil_positions;
712 N3fArraySamplePtr vertex_normals;
713 N3fArraySamplePtr face_normals;
715 V2fArraySamplePtr uvs;
716 UInt32ArraySamplePtr uvs_indices;
719 static void read_mverts_interp(MVert *mverts, const P3fArraySamplePtr &positions, const P3fArraySamplePtr &ceil_positions, const float weight)
722 for (int i = 0; i < positions->size(); ++i) {
723 MVert &mvert = mverts[i];
724 const Imath::V3f &floor_pos = (*positions)[i];
725 const Imath::V3f &ceil_pos = (*ceil_positions)[i];
727 interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), weight);
728 copy_zup_from_yup(mvert.co, tmp);
734 static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data)
736 MVert *mverts = config.mvert;
737 const P3fArraySamplePtr &positions = mesh_data.positions;
738 const N3fArraySamplePtr &normals = mesh_data.vertex_normals;
740 if ( config.weight != 0.0f
741 && mesh_data.ceil_positions != NULL
742 && mesh_data.ceil_positions->size() == positions->size())
744 read_mverts_interp(mverts, positions, mesh_data.ceil_positions, config.weight);
748 read_mverts(mverts, positions, normals);
751 void read_mverts(MVert *mverts, const P3fArraySamplePtr &positions, const N3fArraySamplePtr &normals)
753 for (int i = 0; i < positions->size(); ++i) {
754 MVert &mvert = mverts[i];
755 Imath::V3f pos_in = (*positions)[i];
757 copy_zup_from_yup(mvert.co, pos_in.getValue());
762 Imath::V3f nor_in = (*normals)[i];
765 normal_float_to_short_v3(no, nor_in.getValue());
767 copy_zup_from_yup(mvert.no, no);
772 static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data)
774 MPoly *mpolys = config.mpoly;
775 MLoop *mloops = config.mloop;
776 MLoopUV *mloopuvs = config.mloopuv;
778 const Int32ArraySamplePtr &face_indices = mesh_data.face_indices;
779 const Int32ArraySamplePtr &face_counts = mesh_data.face_counts;
780 const V2fArraySamplePtr &uvs = mesh_data.uvs;
781 const UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices;
782 const N3fArraySamplePtr &normals = mesh_data.face_normals;
784 const bool do_uvs = (mloopuvs && uvs && uvs_indices) && (uvs_indices->size() == face_indices->size());
785 unsigned int loop_index = 0;
786 unsigned int rev_loop_index = 0;
787 unsigned int uv_index = 0;
789 for (int i = 0; i < face_counts->size(); ++i) {
790 const int face_size = (*face_counts)[i];
792 MPoly &poly = mpolys[i];
793 poly.loopstart = loop_index;
794 poly.totloop = face_size;
796 if (normals != NULL) {
797 poly.flag |= ME_SMOOTH;
800 /* NOTE: Alembic data is stored in the reverse order. */
801 rev_loop_index = loop_index + (face_size - 1);
803 for (int f = 0; f < face_size; ++f, ++loop_index, --rev_loop_index) {
804 MLoop &loop = mloops[rev_loop_index];
805 loop.v = (*face_indices)[loop_index];
808 MLoopUV &loopuv = mloopuvs[rev_loop_index];
810 uv_index = (*uvs_indices)[loop_index];
811 loopuv.uv[0] = (*uvs)[uv_index][0];
812 loopuv.uv[1] = (*uvs)[uv_index][1];
818 ABC_INLINE void read_uvs_params(CDStreamConfig &config,
819 AbcMeshData &abc_data,
820 const IV2fGeomParam &uv,
821 const ISampleSelector &selector)
827 IV2fGeomParam::Sample uvsamp;
828 uv.getIndexed(uvsamp, selector);
830 abc_data.uvs = uvsamp.getVals();
831 abc_data.uvs_indices = uvsamp.getIndices();
833 if (abc_data.uvs_indices->size() == config.totloop) {
834 std::string name = Alembic::Abc::GetSourceName(uv.getMetaData());
836 /* According to the convention, primary UVs should have had their name
837 * set using Alembic::Abc::SetSourceName, but you can't expect everyone
838 * to follow it! :) */
843 void *cd_ptr = config.add_customdata_cb(config.user_data, name.c_str(), CD_MLOOPUV);
844 config.mloopuv = static_cast<MLoopUV *>(cd_ptr);
848 /* TODO(kevin): normals from Alembic files are not read in anymore, this is due
849 * to the fact that there are many issues that are not so easy to solve, mainly
850 * regarding the way normals are handled in Blender (MPoly.flag vs loop normals).
852 ABC_INLINE void read_normals_params(AbcMeshData &abc_data,
853 const IN3fGeomParam &normals,
854 const ISampleSelector &selector)
856 if (!normals.valid()) {
860 IN3fGeomParam::Sample normsamp = normals.getExpandedValue(selector);
862 if (normals.getScope() == kFacevaryingScope) {
863 abc_data.face_normals = normsamp.getVals();
865 else if ((normals.getScope() == kVertexScope) || (normals.getScope() == kVaryingScope)) {
866 abc_data.vertex_normals = N3fArraySamplePtr();
870 static bool check_smooth_poly_flag(DerivedMesh *dm)
872 MPoly *mpolys = dm->getPolyArray(dm);
874 for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) {
875 MPoly &poly = mpolys[i];
877 if ((poly.flag & ME_SMOOTH) != 0) {
885 static void set_smooth_poly_flag(DerivedMesh *dm)
887 MPoly *mpolys = dm->getPolyArray(dm);
889 for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) {
890 MPoly &poly = mpolys[i];
891 poly.flag |= ME_SMOOTH;
895 static void *add_customdata_cb(void *user_data, const char *name, int data_type)
897 DerivedMesh *dm = static_cast<DerivedMesh *>(user_data);
898 CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
900 CustomData *loopdata;
903 /* unsupported custom data type -- don't do anything. */
904 if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) {
908 loopdata = dm->getLoopDataLayout(dm);
909 cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name);
910 if (cd_ptr != NULL) {
911 /* layer already exists, so just return it. */
915 /* create a new layer, taking care to construct the hopefully-soon-to-be-removed
916 * CD_MTEXPOLY layer too, with the same name. */
917 numloops = dm->getNumLoops(dm);
918 cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT,
919 NULL, numloops, name);
923 static void get_weight_and_index(CDStreamConfig &config,
924 Alembic::AbcCoreAbstract::TimeSamplingPtr time_sampling,
925 size_t samples_number)
927 Alembic::AbcGeom::index_t i0, i1;
929 config.weight = get_weight_and_index(config.time,
936 config.ceil_index = i1;
939 static void read_mesh_sample(ImportSettings *settings,
940 const IPolyMeshSchema &schema,
941 const ISampleSelector &selector,
942 CDStreamConfig &config,
945 const IPolyMeshSchema::Sample sample = schema.getValue(selector);
947 AbcMeshData abc_mesh_data;
948 abc_mesh_data.face_counts = sample.getFaceCounts();
949 abc_mesh_data.face_indices = sample.getFaceIndices();
950 abc_mesh_data.positions = sample.getPositions();
952 read_normals_params(abc_mesh_data, schema.getNormalsParam(), selector);
954 do_normals = (abc_mesh_data.face_normals != NULL);
956 get_weight_and_index(config, schema.getTimeSampling(), schema.getNumSamples());
958 if (config.weight != 0.0f) {
959 Alembic::AbcGeom::IPolyMeshSchema::Sample ceil_sample;
960 schema.get(ceil_sample, Alembic::Abc::ISampleSelector(config.ceil_index));
961 abc_mesh_data.ceil_positions = ceil_sample.getPositions();
964 if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
965 read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
968 if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
969 read_mverts(config, abc_mesh_data);
972 if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
973 read_mpolys(config, abc_mesh_data);
976 if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
977 read_custom_data(schema.getArbGeomParams(), config, selector);
980 /* TODO: face sets */
983 CDStreamConfig get_config(DerivedMesh *dm)
985 CDStreamConfig config;
987 config.user_data = dm;
988 config.mvert = dm->getVertArray(dm);
989 config.mloop = dm->getLoopArray(dm);
990 config.mpoly = dm->getPolyArray(dm);
991 config.totloop = dm->getNumLoops(dm);
992 config.totpoly = dm->getNumPolys(dm);
993 config.loopdata = dm->getLoopDataLayout(dm);
994 config.add_customdata_cb = add_customdata_cb;
999 /* ************************************************************************** */
1001 AbcMeshReader::AbcMeshReader(const IObject &object, ImportSettings &settings)
1002 : AbcObjectReader(object, settings)
1004 m_settings->read_flag |= MOD_MESHSEQ_READ_ALL;
1006 IPolyMesh ipoly_mesh(m_iobject, kWrapExisting);
1007 m_schema = ipoly_mesh.getSchema();
1009 get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time);
1012 bool AbcMeshReader::valid() const
1014 return m_schema.valid();
1017 void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
1019 Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
1021 m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
1022 m_object->data = mesh;
1024 DerivedMesh *dm = CDDM_from_mesh(mesh);
1025 DerivedMesh *ndm = this->read_derivedmesh(dm, sample_sel, MOD_MESHSEQ_READ_ALL, NULL);
1031 DM_to_mesh(ndm, mesh, m_object, CD_MASK_MESH, true);
1033 if (m_settings->validate_meshes) {
1034 BKE_mesh_validate(mesh, false, false);
1037 readFaceSetsSample(bmain, mesh, 0, sample_sel);
1039 if (has_animations(m_schema, m_settings)) {
1044 bool AbcMeshReader::accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
1045 const Object *const ob,
1046 const char **err_str) const
1048 if (!Alembic::AbcGeom::IPolyMesh::matches(alembic_header)) {
1049 *err_str = "Object type mismatch, Alembic object path pointed to PolyMesh when importing, but not any more.";
1053 if (ob->type != OB_MESH) {
1054 *err_str = "Object type mismatch, Alembic object path points to PolyMesh.";
1061 DerivedMesh *AbcMeshReader::read_derivedmesh(DerivedMesh *dm,
1062 const ISampleSelector &sample_sel,
1064 const char **err_str)
1066 const IPolyMeshSchema::Sample sample = m_schema.getValue(sample_sel);
1068 const P3fArraySamplePtr &positions = sample.getPositions();
1069 const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
1070 const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
1072 DerivedMesh *new_dm = NULL;
1074 /* Only read point data when streaming meshes, unless we need to create new ones. */
1075 ImportSettings settings;
1076 settings.read_flag |= read_flag;
1078 if (dm->getNumVerts(dm) != positions->size()) {
1079 new_dm = CDDM_from_template(dm,
1083 face_indices->size(),
1084 face_counts->size());
1086 settings.read_flag |= MOD_MESHSEQ_READ_ALL;
1089 /* If the face count changed (e.g. by triangulation), only read points.
1090 * This prevents crash from T49813.
1091 * TODO(kevin): perhaps find a better way to do this? */
1092 if (face_counts->size() != dm->getNumPolys(dm) ||
1093 face_indices->size() != dm->getNumLoops(dm))
1095 settings.read_flag = MOD_MESHSEQ_READ_VERT;
1098 *err_str = "Topology has changed, perhaps by triangulating the"
1099 " mesh. Only vertices will be read!";
1104 CDStreamConfig config = get_config(new_dm ? new_dm : dm);
1105 config.time = sample_sel.getRequestedTime();
1107 bool do_normals = false;
1108 read_mesh_sample(&settings, m_schema, sample_sel, config, do_normals);
1111 /* Check if we had ME_SMOOTH flag set to restore it. */
1112 if (!do_normals && check_smooth_poly_flag(dm)) {
1113 set_smooth_poly_flag(new_dm);
1116 CDDM_calc_normals(new_dm);
1117 CDDM_calc_edges(new_dm);
1123 CDDM_calc_normals(dm);
1129 void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start,
1130 const ISampleSelector &sample_sel)
1132 std::vector<std::string> face_sets;
1133 m_schema.getFaceSetNames(face_sets);
1135 if (face_sets.empty()) {
1139 std::map<std::string, int> mat_map;
1140 int current_mat = 0;
1142 for (int i = 0; i < face_sets.size(); ++i) {
1143 const std::string &grp_name = face_sets[i];
1145 if (mat_map.find(grp_name) == mat_map.end()) {
1146 mat_map[grp_name] = 1 + current_mat++;
1149 const int assigned_mat = mat_map[grp_name];
1151 const IFaceSet faceset = m_schema.getFaceSet(grp_name);
1153 if (!faceset.valid()) {
1157 const IFaceSetSchema face_schem = faceset.getSchema();
1158 const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
1159 const Int32ArraySamplePtr group_faces = face_sample.getFaces();
1160 const size_t num_group_faces = group_faces->size();
1162 for (size_t l = 0; l < num_group_faces; l++) {
1163 size_t pos = (*group_faces)[l] + poly_start;
1165 if (pos >= mesh->totpoly) {
1166 std::cerr << "Faceset overflow on " << faceset.getName() << '\n';
1170 MPoly &poly = mesh->mpoly[pos];
1171 poly.mat_nr = assigned_mat - 1;
1175 utils::assign_materials(bmain, m_object, mat_map);
1178 /* ************************************************************************** */
1180 ABC_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2)
1182 for (int i = 0, e = totedge; i < e; ++i) {
1183 MEdge &edge = edges[i];
1185 if (edge.v1 == v1 && edge.v2 == v2) {
1193 static void read_subd_sample(ImportSettings *settings,
1194 const ISubDSchema &schema,
1195 const ISampleSelector &selector,
1196 CDStreamConfig &config)
1198 const ISubDSchema::Sample sample = schema.getValue(selector);
1200 AbcMeshData abc_mesh_data;
1201 abc_mesh_data.face_counts = sample.getFaceCounts();
1202 abc_mesh_data.face_indices = sample.getFaceIndices();
1203 abc_mesh_data.vertex_normals = N3fArraySamplePtr();
1204 abc_mesh_data.face_normals = N3fArraySamplePtr();
1205 abc_mesh_data.positions = sample.getPositions();
1207 get_weight_and_index(config, schema.getTimeSampling(), schema.getNumSamples());
1209 if (config.weight != 0.0f) {
1210 Alembic::AbcGeom::ISubDSchema::Sample ceil_sample;
1211 schema.get(ceil_sample, Alembic::Abc::ISampleSelector(config.ceil_index));
1212 abc_mesh_data.ceil_positions = ceil_sample.getPositions();
1215 if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
1216 read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
1219 if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
1220 read_mverts(config, abc_mesh_data);
1223 if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
1224 read_mpolys(config, abc_mesh_data);
1227 if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
1228 read_custom_data(schema.getArbGeomParams(), config, selector);
1231 /* TODO: face sets */
1234 /* ************************************************************************** */
1236 AbcSubDReader::AbcSubDReader(const IObject &object, ImportSettings &settings)
1237 : AbcObjectReader(object, settings)
1239 m_settings->read_flag |= MOD_MESHSEQ_READ_ALL;
1241 ISubD isubd_mesh(m_iobject, kWrapExisting);
1242 m_schema = isubd_mesh.getSchema();
1244 get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time);
1247 bool AbcSubDReader::valid() const
1249 return m_schema.valid();
1252 bool AbcSubDReader::accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
1253 const Object *const ob,
1254 const char **err_str) const
1256 if (!Alembic::AbcGeom::ISubD::matches(alembic_header)) {
1257 *err_str = "Object type mismatch, Alembic object path pointed to SubD when importing, but not any more.";
1261 if (ob->type != OB_MESH) {
1262 *err_str = "Object type mismatch, Alembic object path points to SubD.";
1269 void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
1271 Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
1273 m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
1274 m_object->data = mesh;
1276 DerivedMesh *dm = CDDM_from_mesh(mesh);
1277 DerivedMesh *ndm = this->read_derivedmesh(dm, sample_sel, MOD_MESHSEQ_READ_ALL, NULL);
1283 DM_to_mesh(ndm, mesh, m_object, CD_MASK_MESH, true);
1285 const ISubDSchema::Sample sample = m_schema.getValue(sample_sel);
1286 Int32ArraySamplePtr indices = sample.getCreaseIndices();
1287 Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses();
1289 MEdge *edges = mesh->medge;
1291 if (indices && sharpnesses) {
1292 for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, ++s) {
1293 MEdge *edge = find_edge(edges, mesh->totedge, (*indices)[i], (*indices)[i + 1]);
1296 edge->crease = FTOCHAR((*sharpnesses)[s]);
1300 mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE;
1303 BKE_mesh_calc_normals(mesh);
1304 BKE_mesh_calc_edges(mesh, false, false);
1306 if (m_settings->validate_meshes) {
1307 BKE_mesh_validate(mesh, false, false);
1310 if (has_animations(m_schema, m_settings)) {
1315 DerivedMesh *AbcSubDReader::read_derivedmesh(DerivedMesh *dm,
1316 const ISampleSelector &sample_sel,
1318 const char **err_str)
1320 const ISubDSchema::Sample sample = m_schema.getValue(sample_sel);
1322 const P3fArraySamplePtr &positions = sample.getPositions();
1323 const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
1324 const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
1326 DerivedMesh *new_dm = NULL;
1328 ImportSettings settings;
1329 settings.read_flag |= read_flag;
1331 if (dm->getNumVerts(dm) != positions->size()) {
1332 new_dm = CDDM_from_template(dm,
1336 face_indices->size(),
1337 face_counts->size());
1339 settings.read_flag |= MOD_MESHSEQ_READ_ALL;
1342 /* If the face count changed (e.g. by triangulation), only read points.
1343 * This prevents crash from T49813.
1344 * TODO(kevin): perhaps find a better way to do this? */
1345 if (face_counts->size() != dm->getNumPolys(dm) ||
1346 face_indices->size() != dm->getNumLoops(dm))
1348 settings.read_flag = MOD_MESHSEQ_READ_VERT;
1351 *err_str = "Topology has changed, perhaps by triangulating the"
1352 " mesh. Only vertices will be read!";
1357 /* Only read point data when streaming meshes, unless we need to create new ones. */
1358 CDStreamConfig config = get_config(new_dm ? new_dm : dm);
1359 config.time = sample_sel.getRequestedTime();
1360 read_subd_sample(&settings, m_schema, sample_sel, config);
1363 /* Check if we had ME_SMOOTH flag set to restore it. */
1364 if (check_smooth_poly_flag(dm)) {
1365 set_smooth_poly_flag(new_dm);
1368 CDDM_calc_normals(new_dm);
1369 CDDM_calc_edges(new_dm);