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:
authorLukas Tönne <lukas.toenne@gmail.com>2016-08-09 17:37:15 +0300
committerLukas Tönne <lukas.toenne@gmail.com>2016-08-09 17:37:15 +0300
commit4a801f6c6f0421ffd515c12422dd197441440520 (patch)
tree5415e844b460bb2aa07b9467c72e13abc2e61228 /source/blender/alembic/intern
parent49c63d46db8c055152d9e431e89405f9b51a4bbe (diff)
parent02719521d2e25abcc8ffcccc086d3a651986f52f (diff)
Merge branch 'master' into object_nodesobject_nodes
Diffstat (limited to 'source/blender/alembic/intern')
-rw-r--r--source/blender/alembic/intern/abc_camera.cc162
-rw-r--r--source/blender/alembic/intern/abc_camera.h61
-rw-r--r--source/blender/alembic/intern/abc_curves.cc355
-rw-r--r--source/blender/alembic/intern/abc_curves.h65
-rw-r--r--source/blender/alembic/intern/abc_customdata.cc379
-rw-r--r--source/blender/alembic/intern/abc_customdata.h93
-rw-r--r--source/blender/alembic/intern/abc_exporter.cc600
-rw-r--r--source/blender/alembic/intern/abc_exporter.h112
-rw-r--r--source/blender/alembic/intern/abc_hair.cc290
-rw-r--r--source/blender/alembic/intern/abc_hair.h66
-rw-r--r--source/blender/alembic/intern/abc_mesh.cc1213
-rw-r--r--source/blender/alembic/intern/abc_mesh.h152
-rw-r--r--source/blender/alembic/intern/abc_nurbs.cc367
-rw-r--r--source/blender/alembic/intern/abc_nurbs.h63
-rw-r--r--source/blender/alembic/intern/abc_object.cc238
-rw-r--r--source/blender/alembic/intern/abc_object.h169
-rw-r--r--source/blender/alembic/intern/abc_points.cc198
-rw-r--r--source/blender/alembic/intern/abc_points.h70
-rw-r--r--source/blender/alembic/intern/abc_transform.cc152
-rw-r--r--source/blender/alembic/intern/abc_transform.h73
-rw-r--r--source/blender/alembic/intern/abc_util.cc437
-rw-r--r--source/blender/alembic/intern/abc_util.h125
-rw-r--r--source/blender/alembic/intern/alembic_capi.cc1136
23 files changed, 6576 insertions, 0 deletions
diff --git a/source/blender/alembic/intern/abc_camera.cc b/source/blender/alembic/intern/abc_camera.cc
new file mode 100644
index 00000000000..38a6d3d33cf
--- /dev/null
+++ b/source/blender/alembic/intern/abc_camera.cc
@@ -0,0 +1,162 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_camera.h"
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_camera_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_camera.h"
+#include "BKE_object.h"
+
+#include "BLI_math.h"
+#include "BLI_string.h"
+}
+
+using Alembic::AbcGeom::ICamera;
+using Alembic::AbcGeom::ICompoundProperty;
+using Alembic::AbcGeom::IFloatProperty;
+using Alembic::AbcGeom::ISampleSelector;
+
+using Alembic::AbcGeom::OCamera;
+using Alembic::AbcGeom::OFloatProperty;
+
+using Alembic::AbcGeom::CameraSample;
+using Alembic::AbcGeom::kWrapExisting;
+
+/* ************************************************************************** */
+
+AbcCameraWriter::AbcCameraWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings)
+ : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+ OCamera camera(parent->alembicXform(), m_name, m_time_sampling);
+ m_camera_schema = camera.getSchema();
+
+ m_custom_data_container = m_camera_schema.getUserProperties();
+ m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling);
+ m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling);
+}
+
+void AbcCameraWriter::do_write()
+{
+ Camera *cam = static_cast<Camera *>(m_object->data);
+
+ m_stereo_distance.set(cam->stereo.convergence_distance);
+ m_eye_separation.set(cam->stereo.interocular_distance);
+
+ const double apperture_x = cam->sensor_x / 10.0;
+ const double apperture_y = cam->sensor_y / 10.0;
+ const double film_aspect = apperture_x / apperture_y;
+
+ m_camera_sample.setFocalLength(cam->lens);
+ m_camera_sample.setHorizontalAperture(apperture_x);
+ m_camera_sample.setVerticalAperture(apperture_y);
+ m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx);
+ m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect);
+ m_camera_sample.setNearClippingPlane(cam->clipsta);
+ m_camera_sample.setFarClippingPlane(cam->clipend);
+
+ if (cam->dof_ob) {
+ Imath::V3f v(m_object->loc[0] - cam->dof_ob->loc[0],
+ m_object->loc[1] - cam->dof_ob->loc[1],
+ m_object->loc[2] - cam->dof_ob->loc[2]);
+ m_camera_sample.setFocusDistance(v.length());
+ }
+ else {
+ m_camera_sample.setFocusDistance(cam->gpu_dof.focus_distance);
+ }
+
+ /* Blender camera does not have an fstop param, so try to find a custom prop
+ * instead. */
+ m_camera_sample.setFStop(cam->gpu_dof.fstop);
+
+ m_camera_sample.setLensSqueezeRatio(1.0);
+ m_camera_schema.set(m_camera_sample);
+}
+
+/* ************************************************************************** */
+
+AbcCameraReader::AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ ICamera abc_cam(m_iobject, kWrapExisting);
+ m_schema = abc_cam.getSchema();
+
+ get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcCameraReader::valid() const
+{
+ return m_schema.valid();
+}
+
+void AbcCameraReader::readObjectData(Main *bmain, float time)
+{
+ Camera *bcam = static_cast<Camera *>(BKE_camera_add(bmain, "abc_camera"));
+
+ ISampleSelector sample_sel(time);
+ CameraSample cam_sample;
+ m_schema.get(cam_sample, sample_sel);
+
+ ICompoundProperty customDataContainer = m_schema.getUserProperties();
+
+ if (customDataContainer.valid() &&
+ customDataContainer.getPropertyHeader("stereoDistance") &&
+ customDataContainer.getPropertyHeader("eyeSeparation"))
+ {
+ IFloatProperty convergence_plane(customDataContainer, "stereoDistance");
+ IFloatProperty eye_separation(customDataContainer, "eyeSeparation");
+
+ bcam->stereo.interocular_distance = eye_separation.getValue(sample_sel);
+ bcam->stereo.convergence_distance = convergence_plane.getValue(sample_sel);
+ }
+
+ const float lens = cam_sample.getFocalLength();
+ const float apperture_x = cam_sample.getHorizontalAperture();
+ const float apperture_y = cam_sample.getVerticalAperture();
+ const float h_film_offset = cam_sample.getHorizontalFilmOffset();
+ const float v_film_offset = cam_sample.getVerticalFilmOffset();
+ const float film_aspect = apperture_x / apperture_y;
+
+ bcam->lens = lens;
+ bcam->sensor_x = apperture_x * 10;
+ bcam->sensor_y = apperture_y * 10;
+ bcam->shiftx = h_film_offset / apperture_x;
+ bcam->shifty = v_film_offset / apperture_y / film_aspect;
+ bcam->clipsta = max_ff(0.1f, cam_sample.getNearClippingPlane());
+ bcam->clipend = cam_sample.getFarClippingPlane();
+ bcam->gpu_dof.focus_distance = cam_sample.getFocusDistance();
+ bcam->gpu_dof.fstop = cam_sample.getFStop();
+
+ BLI_strncpy(bcam->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1);
+
+ m_object = BKE_object_add_only_object(bmain, OB_CAMERA, m_object_name.c_str());
+ m_object->data = bcam;
+}
diff --git a/source/blender/alembic/intern/abc_camera.h b/source/blender/alembic/intern/abc_camera.h
new file mode 100644
index 00000000000..fafb4d3eb39
--- /dev/null
+++ b/source/blender/alembic/intern/abc_camera.h
@@ -0,0 +1,61 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_CAMERA_H__
+#define __ABC_CAMERA_H__
+
+#include "abc_object.h"
+
+/* ************************************************************************** */
+
+class AbcCameraWriter : public AbcObjectWriter {
+ Alembic::AbcGeom::OCameraSchema m_camera_schema;
+ Alembic::AbcGeom::CameraSample m_camera_sample;
+ Alembic::AbcGeom::OCompoundProperty m_custom_data_container;
+ Alembic::AbcGeom::OFloatProperty m_stereo_distance;
+ Alembic::AbcGeom::OFloatProperty m_eye_separation;
+
+public:
+ AbcCameraWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings);
+
+private:
+ virtual void do_write();
+};
+
+/* ************************************************************************** */
+
+class AbcCameraReader : public AbcObjectReader {
+ Alembic::AbcGeom::ICameraSchema m_schema;
+
+public:
+ AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+};
+
+#endif /* __ABC_CAMERA_H__ */ \ No newline at end of file
diff --git a/source/blender/alembic/intern/abc_curves.cc b/source/blender/alembic/intern/abc_curves.cc
new file mode 100644
index 00000000000..4e1e4e7e490
--- /dev/null
+++ b/source/blender/alembic/intern/abc_curves.cc
@@ -0,0 +1,355 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#include "abc_curves.h"
+
+#include <cstdio>
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_listbase.h"
+
+#include "BKE_curve.h"
+#include "BKE_object.h"
+
+#include "ED_curve.h"
+}
+
+using Alembic::Abc::IInt32ArrayProperty;
+using Alembic::Abc::Int32ArraySamplePtr;
+using Alembic::Abc::FloatArraySamplePtr;
+using Alembic::Abc::P3fArraySamplePtr;
+using Alembic::Abc::UcharArraySamplePtr;
+
+using Alembic::AbcGeom::ICurves;
+using Alembic::AbcGeom::ICurvesSchema;
+using Alembic::AbcGeom::IFloatGeomParam;
+using Alembic::AbcGeom::ISampleSelector;
+using Alembic::AbcGeom::kWrapExisting;
+using Alembic::AbcGeom::CurvePeriodicity;
+
+using Alembic::AbcGeom::OCurves;
+using Alembic::AbcGeom::OCurvesSchema;
+using Alembic::AbcGeom::ON3fGeomParam;
+using Alembic::AbcGeom::OV2fGeomParam;
+
+/* ************************************************************************** */
+
+AbcCurveWriter::AbcCurveWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings)
+ : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+ OCurves curves(parent->alembicXform(), m_name, m_time_sampling);
+ m_schema = curves.getSchema();
+}
+
+void AbcCurveWriter::do_write()
+{
+ Curve *curve = static_cast<Curve *>(m_object->data);
+
+ std::vector<Imath::V3f> verts;
+ std::vector<int32_t> vert_counts;
+ std::vector<float> widths;
+ std::vector<float> weights;
+ std::vector<float> knots;
+ std::vector<uint8_t> orders;
+ Imath::V3f temp_vert;
+
+ Alembic::AbcGeom::BasisType curve_basis;
+ Alembic::AbcGeom::CurveType curve_type;
+ Alembic::AbcGeom::CurvePeriodicity periodicity;
+
+ Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
+ for (; nurbs; nurbs = nurbs->next) {
+ if (nurbs->bp) {
+ curve_basis = Alembic::AbcGeom::kNoBasis;
+ curve_type = Alembic::AbcGeom::kLinear;
+
+ const int totpoint = nurbs->pntsu * nurbs->pntsv;
+
+ const BPoint *point = nurbs->bp;
+
+ for (int i = 0; i < totpoint; ++i, ++point) {
+ copy_zup_yup(temp_vert.getValue(), point->vec);
+ verts.push_back(temp_vert);
+ weights.push_back(point->vec[3]);
+ widths.push_back(point->radius);
+ }
+ }
+ else if (nurbs->bezt) {
+ curve_basis = Alembic::AbcGeom::kBezierBasis;
+ curve_type = Alembic::AbcGeom::kCubic;
+
+ const int totpoint = nurbs->pntsu;
+
+ const BezTriple *bezier = nurbs->bezt;
+
+ /* TODO(kevin): store info about handles, Alembic doesn't have this. */
+ for (int i = 0; i < totpoint; ++i, ++bezier) {
+ copy_zup_yup(temp_vert.getValue(), bezier->vec[1]);
+ verts.push_back(temp_vert);
+ widths.push_back(bezier->radius);
+ }
+ }
+
+ if ((nurbs->flagu & CU_NURB_ENDPOINT) != 0) {
+ periodicity = Alembic::AbcGeom::kNonPeriodic;
+ }
+ else if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) {
+ periodicity = Alembic::AbcGeom::kPeriodic;
+
+ /* Duplicate the start points to indicate that the curve is actually
+ * cyclic since other software need those.
+ */
+
+ for (int i = 0; i < nurbs->orderu; ++i) {
+ verts.push_back(verts[i]);
+ }
+ }
+
+ if (nurbs->knotsu != NULL) {
+ const size_t num_knots = KNOTSU(nurbs);
+
+ /* Add an extra knot at the beggining and end of the array since most apps
+ * require/expect them. */
+ knots.resize(num_knots + 2);
+
+ for (int i = 0; i < num_knots; ++i) {
+ knots[i + 1] = nurbs->knotsu[i];
+ }
+
+ if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) {
+ knots[0] = nurbs->knotsu[0];
+ knots[num_knots - 1] = nurbs->knotsu[num_knots - 1];
+ }
+ else {
+ knots[0] = (2.0f * nurbs->knotsu[0] - nurbs->knotsu[1]);
+ knots[num_knots - 1] = (2.0f * nurbs->knotsu[num_knots - 1] - nurbs->knotsu[num_knots - 2]);
+ }
+ }
+
+ orders.push_back(nurbs->orderu + 1);
+ vert_counts.push_back(verts.size());
+ }
+
+ Alembic::AbcGeom::OFloatGeomParam::Sample width_sample;
+ width_sample.setVals(widths);
+
+ m_sample = OCurvesSchema::Sample(verts,
+ vert_counts,
+ curve_type,
+ periodicity,
+ width_sample,
+ OV2fGeomParam::Sample(), /* UVs */
+ ON3fGeomParam::Sample(), /* normals */
+ curve_basis,
+ weights,
+ orders,
+ knots);
+
+ m_sample.setSelfBounds(bounds());
+ m_schema.set(m_sample);
+}
+
+/* ************************************************************************** */
+
+AbcCurveReader::AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ ICurves abc_curves(object, kWrapExisting);
+ m_curves_schema = abc_curves.getSchema();
+
+ get_min_max_time(m_curves_schema, m_min_time, m_max_time);
+}
+
+bool AbcCurveReader::valid() const
+{
+ return m_curves_schema.valid();
+}
+
+void AbcCurveReader::readObjectData(Main *bmain, float time)
+{
+ Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE);
+
+ cu->flag |= CU_DEFORM_FILL | CU_3D;
+ cu->actvert = CU_ACT_NONE;
+
+ m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str());
+ m_object->data = cu;
+
+ read_curve_sample(cu, m_curves_schema, time);
+
+ if (has_animations(m_curves_schema, m_settings)) {
+ addCacheModifier();
+ }
+}
+
+/* ************************************************************************** */
+
+void read_curve_sample(Curve *cu, const ICurvesSchema &schema, const float time)
+{
+ const ISampleSelector sample_sel(time);
+ ICurvesSchema::Sample smp = schema.getValue(sample_sel);
+ const Int32ArraySamplePtr num_vertices = smp.getCurvesNumVertices();
+ const P3fArraySamplePtr positions = smp.getPositions();
+ const FloatArraySamplePtr weights = smp.getPositionWeights();
+ const FloatArraySamplePtr knots = smp.getKnots();
+ const CurvePeriodicity periodicity = smp.getWrap();
+ const UcharArraySamplePtr orders = smp.getOrders();
+
+ const IFloatGeomParam widths_param = schema.getWidthsParam();
+ FloatArraySamplePtr radiuses;
+
+ if (widths_param.valid()) {
+ IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel);
+ radiuses = wsample.getVals();
+ }
+
+ int knot_offset = 0;
+
+ size_t idx = 0;
+ for (size_t i = 0; i < num_vertices->size(); ++i) {
+ const int num_verts = (*num_vertices)[i];
+
+ Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb"));
+ nu->resolu = cu->resolu;
+ nu->resolv = cu->resolv;
+ nu->pntsu = num_verts;
+ nu->pntsv = 1;
+ nu->flag |= CU_SMOOTH;
+
+ nu->orderu = num_verts;
+
+ if (smp.getType() == Alembic::AbcGeom::kCubic) {
+ nu->orderu = 3;
+ }
+ else if (orders && orders->size() > i) {
+ nu->orderu = static_cast<short>((*orders)[i] - 1);
+ }
+
+ if (periodicity == Alembic::AbcGeom::kNonPeriodic) {
+ nu->flagu |= CU_NURB_ENDPOINT;
+ }
+ else if (periodicity == Alembic::AbcGeom::kPeriodic) {
+ nu->flagu |= CU_NURB_CYCLIC;
+
+ /* Check the number of points which overlap, we don't have
+ * overlapping points in Blender, but other software do use them to
+ * indicate that a curve is actually cyclic. Usually the number of
+ * overlapping points is equal to the order/degree of the curve.
+ */
+
+ const int start = idx;
+ const int end = idx + num_verts;
+ int overlap = 0;
+
+ for (int j = start, k = end - nu->orderu; j < nu->orderu; ++j, ++k) {
+ const Imath::V3f &p1 = (*positions)[j];
+ const Imath::V3f &p2 = (*positions)[k];
+
+ if (p1 != p2) {
+ break;
+ }
+
+ ++overlap;
+ }
+
+ /* TODO: Special case, need to figure out how it coincides with knots. */
+ if (overlap == 0 && num_verts > 2 && (*positions)[start] == (*positions)[end - 1]) {
+ overlap = 1;
+ }
+
+ /* There is no real cycles. */
+ if (overlap == 0) {
+ nu->flagu &= ~CU_NURB_CYCLIC;
+ nu->flagu |= CU_NURB_ENDPOINT;
+ }
+
+ nu->pntsu -= overlap;
+ }
+
+ const bool do_weights = (weights != NULL) && (weights->size() > 1);
+ float weight = 1.0f;
+
+ const bool do_radius = (radiuses != NULL) && (radiuses->size() > 1);
+ float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : 1.0f;
+
+ nu->type = CU_NURBS;
+
+ nu->bp = static_cast<BPoint *>(MEM_callocN(sizeof(BPoint) * nu->pntsu, "abc_getnurb"));
+ BPoint *bp = nu->bp;
+
+ for (int j = 0; j < nu->pntsu; ++j, ++bp, ++idx) {
+ const Imath::V3f &pos = (*positions)[idx];
+
+ if (do_radius) {
+ radius = (*radiuses)[idx];
+ }
+
+ if (do_weights) {
+ weight = (*weights)[idx];
+ }
+
+ copy_yup_zup(bp->vec, pos.getValue());
+ bp->vec[3] = weight;
+ bp->f1 = SELECT;
+ bp->radius = radius;
+ bp->weight = 1.0f;
+ }
+
+ if (knots && knots->size() != 0) {
+ nu->knotsu = static_cast<float *>(MEM_callocN(KNOTSU(nu) * sizeof(float), "abc_setsplineknotsu"));
+
+ /* TODO: second check is temporary, for until the check for cycles is rock solid. */
+ if (periodicity == Alembic::AbcGeom::kPeriodic && (KNOTSU(nu) == knots->size() - 2)) {
+ /* Skip first and last knots. */
+ for (size_t i = 1; i < knots->size() - 1; ++i) {
+ nu->knotsu[i - 1] = (*knots)[knot_offset + i];
+ }
+ }
+ else {
+ /* TODO: figure out how to use the knots array from other
+ * software in this case. */
+ BKE_nurb_knot_calc_u(nu);
+ }
+
+ knot_offset += knots->size();
+ }
+ else {
+ BKE_nurb_knot_calc_u(nu);
+ }
+
+ BLI_addtail(BKE_curve_nurbs_get(cu), nu);
+ }
+}
diff --git a/source/blender/alembic/intern/abc_curves.h b/source/blender/alembic/intern/abc_curves.h
new file mode 100644
index 00000000000..ee47f1931ea
--- /dev/null
+++ b/source/blender/alembic/intern/abc_curves.h
@@ -0,0 +1,65 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifndef __ABC_CURVES_H__
+#define __ABC_CURVES_H__
+
+#include "abc_object.h"
+
+struct Curve;
+
+/* ************************************************************************** */
+
+class AbcCurveWriter : public AbcObjectWriter {
+ Alembic::AbcGeom::OCurvesSchema m_schema;
+ Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
+
+public:
+ AbcCurveWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings);
+
+ void do_write();
+};
+
+/* ************************************************************************** */
+
+class AbcCurveReader : public AbcObjectReader {
+ Alembic::AbcGeom::ICurvesSchema m_curves_schema;
+
+public:
+ AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+};
+
+/* ************************************************************************** */
+
+void read_curve_sample(Curve *cu, const Alembic::AbcGeom::ICurvesSchema &schema, const float time);
+
+#endif /* __ABC_CURVES_H__ */ \ No newline at end of file
diff --git a/source/blender/alembic/intern/abc_customdata.cc b/source/blender/alembic/intern/abc_customdata.cc
new file mode 100644
index 00000000000..ebf1b2ba96e
--- /dev/null
+++ b/source/blender/alembic/intern/abc_customdata.cc
@@ -0,0 +1,379 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#include "abc_customdata.h"
+
+#include <Alembic/AbcGeom/All.h>
+#include <algorithm>
+
+extern "C" {
+#include "DNA_customdata_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+}
+
+/* NOTE: for now only UVs and Vertex Colors are supported for streaming.
+ * Although Alembic only allows for a single UV layer per {I|O}Schema, and does
+ * not have a vertex color concept, there is a convention between DCCs to write
+ * such data in a way that lets other DCC know what they are for. See comments
+ * in the write code for the conventions. */
+
+using Alembic::AbcGeom::kVertexScope;
+using Alembic::AbcGeom::kFacevaryingScope;
+
+using Alembic::Abc::C4fArraySample;
+using Alembic::Abc::UInt32ArraySample;
+using Alembic::Abc::V2fArraySample;
+
+using Alembic::AbcGeom::OV2fGeomParam;
+using Alembic::AbcGeom::OC4fGeomParam;
+
+static void get_uvs(const CDStreamConfig &config,
+ std::vector<Imath::V2f> &uvs,
+ std::vector<uint32_t> &uvidx,
+ void *cd_data)
+{
+ MLoopUV *mloopuv_array = static_cast<MLoopUV *>(cd_data);
+
+ if (!mloopuv_array) {
+ return;
+ }
+
+ const int num_poly = config.totpoly;
+ MPoly *polygons = config.mpoly;
+
+ if (!config.pack_uvs) {
+ int cnt = 0;
+ uvidx.resize(config.totloop);
+ uvs.resize(config.totloop);
+
+ for (int i = 0; i < num_poly; ++i) {
+ MPoly &current_poly = polygons[i];
+ MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop;
+
+ for (int j = 0; j < current_poly.totloop; ++j, ++cnt) {
+ --loopuvpoly;
+
+ uvidx[cnt] = cnt;
+ uvs[cnt][0] = loopuvpoly->uv[0];
+ uvs[cnt][1] = loopuvpoly->uv[1];
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < num_poly; ++i) {
+ MPoly &current_poly = polygons[i];
+ MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop;
+
+ for (int j = 0; j < current_poly.totloop; ++j) {
+ loopuvpoly--;
+ Imath::V2f uv(loopuvpoly->uv[0], loopuvpoly->uv[1]);
+
+ std::vector<Imath::V2f>::iterator it = std::find(uvs.begin(), uvs.end(), uv);
+
+ if (it == uvs.end()) {
+ uvidx.push_back(uvs.size());
+ uvs.push_back(uv);
+ }
+ else {
+ uvidx.push_back(std::distance(uvs.begin(), it));
+ }
+ }
+ }
+ }
+}
+
+const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data)
+{
+ const int active_uvlayer = CustomData_get_active_layer(data, CD_MLOOPUV);
+
+ if (active_uvlayer < 0) {
+ return "";
+ }
+
+ void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer);
+
+ get_uvs(config, sample.uvs, sample.indices, cd_data);
+
+ return CustomData_get_layer_name(data, CD_MLOOPUV, active_uvlayer);
+}
+
+/* Convention to write UVs:
+ * - V2fGeomParam on the arbGeomParam
+ * - set scope as face varying
+ * - (optional due to its behaviour) tag as UV using Alembic::AbcGeom::SetIsUV
+ */
+static void write_uv(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name)
+{
+ std::vector<uint32_t> indices;
+ std::vector<Imath::V2f> uvs;
+
+ get_uvs(config, uvs, indices, data);
+
+ if (indices.empty() || uvs.empty()) {
+ return;
+ }
+
+ OV2fGeomParam param(prop, name, true, kFacevaryingScope, 1);
+
+ OV2fGeomParam::Sample sample(
+ V2fArraySample(&uvs.front(), uvs.size()),
+ UInt32ArraySample(&indices.front(), indices.size()),
+ kFacevaryingScope);
+
+ param.set(sample);
+}
+
+/* Convention to write Vertex Colors:
+ * - C3fGeomParam/C4fGeomParam on the arbGeomParam
+ * - set scope as vertex varying
+ */
+static void write_mcol(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name)
+{
+ const float cscale = 1.0f / 255.0f;
+ MPoly *polys = config.mpoly;
+ MLoop *mloops = config.mloop;
+ MCol *cfaces = static_cast<MCol *>(data);
+
+ std::vector<Imath::C4f> buffer(config.totvert);
+
+ Imath::C4f col;
+
+ for (int i = 0; i < config.totpoly; ++i) {
+ MPoly *p = &polys[i];
+ MCol *cface = &cfaces[p->loopstart + p->totloop];
+ MLoop *mloop = &mloops[p->loopstart + p->totloop];
+
+ for (int j = 0; j < p->totloop; ++j) {
+ cface--;
+ mloop--;
+
+ col[0] = cface->a * cscale;
+ col[1] = cface->r * cscale;
+ col[2] = cface->g * cscale;
+ col[3] = cface->b * cscale;
+
+ buffer[mloop->v] = col;
+ }
+ }
+
+ OC4fGeomParam param(prop, name, true, kFacevaryingScope, 1);
+
+ OC4fGeomParam::Sample sample(
+ C4fArraySample(&buffer.front(), buffer.size()),
+ kVertexScope);
+
+ param.set(sample);
+}
+
+void write_custom_data(const OCompoundProperty &prop, const CDStreamConfig &config, CustomData *data, int data_type)
+{
+ CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
+
+ if (!CustomData_has_layer(data, cd_data_type)) {
+ return;
+ }
+
+ const int active_layer = CustomData_get_active_layer(data, cd_data_type);
+ const int tot_layers = CustomData_number_of_layers(data, cd_data_type);
+
+ for (int i = 0; i < tot_layers; ++i) {
+ void *cd_data = CustomData_get_layer_n(data, cd_data_type, i);
+ const char *name = CustomData_get_layer_name(data, cd_data_type, i);
+
+ if (cd_data_type == CD_MLOOPUV) {
+ /* Already exported. */
+ if (i == active_layer) {
+ continue;
+ }
+
+ write_uv(prop, config, cd_data, name);
+ }
+ else if (cd_data_type == CD_MLOOPCOL) {
+ write_mcol(prop, config, cd_data, name);
+ }
+ }
+}
+
+/* ************************************************************************** */
+
+using Alembic::Abc::C3fArraySamplePtr;
+using Alembic::Abc::C4fArraySamplePtr;
+using Alembic::Abc::PropertyHeader;
+
+using Alembic::AbcGeom::IC3fGeomParam;
+using Alembic::AbcGeom::IC4fGeomParam;
+using Alembic::AbcGeom::IV2fGeomParam;
+
+static void read_mcols(const CDStreamConfig &config, void *data,
+ const C3fArraySamplePtr &c3f_ptr, const C4fArraySamplePtr &c4f_ptr)
+{
+ MCol *cfaces = static_cast<MCol *>(data);
+ MPoly *polys = config.mpoly;
+ MLoop *mloops = config.mloop;
+
+ if (c3f_ptr) {
+ for (int i = 0; i < config.totpoly; ++i) {
+ MPoly *p = &polys[i];
+ MCol *cface = &cfaces[p->loopstart + p->totloop];
+ MLoop *mloop = &mloops[p->loopstart + p->totloop];
+
+ for (int j = 0; j < p->totloop; ++j) {
+ cface--;
+ mloop--;
+ const Imath::C3f &color = (*c3f_ptr)[mloop->v];
+ cface->a = FTOCHAR(color[0]);
+ cface->r = FTOCHAR(color[1]);
+ cface->g = FTOCHAR(color[2]);
+ cface->b = 255;
+ }
+ }
+ }
+ else if (c4f_ptr) {
+ for (int i = 0; i < config.totpoly; ++i) {
+ MPoly *p = &polys[i];
+ MCol *cface = &cfaces[p->loopstart + p->totloop];
+ MLoop *mloop = &mloops[p->loopstart + p->totloop];
+
+ for (int j = 0; j < p->totloop; ++j) {
+ cface--;
+ mloop--;
+ const Imath::C4f &color = (*c4f_ptr)[mloop->v];
+ cface->a = FTOCHAR(color[0]);
+ cface->r = FTOCHAR(color[1]);
+ cface->g = FTOCHAR(color[2]);
+ cface->b = FTOCHAR(color[3]);
+ }
+ }
+ }
+}
+
+static void read_uvs(const CDStreamConfig &config, void *data,
+ const Alembic::AbcGeom::V2fArraySamplePtr &uvs,
+ const Alembic::AbcGeom::UInt32ArraySamplePtr &indices)
+{
+ MPoly *mpolys = config.mpoly;
+ MLoopUV *mloopuvs = static_cast<MLoopUV *>(data);
+
+ unsigned int uv_index, loop_index;
+
+ for (int i = 0; i < config.totpoly; ++i) {
+ MPoly &poly = mpolys[i];
+
+ for (int f = 0; f < poly.totloop; ++f) {
+ loop_index = poly.loopstart + f;
+ uv_index = (*indices)[loop_index];
+ const Imath::V2f &uv = (*uvs)[uv_index];
+
+ MLoopUV &loopuv = mloopuvs[loop_index];
+ loopuv.uv[0] = uv[0];
+ loopuv.uv[1] = uv[1];
+ }
+ }
+}
+
+static void read_custom_data_ex(const ICompoundProperty &prop,
+ const PropertyHeader &prop_header,
+ const CDStreamConfig &config,
+ const Alembic::Abc::ISampleSelector &iss,
+ int data_type)
+{
+ if (data_type == CD_MLOOPCOL) {
+ C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr();
+ C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr();
+
+ if (IC3fGeomParam::matches(prop_header)) {
+ IC3fGeomParam color_param(prop, prop_header.getName());
+ IC3fGeomParam::Sample sample;
+ color_param.getIndexed(sample, iss);
+
+ c3f_ptr = sample.getVals();
+ }
+ else if (IC4fGeomParam::matches(prop_header)) {
+ IC4fGeomParam color_param(prop, prop_header.getName());
+ IC4fGeomParam::Sample sample;
+ color_param.getIndexed(sample, iss);
+
+ c4f_ptr = sample.getVals();
+ }
+
+ void *cd_data = config.add_customdata_cb(config.user_data,
+ prop_header.getName().c_str(),
+ data_type);
+
+ read_mcols(config, cd_data, c3f_ptr, c4f_ptr);
+ }
+ else if (data_type == CD_MLOOPUV) {
+ IV2fGeomParam uv_param(prop, prop_header.getName());
+ IV2fGeomParam::Sample sample;
+ uv_param.getIndexed(sample, iss);
+
+ if (uv_param.getScope() != kFacevaryingScope) {
+ return;
+ }
+
+ void *cd_data = config.add_customdata_cb(config.user_data,
+ prop_header.getName().c_str(),
+ data_type);
+
+ read_uvs(config, cd_data, sample.getVals(), sample.getIndices());
+ }
+}
+
+void read_custom_data(const ICompoundProperty &prop, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss)
+{
+ if (!prop.valid()) {
+ return;
+ }
+
+ int num_uvs = 0;
+ int num_colors = 0;
+
+ const size_t num_props = prop.getNumProperties();
+
+ for (size_t i = 0; i < num_props; ++i) {
+ const Alembic::Abc::PropertyHeader &prop_header = prop.getPropertyHeader(i);
+
+ /* Read UVs according to convention. */
+ if (IV2fGeomParam::matches(prop_header) && Alembic::AbcGeom::isUV(prop_header)) {
+ if (++num_uvs > MAX_MTFACE) {
+ continue;
+ }
+
+ read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPUV);
+ continue;
+ }
+
+ /* Read vertex colors according to convention. */
+ if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) {
+ if (++num_colors > MAX_MCOL) {
+ continue;
+ }
+
+ read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPCOL);
+ continue;
+ }
+ }
+}
diff --git a/source/blender/alembic/intern/abc_customdata.h b/source/blender/alembic/intern/abc_customdata.h
new file mode 100644
index 00000000000..3b16c0d9796
--- /dev/null
+++ b/source/blender/alembic/intern/abc_customdata.h
@@ -0,0 +1,93 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifndef __ABC_CUSTOMDATA_H__
+#define __ABC_CUSTOMDATA_H__
+
+#include <Alembic/Abc/All.h>
+
+struct CustomData;
+struct MLoop;
+struct MLoopUV;
+struct MPoly;
+struct MVert;
+
+using Alembic::Abc::ICompoundProperty;
+using Alembic::Abc::OCompoundProperty;
+
+struct UVSample {
+ std::vector<Imath::V2f> uvs;
+ std::vector<uint32_t> indices;
+};
+
+struct CDStreamConfig {
+ MLoop *mloop;
+ int totloop;
+
+ MPoly *mpoly;
+ int totpoly;
+
+ MVert *mvert;
+ int totvert;
+
+ MLoopUV *mloopuv;
+
+ CustomData *loopdata;
+
+ bool pack_uvs;
+
+ /* TODO(kevin): might need a better way to handle adding and/or updating
+ * custom datas such that it updates the custom data holder and its pointers
+ * properly. */
+ void *user_data;
+ void *(*add_customdata_cb)(void *user_data, const char *name, int data_type);
+
+ CDStreamConfig()
+ : mloop(NULL)
+ , totloop(0)
+ , mpoly(NULL)
+ , totpoly(0)
+ , totvert(0)
+ , pack_uvs(false)
+ , user_data(NULL)
+ , add_customdata_cb(NULL)
+ {}
+};
+
+/* Get the UVs for the main UV property on a OSchema.
+ * Returns the name of the UV layer.
+ *
+ * For now the active layer is used, maybe needs a better way to choose this. */
+const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data);
+
+void write_custom_data(const OCompoundProperty &prop,
+ const CDStreamConfig &config,
+ CustomData *data,
+ int data_type);
+
+void read_custom_data(const ICompoundProperty &prop,
+ const CDStreamConfig &config,
+ const Alembic::Abc::ISampleSelector &iss);
+
+#endif /* __ABC_CUSTOMDATA_H__ */
diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc
new file mode 100644
index 00000000000..127e8853789
--- /dev/null
+++ b/source/blender/alembic/intern/abc_exporter.cc
@@ -0,0 +1,600 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_exporter.h"
+
+#include <cmath>
+
+#ifdef WITH_ALEMBIC_HDF5
+# include <Alembic/AbcCoreHDF5/All.h>
+#endif
+
+#include <Alembic/AbcCoreOgawa/All.h>
+
+#include "abc_camera.h"
+#include "abc_curves.h"
+#include "abc_hair.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_mesh_types.h"
+#include "DNA_modifier_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_modifier.h"
+#include "BKE_particle.h"
+#include "BKE_scene.h"
+}
+
+using Alembic::Abc::TimeSamplingPtr;
+using Alembic::Abc::OBox3dProperty;
+
+/* ************************************************************************** */
+
+ExportSettings::ExportSettings()
+ : scene(NULL)
+ , selected_only(false)
+ , visible_layers_only(false)
+ , renderable_only(false)
+ , frame_start(1)
+ , frame_end(1)
+ , frame_step_xform(1)
+ , frame_step_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)
+ , apply_subdiv(false)
+ , use_subdiv_schema(false)
+ , export_child_hairs(true)
+ , export_ogawa(true)
+ , pack_uv(false)
+ , 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_is_shape(Object *ob)
+{
+ switch (ob->type) {
+ case OB_MESH:
+ if (object_is_smoke_sim(ob)) {
+ return false;
+ }
+
+ return true;
+ case OB_CURVE:
+ case OB_SURF:
+ case OB_CAMERA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool export_object(const ExportSettings * const settings, Object *ob)
+{
+ if (settings->selected_only && !parent_selected(ob)) {
+ return false;
+ }
+
+ if (settings->visible_layers_only && !(settings->scene->lay & ob->lay)) {
+ return false;
+ }
+
+ if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) {
+ return false;
+ }
+
+ return true;
+}
+
+/* ************************************************************************** */
+
+AbcExporter::AbcExporter(Scene *scene, const char *filename, ExportSettings &settings)
+ : m_settings(settings)
+ , m_filename(filename)
+ , m_trans_sampling_index(0)
+ , m_shape_sampling_index(0)
+ , m_scene(scene)
+{}
+
+AbcExporter::~AbcExporter()
+{
+ std::map<std::string, AbcTransformWriter*>::iterator it, e;
+ for (it = m_xforms.begin(), e = m_xforms.end(); it != e; ++it) {
+ delete it->second;
+ }
+
+ for (int i = 0, e = m_shapes.size(); i != e; ++i) {
+ delete m_shapes[i];
+ }
+}
+
+void AbcExporter::getShutterSamples(double step, bool time_relative,
+ std::vector<double> &samples)
+{
+ samples.clear();
+
+ const double time_factor = time_relative ? m_scene->r.frs_sec : 1.0;
+ const double shutter_open = m_settings.shutter_open;
+ const double shutter_close = m_settings.shutter_close;
+
+ /* sample all frame */
+ if (shutter_open == 0.0 && shutter_close == 1.0) {
+ for (double t = 0; t < 1.0; t += step) {
+ samples.push_back(t / time_factor);
+ }
+ }
+ else {
+ /* sample between shutter open & close */
+ const int nsamples = std::max((1.0 / step) - 1.0, 1.0);
+ const double time_inc = (shutter_close - shutter_open) / nsamples;
+
+ for (double t = shutter_open; t <= shutter_close; t += time_inc) {
+ samples.push_back(t / time_factor);
+ }
+ }
+}
+
+Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step)
+{
+ TimeSamplingPtr time_sampling;
+ std::vector<double> samples;
+
+ if (m_settings.frame_start == m_settings.frame_end) {
+ time_sampling.reset(new Alembic::Abc::TimeSampling());
+ return time_sampling;
+ }
+
+ getShutterSamples(step, true, samples);
+
+ Alembic::Abc::TimeSamplingType ts(static_cast<uint32_t>(samples.size()), 1.0 / m_scene->r.frs_sec);
+ time_sampling.reset(new Alembic::Abc::TimeSampling(ts, samples));
+
+ return time_sampling;
+}
+
+void AbcExporter::getFrameSet(double step, std::set<double> &frames)
+{
+ frames.clear();
+
+ std::vector<double> shutter_samples;
+
+ getShutterSamples(step, false, shutter_samples);
+
+ for (int frame = m_settings.frame_start; frame <= m_settings.frame_end; ++frame) {
+ for (int j = 0, e = shutter_samples.size(); j < e; ++j) {
+ frames.insert(frame + shutter_samples[j]);
+ }
+ }
+}
+
+void AbcExporter::operator()(Main *bmain, float &progress, bool &was_canceled)
+{
+ std::string scene_name;
+
+ if (bmain->name[0] != '\0') {
+ char scene_file_name[FILE_MAX];
+ BLI_strncpy(scene_file_name, bmain->name, FILE_MAX);
+ scene_name = scene_file_name;
+ }
+ else {
+ scene_name = "untitled";
+ }
+
+ Scene *scene = m_scene;
+ const int fps = FPS;
+ char buf[16];
+ snprintf(buf, 15, "%d", fps);
+ const std::string str_fps = buf;
+
+ Alembic::AbcCoreAbstract::MetaData md;
+ md.set("FramesPerTimeUnit", str_fps);
+
+ Alembic::Abc::Argument arg(md);
+
+#ifdef WITH_ALEMBIC_HDF5
+ if (!m_settings.export_ogawa) {
+ m_archive = Alembic::Abc::CreateArchiveWithInfo(Alembic::AbcCoreHDF5::WriteArchive(),
+ m_filename,
+ "Blender",
+ scene_name,
+ Alembic::Abc::ErrorHandler::kThrowPolicy,
+ arg);
+ }
+ else
+#endif
+ {
+ m_archive = Alembic::Abc::CreateArchiveWithInfo(Alembic::AbcCoreOgawa::WriteArchive(),
+ m_filename,
+ "Blender",
+ scene_name,
+ Alembic::Abc::ErrorHandler::kThrowPolicy,
+ arg);
+ }
+
+ /* Create time samplings for transforms and shapes. */
+
+ TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_step_xform);
+
+ m_trans_sampling_index = m_archive.addTimeSampling(*trans_time);
+
+ TimeSamplingPtr shape_time;
+
+ if ((m_settings.frame_step_shape == m_settings.frame_step_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_step_shape);
+ m_shape_sampling_index = m_archive.addTimeSampling(*shape_time);
+ }
+
+ OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds(m_archive, m_trans_sampling_index);
+
+ if (m_settings.flatten_hierarchy) {
+ createTransformWritersFlat();
+ }
+ else {
+ createTransformWritersHierarchy(bmain->eval_ctx);
+ }
+
+ createShapeWriters(bmain->eval_ctx);
+
+ /* Make a list of frames to export. */
+
+ std::set<double> xform_frames;
+ getFrameSet(m_settings.frame_step_xform, xform_frames);
+
+ std::set<double> shape_frames;
+ getFrameSet(m_settings.frame_step_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;
+ }
+
+ double f = *begin;
+ setCurrentFrame(bmain, f);
+
+ if (shape_frames.count(f) != 0) {
+ for (int i = 0, e = m_shapes.size(); i != e; ++i) {
+ m_shapes[i]->write();
+ }
+ }
+
+ if (xform_frames.count(f) == 0) {
+ continue;
+ }
+
+ std::map<std::string, AbcTransformWriter *>::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(EvaluationContext *eval_ctx)
+{
+ Base *base = static_cast<Base *>(m_scene->base.first);
+
+ while (base) {
+ Object *ob = base->object;
+
+ if (export_object(&m_settings, ob)) {
+ switch(ob->type) {
+ case OB_LAMP:
+ case OB_LATTICE:
+ case OB_MBALL:
+ case OB_SPEAKER:
+ /* We do not export transforms for objects of these classes. */
+ break;
+
+ default:
+ exploreTransform(eval_ctx, ob, ob->parent, NULL);
+ }
+ }
+
+ base = base->next;
+ }
+}
+
+void AbcExporter::createTransformWritersFlat()
+{
+ Base *base = static_cast<Base *>(m_scene->base.first);
+
+ while (base) {
+ Object *ob = base->object;
+
+ if (export_object(&m_settings, ob) && object_is_shape(ob)) {
+ std::string name = get_id_name(ob);
+ m_xforms[name] = new AbcTransformWriter(ob, m_archive.getTop(), 0, m_trans_sampling_index, m_settings);
+ }
+
+ base = base->next;
+ }
+}
+
+void AbcExporter::exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent)
+{
+ createTransformWriter(ob, parent, dupliObParent);
+
+ ListBase *lb = object_duplilist(eval_ctx, m_scene, ob);
+
+ if (lb) {
+ DupliObject *link = static_cast<DupliObject *>(lb->first);
+ Object *dupli_ob = NULL;
+ Object *dupli_parent = NULL;
+
+ while (link) {
+ if (link->type == OB_DUPLIGROUP) {
+ dupli_ob = link->ob;
+ dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob;
+
+ exploreTransform(eval_ctx, dupli_ob, dupli_parent, ob);
+ }
+
+ link = link->next;
+ }
+ }
+
+ free_object_duplilist(lb);
+}
+
+void AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupliObParent)
+{
+ const std::string name = get_object_dag_path_name(ob, dupliObParent);
+
+ /* check if we have already created a transform writer for this object */
+ if (m_xforms.find(name) != m_xforms.end()){
+ std::cerr << "xform " << name << " already exists\n";
+ return;
+ }
+
+ AbcTransformWriter *parent_xform = NULL;
+
+ if (parent) {
+ const std::string parentname = get_object_dag_path_name(parent, dupliObParent);
+ parent_xform = getXForm(parentname);
+
+ if (!parent_xform) {
+ if (parent->parent) {
+ createTransformWriter(parent, parent->parent, dupliObParent);
+ }
+ else {
+ createTransformWriter(parent, dupliObParent, dupliObParent);
+ }
+
+ parent_xform = getXForm(parentname);
+ }
+ }
+
+ if (parent_xform) {
+ m_xforms[name] = new AbcTransformWriter(ob, parent_xform->alembicXform(), parent_xform, m_trans_sampling_index, m_settings);
+ m_xforms[name]->setParent(parent);
+ }
+ else {
+ m_xforms[name] = new AbcTransformWriter(ob, m_archive.getTop(), NULL, m_trans_sampling_index, m_settings);
+ }
+}
+
+void AbcExporter::createShapeWriters(EvaluationContext *eval_ctx)
+{
+ Base *base = static_cast<Base *>(m_scene->base.first);
+
+ while (base) {
+ Object *ob = base->object;
+ exploreObject(eval_ctx, ob, NULL);
+
+ base = base->next;
+ }
+}
+
+void AbcExporter::exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent)
+{
+ ListBase *lb = object_duplilist(eval_ctx, m_scene, ob);
+
+ createShapeWriter(ob, dupliObParent);
+
+ if (lb) {
+ DupliObject *dupliob = static_cast<DupliObject *>(lb->first);
+
+ while (dupliob) {
+ if (dupliob->type == OB_DUPLIGROUP) {
+ exploreObject(eval_ctx, dupliob->ob, ob);
+ }
+
+ dupliob = dupliob->next;
+ }
+ }
+
+ free_object_duplilist(lb);
+}
+
+void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent)
+{
+ if (!object_is_shape(ob)) {
+ return;
+ }
+
+ if (!export_object(&m_settings, 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) {
+ std::cerr << __func__ << ": xform " << name << " is NULL\n";
+ 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 (psys->part->type == PART_HAIR) {
+ m_settings.export_child_hairs = true;
+ m_shapes.push_back(new AbcHairWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
+ }
+ else if (psys->part->type == PART_EMITTER) {
+ m_shapes.push_back(new AbcPointsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
+ }
+ }
+
+ switch(ob->type) {
+ case OB_MESH:
+ {
+ Mesh *me = static_cast<Mesh *>(ob->data);
+
+ if (!me || me->totvert == 0) {
+ return;
+ }
+
+ m_shapes.push_back(new AbcMeshWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings));
+ break;
+ }
+ case OB_SURF:
+ {
+ Curve *cu = static_cast<Curve *>(ob->data);
+
+ if (!cu) {
+ return;
+ }
+
+ m_shapes.push_back(new AbcNurbsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings));
+ break;
+ }
+ case OB_CURVE:
+ {
+ Curve *cu = static_cast<Curve *>(ob->data);
+
+ if (!cu) {
+ return;
+ }
+
+ m_shapes.push_back(new AbcCurveWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings));
+ break;
+ }
+ case OB_CAMERA:
+ {
+ Camera *cam = static_cast<Camera *>(ob->data);
+
+ if (cam->type == CAM_PERSP) {
+ m_shapes.push_back(new AbcCameraWriter(m_scene, 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_scene->r.cfra = std::floor(t);
+ m_scene->r.subframe = t - m_scene->r.cfra;
+ BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, m_scene, m_scene->lay);
+}
diff --git a/source/blender/alembic/intern/abc_exporter.h b/source/blender/alembic/intern/abc_exporter.h
new file mode 100644
index 00000000000..070eb9ea81a
--- /dev/null
+++ b/source/blender/alembic/intern/abc_exporter.h
@@ -0,0 +1,112 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_EXPORTER_H__
+#define __ABC_EXPORTER_H__
+
+#include <Alembic/Abc/All.h>
+#include <map>
+#include <set>
+#include <vector>
+
+class AbcObjectWriter;
+class AbcTransformWriter;
+
+struct EvaluationContext;
+struct Main;
+struct Object;
+struct Scene;
+
+struct ExportSettings {
+ ExportSettings();
+
+ Scene *scene;
+
+ bool selected_only;
+ bool visible_layers_only;
+ bool renderable_only;
+
+ double frame_start, frame_end;
+ double frame_step_xform;
+ double frame_step_shape;
+ double shutter_open;
+ double shutter_close;
+ float global_scale;
+
+ bool flatten_hierarchy;
+
+ bool export_normals;
+ bool export_uvs;
+ bool export_vcols;
+ bool export_face_sets;
+ bool export_vweigths;
+
+ bool apply_subdiv;
+ bool use_subdiv_schema;
+ bool export_child_hairs;
+ bool export_ogawa;
+ bool pack_uv;
+
+ bool do_convert_axis;
+ float convert_matrix[3][3];
+};
+
+class AbcExporter {
+ ExportSettings &m_settings;
+
+ const char *m_filename;
+
+ Alembic::Abc::OArchive m_archive;
+ unsigned int m_trans_sampling_index, m_shape_sampling_index;
+
+ Scene *m_scene;
+
+ std::map<std::string, AbcTransformWriter *> m_xforms;
+ std::vector<AbcObjectWriter *> m_shapes;
+
+public:
+ AbcExporter(Scene *scene, const char *filename, ExportSettings &settings);
+ ~AbcExporter();
+
+ void operator()(Main *bmain, float &progress, bool &was_canceled);
+
+private:
+ void getShutterSamples(double step, bool time_relative, std::vector<double> &samples);
+
+ Alembic::Abc::TimeSamplingPtr createTimeSampling(double step);
+
+ void getFrameSet(double step, std::set<double> &frames);
+
+ void createTransformWritersHierarchy(EvaluationContext *eval_ctx);
+ void createTransformWritersFlat();
+ void createTransformWriter(Object *ob, Object *parent, Object *dupliObParent);
+ void exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent = NULL);
+ void exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent);
+ void createShapeWriters(EvaluationContext *eval_ctx);
+ void createShapeWriter(Object *ob, Object *dupliObParent);
+
+ AbcTransformWriter *getXForm(const std::string &name);
+
+ void setCurrentFrame(Main *bmain, double t);
+};
+
+#endif /* __ABC_EXPORTER_H__ */
diff --git a/source/blender/alembic/intern/abc_hair.cc b/source/blender/alembic/intern/abc_hair.cc
new file mode 100644
index 00000000000..45bf9b7ab8a
--- /dev/null
+++ b/source/blender/alembic/intern/abc_hair.cc
@@ -0,0 +1,290 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_hair.h"
+
+#include <cstdio>
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_modifier_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math_geom.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+}
+
+using Alembic::Abc::P3fArraySamplePtr;
+
+using Alembic::AbcGeom::OCurves;
+using Alembic::AbcGeom::OCurvesSchema;
+using Alembic::AbcGeom::ON3fGeomParam;
+using Alembic::AbcGeom::OV2fGeomParam;
+
+/* ************************************************************************** */
+
+AbcHairWriter::AbcHairWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings,
+ ParticleSystem *psys)
+ : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+ m_psys = psys;
+
+ OCurves curves(parent->alembicXform(), m_name, m_time_sampling);
+ m_schema = curves.getSchema();
+}
+
+void AbcHairWriter::do_write()
+{
+ if (!m_psys) {
+ return;
+ }
+
+ ParticleSystemModifierData *psmd = psys_get_modifier(m_object, m_psys);
+
+ if (!psmd->dm_final) {
+ return;
+ }
+
+ DerivedMesh *dm = mesh_create_derived_view(m_scene, m_object, CD_MASK_MESH);
+ DM_ensure_tessface(dm);
+ DM_update_tessface_data(dm);
+
+ std::vector<Imath::V3f> verts;
+ std::vector<int32_t> hvertices;
+ std::vector<Imath::V2f> uv_values;
+ std::vector<Imath::V3f> norm_values;
+
+ if (m_psys->pathcache) {
+ ParticleSettings *part = m_psys->part;
+
+ write_hair_sample(dm, part, verts, norm_values, uv_values, hvertices);
+
+ if (m_settings.export_child_hairs && m_psys->childcache) {
+ write_hair_child_sample(dm, part, verts, norm_values, uv_values, hvertices);
+ }
+ }
+
+ dm->release(dm);
+
+ Alembic::Abc::P3fArraySample iPos(verts);
+ m_sample = OCurvesSchema::Sample(iPos, hvertices);
+ m_sample.setBasis(Alembic::AbcGeom::kNoBasis);
+ m_sample.setType(Alembic::AbcGeom::kLinear);
+ m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic);
+
+ if (!uv_values.empty()) {
+ OV2fGeomParam::Sample uv_smp;
+ uv_smp.setVals(uv_values);
+ m_sample.setUVs(uv_smp);
+ }
+
+ if (!norm_values.empty()) {
+ ON3fGeomParam::Sample norm_smp;
+ norm_smp.setVals(norm_values);
+ m_sample.setNormals(norm_smp);
+ }
+
+ m_sample.setSelfBounds(bounds());
+ m_schema.set(m_sample);
+}
+
+void AbcHairWriter::write_hair_sample(DerivedMesh *dm,
+ ParticleSettings *part,
+ std::vector<Imath::V3f> &verts,
+ std::vector<Imath::V3f> &norm_values,
+ std::vector<Imath::V2f> &uv_values,
+ std::vector<int32_t> &hvertices)
+{
+ /* Get untransformed vertices, there's a xform under the hair. */
+ float inv_mat[4][4];
+ invert_m4_m4_safe(inv_mat, m_object->obmat);
+
+ MTFace *mtface = static_cast<MTFace *>(CustomData_get_layer(&dm->faceData, CD_MTFACE));
+ MFace *mface = dm->getTessFaceArray(dm);
+ MVert *mverts = dm->getVertArray(dm);
+
+ if (!mtface || !mface) {
+ std::fprintf(stderr, "Warning, no UV set found for underlying geometry.\n");
+ }
+
+ ParticleData * pa = m_psys->particles;
+ int k;
+
+ ParticleCacheKey **cache = m_psys->pathcache;
+ ParticleCacheKey *path;
+ float normal[3];
+ Imath::V3f tmp_nor;
+
+ for (int p = 0; p < m_psys->totpart; ++p, ++pa) {
+ /* underlying info for faces-only emission */
+ path = cache[p];
+
+ if (part->from == PART_FROM_FACE && mtface) {
+ const int num = pa->num_dmcache >= 0 ? pa->num_dmcache : pa->num;
+
+ if (num < dm->getNumTessFaces(dm)) {
+ MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, num, CD_MFACE));
+ MTFace *tface = mtface + num;
+
+ if (mface) {
+ float r_uv[2], mapfw[4], vec[3];
+
+ psys_interpolate_uvs(tface, face->v4, pa->fuv, r_uv);
+ uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1]));
+
+ psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, normal, NULL, NULL, NULL, NULL);
+
+ copy_zup_yup(tmp_nor.getValue(), normal);
+ norm_values.push_back(tmp_nor);
+ }
+ }
+ else {
+ std::fprintf(stderr, "Particle to faces overflow (%d/%d)\n", num, dm->getNumTessFaces(dm));
+ }
+ }
+ else if (part->from == PART_FROM_VERT && mtface) {
+ /* vertex id */
+ const int num = (pa->num_dmcache >= 0) ? pa->num_dmcache : pa->num;
+
+ /* iterate over all faces to find a corresponding underlying UV */
+ for (int n = 0; n < dm->getNumTessFaces(dm); ++n) {
+ MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, n, CD_MFACE));
+ MTFace *tface = mtface + n;
+ unsigned int vtx[4];
+ vtx[0] = face->v1;
+ vtx[1] = face->v2;
+ vtx[2] = face->v3;
+ vtx[3] = face->v4;
+ bool found = false;
+
+ for (int o = 0; o < 4; ++o) {
+ if (o > 2 && vtx[o] == 0) {
+ break;
+ }
+
+ if (vtx[o] == num) {
+ uv_values.push_back(Imath::V2f(tface->uv[o][0], tface->uv[o][1]));
+
+ MVert *mv = mverts + vtx[o];
+
+ normal_short_to_float_v3(normal, mv->no);
+ copy_zup_yup(tmp_nor.getValue(), normal);
+ norm_values.push_back(tmp_nor);
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ break;
+ }
+ }
+ }
+
+ int steps = path->segments + 1;
+ hvertices.push_back(steps);
+
+ for (k = 0; k < steps; ++k) {
+ float vert[3];
+ copy_v3_v3(vert, path->co);
+ mul_m4_v3(inv_mat, vert);
+
+ /* Convert Z-up to Y-up. */
+ verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1]));
+
+ ++path;
+ }
+ }
+}
+
+void AbcHairWriter::write_hair_child_sample(DerivedMesh *dm,
+ ParticleSettings *part,
+ std::vector<Imath::V3f> &verts,
+ std::vector<Imath::V3f> &norm_values,
+ std::vector<Imath::V2f> &uv_values,
+ std::vector<int32_t> &hvertices)
+{
+ /* Get untransformed vertices, there's a xform under the hair. */
+ float inv_mat[4][4];
+ invert_m4_m4_safe(inv_mat, m_object->obmat);
+
+ MTFace *mtface = static_cast<MTFace *>(CustomData_get_layer(&dm->faceData, CD_MTFACE));
+ MFace *mface = dm->getTessFaceArray(dm);
+ MVert *mverts = dm->getVertArray(dm);
+
+ if (!mtface || !mface) {
+ std::fprintf(stderr, "Warning, no UV set found for underlying geometry.\n");
+ }
+
+ ParticleCacheKey **cache = m_psys->childcache;
+ ParticleCacheKey *path;
+
+ ChildParticle *pc = m_psys->child;
+
+ for (int p = 0; p < m_psys->totchild; ++p, ++pc) {
+ path = cache[p];
+
+ if (part->from == PART_FROM_FACE) {
+ const int num = pc->num;
+
+ MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, num, CD_MFACE));
+ MTFace *tface = mtface + num;
+
+ if (mface && mtface) {
+ float r_uv[2], tmpnor[3], mapfw[4], vec[3];
+
+ psys_interpolate_uvs(tface, face->v4, pc->fuv, r_uv);
+ uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1]));
+
+ psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, tmpnor, NULL, NULL, NULL, NULL);
+
+ /* Convert Z-up to Y-up. */
+ norm_values.push_back(Imath::V3f(tmpnor[0], tmpnor[2], -tmpnor[1]));
+ }
+ }
+
+ int steps = path->segments + 1;
+ hvertices.push_back(steps);
+
+ for (int k = 0; k < steps; ++k) {
+ float vert[3];
+ copy_v3_v3(vert, path->co);
+ mul_m4_v3(inv_mat, vert);
+
+ /* Convert Z-up to Y-up. */
+ verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1]));
+
+ ++path;
+ }
+ }
+}
diff --git a/source/blender/alembic/intern/abc_hair.h b/source/blender/alembic/intern/abc_hair.h
new file mode 100644
index 00000000000..d132b60be12
--- /dev/null
+++ b/source/blender/alembic/intern/abc_hair.h
@@ -0,0 +1,66 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_HAIR_H__
+#define __ABC_HAIR_H__
+
+#include "abc_object.h"
+
+struct DerivedMesh;
+struct ParticleSettings;
+struct ParticleSystem;
+
+/* ************************************************************************** */
+
+class AbcHairWriter : public AbcObjectWriter {
+ ParticleSystem *m_psys;
+
+ Alembic::AbcGeom::OCurvesSchema m_schema;
+ Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
+
+public:
+ AbcHairWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings,
+ ParticleSystem *psys);
+
+private:
+ virtual void do_write();
+
+ void write_hair_sample(DerivedMesh *dm,
+ ParticleSettings *part,
+ std::vector<Imath::V3f> &verts,
+ std::vector<Imath::V3f> &norm_values,
+ std::vector<Imath::V2f> &uv_values,
+ std::vector<int32_t> &hvertices);
+
+ void write_hair_child_sample(DerivedMesh *dm,
+ ParticleSettings *part,
+ std::vector<Imath::V3f> &verts,
+ std::vector<Imath::V3f> &norm_values,
+ std::vector<Imath::V2f> &uv_values,
+ std::vector<int32_t> &hvertices);
+};
+
+#endif /* __ABC_HAIR_H__ */
diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc
new file mode 100644
index 00000000000..f1c7b6b3aa3
--- /dev/null
+++ b/source/blender/alembic/intern/abc_mesh.cc
@@ -0,0 +1,1213 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_mesh.h"
+
+#include <algorithm>
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_fluidsim.h"
+#include "DNA_object_types.h"
+
+#include "BLI_math_geom.h"
+#include "BLI_string.h"
+
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_mesh.h"
+}
+
+using Alembic::Abc::FloatArraySample;
+using Alembic::Abc::ICompoundProperty;
+using Alembic::Abc::Int32ArraySample;
+using Alembic::Abc::Int32ArraySamplePtr;
+using Alembic::Abc::P3fArraySamplePtr;
+using Alembic::Abc::V2fArraySample;
+using Alembic::Abc::V3fArraySample;
+using Alembic::Abc::C4fArraySample;
+
+using Alembic::AbcGeom::IFaceSet;
+using Alembic::AbcGeom::IFaceSetSchema;
+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::OArrayProperty;
+using Alembic::AbcGeom::OBoolProperty;
+using Alembic::AbcGeom::OC3fArrayProperty;
+using Alembic::AbcGeom::OC3fGeomParam;
+using Alembic::AbcGeom::OC4fGeomParam;
+using Alembic::AbcGeom::OCompoundProperty;
+using Alembic::AbcGeom::OFaceSet;
+using Alembic::AbcGeom::OFaceSetSchema;
+using Alembic::AbcGeom::OFloatGeomParam;
+using Alembic::AbcGeom::OInt32GeomParam;
+using Alembic::AbcGeom::ON3fArrayProperty;
+using Alembic::AbcGeom::ON3fGeomParam;
+using Alembic::AbcGeom::OPolyMesh;
+using Alembic::AbcGeom::OPolyMeshSchema;
+using Alembic::AbcGeom::OSubD;
+using Alembic::AbcGeom::OSubDSchema;
+using Alembic::AbcGeom::OV2fGeomParam;
+using Alembic::AbcGeom::OV3fGeomParam;
+
+using Alembic::AbcGeom::kFacevaryingScope;
+using Alembic::AbcGeom::kVaryingScope;
+using Alembic::AbcGeom::kVertexScope;
+using Alembic::AbcGeom::kWrapExisting;
+using Alembic::AbcGeom::UInt32ArraySample;
+using Alembic::AbcGeom::N3fArraySamplePtr;
+using Alembic::AbcGeom::IN3fGeomParam;
+
+/* ************************************************************************** */
+
+/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
+
+static void get_vertices(DerivedMesh *dm, std::vector<Imath::V3f> &points)
+{
+ points.clear();
+ points.resize(dm->getNumVerts(dm));
+
+ MVert *verts = dm->getVertArray(dm);
+
+ for (int i = 0, e = dm->getNumVerts(dm); i < e; ++i) {
+ copy_zup_yup(points[i].getValue(), verts[i].co);
+ }
+}
+
+static void get_topology(DerivedMesh *dm,
+ std::vector<int32_t> &poly_verts,
+ std::vector<int32_t> &loop_counts,
+ bool &smooth_normal)
+{
+ const int num_poly = dm->getNumPolys(dm);
+ const int num_loops = dm->getNumLoops(dm);
+ MLoop *mloop = dm->getLoopArray(dm);
+ MPoly *mpoly = dm->getPolyArray(dm);
+
+ poly_verts.clear();
+ loop_counts.clear();
+ poly_verts.reserve(num_loops);
+ loop_counts.reserve(num_poly);
+
+ /* NOTE: data needs to be written in the reverse order. */
+ for (int i = 0; i < num_poly; ++i) {
+ MPoly &poly = mpoly[i];
+ loop_counts.push_back(poly.totloop);
+
+ smooth_normal |= ((poly.flag & ME_SMOOTH) != 0);
+
+ MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1);
+
+ for (int j = 0; j < poly.totloop; ++j, --loop) {
+ poly_verts.push_back(loop->v);
+ }
+ }
+}
+
+static void get_material_indices(DerivedMesh *dm, std::vector<int32_t> &indices)
+{
+ indices.clear();
+ indices.reserve(dm->getNumTessFaces(dm));
+
+ MPoly *mpolys = dm->getPolyArray(dm);
+
+ for (int i = 1, e = dm->getNumPolys(dm); i < e; ++i) {
+ MPoly *mpoly = &mpolys[i];
+ indices.push_back(mpoly->mat_nr);
+ }
+}
+
+static void get_creases(DerivedMesh *dm,
+ std::vector<int32_t> &indices,
+ std::vector<int32_t> &lengths,
+ std::vector<float> &sharpnesses)
+{
+ const float factor = 1.0f / 255.0f;
+
+ indices.clear();
+ lengths.clear();
+ sharpnesses.clear();
+
+ MEdge *edge = dm->getEdgeArray(dm);
+
+ for (int i = 0, e = dm->getNumEdges(dm); i < e; ++i) {
+ const float sharpness = static_cast<float>(edge[i].crease) * factor;
+
+ if (sharpness != 0.0f) {
+ indices.push_back(edge[i].v1);
+ indices.push_back(edge[i].v2);
+ sharpnesses.push_back(sharpness);
+ }
+ }
+
+ lengths.resize(sharpnesses.size(), 2);
+}
+
+static void get_vertex_normals(DerivedMesh *dm, std::vector<Imath::V3f> &normals)
+{
+ normals.clear();
+ normals.resize(dm->getNumVerts(dm));
+
+ MVert *verts = dm->getVertArray(dm);
+ float no[3];
+
+ for (int i = 0, e = dm->getNumVerts(dm); i < e; ++i) {
+ normal_short_to_float_v3(no, verts[i].no);
+ copy_zup_yup(normals[i].getValue(), no);
+ }
+}
+
+static void get_loop_normals(DerivedMesh *dm, std::vector<Imath::V3f> &normals)
+{
+ MPoly *mpoly = dm->getPolyArray(dm);
+ MPoly *mp = mpoly;
+
+ MLoop *mloop = dm->getLoopArray(dm);
+ MLoop *ml = mloop;
+
+ MVert *verts = dm->getVertArray(dm);
+
+ const float (*lnors)[3] = static_cast<float(*)[3]>(dm->getLoopDataArray(dm, CD_NORMAL));
+
+ normals.clear();
+ normals.resize(dm->getNumLoops(dm));
+
+ unsigned loop_index = 0;
+
+ /* NOTE: data needs to be written in the reverse order. */
+
+ if (lnors) {
+ for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i, ++mp) {
+ ml = mloop + mp->loopstart + (mp->totloop - 1);
+
+ for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
+ const int index = ml->v;
+ copy_zup_yup(normals[loop_index].getValue(), lnors[index]);
+ }
+ }
+ }
+ else {
+ float no[3];
+
+ for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i, ++mp) {
+ ml = mloop + mp->loopstart + (mp->totloop - 1);
+
+ /* Flat shaded, use common normal for all verts. */
+ if ((mp->flag & ME_SMOOTH) == 0) {
+ BKE_mesh_calc_poly_normal(mp, ml - (mp->totloop - 1), verts, no);
+
+ for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
+ copy_zup_yup(normals[loop_index].getValue(), no);
+ }
+ }
+ else {
+ /* Smooth shaded, use individual vert normals. */
+ for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
+ normal_short_to_float_v3(no, verts[ml->v].no);
+ copy_zup_yup(normals[loop_index].getValue(), no);
+ }
+ }
+ }
+ }
+}
+
+/* *************** Modifiers *************** */
+
+/* check if the mesh is a subsurf, ignoring disabled modifiers and
+ * displace if it's after subsurf. */
+static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob)
+{
+ ModifierData *md = static_cast<ModifierData *>(ob->modifiers.last);
+
+ for (; md; md = md->prev) {
+ if (!modifier_isEnabled(scene, md, eModifierMode_Render)) {
+ continue;
+ }
+
+ if (md->type == eModifierType_Subsurf) {
+ SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData*>(md);
+
+ if (smd->subdivType == ME_CC_SUBSURF) {
+ return md;
+ }
+ }
+
+ /* mesh is not a subsurf. break */
+ if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) {
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob)
+{
+ ModifierData *md = modifiers_findByType(ob, eModifierType_Fluidsim);
+
+ if (md && (modifier_isEnabled(scene, md, eModifierMode_Render))) {
+ FluidsimModifierData *fsmd = reinterpret_cast<FluidsimModifierData *>(md);
+
+ if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) {
+ return md;
+ }
+ }
+
+ return NULL;
+}
+
+/* ************************************************************************** */
+
+AbcMeshWriter::AbcMeshWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings)
+ : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+ m_is_animated = isAnimated();
+ m_subsurf_mod = NULL;
+ m_has_per_face_materials = false;
+ m_is_subd = false;
+
+ /* If the object is static, use the default static time sampling. */
+ if (!m_is_animated) {
+ time_sampling = 0;
+ }
+
+ if (!m_settings.apply_subdiv) {
+ m_subsurf_mod = get_subsurf_modifier(m_scene, m_object);
+ m_is_subd = (m_subsurf_mod != NULL);
+ }
+
+ m_is_liquid = (get_liquid_sim_modifier(m_scene, m_object) != NULL);
+
+ while (parent->alembicXform().getChildHeader(m_name)) {
+ m_name.append("_");
+ }
+
+ if (m_settings.use_subdiv_schema && m_is_subd) {
+ OSubD subd(parent->alembicXform(), m_name, m_time_sampling);
+ m_subdiv_schema = subd.getSchema();
+ }
+ else {
+ OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling);
+ m_mesh_schema = mesh.getSchema();
+
+ OCompoundProperty typeContainer = m_mesh_schema.getUserProperties();
+ OBoolProperty type(typeContainer, "meshtype");
+ type.set(m_is_subd);
+ }
+}
+
+AbcMeshWriter::~AbcMeshWriter()
+{
+ if (m_subsurf_mod) {
+ m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
+ }
+}
+
+bool AbcMeshWriter::isAnimated() const
+{
+ /* Check if object has shape keys. */
+ Mesh *me = static_cast<Mesh *>(m_object->data);
+
+ if (me->key) {
+ return true;
+ }
+
+ /* Test modifiers. */
+ ModifierData *md = static_cast<ModifierData *>(m_object->modifiers.first);
+
+ while (md) {
+ if (md->type != eModifierType_Subsurf) {
+ return true;
+ }
+
+ md = md->next;
+ }
+
+ return false;
+}
+
+void AbcMeshWriter::do_write()
+{
+ /* We have already stored a sample for this object. */
+ if (!m_first_frame && !m_is_animated)
+ return;
+
+ DerivedMesh *dm = getFinalMesh();
+
+ try {
+ if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) {
+ writeSubD(dm);
+ }
+ else {
+ writeMesh(dm);
+ }
+
+ freeMesh(dm);
+ }
+ catch (...) {
+ freeMesh(dm);
+ throw;
+ }
+}
+
+void AbcMeshWriter::writeMesh(DerivedMesh *dm)
+{
+ std::vector<Imath::V3f> points, normals;
+ std::vector<int32_t> poly_verts, loop_counts;
+
+ bool smooth_normal = false;
+
+ get_vertices(dm, points);
+ get_topology(dm, poly_verts, loop_counts, smooth_normal);
+
+ if (m_first_frame) {
+ writeCommonData(dm, m_mesh_schema);
+ }
+
+ m_mesh_sample = OPolyMeshSchema::Sample(V3fArraySample(points),
+ Int32ArraySample(poly_verts),
+ Int32ArraySample(loop_counts));
+
+ UVSample sample;
+ if (m_settings.export_uvs) {
+ const char *name = get_uv_sample(sample, m_custom_data_config, &dm->loopData);
+
+ if (!sample.indices.empty() && !sample.uvs.empty()) {
+ OV2fGeomParam::Sample uv_sample;
+ uv_sample.setVals(V2fArraySample(sample.uvs));
+ uv_sample.setIndices(UInt32ArraySample(sample.indices));
+ uv_sample.setScope(kFacevaryingScope);
+
+ m_mesh_schema.setUVSourceName(name);
+ m_mesh_sample.setUVs(uv_sample);
+ }
+
+ write_custom_data(m_mesh_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPUV);
+ }
+
+ if (m_settings.export_normals) {
+ if (smooth_normal) {
+ get_loop_normals(dm, normals);
+ }
+ else {
+ get_vertex_normals(dm, normals);
+ }
+
+ ON3fGeomParam::Sample normals_sample;
+ if (!normals.empty()) {
+ normals_sample.setScope((smooth_normal) ? kFacevaryingScope : kVertexScope);
+ normals_sample.setVals(V3fArraySample(normals));
+ }
+
+ m_mesh_sample.setNormals(normals_sample);
+ }
+
+ if (m_is_liquid) {
+ std::vector<Imath::V3f> velocities;
+ getVelocities(dm, velocities);
+
+ m_mesh_sample.setVelocities(V3fArraySample(velocities));
+ }
+
+ m_mesh_sample.setSelfBounds(bounds());
+
+ m_mesh_schema.set(m_mesh_sample);
+
+ writeArbGeoParams(dm);
+}
+
+void AbcMeshWriter::writeSubD(DerivedMesh *dm)
+{
+ std::vector<float> crease_sharpness;
+ std::vector<Imath::V3f> points;
+ std::vector<int32_t> poly_verts, loop_counts;
+ std::vector<int32_t> crease_indices, crease_lengths;
+
+ bool smooth_normal = false;
+
+ get_vertices(dm, points);
+ get_topology(dm, poly_verts, loop_counts, smooth_normal);
+ get_creases(dm, crease_indices, crease_lengths, crease_sharpness);
+
+ if (m_first_frame) {
+ /* create materials' face_sets */
+ writeCommonData(dm, m_subdiv_schema);
+ }
+
+ m_subdiv_sample = OSubDSchema::Sample(V3fArraySample(points),
+ Int32ArraySample(poly_verts),
+ Int32ArraySample(loop_counts));
+
+ UVSample sample;
+ if (m_settings.export_uvs) {
+ const char *name = get_uv_sample(sample, m_custom_data_config, &dm->loopData);
+
+ if (!sample.indices.empty() && !sample.uvs.empty()) {
+ OV2fGeomParam::Sample uv_sample;
+ uv_sample.setVals(V2fArraySample(sample.uvs));
+ uv_sample.setIndices(UInt32ArraySample(sample.indices));
+ uv_sample.setScope(kFacevaryingScope);
+
+ m_subdiv_schema.setUVSourceName(name);
+ m_subdiv_sample.setUVs(uv_sample);
+ }
+
+ write_custom_data(m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPUV);
+ }
+
+ if (!crease_indices.empty()) {
+ m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices));
+ m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths));
+ m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness));
+ }
+
+ m_subdiv_sample.setSelfBounds(bounds());
+ m_subdiv_schema.set(m_subdiv_sample);
+
+ writeArbGeoParams(dm);
+}
+
+template <typename Schema>
+void AbcMeshWriter::writeCommonData(DerivedMesh *dm, Schema &schema)
+{
+ std::map< std::string, std::vector<int32_t> > geo_groups;
+ getGeoGroups(dm, geo_groups);
+
+ std::map< std::string, std::vector<int32_t> >::iterator it;
+ for (it = geo_groups.begin(); it != geo_groups.end(); ++it) {
+ OFaceSet face_set = schema.createFaceSet(it->first);
+ OFaceSetSchema::Sample samp;
+ samp.setFaces(Int32ArraySample(it->second));
+ face_set.getSchema().set(samp);
+ }
+}
+
+DerivedMesh *AbcMeshWriter::getFinalMesh()
+{
+ /* We don't want subdivided mesh data */
+ if (m_subsurf_mod) {
+ m_subsurf_mod->mode |= eModifierMode_DisableTemporary;
+ }
+
+ DerivedMesh *dm = mesh_create_derived_render(m_scene, m_object, CD_MASK_MESH);
+
+ if (m_subsurf_mod) {
+ m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
+ }
+
+ m_custom_data_config.pack_uvs = m_settings.pack_uv;
+ m_custom_data_config.mpoly = dm->getPolyArray(dm);
+ m_custom_data_config.mloop = dm->getLoopArray(dm);
+ m_custom_data_config.totpoly = dm->getNumPolys(dm);
+ m_custom_data_config.totloop = dm->getNumLoops(dm);
+ m_custom_data_config.totvert = dm->getNumVerts(dm);
+
+ return dm;
+}
+
+void AbcMeshWriter::freeMesh(DerivedMesh *dm)
+{
+ dm->release(dm);
+}
+
+void AbcMeshWriter::writeArbGeoParams(DerivedMesh *dm)
+{
+ if (m_is_liquid) {
+ /* We don't need anything more for liquid meshes. */
+ return;
+ }
+
+ if (m_settings.export_vcols) {
+ if (m_subdiv_schema.valid()) {
+ write_custom_data(m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPCOL);
+ }
+ else {
+ write_custom_data(m_mesh_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPCOL);
+ }
+ }
+
+ if (m_first_frame && m_has_per_face_materials) {
+ std::vector<int32_t> material_indices;
+
+ if (m_settings.export_face_sets) {
+ get_material_indices(dm, material_indices);
+
+ OFaceSetSchema::Sample samp;
+ samp.setFaces(Int32ArraySample(material_indices));
+ m_face_set.getSchema().set(samp);
+ }
+ }
+}
+
+void AbcMeshWriter::getVelocities(DerivedMesh *dm, std::vector<Imath::V3f> &vels)
+{
+ const int totverts = dm->getNumVerts(dm);
+
+ vels.clear();
+ vels.resize(totverts);
+
+ ModifierData *md = get_liquid_sim_modifier(m_scene, m_object);
+ FluidsimModifierData *fmd = reinterpret_cast<FluidsimModifierData *>(md);
+ FluidsimSettings *fss = fmd->fss;
+
+ if (fss->meshVelocities) {
+ float *mesh_vels = reinterpret_cast<float *>(fss->meshVelocities);
+
+ for (int i = 0; i < totverts; ++i) {
+ copy_zup_yup(vels[i].getValue(), mesh_vels);
+ mesh_vels += 3;
+ }
+ }
+ else {
+ std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f));
+ }
+}
+
+void AbcMeshWriter::getGeoGroups(
+ DerivedMesh *dm,
+ std::map<std::string, std::vector<int32_t> > &geo_groups)
+{
+ const int num_poly = dm->getNumPolys(dm);
+ MPoly *polygons = dm->getPolyArray(dm);
+
+ for (int i = 0; i < num_poly; ++i) {
+ MPoly &current_poly = polygons[i];
+ short mnr = current_poly.mat_nr;
+
+ Material *mat = give_current_material(m_object, mnr + 1);
+
+ if (!mat) {
+ continue;
+ }
+
+ std::string name = get_id_name(&mat->id);
+
+ if (geo_groups.find(name) == geo_groups.end()) {
+ std::vector<int32_t> faceArray;
+ geo_groups[name] = faceArray;
+ }
+
+ geo_groups[name].push_back(i);
+ }
+
+ if (geo_groups.size() == 0) {
+ Material *mat = give_current_material(m_object, 1);
+
+ std::string name = (mat) ? get_id_name(&mat->id) : "default";
+
+ std::vector<int32_t> faceArray;
+
+ for (int i = 0, e = dm->getNumTessFaces(dm); i < e; ++i) {
+ faceArray.push_back(i);
+ }
+
+ geo_groups[name] = faceArray;
+ }
+}
+
+/* ************************************************************************** */
+
+/* Some helpers for mesh generation */
+namespace utils {
+
+void mesh_add_verts(Mesh *mesh, size_t len)
+{
+ if (len == 0) {
+ return;
+ }
+
+ const int totvert = mesh->totvert + len;
+ CustomData vdata;
+ CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert);
+ CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert);
+
+ if (!CustomData_has_layer(&vdata, CD_MVERT)) {
+ CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert);
+ }
+
+ CustomData_free(&mesh->vdata, mesh->totvert);
+ mesh->vdata = vdata;
+ BKE_mesh_update_customdata_pointers(mesh, false);
+
+ mesh->totvert = totvert;
+}
+
+static void mesh_add_mloops(Mesh *mesh, size_t len)
+{
+ if (len == 0) {
+ return;
+ }
+
+ /* new face count */
+ const int totloops = mesh->totloop + len;
+
+ CustomData ldata;
+ CustomData_copy(&mesh->ldata, &ldata, CD_MASK_MESH, CD_DEFAULT, totloops);
+ CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop);
+
+ if (!CustomData_has_layer(&ldata, CD_MLOOP)) {
+ CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloops);
+ }
+
+ CustomData_free(&mesh->ldata, mesh->totloop);
+ mesh->ldata = ldata;
+ BKE_mesh_update_customdata_pointers(mesh, false);
+
+ mesh->totloop = totloops;
+}
+
+static void mesh_add_mpolygons(Mesh *mesh, size_t len)
+{
+ if (len == 0) {
+ return;
+ }
+
+ const int totpolys = mesh->totpoly + len;
+
+ CustomData pdata;
+ CustomData_copy(&mesh->pdata, &pdata, CD_MASK_MESH, CD_DEFAULT, totpolys);
+ CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly);
+
+ if (!CustomData_has_layer(&pdata, CD_MPOLY)) {
+ CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpolys);
+ }
+
+ CustomData_free(&mesh->pdata, mesh->totpoly);
+ mesh->pdata = pdata;
+ BKE_mesh_update_customdata_pointers(mesh, false);
+
+ mesh->totpoly = totpolys;
+}
+
+static void build_mat_map(const Main *bmain, std::map<std::string, Material *> &mat_map)
+{
+ Material *material = static_cast<Material *>(bmain->mat.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(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_name;
+
+ if (mat_iter == mat_map.end()) {
+ assigned_name = BKE_material_add(bmain, mat_name.c_str());
+ mat_map[mat_name] = assigned_name;
+ }
+ else {
+ assigned_name = mat_iter->second;
+ }
+
+ assign_material(ob, assigned_name, it->second, BKE_MAT_ASSIGN_OBJECT);
+ }
+ }
+}
+
+} /* namespace utils */
+
+/* ************************************************************************** */
+
+using Alembic::AbcGeom::UInt32ArraySamplePtr;
+using Alembic::AbcGeom::V2fArraySamplePtr;
+
+struct AbcMeshData {
+ Int32ArraySamplePtr face_indices;
+ Int32ArraySamplePtr face_counts;
+
+ P3fArraySamplePtr positions;
+
+ N3fArraySamplePtr vertex_normals;
+ N3fArraySamplePtr face_normals;
+
+ V2fArraySamplePtr uvs;
+ UInt32ArraySamplePtr uvs_indices;
+};
+
+static void *add_customdata_cb(void *user_data, const char *name, int data_type)
+{
+ Mesh *mesh = static_cast<Mesh *>(user_data);
+ CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
+ void *cd_ptr = NULL;
+
+ int index = -1;
+ if (cd_data_type == CD_MLOOPUV) {
+ index = ED_mesh_uv_texture_add(mesh, name, true);
+ cd_ptr = CustomData_get_layer(&mesh->ldata, cd_data_type);
+ }
+ else if (cd_data_type == CD_MLOOPCOL) {
+ index = ED_mesh_color_add(mesh, name, true);
+ cd_ptr = CustomData_get_layer(&mesh->ldata, cd_data_type);
+ }
+
+ if (index == -1) {
+ return NULL;
+ }
+
+ return cd_ptr;
+}
+
+CDStreamConfig create_config(Mesh *mesh)
+{
+ CDStreamConfig config;
+
+ config.mvert = mesh->mvert;
+ config.mpoly = mesh->mpoly;
+ config.mloop = mesh->mloop;
+ config.totpoly = mesh->totpoly;
+ config.totloop = mesh->totloop;
+ config.user_data = mesh;
+ config.loopdata = &mesh->ldata;
+ config.add_customdata_cb = add_customdata_cb;
+
+ return config;
+}
+
+static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data)
+{
+ MVert *mverts = config.mvert;
+ const P3fArraySamplePtr &positions = mesh_data.positions;
+ const N3fArraySamplePtr &normals = mesh_data.vertex_normals;
+
+ read_mverts(mverts, positions, normals);
+}
+
+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_yup_zup(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_yup_zup(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 UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices;
+ const N3fArraySamplePtr &normals = mesh_data.face_normals;
+
+ 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;
+
+ if (normals != NULL) {
+ 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];
+ loopuv.uv[0] = (*uvs)[uv_index][0];
+ loopuv.uv[1] = (*uvs)[uv_index][1];
+ }
+ }
+ }
+}
+
+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.user_data, name.c_str(), CD_MLOOPUV);
+ config.mloopuv = static_cast<MLoopUV *>(cd_ptr);
+ }
+}
+
+/* TODO(kevin): normals from Alembic files are not read in anymore, this is due
+ * to the fact that there are many issues that are not so easy to solve, mainly
+ * regarding the way normals are handled in Blender (MPoly.flag vs loop normals).
+ */
+ABC_INLINE void read_normals_params(AbcMeshData &abc_data,
+ const IN3fGeomParam &normals,
+ const ISampleSelector &selector)
+{
+ if (!normals.valid()) {
+ return;
+ }
+
+ IN3fGeomParam::Sample normsamp = normals.getExpandedValue(selector);
+
+ if (normals.getScope() == kFacevaryingScope) {
+ abc_data.face_normals = normsamp.getVals();
+ }
+ else if ((normals.getScope() == kVertexScope) || (normals.getScope() == kVaryingScope)) {
+ abc_data.vertex_normals = N3fArraySamplePtr();
+ }
+}
+
+/* ************************************************************************** */
+
+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_schema, m_min_time, m_max_time);
+}
+
+bool AbcMeshReader::valid() const
+{
+ return m_schema.valid();
+}
+
+void AbcMeshReader::readObjectData(Main *bmain, float time)
+{
+ 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;
+
+ const ISampleSelector sample_sel(time);
+ const IPolyMeshSchema::Sample sample = m_schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+ const Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+ utils::mesh_add_verts(mesh, positions->size());
+ utils::mesh_add_mpolygons(mesh, face_counts->size());
+ utils::mesh_add_mloops(mesh, face_indices->size());
+
+ m_mesh_data = create_config(mesh);
+
+ bool has_smooth_normals = false;
+ read_mesh_sample(m_settings, m_schema, sample_sel, m_mesh_data, has_smooth_normals);
+
+ BKE_mesh_calc_normals(mesh);
+ BKE_mesh_calc_edges(mesh, false, false);
+
+ if (m_settings->validate_meshes) {
+ BKE_mesh_validate(mesh, false, false);
+ }
+
+ readFaceSetsSample(bmain, mesh, 0, sample_sel);
+
+ if (has_animations(m_schema, m_settings)) {
+ addCacheModifier();
+ }
+}
+
+void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start,
+ const ISampleSelector &sample_sel)
+{
+ std::vector<std::string> face_sets;
+ m_schema.getFaceSetNames(face_sets);
+
+ if (face_sets.empty()) {
+ return;
+ }
+
+ std::map<std::string, int> mat_map;
+ int current_mat = 0;
+
+ for (int i = 0; i < face_sets.size(); ++i) {
+ const std::string &grp_name = face_sets[i];
+
+ if (mat_map.find(grp_name) == mat_map.end()) {
+ mat_map[grp_name] = 1 + current_mat++;
+ }
+
+ const int assigned_mat = mat_map[grp_name];
+
+ const IFaceSet faceset = m_schema.getFaceSet(grp_name);
+
+ if (!faceset.valid()) {
+ 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] + poly_start;
+
+ if (pos >= mesh->totpoly) {
+ std::cerr << "Faceset overflow on " << faceset.getName() << '\n';
+ break;
+ }
+
+ MPoly &poly = mesh->mpoly[pos];
+ poly.mat_nr = assigned_mat - 1;
+ }
+ }
+
+ utils::assign_materials(bmain, m_object, mat_map);
+}
+
+void read_mesh_sample(ImportSettings *settings,
+ const IPolyMeshSchema &schema,
+ const ISampleSelector &selector,
+ CDStreamConfig &config,
+ bool &do_normals)
+{
+ 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();
+
+ read_normals_params(abc_mesh_data, schema.getNormalsParam(), selector);
+
+ do_normals = (abc_mesh_data.face_normals != NULL);
+
+ 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);
+ }
+
+ if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
+ read_custom_data(schema.getArbGeomParams(), config, selector);
+ }
+
+ /* TODO: face sets */
+}
+
+/* ************************************************************************** */
+
+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;
+}
+
+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_schema, m_min_time, m_max_time);
+}
+
+bool AbcSubDReader::valid() const
+{
+ return m_schema.valid();
+}
+
+void AbcSubDReader::readObjectData(Main *bmain, float time)
+{
+ 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;
+
+ const ISampleSelector sample_sel(time);
+ const ISubDSchema::Sample sample = m_schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+ const Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+ utils::mesh_add_verts(mesh, positions->size());
+ utils::mesh_add_mpolygons(mesh, face_counts->size());
+ utils::mesh_add_mloops(mesh, face_indices->size());
+
+ m_mesh_data = create_config(mesh);
+
+ read_subd_sample(m_settings, m_schema, sample_sel, m_mesh_data);
+
+ Int32ArraySamplePtr indices = sample.getCreaseIndices();
+ Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses();
+
+ MEdge *edges = mesh->medge;
+
+ if (indices && sharpnesses) {
+ for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, ++s) {
+ MEdge *edge = find_edge(edges, mesh->totedge, (*indices)[i], (*indices)[i + 1]);
+
+ if (edge) {
+ edge->crease = FTOCHAR((*sharpnesses)[s]);
+ }
+ }
+
+ mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE;
+ }
+
+ BKE_mesh_calc_normals(mesh);
+ BKE_mesh_calc_edges(mesh, false, false);
+
+ if (m_settings->validate_meshes) {
+ BKE_mesh_validate(mesh, false, false);
+ }
+
+ if (has_animations(m_schema, m_settings)) {
+ addCacheModifier();
+ }
+}
+
+void read_subd_sample(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.vertex_normals = N3fArraySamplePtr();
+ abc_mesh_data.face_normals = N3fArraySamplePtr();
+ abc_mesh_data.positions = 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);
+ }
+
+ if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
+ read_custom_data(schema.getArbGeomParams(), config, selector);
+ }
+
+ /* TODO: face sets */
+}
diff --git a/source/blender/alembic/intern/abc_mesh.h b/source/blender/alembic/intern/abc_mesh.h
new file mode 100644
index 00000000000..9dc222e5206
--- /dev/null
+++ b/source/blender/alembic/intern/abc_mesh.h
@@ -0,0 +1,152 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_MESH_H__
+#define __ABC_MESH_H__
+
+#include "abc_customdata.h"
+#include "abc_object.h"
+
+struct DerivedMesh;
+struct Mesh;
+struct ModifierData;
+
+/* ************************************************************************** */
+
+class AbcMeshWriter : public AbcObjectWriter {
+ Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema;
+ Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample;
+
+ Alembic::AbcGeom::OSubDSchema m_subdiv_schema;
+ Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample;
+
+ bool m_has_per_face_materials;
+ Alembic::AbcGeom::OFaceSet m_face_set;
+ Alembic::Abc::OArrayProperty m_mat_indices;
+
+ bool m_is_animated;
+ ModifierData *m_subsurf_mod;
+
+ CDStreamConfig m_custom_data_config;
+
+ bool m_is_liquid;
+ bool m_is_subd;
+
+public:
+ AbcMeshWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings);
+
+ ~AbcMeshWriter();
+
+private:
+ virtual void do_write();
+
+ bool isAnimated() const;
+
+ void writeMesh(DerivedMesh *dm);
+ void writeSubD(DerivedMesh *dm);
+
+ void getMeshInfo(DerivedMesh *dm, std::vector<float> &points,
+ std::vector<int32_t> &facePoints,
+ std::vector<int32_t> &faceCounts,
+ std::vector<int32_t> &creaseIndices,
+ std::vector<int32_t> &creaseLengths,
+ std::vector<float> &creaseSharpness);
+
+ DerivedMesh *getFinalMesh();
+ void freeMesh(DerivedMesh *dm);
+
+ void getMaterialIndices(DerivedMesh *dm, std::vector<int32_t> &indices);
+
+ void writeArbGeoParams(DerivedMesh *dm);
+ void getGeoGroups(DerivedMesh *dm, std::map<std::string, std::vector<int32_t> > &geoGroups);
+
+ /* fluid surfaces support */
+ void getVelocities(DerivedMesh *dm, std::vector<Imath::V3f> &vels);
+
+ template <typename Schema>
+ void writeCommonData(DerivedMesh *dm, Schema &schema);
+};
+
+/* ************************************************************************** */
+
+class AbcMeshReader : public AbcObjectReader {
+ Alembic::AbcGeom::IPolyMeshSchema m_schema;
+
+ CDStreamConfig m_mesh_data;
+
+public:
+ AbcMeshReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+
+private:
+ void readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start,
+ const Alembic::AbcGeom::ISampleSelector &sample_sel);
+};
+
+void read_mesh_sample(ImportSettings *settings,
+ const Alembic::AbcGeom::IPolyMeshSchema &schema,
+ const Alembic::AbcGeom::ISampleSelector &selector,
+ CDStreamConfig &config,
+ bool &do_normals);
+
+/* ************************************************************************** */
+
+class AbcSubDReader : public AbcObjectReader {
+ Alembic::AbcGeom::ISubDSchema m_schema;
+
+ CDStreamConfig m_mesh_data;
+
+public:
+ AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+};
+
+void read_subd_sample(ImportSettings *settings,
+ const Alembic::AbcGeom::ISubDSchema &schema,
+ const Alembic::AbcGeom::ISampleSelector &selector,
+ CDStreamConfig &config);
+
+/* ************************************************************************** */
+
+namespace utils {
+
+void mesh_add_verts(struct Mesh *mesh, size_t len);
+
+}
+
+void read_mverts(MVert *mverts,
+ const Alembic::AbcGeom::P3fArraySamplePtr &positions,
+ const Alembic::AbcGeom::N3fArraySamplePtr &normals);
+
+CDStreamConfig create_config(Mesh *mesh);
+
+#endif /* __ABC_MESH_H__ */
diff --git a/source/blender/alembic/intern/abc_nurbs.cc b/source/blender/alembic/intern/abc_nurbs.cc
new file mode 100644
index 00000000000..a3c18ad6301
--- /dev/null
+++ b/source/blender/alembic/intern/abc_nurbs.cc
@@ -0,0 +1,367 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_nurbs.h"
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+
+#include "BKE_curve.h"
+#include "BKE_object.h"
+}
+
+using Alembic::AbcGeom::bool_t;
+using Alembic::AbcGeom::FloatArraySample;
+using Alembic::AbcGeom::FloatArraySamplePtr;
+using Alembic::AbcGeom::MetaData;
+using Alembic::AbcGeom::P3fArraySamplePtr;
+using Alembic::AbcGeom::kWrapExisting;
+
+using Alembic::AbcGeom::IBoolProperty;
+using Alembic::AbcGeom::ICompoundProperty;
+using Alembic::AbcGeom::INuPatch;
+using Alembic::AbcGeom::INuPatchSchema;
+using Alembic::AbcGeom::IObject;
+using Alembic::AbcGeom::ISampleSelector;
+
+using Alembic::AbcGeom::OBoolProperty;
+using Alembic::AbcGeom::OCompoundProperty;
+using Alembic::AbcGeom::ONuPatch;
+using Alembic::AbcGeom::ONuPatchSchema;
+
+/* ************************************************************************** */
+
+AbcNurbsWriter::AbcNurbsWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings)
+ : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+ m_is_animated = isAnimated();
+
+ /* if the object is static, use the default static time sampling */
+ if (!m_is_animated) {
+ m_time_sampling = 0;
+ }
+
+ Curve *curve = static_cast<Curve *>(m_object->data);
+ size_t numNurbs = BLI_listbase_count(&curve->nurb);
+
+ for (size_t i = 0; i < numNurbs; ++i) {
+ std::stringstream str;
+ str << m_name << '_' << i;
+
+ while (parent->alembicXform().getChildHeader(str.str())) {
+ str << "_";
+ }
+
+ ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling);
+ m_nurbs_schema.push_back(nurbs.getSchema());
+ }
+}
+
+bool AbcNurbsWriter::isAnimated() const
+{
+ /* check if object has shape keys */
+ Curve *cu = static_cast<Curve *>(m_object->data);
+ return (cu->key != NULL);
+}
+
+static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots)
+{
+ if (num_knots <= 1) {
+ return;
+ }
+
+ /* Add an extra knot at the beggining and end of the array since most apps
+ * require/expect them. */
+ knots.reserve(num_knots + 2);
+
+ knots.push_back(0.0f);
+
+ for (int i = 0; i < num_knots; ++i) {
+ knots.push_back(nu_knots[i]);
+ }
+
+ knots[0] = 2.0f * knots[1] - knots[2];
+ knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]);
+}
+
+void AbcNurbsWriter::do_write()
+{
+ /* we have already stored a sample for this object. */
+ if (!m_first_frame && !m_is_animated) {
+ return;
+ }
+
+ if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) {
+ return;
+ }
+
+ Curve *curve = static_cast<Curve *>(m_object->data);
+ ListBase *nulb;
+
+ if (m_object->curve_cache->deformed_nurbs.first != NULL) {
+ nulb = &m_object->curve_cache->deformed_nurbs;
+ }
+ else {
+ nulb = BKE_curve_nurbs_get(curve);
+ }
+
+ size_t count = 0;
+ for (Nurb *nu = static_cast<Nurb *>(nulb->first); nu; nu = nu->next, ++count) {
+ std::vector<float> knotsU;
+ get_knots(knotsU, KNOTSU(nu), nu->knotsu);
+
+ std::vector<float> knotsV;
+ get_knots(knotsV, KNOTSV(nu), nu->knotsv);
+
+ const int size = nu->pntsu * nu->pntsv;
+ std::vector<Imath::V3f> positions(size);
+ std::vector<float> weights(size);
+
+ const BPoint *bp = nu->bp;
+
+ for (int i = 0; i < size; ++i, ++bp) {
+ copy_zup_yup(positions[i].getValue(), bp->vec);
+ weights[i] = bp->vec[3];
+ }
+
+ ONuPatchSchema::Sample sample;
+ sample.setUOrder(nu->orderu + 1);
+ sample.setVOrder(nu->orderv + 1);
+ sample.setPositions(positions);
+ sample.setPositionWeights(weights);
+ sample.setUKnot(FloatArraySample(knotsU));
+ sample.setVKnot(FloatArraySample(knotsV));
+ sample.setNu(nu->pntsu);
+ sample.setNv(nu->pntsv);
+
+ /* TODO(kevin): to accomodate other software we should duplicate control
+ * points to indicate that a NURBS is cyclic. */
+ OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties();
+
+ if ((nu->flagu & CU_NURB_ENDPOINT) != 0) {
+ OBoolProperty prop(user_props, "endpoint_u");
+ prop.set(true);
+ }
+
+ if ((nu->flagv & CU_NURB_ENDPOINT) != 0) {
+ OBoolProperty prop(user_props, "endpoint_v");
+ prop.set(true);
+ }
+
+ if ((nu->flagu & CU_NURB_CYCLIC) != 0) {
+ OBoolProperty prop(user_props, "cyclic_u");
+ prop.set(true);
+ }
+
+ if ((nu->flagv & CU_NURB_CYCLIC) != 0) {
+ OBoolProperty prop(user_props, "cyclic_v");
+ prop.set(true);
+ }
+
+ m_nurbs_schema[count].set(sample);
+ }
+}
+
+/* ************************************************************************** */
+
+AbcNurbsReader::AbcNurbsReader(const IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ getNurbsPatches(m_iobject);
+ get_min_max_time(m_schemas[0].first, m_min_time, m_max_time);
+}
+
+bool AbcNurbsReader::valid() const
+{
+ if (m_schemas.empty()) {
+ return false;
+ }
+
+ std::vector< std::pair<INuPatchSchema, IObject> >::const_iterator it;
+ for (it = m_schemas.begin(); it != m_schemas.end(); ++it) {
+ const INuPatchSchema &schema = it->first;
+
+ if (!schema.valid()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots)
+{
+ if (!knots || knots->size() == 0) {
+ return false;
+ }
+
+ /* Skip first and last knots, as they are used for padding. */
+ const size_t num_knots = knots->size() - 2;
+ nu_knots = static_cast<float *>(MEM_callocN(num_knots * sizeof(float), "abc_setsplineknotsu"));
+
+ for (size_t i = 0; i < num_knots; ++i) {
+ nu_knots[i] = (*knots)[i + 1];
+ }
+
+ return true;
+}
+
+void AbcNurbsReader::readObjectData(Main *bmain, float time)
+{
+ Curve *cu = static_cast<Curve *>(BKE_curve_add(bmain, "abc_curve", OB_SURF));
+ cu->actvert = CU_ACT_NONE;
+
+ std::vector< std::pair<INuPatchSchema, IObject> >::iterator it;
+
+ for (it = m_schemas.begin(); it != m_schemas.end(); ++it) {
+ Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb"));
+ nu->flag = CU_SMOOTH;
+ nu->type = CU_NURBS;
+ nu->resolu = cu->resolu;
+ nu->resolv = cu->resolv;
+
+ const ISampleSelector sample_sel(time);
+ const INuPatchSchema &schema = it->first;
+ const INuPatchSchema::Sample smp = schema.getValue(sample_sel);
+
+ nu->orderu = smp.getUOrder() - 1;
+ nu->orderv = smp.getVOrder() - 1;
+ nu->pntsu = smp.getNumU();
+ nu->pntsv = smp.getNumV();
+
+ /* Read positions and weights. */
+
+ const P3fArraySamplePtr positions = smp.getPositions();
+ const FloatArraySamplePtr weights = smp.getPositionWeights();
+
+ const size_t num_points = positions->size();
+
+ nu->bp = static_cast<BPoint *>(MEM_callocN(num_points * sizeof(BPoint), "abc_setsplinetype"));
+
+ BPoint *bp = nu->bp;
+ float posw_in = 1.0f;
+
+ for (int i = 0; i < num_points; ++i, ++bp) {
+ const Imath::V3f &pos_in = (*positions)[i];
+
+ if (weights) {
+ posw_in = (*weights)[i];
+ }
+
+ copy_yup_zup(bp->vec, pos_in.getValue());
+ bp->vec[3] = posw_in;
+ bp->f1 = SELECT;
+ bp->radius = 1.0f;
+ bp->weight = 1.0f;
+ }
+
+ /* Read knots. */
+
+ if (!set_knots(smp.getUKnot(), nu->knotsu)) {
+ BKE_nurb_knot_calc_u(nu);
+ }
+
+ if (!set_knots(smp.getVKnot(), nu->knotsv)) {
+ BKE_nurb_knot_calc_v(nu);
+ }
+
+ /* Read flags. */
+
+ ICompoundProperty user_props = schema.getUserProperties();
+
+ if (has_property(user_props, "enpoint_u")) {
+ nu->flagu |= CU_NURB_ENDPOINT;
+ }
+
+ if (has_property(user_props, "enpoint_v")) {
+ nu->flagv |= CU_NURB_ENDPOINT;
+ }
+
+ if (has_property(user_props, "cyclic_u")) {
+ nu->flagu |= CU_NURB_CYCLIC;
+ }
+
+ if (has_property(user_props, "cyclic_v")) {
+ nu->flagv |= CU_NURB_CYCLIC;
+ }
+
+ BLI_addtail(BKE_curve_nurbs_get(cu), nu);
+ }
+
+ BLI_strncpy(cu->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1);
+
+ m_object = BKE_object_add_only_object(bmain, OB_SURF, m_object_name.c_str());
+ m_object->data = cu;
+}
+
+void AbcNurbsReader::getNurbsPatches(const IObject &obj)
+{
+ if (!obj.valid()) {
+ return;
+ }
+
+ const int num_children = obj.getNumChildren();
+
+ if (num_children == 0) {
+ INuPatch abc_nurb(obj, kWrapExisting);
+ INuPatchSchema schem = abc_nurb.getSchema();
+ m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, obj));
+ return;
+ }
+
+ for (int i = 0; i < num_children; ++i) {
+ bool ok = true;
+ IObject child(obj, obj.getChildHeader(i).getName());
+
+ if (!m_name.empty() && child.valid() && !begins_with(child.getFullName(), m_name)) {
+ ok = false;
+ }
+
+ if (!child.valid()) {
+ continue;
+ }
+
+ const MetaData &md = child.getMetaData();
+
+ if (INuPatch::matches(md) && ok) {
+ INuPatch abc_nurb(child, kWrapExisting);
+ INuPatchSchema schem = abc_nurb.getSchema();
+ m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, child));
+ }
+
+ getNurbsPatches(child);
+ }
+}
diff --git a/source/blender/alembic/intern/abc_nurbs.h b/source/blender/alembic/intern/abc_nurbs.h
new file mode 100644
index 00000000000..1b2e7a8391f
--- /dev/null
+++ b/source/blender/alembic/intern/abc_nurbs.h
@@ -0,0 +1,63 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_NURBS_H__
+#define __ABC_NURBS_H__
+
+#include "abc_object.h"
+
+/* ************************************************************************** */
+
+class AbcNurbsWriter : public AbcObjectWriter {
+ std::vector<Alembic::AbcGeom::ONuPatchSchema> m_nurbs_schema;
+ bool m_is_animated;
+
+public:
+ AbcNurbsWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings);
+
+private:
+ virtual void do_write();
+
+ bool isAnimated() const;
+};
+
+/* ************************************************************************** */
+
+class AbcNurbsReader : public AbcObjectReader {
+ std::vector< std::pair<Alembic::AbcGeom::INuPatchSchema, Alembic::Abc::IObject> > m_schemas;
+
+public:
+ AbcNurbsReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+
+private:
+ void getNurbsPatches(const Alembic::Abc::IObject &obj);
+};
+
+#endif /* __ABC_NURBS_H__ */
diff --git a/source/blender/alembic/intern/abc_object.cc b/source/blender/alembic/intern/abc_object.cc
new file mode 100644
index 00000000000..5b7b85f10ea
--- /dev/null
+++ b/source/blender/alembic/intern/abc_object.cc
@@ -0,0 +1,238 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_object.h"
+
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_cachefile_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_space_types.h" /* for FILE_MAX */
+
+#include "BKE_constraint.h"
+#include "BKE_depsgraph.h"
+#include "BKE_idprop.h"
+#include "BKE_library.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+}
+
+using Alembic::AbcGeom::IObject;
+using Alembic::AbcGeom::IXform;
+using Alembic::AbcGeom::IXformSchema;
+
+using Alembic::AbcGeom::OCompoundProperty;
+using Alembic::AbcGeom::ODoubleArrayProperty;
+using Alembic::AbcGeom::ODoubleProperty;
+using Alembic::AbcGeom::OFloatArrayProperty;
+using Alembic::AbcGeom::OFloatProperty;
+using Alembic::AbcGeom::OInt32ArrayProperty;
+using Alembic::AbcGeom::OInt32Property;
+using Alembic::AbcGeom::OStringArrayProperty;
+using Alembic::AbcGeom::OStringProperty;
+
+/* ************************************************************************** */
+
+AbcObjectWriter::AbcObjectWriter(Scene *scene,
+ Object *ob,
+ uint32_t time_sampling,
+ ExportSettings &settings,
+ AbcObjectWriter *parent)
+ : m_object(ob)
+ , m_settings(settings)
+ , m_scene(scene)
+ , m_time_sampling(time_sampling)
+ , m_first_frame(true)
+{
+ m_name = get_id_name(m_object) + "Shape";
+
+ if (parent) {
+ parent->addChild(this);
+ }
+}
+
+AbcObjectWriter::~AbcObjectWriter()
+{}
+
+void AbcObjectWriter::addChild(AbcObjectWriter *child)
+{
+ m_children.push_back(child);
+}
+
+Imath::Box3d AbcObjectWriter::bounds()
+{
+ BoundBox *bb = BKE_object_boundbox_get(this->m_object);
+
+ if (!bb) {
+ if (this->m_object->type != OB_CAMERA) {
+ std::cerr << "Boundbox is null!\n";
+ }
+
+ return Imath::Box3d();
+ }
+
+ /* Convert Z-up to Y-up. */
+ this->m_bounds.min.x = bb->vec[0][0];
+ this->m_bounds.min.y = bb->vec[0][2];
+ this->m_bounds.min.z = -bb->vec[0][1];
+
+ this->m_bounds.max.x = bb->vec[6][0];
+ this->m_bounds.max.y = bb->vec[6][2];
+ this->m_bounds.max.z = -bb->vec[6][1];
+
+ return this->m_bounds;
+}
+
+void AbcObjectWriter::write()
+{
+ do_write();
+ m_first_frame = false;
+}
+
+/* ************************************************************************** */
+
+AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings)
+ : m_name("")
+ , m_object_name("")
+ , m_data_name("")
+ , m_object(NULL)
+ , m_iobject(object)
+ , m_settings(&settings)
+ , m_min_time(std::numeric_limits<chrono_t>::max())
+ , m_max_time(std::numeric_limits<chrono_t>::min())
+{
+ m_name = object.getFullName();
+ std::vector<std::string> parts;
+ split(m_name, '/', parts);
+
+ if (parts.size() >= 2) {
+ m_object_name = parts[parts.size() - 2];
+ m_data_name = parts[parts.size() - 1];
+ }
+ else {
+ m_object_name = m_data_name = parts[parts.size() - 1];
+ }
+}
+
+AbcObjectReader::~AbcObjectReader()
+{}
+
+const IObject &AbcObjectReader::iobject() const
+{
+ return m_iobject;
+}
+
+Object *AbcObjectReader::object() const
+{
+ return m_object;
+}
+
+void AbcObjectReader::readObjectMatrix(const float time)
+{
+ IXform ixform;
+ bool has_alembic_parent = false;
+
+ /* Check that we have an empty object (locator, bone head/tail...). */
+ if (IXform::matches(m_iobject.getMetaData())) {
+ ixform = IXform(m_iobject, Alembic::AbcGeom::kWrapExisting);
+
+ /* See comment below. */
+ has_alembic_parent = m_iobject.getParent().getParent().valid();
+ }
+ /* Check that we have an object with actual data. */
+ else if (IXform::matches(m_iobject.getParent().getMetaData())) {
+ ixform = IXform(m_iobject.getParent(), Alembic::AbcGeom::kWrapExisting);
+
+ /* This is a bit hackish, but we need to make sure that extra
+ * transformations added to the matrix (rotation/scale) are only applied
+ * to root objects. The way objects and their hierarchy are created will
+ * need to be revisited at some point but for now this seems to do the
+ * trick.
+ *
+ * Explanation of the trick:
+ * The first getParent() will return this object's transformation matrix.
+ * The second getParent() will get the parent of the transform, but this
+ * might be the archive root ('/') which is valid, so we go passed it to
+ * make sure that there is no parent.
+ */
+ has_alembic_parent = m_iobject.getParent().getParent().getParent().valid();
+ }
+ /* Should not happen. */
+ else {
+ return;
+ }
+
+ const IXformSchema &schema(ixform.getSchema());
+
+ if (!schema.valid()) {
+ return;
+ }
+
+ Alembic::AbcGeom::ISampleSelector sample_sel(time);
+ Alembic::AbcGeom::XformSample xs;
+ schema.get(xs, sample_sel);
+
+ create_input_transform(sample_sel, ixform, m_object, m_object->obmat, m_settings->scale, has_alembic_parent);
+
+ invert_m4_m4(m_object->imat, m_object->obmat);
+
+ BKE_object_apply_mat4(m_object, m_object->obmat, false, false);
+
+ if (!schema.isConstant()) {
+ bConstraint *con = BKE_constraint_add_for_object(m_object, NULL, CONSTRAINT_TYPE_TRANSFORM_CACHE);
+ bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data);
+ BLI_strncpy(data->object_path, m_iobject.getFullName().c_str(), FILE_MAX);
+
+ data->cache_file = m_settings->cache_file;
+ id_us_plus(&data->cache_file->id);
+ }
+}
+
+void AbcObjectReader::addCacheModifier() const
+{
+ ModifierData *md = modifier_new(eModifierType_MeshSequenceCache);
+ BLI_addtail(&m_object->modifiers, md);
+
+ MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
+
+ mcmd->cache_file = m_settings->cache_file;
+ id_us_plus(&mcmd->cache_file->id);
+
+ BLI_strncpy(mcmd->object_path, m_iobject.getFullName().c_str(), FILE_MAX);
+}
+
+chrono_t AbcObjectReader::minTime() const
+{
+ return m_min_time;
+}
+
+chrono_t AbcObjectReader::maxTime() const
+{
+ return m_max_time;
+}
diff --git a/source/blender/alembic/intern/abc_object.h b/source/blender/alembic/intern/abc_object.h
new file mode 100644
index 00000000000..2e885f296b1
--- /dev/null
+++ b/source/blender/alembic/intern/abc_object.h
@@ -0,0 +1,169 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_OBJECT_H__
+#define __ABC_OBJECT_H__
+
+#include <Alembic/Abc/All.h>
+#include <Alembic/AbcGeom/All.h>
+
+#include "abc_exporter.h"
+
+extern "C" {
+#include "DNA_ID.h"
+}
+
+class AbcTransformWriter;
+
+struct Main;
+struct Object;
+
+/* ************************************************************************** */
+
+class AbcObjectWriter {
+protected:
+ Object *m_object;
+ ExportSettings &m_settings;
+
+ Scene *m_scene;
+ uint32_t m_time_sampling;
+
+ Imath::Box3d m_bounds;
+ std::vector<AbcObjectWriter *> m_children;
+
+ std::vector< std::pair<std::string, IDProperty *> > m_props;
+
+ bool m_first_frame;
+ std::string m_name;
+
+public:
+ AbcObjectWriter(Scene *scene,
+ Object *ob,
+ uint32_t time_sampling,
+ ExportSettings &settings,
+ AbcObjectWriter *parent = NULL);
+
+ virtual ~AbcObjectWriter();
+
+ void addChild(AbcObjectWriter *child);
+
+ virtual Imath::Box3d bounds();
+
+ void write();
+
+private:
+ virtual void do_write() = 0;
+};
+
+/* ************************************************************************** */
+
+class CacheFile;
+
+struct ImportSettings {
+ bool do_convert_mat;
+ float conversion_mat[4][4];
+
+ int from_up;
+ int from_forward;
+ float scale;
+ bool is_sequence;
+ bool set_frame_range;
+
+ /* Length and frame offset of file sequences. */
+ int sequence_len;
+ int offset;
+
+ /* From MeshSeqCacheModifierData.read_flag */
+ int read_flag;
+
+ bool validate_meshes;
+
+ CacheFile *cache_file;
+
+ ImportSettings()
+ : do_convert_mat(false)
+ , from_up(0)
+ , from_forward(0)
+ , scale(1.0f)
+ , is_sequence(false)
+ , set_frame_range(false)
+ , sequence_len(1)
+ , offset(0)
+ , read_flag(0)
+ , validate_meshes(false)
+ , cache_file(NULL)
+ {}
+};
+
+template <typename Schema>
+static bool has_animations(Schema &schema, ImportSettings *settings)
+{
+ if (settings->is_sequence) {
+ return true;
+ }
+
+ if (!schema.isConstant()) {
+ return true;
+ }
+
+ return false;
+}
+
+/* ************************************************************************** */
+
+using Alembic::AbcCoreAbstract::chrono_t;
+
+class AbcObjectReader {
+protected:
+ std::string m_name;
+ std::string m_object_name;
+ std::string m_data_name;
+ Object *m_object;
+ Alembic::Abc::IObject m_iobject;
+
+ ImportSettings *m_settings;
+
+ chrono_t m_min_time;
+ chrono_t m_max_time;
+
+public:
+ explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ virtual ~AbcObjectReader();
+
+ const Alembic::Abc::IObject &iobject() const;
+
+ Object *object() const;
+
+ virtual bool valid() const = 0;
+
+ virtual void readObjectData(Main *bmain, float time) = 0;
+
+ void readObjectMatrix(const float time);
+
+ void addCacheModifier() const;
+
+ chrono_t minTime() const;
+ chrono_t maxTime() const;
+};
+
+#endif /* __ABC_OBJECT_H__ */
diff --git a/source/blender/alembic/intern/abc_points.cc b/source/blender/alembic/intern/abc_points.cc
new file mode 100644
index 00000000000..fa5b71ac094
--- /dev/null
+++ b/source/blender/alembic/intern/abc_points.cc
@@ -0,0 +1,198 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#include "abc_points.h"
+
+#include "abc_mesh.h"
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_mesh_types.h"
+
+#include "BKE_lattice.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+#include "BKE_scene.h"
+
+#include "BLI_math.h"
+}
+
+using Alembic::AbcGeom::kVertexScope;
+using Alembic::AbcGeom::kWrapExisting;
+using Alembic::AbcGeom::P3fArraySamplePtr;
+using Alembic::AbcGeom::N3fArraySamplePtr;
+
+using Alembic::AbcGeom::ICompoundProperty;
+using Alembic::AbcGeom::IN3fArrayProperty;
+using Alembic::AbcGeom::IPoints;
+using Alembic::AbcGeom::IPointsSchema;
+using Alembic::AbcGeom::ISampleSelector;
+
+using Alembic::AbcGeom::OPoints;
+using Alembic::AbcGeom::OPointsSchema;
+
+/* ************************************************************************** */
+
+AbcPointsWriter::AbcPointsWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings,
+ ParticleSystem *psys)
+ : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+ m_psys = psys;
+
+ OPoints points(parent->alembicXform(), m_name, m_time_sampling);
+ m_schema = points.getSchema();
+}
+
+void AbcPointsWriter::do_write()
+{
+ if (!m_psys) {
+ return;
+ }
+
+ std::vector<Imath::V3f> points;
+ std::vector<Imath::V3f> velocities;
+ std::vector<float> widths;
+ std::vector<uint64_t> ids;
+
+ ParticleKey state;
+
+ ParticleSimulationData sim;
+ sim.scene = m_scene;
+ sim.ob = m_object;
+ sim.psys = m_psys;
+
+ m_psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+ uint64_t index = 0;
+ for (int p = 0; p < m_psys->totpart; p++) {
+ float pos[3], vel[3];
+
+ if (m_psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) {
+ continue;
+ }
+
+ state.time = BKE_scene_frame_get(m_scene);
+
+ if (psys_get_particle_state(&sim, p, &state, 0) == 0) {
+ continue;
+ }
+
+ /* location */
+ mul_v3_m4v3(pos, m_object->imat, state.co);
+
+ /* velocity */
+ sub_v3_v3v3(vel, state.co, m_psys->particles[p].prev_state.co);
+
+ /* Convert Z-up to Y-up. */
+ points.push_back(Imath::V3f(pos[0], pos[2], -pos[1]));
+ velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1]));
+ widths.push_back(m_psys->particles[p].size);
+ ids.push_back(index++);
+ }
+
+ if (m_psys->lattice_deform_data) {
+ end_latt_deform(m_psys->lattice_deform_data);
+ m_psys->lattice_deform_data = NULL;
+ }
+
+ Alembic::Abc::P3fArraySample psample(points);
+ Alembic::Abc::UInt64ArraySample idsample(ids);
+ Alembic::Abc::V3fArraySample vsample(velocities);
+ Alembic::Abc::FloatArraySample wsample_array(widths);
+ Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope);
+
+ m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample);
+ m_sample.setSelfBounds(bounds());
+
+ m_schema.set(m_sample);
+}
+
+/* ************************************************************************** */
+
+AbcPointsReader::AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ IPoints ipoints(m_iobject, kWrapExisting);
+ m_schema = ipoints.getSchema();
+ get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcPointsReader::valid() const
+{
+ return m_schema.valid();
+}
+
+void AbcPointsReader::readObjectData(Main *bmain, float time)
+{
+ Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
+
+ const ISampleSelector sample_sel(time);
+ m_sample = m_schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = m_sample.getPositions();
+ utils::mesh_add_verts(mesh, positions->size());
+
+ CDStreamConfig config = create_config(mesh);
+ read_points_sample(m_schema, sample_sel, config, time);
+
+ if (m_settings->validate_meshes) {
+ BKE_mesh_validate(mesh, false, false);
+ }
+
+ m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
+ m_object->data = mesh;
+
+ if (has_animations(m_schema, m_settings)) {
+ addCacheModifier();
+ }
+}
+
+void read_points_sample(const IPointsSchema &schema,
+ const ISampleSelector &selector,
+ CDStreamConfig &config,
+ float time)
+{
+ Alembic::AbcGeom::IPointsSchema::Sample sample = schema.getValue(selector);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+
+ ICompoundProperty prop = schema.getArbGeomParams();
+ N3fArraySamplePtr vnormals;
+
+ if (has_property(prop, "N")) {
+ const IN3fArrayProperty &normals_prop = IN3fArrayProperty(prop, "N", time);
+
+ if (normals_prop) {
+ vnormals = normals_prop.getValue(selector);
+ }
+ }
+
+ read_mverts(config.mvert, positions, vnormals);
+}
diff --git a/source/blender/alembic/intern/abc_points.h b/source/blender/alembic/intern/abc_points.h
new file mode 100644
index 00000000000..51f3103bd8b
--- /dev/null
+++ b/source/blender/alembic/intern/abc_points.h
@@ -0,0 +1,70 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifndef __ABC_POINTS_H__
+#define __ABC_POINTS_H__
+
+#include "abc_object.h"
+#include "abc_customdata.h"
+
+class ParticleSystem;
+
+/* ************************************************************************** */
+
+class AbcPointsWriter : public AbcObjectWriter {
+ Alembic::AbcGeom::OPointsSchema m_schema;
+ Alembic::AbcGeom::OPointsSchema::Sample m_sample;
+ ParticleSystem *m_psys;
+
+public:
+ AbcPointsWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings,
+ ParticleSystem *psys);
+
+ void do_write();
+};
+
+/* ************************************************************************** */
+
+class AbcPointsReader : public AbcObjectReader {
+ Alembic::AbcGeom::IPointsSchema m_schema;
+ Alembic::AbcGeom::IPointsSchema::Sample m_sample;
+
+public:
+ AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+};
+
+void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema,
+ const Alembic::AbcGeom::ISampleSelector &selector,
+ CDStreamConfig &config,
+ float time);
+
+#endif /* __ABC_POINTS_H__ */
diff --git a/source/blender/alembic/intern/abc_transform.cc b/source/blender/alembic/intern/abc_transform.cc
new file mode 100644
index 00000000000..3326ae0ed84
--- /dev/null
+++ b/source/blender/alembic/intern/abc_transform.cc
@@ -0,0 +1,152 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_transform.h"
+
+#include <OpenEXR/ImathBoxAlgo.h>
+
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_object_types.h"
+
+#include "BLI_math.h"
+
+#include "BKE_object.h"
+}
+
+using Alembic::AbcGeom::OObject;
+using Alembic::AbcGeom::OXform;
+
+/* ************************************************************************** */
+
+static bool has_parent_camera(Object *ob)
+{
+ if (!ob->parent) {
+ return false;
+ }
+
+ Object *parent = ob->parent;
+
+ if (parent->type == OB_CAMERA) {
+ return true;
+ }
+
+ return has_parent_camera(parent);
+}
+
+/* ************************************************************************** */
+
+AbcTransformWriter::AbcTransformWriter(Object *ob,
+ const OObject &abc_parent,
+ AbcTransformWriter *parent,
+ unsigned int time_sampling,
+ ExportSettings &settings)
+ : AbcObjectWriter(NULL, ob, time_sampling, settings, parent)
+{
+ m_is_animated = hasAnimation(m_object);
+ m_parent = NULL;
+
+ if (!m_is_animated) {
+ time_sampling = 0;
+ }
+
+ m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling);
+ m_schema = m_xform.getSchema();
+}
+
+void AbcTransformWriter::do_write()
+{
+ if (m_first_frame) {
+ m_visibility = Alembic::AbcGeom::CreateVisibilityProperty(m_xform, m_xform.getSchema().getTimeSampling());
+ }
+
+ m_visibility.set(!(m_object->restrictflag & OB_RESTRICT_VIEW));
+
+ if (!m_first_frame && !m_is_animated) {
+ return;
+ }
+
+ float mat[4][4];
+ create_transform_matrix(m_object, mat);
+
+ /* Only apply rotation to root camera, parenting will propagate it. */
+ if (m_object->type == OB_CAMERA && !has_parent_camera(m_object)) {
+ float rot_mat[4][4];
+ unit_m4(rot_mat);
+ rotate_m4(rot_mat, 'X', -M_PI_2);
+ mul_m4_m4m4(mat, mat, rot_mat);
+ }
+
+ if (!m_object->parent) {
+ /* Only apply scaling to root objects, parenting will propagate it. */
+ float scale_mat[4][4];
+ scale_m4_fl(scale_mat, m_settings.global_scale);
+ mul_m4_m4m4(mat, mat, scale_mat);
+ mul_v3_fl(mat[3], m_settings.global_scale);
+ }
+
+ m_matrix = convert_matrix(mat);
+
+ m_sample.setMatrix(m_matrix);
+ m_schema.set(m_sample);
+}
+
+Imath::Box3d AbcTransformWriter::bounds()
+{
+ Imath::Box3d bounds;
+
+ for (int i = 0; i < m_children.size(); ++i) {
+ Imath::Box3d box(m_children[i]->bounds());
+ bounds.extendBy(box);
+ }
+
+ return Imath::transform(bounds, m_matrix);
+}
+
+bool AbcTransformWriter::hasAnimation(Object */*ob*/) const
+{
+ /* TODO(kevin): implement this. */
+ return true;
+}
+
+/* ************************************************************************** */
+
+AbcEmptyReader::AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ Alembic::AbcGeom::IXform xform(object, Alembic::AbcGeom::kWrapExisting);
+ m_schema = xform.getSchema();
+
+ get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcEmptyReader::valid() const
+{
+ return m_schema.valid();
+}
+
+void AbcEmptyReader::readObjectData(Main *bmain, float /*time*/)
+{
+ m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_object_name.c_str());
+ m_object->data = NULL;
+}
diff --git a/source/blender/alembic/intern/abc_transform.h b/source/blender/alembic/intern/abc_transform.h
new file mode 100644
index 00000000000..6a3aae216f2
--- /dev/null
+++ b/source/blender/alembic/intern/abc_transform.h
@@ -0,0 +1,73 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_TRANSFORM_H__
+#define __ABC_TRANSFORM_H__
+
+#include "abc_object.h"
+
+#include <Alembic/AbcGeom/All.h>
+
+/* ************************************************************************** */
+
+class AbcTransformWriter : public AbcObjectWriter {
+ Alembic::AbcGeom::OXform m_xform;
+ Alembic::AbcGeom::OXformSchema m_schema;
+ Alembic::AbcGeom::XformSample m_sample;
+ Alembic::AbcGeom::OVisibilityProperty m_visibility;
+ Alembic::Abc::M44d m_matrix;
+
+ bool m_is_animated;
+ Object *m_parent;
+ bool m_visible;
+
+public:
+ AbcTransformWriter(Object *ob,
+ const Alembic::AbcGeom::OObject &abc_parent,
+ AbcTransformWriter *parent,
+ unsigned int time_sampling,
+ ExportSettings &settings);
+
+ Alembic::AbcGeom::OXform &alembicXform() { return m_xform;}
+ virtual Imath::Box3d bounds();
+ void setParent(Object *p) { m_parent = p; }
+
+private:
+ virtual void do_write();
+
+ bool hasAnimation(Object *ob) const;
+};
+
+/* ************************************************************************** */
+
+class AbcEmptyReader : public AbcObjectReader {
+ Alembic::AbcGeom::IXformSchema m_schema;
+
+public:
+ AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+};
+
+#endif /* __ABC_TRANSFORM_H__ */
diff --git a/source/blender/alembic/intern/abc_util.cc b/source/blender/alembic/intern/abc_util.cc
new file mode 100644
index 00000000000..fbab0bcdf34
--- /dev/null
+++ b/source/blender/alembic/intern/abc_util.cc
@@ -0,0 +1,437 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_util.h"
+
+#include <algorithm>
+
+extern "C" {
+#include "DNA_object_types.h"
+
+#include "BLI_math.h"
+}
+
+std::string get_id_name(Object *ob)
+{
+ if (!ob) {
+ return "";
+ }
+
+ return get_id_name(&ob->id);
+}
+
+std::string get_id_name(ID *id)
+{
+ std::string name(id->name + 2);
+ std::replace(name.begin(), name.end(), ' ', '_');
+ std::replace(name.begin(), name.end(), '.', '_');
+ std::replace(name.begin(), name.end(), ':', '_');
+
+ return name;
+}
+
+std::string get_object_dag_path_name(Object *ob, Object *dupli_parent)
+{
+ std::string name = get_id_name(ob);
+
+ Object *p = ob->parent;
+
+ while (p) {
+ name = get_id_name(p) + "/" + name;
+ p = p->parent;
+ }
+
+ if (dupli_parent && (ob != dupli_parent)) {
+ name = get_id_name(dupli_parent) + "/" + name;
+ }
+
+ return name;
+}
+
+bool object_selected(Object *ob)
+{
+ return ob->flag & SELECT;
+}
+
+bool parent_selected(Object *ob)
+{
+ if (object_selected(ob)) {
+ return true;
+ }
+
+ bool do_export = false;
+
+ Object *parent = ob->parent;
+
+ while (parent != NULL) {
+ if (object_selected(parent)) {
+ do_export = true;
+ break;
+ }
+
+ parent = parent->parent;
+ }
+
+ return do_export;
+}
+
+Imath::M44d convert_matrix(float mat[4][4])
+{
+ Imath::M44d m;
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ m[i][j] = mat[i][j];
+ }
+ }
+
+ return m;
+}
+
+void split(const std::string &s, const char delim, std::vector<std::string> &tokens)
+{
+ tokens.clear();
+
+ std::stringstream ss(s);
+ std::string item;
+
+ while (std::getline(ss, item, delim)) {
+ if (!item.empty()) {
+ tokens.push_back(item);
+ }
+ }
+}
+
+/* Create a rotation matrix for each axis from euler angles.
+ * Euler angles are swaped to change coordinate system. */
+static void create_rotation_matrix(
+ float rot_x_mat[3][3], float rot_y_mat[3][3],
+ float rot_z_mat[3][3], const float euler[3], const bool to_yup)
+{
+ const float rx = euler[0];
+ const float ry = (to_yup) ? euler[2] : -euler[2];
+ const float rz = (to_yup) ? -euler[1] : euler[1];
+
+ unit_m3(rot_x_mat);
+ unit_m3(rot_y_mat);
+ unit_m3(rot_z_mat);
+
+ rot_x_mat[1][1] = cos(rx);
+ rot_x_mat[2][1] = -sin(rx);
+ rot_x_mat[1][2] = sin(rx);
+ rot_x_mat[2][2] = cos(rx);
+
+ rot_y_mat[2][2] = cos(ry);
+ rot_y_mat[0][2] = -sin(ry);
+ rot_y_mat[2][0] = sin(ry);
+ rot_y_mat[0][0] = cos(ry);
+
+ rot_z_mat[0][0] = cos(rz);
+ rot_z_mat[1][0] = -sin(rz);
+ rot_z_mat[0][1] = sin(rz);
+ rot_z_mat[1][1] = cos(rz);
+}
+
+/* Recompute transform matrix of object in new coordinate system
+ * (from Y-Up to Z-Up). */
+void create_transform_matrix(float r_mat[4][4])
+{
+ float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], transform_mat[4][4];
+ float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3];
+ float loc[3], scale[3], euler[3];
+
+ zero_v3(loc);
+ zero_v3(scale);
+ zero_v3(euler);
+ unit_m3(rot);
+ unit_m3(rot_mat);
+ unit_m4(scale_mat);
+ unit_m4(transform_mat);
+ unit_m4(invmat);
+
+ /* Compute rotation matrix. */
+
+ /* Extract location, rotation, and scale from matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, r_mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_XYZ, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, false);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+
+ /* Add rotation matrix to transformation matrix. */
+ copy_m4_m3(transform_mat, rot_mat);
+
+ /* Add translation to transformation matrix. */
+ copy_yup_zup(transform_mat[3], loc);
+
+ /* Create scale matrix. */
+ scale_mat[0][0] = scale[0];
+ scale_mat[1][1] = scale[2];
+ scale_mat[2][2] = scale[1];
+
+ /* Add scale to transformation matrix. */
+ mul_m4_m4m4(transform_mat, transform_mat, scale_mat);
+
+ copy_m4_m4(r_mat, transform_mat);
+}
+
+void create_input_transform(const Alembic::AbcGeom::ISampleSelector &sample_sel,
+ const Alembic::AbcGeom::IXform &ixform, Object *ob,
+ float r_mat[4][4], float scale, bool has_alembic_parent)
+{
+
+ const Alembic::AbcGeom::IXformSchema &ixform_schema = ixform.getSchema();
+ Alembic::AbcGeom::XformSample xs;
+ ixform_schema.get(xs, sample_sel);
+ const Imath::M44d &xform = xs.getMatrix();
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ r_mat[i][j] = xform[i][j];
+ }
+ }
+
+ if (ob->type == OB_CAMERA) {
+ float cam_to_yup[4][4];
+ unit_m4(cam_to_yup);
+ rotate_m4(cam_to_yup, 'X', M_PI_2);
+ mul_m4_m4m4(r_mat, r_mat, cam_to_yup);
+ }
+
+ create_transform_matrix(r_mat);
+
+ if (ob->parent) {
+ mul_m4_m4m4(r_mat, ob->parent->obmat, r_mat);
+ }
+ /* TODO(kevin) */
+ else if (!has_alembic_parent) {
+ /* Only apply scaling to root objects, parenting will propagate it. */
+ float scale_mat[4][4];
+ scale_m4_fl(scale_mat, scale);
+ mul_m4_m4m4(r_mat, r_mat, scale_mat);
+ mul_v3_fl(r_mat[3], scale);
+ }
+}
+
+/* Recompute transform matrix of object in new coordinate system (from Z-Up to Y-Up). */
+void create_transform_matrix(Object *obj, float transform_mat[4][4])
+{
+ float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], mat[4][4];
+ float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3];
+ float loc[3], scale[3], euler[3];
+
+ zero_v3(loc);
+ zero_v3(scale);
+ zero_v3(euler);
+ unit_m3(rot);
+ unit_m3(rot_mat);
+ unit_m4(scale_mat);
+ unit_m4(transform_mat);
+ unit_m4(invmat);
+ unit_m4(mat);
+
+ /* get local matrix. */
+ if (obj->parent) {
+ invert_m4_m4(invmat, obj->parent->obmat);
+ mul_m4_m4m4(mat, invmat, obj->obmat);
+ }
+ else {
+ copy_m4_m4(mat, obj->obmat);
+ }
+
+ /* Compute rotation matrix. */
+ switch (obj->rotmode) {
+ case ROT_MODE_AXISANGLE:
+ {
+ /* Get euler angles from axis angle rotation. */
+ axis_angle_to_eulO(euler, ROT_MODE_XYZ, obj->rotAxis, obj->rotAngle);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+
+ /* Extract location and scale from matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ break;
+ }
+ case ROT_MODE_QUAT:
+ {
+ float q[4];
+ copy_v4_v4(q, obj->quat);
+
+ /* Swap axis. */
+ q[2] = obj->quat[3];
+ q[3] = -obj->quat[2];
+
+ /* Compute rotation matrix from quaternion. */
+ quat_to_mat3(rot_mat, q);
+
+ /* Extract location and scale from matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ break;
+ }
+ case ROT_MODE_XYZ:
+ {
+ /* Extract location, rotation, and scale form matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_XYZ, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+
+ break;
+ }
+ case ROT_MODE_XZY:
+ {
+ /* Extract location, rotation, and scale form matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_XZY, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+
+ break;
+ }
+ case ROT_MODE_YXZ:
+ {
+ /* Extract location, rotation, and scale form matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_YXZ, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+
+ break;
+ }
+ case ROT_MODE_YZX:
+ {
+ /* Extract location, rotation, and scale form matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_YZX, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+
+ break;
+ }
+ case ROT_MODE_ZXY:
+ {
+ /* Extract location, rotation, and scale form matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_ZXY, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+
+ break;
+ }
+ case ROT_MODE_ZYX:
+ {
+ /* Extract location, rotation, and scale form matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_ZYX, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+
+ break;
+ }
+ }
+
+ /* Add rotation matrix to transformation matrix. */
+ copy_m4_m3(transform_mat, rot_mat);
+
+ /* Add translation to transformation matrix. */
+ copy_zup_yup(transform_mat[3], loc);
+
+ /* Create scale matrix. */
+ scale_mat[0][0] = scale[0];
+ scale_mat[1][1] = scale[2];
+ scale_mat[2][2] = scale[1];
+
+ /* Add scale to transformation matrix. */
+ mul_m4_m4m4(transform_mat, transform_mat, scale_mat);
+}
+
+bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name)
+{
+ if (!prop.valid()) {
+ return false;
+ }
+
+ return prop.getPropertyHeader(name) != NULL;
+}
diff --git a/source/blender/alembic/intern/abc_util.h b/source/blender/alembic/intern/abc_util.h
new file mode 100644
index 00000000000..688a25d85f6
--- /dev/null
+++ b/source/blender/alembic/intern/abc_util.h
@@ -0,0 +1,125 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_UTIL_H__
+#define __ABC_UTIL_H__
+
+#include <Alembic/Abc/All.h>
+#include <Alembic/AbcGeom/All.h>
+
+#ifdef _MSC_VER
+# define ABC_INLINE static __forceinline
+#else
+# define ABC_INLINE static inline
+#endif
+
+using Alembic::Abc::chrono_t;
+
+class ImportSettings;
+
+struct ID;
+struct Object;
+
+std::string get_id_name(ID *id);
+std::string get_id_name(Object *ob);
+std::string get_object_dag_path_name(Object *ob, Object *dupli_parent);
+
+bool object_selected(Object *ob);
+bool parent_selected(Object *ob);
+
+Imath::M44d convert_matrix(float mat[4][4]);
+void create_transform_matrix(float r_mat[4][4]);
+void create_transform_matrix(Object *obj, float transform_mat[4][4]);
+
+void split(const std::string &s, const char delim, std::vector<std::string> &tokens);
+
+template<class TContainer>
+bool begins_with(const TContainer &input, const TContainer &match)
+{
+ return input.size() >= match.size()
+ && std::equal(match.begin(), match.end(), input.begin());
+}
+
+void create_input_transform(const Alembic::AbcGeom::ISampleSelector &sample_sel,
+ const Alembic::AbcGeom::IXform &ixform, Object *ob,
+ float r_mat[4][4], float scale, bool has_alembic_parent = false);
+
+template <typename Schema>
+void get_min_max_time(const Schema &schema, chrono_t &min, chrono_t &max)
+{
+ const Alembic::Abc::TimeSamplingPtr &time_samp = schema.getTimeSampling();
+
+ if (!schema.isConstant()) {
+ const size_t num_samps = schema.getNumSamples();
+
+ if (num_samps > 0) {
+ const chrono_t min_time = time_samp->getSampleTime(0);
+ min = std::min(min, min_time);
+
+ const chrono_t max_time = time_samp->getSampleTime(num_samps - 1);
+ max = std::max(max, max_time);
+ }
+ }
+}
+
+bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name);
+
+/* ************************** */
+
+/* TODO(kevin): for now keeping these transformations hardcoded to make sure
+ * everything works properly, and also because Alembic is almost exclusively
+ * used in Y-up software, but eventually they'll be set by the user in the UI
+ * like other importers/exporters do, to support other axis. */
+
+/* Copy from Y-up to Z-up. */
+
+ABC_INLINE void copy_yup_zup(float zup[3], const float yup[3])
+{
+ zup[0] = yup[0];
+ zup[1] = -yup[2];
+ zup[2] = yup[1];
+}
+
+ABC_INLINE void copy_yup_zup(short zup[3], const short yup[3])
+{
+ zup[0] = yup[0];
+ zup[1] = -yup[2];
+ zup[2] = yup[1];
+}
+
+/* Copy from Z-up to Y-up. */
+
+ABC_INLINE void copy_zup_yup(float yup[3], const float zup[3])
+{
+ yup[0] = zup[0];
+ yup[1] = zup[2];
+ yup[2] = -zup[1];
+}
+
+ABC_INLINE void copy_zup_yup(short yup[3], const short zup[3])
+{
+ yup[0] = zup[0];
+ yup[1] = zup[2];
+ yup[2] = -zup[1];
+}
+
+#endif /* __ABC_UTIL_H__ */
diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc
new file mode 100644
index 00000000000..0e96ac22e11
--- /dev/null
+++ b/source/blender/alembic/intern/alembic_capi.cc
@@ -0,0 +1,1136 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "../ABC_alembic.h"
+
+#ifdef WITH_ALEMBIC_HDF5
+# include <Alembic/AbcCoreHDF5/All.h>
+#endif
+
+#include <Alembic/AbcCoreOgawa/All.h>
+#include <Alembic/AbcMaterial/IMaterial.h>
+
+#include "abc_camera.h"
+#include "abc_curves.h"
+#include "abc_hair.h"
+#include "abc_mesh.h"
+#include "abc_nurbs.h"
+#include "abc_points.h"
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_cachefile_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_cachefile.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_global.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
+
+/* SpaceType struct has a member called 'new' which obviously conflicts with C++
+ * so temporarily redefining the new keyword to make it compile. */
+#define new extern_new
+#include "BKE_screen.h"
+#undef new
+
+#include "BLI_fileops.h"
+#include "BLI_ghash.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+}
+
+using Alembic::Abc::Int32ArraySamplePtr;
+using Alembic::Abc::ObjectHeader;
+
+using Alembic::AbcGeom::ErrorHandler;
+using Alembic::AbcGeom::Exception;
+using Alembic::AbcGeom::MetaData;
+using Alembic::AbcGeom::P3fArraySamplePtr;
+using Alembic::AbcGeom::kWrapExisting;
+
+using Alembic::AbcGeom::IArchive;
+using Alembic::AbcGeom::ICamera;
+using Alembic::AbcGeom::ICurves;
+using Alembic::AbcGeom::ICurvesSchema;
+using Alembic::AbcGeom::IFaceSet;
+using Alembic::AbcGeom::ILight;
+using Alembic::AbcGeom::INuPatch;
+using Alembic::AbcGeom::IObject;
+using Alembic::AbcGeom::IPoints;
+using Alembic::AbcGeom::IPointsSchema;
+using Alembic::AbcGeom::IPolyMesh;
+using Alembic::AbcGeom::IPolyMeshSchema;
+using Alembic::AbcGeom::ISampleSelector;
+using Alembic::AbcGeom::ISubD;
+using Alembic::AbcGeom::IV2fGeomParam;
+using Alembic::AbcGeom::IXform;
+using Alembic::AbcGeom::IXformSchema;
+using Alembic::AbcGeom::N3fArraySamplePtr;
+using Alembic::AbcGeom::XformSample;
+using Alembic::AbcGeom::ICompoundProperty;
+using Alembic::AbcGeom::IN3fArrayProperty;
+using Alembic::AbcGeom::IN3fGeomParam;
+using Alembic::AbcGeom::V3fArraySamplePtr;
+
+using Alembic::AbcMaterial::IMaterial;
+
+struct AbcArchiveHandle {
+ int unused;
+};
+
+ABC_INLINE IArchive *archive_from_handle(AbcArchiveHandle *handle)
+{
+ return reinterpret_cast<IArchive *>(handle);
+}
+
+ABC_INLINE AbcArchiveHandle *handle_from_archive(IArchive *archive)
+{
+ return reinterpret_cast<AbcArchiveHandle *>(archive);
+}
+
+static IArchive *open_archive(const std::string &filename)
+{
+ Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr;
+ IArchive *archive;
+
+ try {
+ archive = new IArchive(Alembic::AbcCoreOgawa::ReadArchive(),
+ filename.c_str(), ErrorHandler::kThrowPolicy,
+ cache_ptr);
+ }
+ catch (const Exception &e) {
+ std::cerr << e.what() << '\n';
+
+#ifdef WITH_ALEMBIC_HDF5
+ try {
+ archive = new IArchive(Alembic::AbcCoreHDF5::ReadArchive(),
+ filename.c_str(), ErrorHandler::kThrowPolicy,
+ cache_ptr);
+ }
+ catch (const Exception &) {
+ std::cerr << e.what() << '\n';
+ return NULL;
+ }
+#else
+ return NULL;
+#endif
+ }
+
+ return archive;
+}
+
+//#define USE_NURBS
+
+/* NOTE: this function is similar to visit_objects below, need to keep them in
+ * sync. */
+static void gather_objects_paths(const IObject &object, ListBase *object_paths)
+{
+ if (!object.valid()) {
+ return;
+ }
+
+ for (int i = 0; i < object.getNumChildren(); ++i) {
+ IObject child = object.getChild(i);
+
+ if (!child.valid()) {
+ continue;
+ }
+
+ bool get_path = false;
+
+ const MetaData &md = child.getMetaData();
+
+ if (IXform::matches(md)) {
+ /* Check whether or not this object is a Maya locator, which is
+ * similar to empties used as parent object in Blender. */
+ if (has_property(child.getProperties(), "locator")) {
+ get_path = true;
+ }
+ else {
+ /* Avoid creating an empty object if the child of this transform
+ * is not a transform (that is an empty). */
+ if (child.getNumChildren() == 1) {
+ if (IXform::matches(child.getChild(0).getMetaData())) {
+ get_path = true;
+ }
+#if 0
+ else {
+ std::cerr << "Skipping " << child.getFullName() << '\n';
+ }
+#endif
+ }
+ else {
+ get_path = true;
+ }
+ }
+ }
+ else if (IPolyMesh::matches(md)) {
+ get_path = true;
+ }
+ else if (ISubD::matches(md)) {
+ get_path = true;
+ }
+ else if (INuPatch::matches(md)) {
+#ifdef USE_NURBS
+ get_path = true;
+#endif
+ }
+ else if (ICamera::matches(md)) {
+ get_path = true;
+ }
+ else if (IPoints::matches(md)) {
+ get_path = true;
+ }
+ else if (IMaterial::matches(md)) {
+ /* Pass for now. */
+ }
+ else if (ILight::matches(md)) {
+ /* Pass for now. */
+ }
+ else if (IFaceSet::matches(md)) {
+ /* Pass, those are handled in the mesh reader. */
+ }
+ else if (ICurves::matches(md)) {
+ get_path = true;
+ }
+ else {
+ assert(false);
+ }
+
+ if (get_path) {
+ AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
+ MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
+
+ BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX);
+
+ BLI_addtail(object_paths, abc_path);
+ }
+
+ gather_objects_paths(child, object_paths);
+ }
+}
+
+AbcArchiveHandle *ABC_create_handle(const char *filename, ListBase *object_paths)
+{
+ IArchive *archive = open_archive(filename);
+
+ if (!archive) {
+ return NULL;
+ }
+
+ if (object_paths) {
+ gather_objects_paths(archive->getTop(), object_paths);
+ }
+
+ return handle_from_archive(archive);
+}
+
+void ABC_free_handle(AbcArchiveHandle *handle)
+{
+ delete archive_from_handle(handle);
+}
+
+int ABC_get_version()
+{
+ return ALEMBIC_LIBRARY_VERSION;
+}
+
+static void find_iobject(const IObject &object, IObject &ret,
+ const std::string &path)
+{
+ if (!object.valid()) {
+ return;
+ }
+
+ std::vector<std::string> tokens;
+ split(path, '/', tokens);
+
+ IObject tmp = object;
+
+ std::vector<std::string>::iterator iter;
+ for (iter = tokens.begin(); iter != tokens.end(); ++iter) {
+ IObject child = tmp.getChild(*iter);
+ tmp = child;
+ }
+
+ ret = tmp;
+}
+
+struct ExportJobData {
+ Scene *scene;
+ Main *bmain;
+
+ char filename[1024];
+ ExportSettings settings;
+
+ short *stop;
+ short *do_update;
+ float *progress;
+
+ bool was_canceled;
+};
+
+static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
+{
+ ExportJobData *data = static_cast<ExportJobData *>(customdata);
+
+ data->stop = stop;
+ data->do_update = do_update;
+ data->progress = progress;
+
+ /* XXX annoying hack: needed to prevent data corruption when changing
+ * scene frame in separate threads
+ */
+ G.is_rendering = true;
+ BKE_spacedata_draw_locks(true);
+
+ G.is_break = false;
+
+ try {
+ Scene *scene = data->scene;
+ AbcExporter exporter(scene, data->filename, data->settings);
+
+ const int orig_frame = CFRA;
+
+ data->was_canceled = false;
+ exporter(data->bmain, *data->progress, data->was_canceled);
+
+ if (CFRA != orig_frame) {
+ CFRA = orig_frame;
+
+ BKE_scene_update_for_newframe(data->bmain->eval_ctx, data->bmain,
+ scene, scene->lay);
+ }
+ }
+ catch (const std::exception &e) {
+ std::cerr << "Abc Export error: " << e.what() << '\n';
+ }
+ catch (...) {
+ std::cerr << "Abc Export error\n";
+ }
+}
+
+static void export_endjob(void *customdata)
+{
+ ExportJobData *data = static_cast<ExportJobData *>(customdata);
+
+ if (data->was_canceled && BLI_exists(data->filename)) {
+ BLI_delete(data->filename, false, false);
+ }
+
+ G.is_rendering = false;
+ BKE_spacedata_draw_locks(false);
+}
+
+void ABC_export(
+ Scene *scene,
+ bContext *C,
+ const char *filepath,
+ const struct AlembicExportParams *params)
+{
+ ExportJobData *job = static_cast<ExportJobData *>(MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
+ job->scene = scene;
+ job->bmain = CTX_data_main(C);
+ BLI_strncpy(job->filename, filepath, 1024);
+
+ job->settings.scene = job->scene;
+ job->settings.frame_start = params->frame_start;
+ job->settings.frame_end = params->frame_end;
+ job->settings.frame_step_xform = params->frame_step_xform;
+ job->settings.frame_step_shape = params->frame_step_shape;
+ job->settings.shutter_open = params->shutter_open;
+ job->settings.shutter_close = params->shutter_close;
+ job->settings.selected_only = params->selected_only;
+ job->settings.export_face_sets = params->face_sets;
+ job->settings.export_normals = params->normals;
+ job->settings.export_uvs = params->uvs;
+ job->settings.export_vcols = params->vcolors;
+ job->settings.apply_subdiv = params->apply_subdiv;
+ job->settings.flatten_hierarchy = params->flatten_hierarchy;
+ job->settings.visible_layers_only = params->visible_layers_only;
+ job->settings.renderable_only = params->renderable_only;
+ job->settings.use_subdiv_schema = params->use_subdiv_schema;
+ job->settings.export_ogawa = (params->compression_type == ABC_ARCHIVE_OGAWA);
+ job->settings.pack_uv = params->packuv;
+ job->settings.global_scale = params->global_scale;
+
+ if (job->settings.frame_start > job->settings.frame_end) {
+ std::swap(job->settings.frame_start, job->settings.frame_end);
+ }
+
+ wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+ CTX_wm_window(C),
+ job->scene,
+ "Alembic Export",
+ WM_JOB_PROGRESS,
+ WM_JOB_TYPE_ALEMBIC);
+
+ /* setup job */
+ WM_jobs_customdata_set(wm_job, job, MEM_freeN);
+ WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
+ WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob);
+
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+}
+
+/* ********************** Import file ********************** */
+
+static void visit_object(const IObject &object,
+ std::vector<AbcObjectReader *> &readers,
+ GHash *parent_map,
+ ImportSettings &settings)
+{
+ if (!object.valid()) {
+ return;
+ }
+
+ for (int i = 0; i < object.getNumChildren(); ++i) {
+ IObject child = object.getChild(i);
+
+ if (!child.valid()) {
+ continue;
+ }
+
+ AbcObjectReader *reader = NULL;
+
+ const MetaData &md = child.getMetaData();
+
+ if (IXform::matches(md)) {
+ bool create_xform = false;
+
+ /* Check whether or not this object is a Maya locator, which is
+ * similar to empties used as parent object in Blender. */
+ if (has_property(child.getProperties(), "locator")) {
+ create_xform = true;
+ }
+ else {
+ /* Avoid creating an empty object if the child of this transform
+ * is not a transform (that is an empty). */
+ if (child.getNumChildren() == 1) {
+ if (IXform::matches(child.getChild(0).getMetaData())) {
+ create_xform = true;
+ }
+#if 0
+ else {
+ std::cerr << "Skipping " << child.getFullName() << '\n';
+ }
+#endif
+ }
+ else {
+ create_xform = true;
+ }
+ }
+
+ if (create_xform) {
+ reader = new AbcEmptyReader(child, settings);
+ }
+ }
+ else if (IPolyMesh::matches(md)) {
+ reader = new AbcMeshReader(child, settings);
+ }
+ else if (ISubD::matches(md)) {
+ reader = new AbcSubDReader(child, settings);
+ }
+ else if (INuPatch::matches(md)) {
+#ifdef USE_NURBS
+ /* TODO(kevin): importing cyclic NURBS from other software crashes
+ * at the moment. This is due to the fact that NURBS in other
+ * software have duplicated points which causes buffer overflows in
+ * Blender. Need to figure out exactly how these points are
+ * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV).
+ * Until this is fixed, disabling NURBS reading. */
+ reader = new AbcNurbsReader(child, settings);
+#endif
+ }
+ else if (ICamera::matches(md)) {
+ reader = new AbcCameraReader(child, settings);
+ }
+ else if (IPoints::matches(md)) {
+ reader = new AbcPointsReader(child, settings);
+ }
+ else if (IMaterial::matches(md)) {
+ /* Pass for now. */
+ }
+ else if (ILight::matches(md)) {
+ /* Pass for now. */
+ }
+ else if (IFaceSet::matches(md)) {
+ /* Pass, those are handled in the mesh reader. */
+ }
+ else if (ICurves::matches(md)) {
+ reader = new AbcCurveReader(child, settings);
+ }
+ else {
+ assert(false);
+ }
+
+ if (reader) {
+ readers.push_back(reader);
+
+ AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
+ MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
+
+ BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX);
+
+ BLI_addtail(&settings.cache_file->object_paths, abc_path);
+
+ /* Cast to `void *` explicitly to avoid compiler errors because it
+ * is a `const char *` which the compiler cast to `const void *`
+ * instead of the expected `void *`. */
+ BLI_ghash_insert(parent_map, (void *)child.getFullName().c_str(), reader);
+ }
+
+ visit_object(child, readers, parent_map, settings);
+ }
+}
+
+enum {
+ ABC_NO_ERROR = 0,
+ ABC_ARCHIVE_FAIL,
+};
+
+struct ImportJobData {
+ Main *bmain;
+ Scene *scene;
+
+ char filename[1024];
+ ImportSettings settings;
+
+ GHash *parent_map;
+ std::vector<AbcObjectReader *> readers;
+
+ short *stop;
+ short *do_update;
+ float *progress;
+
+ char error_code;
+ bool was_cancelled;
+};
+
+ABC_INLINE bool is_mesh_and_strands(const IObject &object)
+{
+ if (object.getNumChildren() != 2) {
+ return false;
+ }
+
+ bool has_mesh = false;
+ bool has_curve = false;
+
+ for (int i = 0; i < object.getNumChildren(); ++i) {
+ const IObject &child = object.getChild(i);
+
+ if (!child.valid()) {
+ continue;
+ }
+
+ const MetaData &md = child.getMetaData();
+
+ if (IPolyMesh::matches(md)) {
+ has_mesh = true;
+ }
+ else if (ISubD::matches(md)) {
+ has_mesh = true;
+ }
+ else if (ICurves::matches(md)) {
+ has_curve = true;
+ }
+ }
+
+ return has_mesh && has_curve;
+}
+
+static void import_startjob(void *user_data, short *stop, short *do_update, float *progress)
+{
+ ImportJobData *data = static_cast<ImportJobData *>(user_data);
+
+ data->stop = stop;
+ data->do_update = do_update;
+ data->progress = progress;
+
+ IArchive *archive = open_archive(data->filename);
+
+ if (!archive || !archive->valid()) {
+ data->error_code = ABC_ARCHIVE_FAIL;
+ return;
+ }
+
+ CacheFile *cache_file = static_cast<CacheFile *>(BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename)));
+
+ /* Decrement the ID ref-count because it is going to be incremented for each
+ * modifier and constraint that it will be attached to, so since currently
+ * it is not used by anyone, its use count will off by one. */
+ id_us_min(&cache_file->id);
+
+ cache_file->is_sequence = data->settings.is_sequence;
+ cache_file->scale = data->settings.scale;
+ cache_file->handle = handle_from_archive(archive);
+ BLI_strncpy(cache_file->filepath, data->filename, 1024);
+
+ data->settings.cache_file = cache_file;
+
+ *data->do_update = true;
+ *data->progress = 0.05f;
+
+ data->parent_map = BLI_ghash_str_new("alembic parent ghash");
+
+ /* Parse Alembic Archive. */
+
+ visit_object(archive->getTop(), data->readers, data->parent_map, data->settings);
+
+ if (G.is_break) {
+ data->was_cancelled = true;
+ return;
+ }
+
+ *data->do_update = true;
+ *data->progress = 0.1f;
+
+ /* Create objects and set scene frame range. */
+
+ const float size = static_cast<float>(data->readers.size());
+ size_t i = 0;
+
+ chrono_t min_time = std::numeric_limits<chrono_t>::max();
+ chrono_t max_time = std::numeric_limits<chrono_t>::min();
+
+ std::vector<AbcObjectReader *>::iterator iter;
+ for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+ AbcObjectReader *reader = *iter;
+
+ if (reader->valid()) {
+ reader->readObjectData(data->bmain, 0.0f);
+ reader->readObjectMatrix(0.0f);
+
+ min_time = std::min(min_time, reader->minTime());
+ max_time = std::max(max_time, reader->maxTime());
+ }
+
+ *data->progress = 0.1f + 0.6f * (++i / size);
+ *data->do_update = true;
+
+ if (G.is_break) {
+ data->was_cancelled = true;
+ return;
+ }
+ }
+
+ if (data->settings.set_frame_range) {
+ Scene *scene = data->scene;
+
+ if (data->settings.is_sequence) {
+ SFRA = data->settings.offset;
+ EFRA = SFRA + (data->settings.sequence_len - 1);
+ CFRA = SFRA;
+ }
+ else if (min_time < max_time) {
+ SFRA = min_time * FPS;
+ EFRA = max_time * FPS;
+ CFRA = SFRA;
+ }
+ }
+
+ /* Setup parentship. */
+
+ i = 0;
+ for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+ const AbcObjectReader *reader = *iter;
+ const AbcObjectReader *parent_reader = NULL;
+ const IObject &iobject = reader->iobject();
+
+ IObject parent = iobject.getParent();
+
+ if (!IXform::matches(iobject.getHeader())) {
+ /* In the case of an non XForm node, the parent is the transform
+ * matrix of the data itself, so we get the its grand parent.
+ */
+
+ /* Special case with object only containing a mesh and some strands,
+ * we want both objects to be parented to the same object. */
+ if (!is_mesh_and_strands(parent)) {
+ parent = parent.getParent();
+ }
+ }
+
+ parent_reader = reinterpret_cast<AbcObjectReader *>(
+ BLI_ghash_lookup(data->parent_map, parent.getFullName().c_str()));
+
+ if (parent_reader) {
+ Object *parent = parent_reader->object();
+
+ if (parent != NULL && reader->object() != parent) {
+ Object *ob = reader->object();
+ ob->parent = parent;
+ }
+ }
+
+ *data->progress = 0.7f + 0.3f * (++i / size);
+ *data->do_update = true;
+
+ if (G.is_break) {
+ data->was_cancelled = true;
+ return;
+ }
+ }
+}
+
+static void import_endjob(void *user_data)
+{
+ ImportJobData *data = static_cast<ImportJobData *>(user_data);
+
+ std::vector<AbcObjectReader *>::iterator iter;
+
+ /* Delete objects on cancelation. */
+ if (data->was_cancelled) {
+ for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+ Object *ob = (*iter)->object();
+
+ if (ob->data) {
+ BKE_libblock_free_us(data->bmain, ob->data);
+ ob->data = NULL;
+ }
+
+ BKE_libblock_free_us(data->bmain, ob);
+ }
+ }
+ else {
+ /* Add object to scene. */
+ BKE_scene_base_deselect_all(data->scene);
+
+ for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+ Object *ob = (*iter)->object();
+ ob->lay = data->scene->lay;
+
+ BKE_scene_base_add(data->scene, ob);
+
+ DAG_id_tag_update_ex(data->bmain, &ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
+ }
+
+ DAG_relations_tag_update(data->bmain);
+ }
+
+ for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+ delete *iter;
+ }
+
+ if (data->parent_map) {
+ BLI_ghash_free(data->parent_map, NULL, NULL);
+ }
+
+ switch (data->error_code) {
+ default:
+ case ABC_NO_ERROR:
+ break;
+ case ABC_ARCHIVE_FAIL:
+ WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail.");
+ break;
+ }
+
+ WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene);
+}
+
+static void import_freejob(void *user_data)
+{
+ ImportJobData *data = static_cast<ImportJobData *>(user_data);
+ delete data;
+}
+
+void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence, bool set_frame_range, int sequence_len, int offset, bool validate_meshes)
+{
+ /* Using new here since MEM_* funcs do not call ctor to properly initialize
+ * data. */
+ ImportJobData *job = new ImportJobData();
+ job->bmain = CTX_data_main(C);
+ job->scene = CTX_data_scene(C);
+ BLI_strncpy(job->filename, filepath, 1024);
+
+ job->settings.scale = scale;
+ job->settings.is_sequence = is_sequence;
+ job->settings.set_frame_range = set_frame_range;
+ job->settings.sequence_len = sequence_len;
+ job->settings.offset = offset;
+ job->settings.validate_meshes = validate_meshes;
+ job->parent_map = NULL;
+ job->error_code = ABC_NO_ERROR;
+ job->was_cancelled = false;
+
+ G.is_break = false;
+
+ wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+ CTX_wm_window(C),
+ job->scene,
+ "Alembic Import",
+ WM_JOB_PROGRESS,
+ WM_JOB_TYPE_ALEMBIC);
+
+ /* setup job */
+ WM_jobs_customdata_set(wm_job, job, import_freejob);
+ WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
+ WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob);
+
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+}
+
+/* ******************************* */
+
+void ABC_get_transform(AbcArchiveHandle *handle, Object *ob, const char *object_path, float r_mat[4][4], float time, float scale)
+{
+ IArchive *archive = archive_from_handle(handle);
+
+ if (!archive || !archive->valid()) {
+ return;
+ }
+
+ IObject tmp;
+ find_iobject(archive->getTop(), tmp, object_path);
+
+ IXform ixform;
+
+ if (IXform::matches(tmp.getHeader())) {
+ ixform = IXform(tmp, kWrapExisting);
+ }
+ else {
+ ixform = IXform(tmp.getParent(), kWrapExisting);
+ }
+
+ IXformSchema schema = ixform.getSchema();
+
+ if (!schema.valid()) {
+ return;
+ }
+
+ ISampleSelector sample_sel(time);
+
+ create_input_transform(sample_sel, ixform, ob, r_mat, scale);
+}
+
+/* ***************************************** */
+
+static bool check_smooth_poly_flag(DerivedMesh *dm)
+{
+ MPoly *mpolys = dm->getPolyArray(dm);
+
+ for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) {
+ MPoly &poly = mpolys[i];
+
+ if ((poly.flag & ME_SMOOTH) != 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void set_smooth_poly_flag(DerivedMesh *dm)
+{
+ MPoly *mpolys = dm->getPolyArray(dm);
+
+ for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) {
+ MPoly &poly = mpolys[i];
+ poly.flag |= ME_SMOOTH;
+ }
+}
+
+static void *add_customdata_cb(void *user_data, const char *name, int data_type)
+{
+ DerivedMesh *dm = static_cast<DerivedMesh *>(user_data);
+ CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
+ void *cd_ptr = NULL;
+
+ if (ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) {
+ cd_ptr = CustomData_get_layer_named(dm->getLoopDataLayout(dm), cd_data_type, name);
+
+ if (cd_ptr == NULL) {
+ cd_ptr = CustomData_add_layer_named(dm->getLoopDataLayout(dm),
+ cd_data_type,
+ CD_DEFAULT,
+ NULL,
+ dm->getNumLoops(dm),
+ name);
+ }
+ }
+
+ return cd_ptr;
+}
+
+ABC_INLINE CDStreamConfig get_config(DerivedMesh *dm)
+{
+ CDStreamConfig config;
+
+ config.user_data = dm;
+ config.mvert = dm->getVertArray(dm);
+ config.mloop = dm->getLoopArray(dm);
+ config.mpoly = dm->getPolyArray(dm);
+ config.totloop = dm->getNumLoops(dm);
+ config.totpoly = dm->getNumPolys(dm);
+ config.loopdata = dm->getLoopDataLayout(dm);
+ config.add_customdata_cb = add_customdata_cb;
+
+ return config;
+}
+
+static DerivedMesh *read_mesh_sample(DerivedMesh *dm, const IObject &iobject, const float time, int read_flag)
+{
+ IPolyMesh mesh(iobject, kWrapExisting);
+ IPolyMeshSchema schema = mesh.getSchema();
+ ISampleSelector sample_sel(time);
+ const IPolyMeshSchema::Sample sample = schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+ const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+ DerivedMesh *new_dm = NULL;
+
+ /* Only read point data when streaming meshes, unless we need to create new ones. */
+ ImportSettings settings;
+ settings.read_flag |= read_flag;
+
+ if (dm->getNumVerts(dm) != positions->size()) {
+ new_dm = CDDM_from_template(dm,
+ positions->size(),
+ 0,
+ 0,
+ face_indices->size(),
+ face_counts->size());
+
+ settings.read_flag |= MOD_MESHSEQ_READ_ALL;
+ }
+
+ CDStreamConfig config = get_config(new_dm ? new_dm : dm);
+
+ bool has_loop_normals = false;
+ read_mesh_sample(&settings, schema, sample_sel, config, has_loop_normals);
+
+ if (new_dm) {
+ /* Check if we had ME_SMOOTH flag set to restore it. */
+ if (!has_loop_normals && check_smooth_poly_flag(dm)) {
+ set_smooth_poly_flag(new_dm);
+ }
+
+ CDDM_calc_normals(new_dm);
+ CDDM_calc_edges(new_dm);
+
+ return new_dm;
+ }
+
+ return dm;
+}
+
+using Alembic::AbcGeom::ISubDSchema;
+
+static DerivedMesh *read_subd_sample(DerivedMesh *dm, const IObject &iobject, const float time, int read_flag)
+{
+ ISubD mesh(iobject, kWrapExisting);
+ ISubDSchema schema = mesh.getSchema();
+ ISampleSelector sample_sel(time);
+ const ISubDSchema::Sample sample = schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+ const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+ DerivedMesh *new_dm = NULL;
+
+ ImportSettings settings;
+ settings.read_flag |= read_flag;
+
+ if (dm->getNumVerts(dm) != positions->size()) {
+ new_dm = CDDM_from_template(dm,
+ positions->size(),
+ 0,
+ 0,
+ face_indices->size(),
+ face_counts->size());
+
+ settings.read_flag |= MOD_MESHSEQ_READ_ALL;
+ }
+
+ /* Only read point data when streaming meshes, unless we need to create new ones. */
+ CDStreamConfig config = get_config(new_dm ? new_dm : dm);
+ read_subd_sample(&settings, schema, sample_sel, config);
+
+ if (new_dm) {
+ /* Check if we had ME_SMOOTH flag set to restore it. */
+ if (check_smooth_poly_flag(dm)) {
+ set_smooth_poly_flag(new_dm);
+ }
+
+ CDDM_calc_normals(new_dm);
+ CDDM_calc_edges(new_dm);
+
+ return new_dm;
+ }
+
+ return dm;
+}
+
+static DerivedMesh *read_points_sample(DerivedMesh *dm, const IObject &iobject, const float time)
+{
+ IPoints points(iobject, kWrapExisting);
+ IPointsSchema schema = points.getSchema();
+ ISampleSelector sample_sel(time);
+ const IPointsSchema::Sample sample = schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+
+ DerivedMesh *new_dm = NULL;
+
+ if (dm->getNumVerts(dm) != positions->size()) {
+ new_dm = CDDM_new(positions->size(), 0, 0, 0, 0);
+ }
+
+ CDStreamConfig config = get_config(new_dm ? new_dm : dm);
+ read_points_sample(schema, sample_sel, config, time);
+
+ return new_dm ? new_dm : dm;
+}
+
+/* NOTE: Alembic only stores data about control points, but the DerivedMesh
+ * passed from the cache modifier contains the displist, which has more data
+ * than the control points, so to avoid corrupting the displist we modify the
+ * object directly and create a new DerivedMesh from that. Also we might need to
+ * create new or delete existing NURBS in the curve.
+ */
+static DerivedMesh *read_curves_sample(Object *ob, const IObject &iobject, const float time)
+{
+ ICurves points(iobject, kWrapExisting);
+ ICurvesSchema schema = points.getSchema();
+ ISampleSelector sample_sel(time);
+ const ICurvesSchema::Sample sample = schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices();
+
+ int vertex_idx = 0;
+ int curve_idx = 0;
+ Curve *curve = static_cast<Curve *>(ob->data);
+
+ const int curve_count = BLI_listbase_count(&curve->nurb);
+
+ if (curve_count != num_vertices->size()) {
+ BKE_nurbList_free(&curve->nurb);
+ read_curve_sample(curve, schema, time);
+ }
+ else {
+ Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
+ for (; nurbs; nurbs = nurbs->next, ++curve_idx) {
+ const int totpoint = (*num_vertices)[curve_idx];
+
+ if (nurbs->bp) {
+ BPoint *point = nurbs->bp;
+
+ for (int i = 0; i < totpoint; ++i, ++point, ++vertex_idx) {
+ const Imath::V3f &pos = (*positions)[vertex_idx];
+ copy_yup_zup(point->vec, pos.getValue());
+ }
+ }
+ else if (nurbs->bezt) {
+ BezTriple *bezier = nurbs->bezt;
+
+ for (int i = 0; i < totpoint; ++i, ++bezier, ++vertex_idx) {
+ const Imath::V3f &pos = (*positions)[vertex_idx];
+ copy_yup_zup(bezier->vec[1], pos.getValue());
+ }
+ }
+ }
+ }
+
+ return CDDM_from_curve(ob);
+}
+
+DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle,
+ Object *ob,
+ DerivedMesh *dm,
+ const char *object_path,
+ const float time,
+ const char **err_str,
+ int read_flag)
+{
+ IArchive *archive = archive_from_handle(handle);
+
+ if (!archive || !archive->valid()) {
+ *err_str = "Invalid archive!";
+ return NULL;
+ }
+
+ IObject iobject;
+ find_iobject(archive->getTop(), iobject, object_path);
+
+ if (!iobject.valid()) {
+ *err_str = "Invalid object: verify object path";
+ return NULL;
+ }
+
+ const ObjectHeader &header = iobject.getHeader();
+
+ if (IPolyMesh::matches(header)) {
+ if (ob->type != OB_MESH) {
+ *err_str = "Object type mismatch: object path points to a mesh!";
+ return NULL;
+ }
+
+ return read_mesh_sample(dm, iobject, time, read_flag);
+ }
+ else if (ISubD::matches(header)) {
+ if (ob->type != OB_MESH) {
+ *err_str = "Object type mismatch: object path points to a subdivision mesh!";
+ return NULL;
+ }
+
+ return read_subd_sample(dm, iobject, time, read_flag);
+ }
+ else if (IPoints::matches(header)) {
+ if (ob->type != OB_MESH) {
+ *err_str = "Object type mismatch: object path points to a point cloud (requires a mesh object)!";
+ return NULL;
+ }
+
+ return read_points_sample(dm, iobject, time);
+ }
+ else if (ICurves::matches(header)) {
+ if (ob->type != OB_CURVE) {
+ *err_str = "Object type mismatch: object path points to a curve!";
+ return NULL;
+ }
+
+ return read_curves_sample(ob, iobject, time);
+ }
+
+ *err_str = "Unsupported object type: verify object path"; // or poke developer
+ return NULL;
+}