Merge branch 'master' into blender2.8
[blender.git] / source / blender / alembic / intern / abc_nurbs.cc
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 #include "abc_nurbs.h"
24
25 #include "abc_transform.h"
26 #include "abc_util.h"
27
28 extern "C" {
29 #include "MEM_guardedalloc.h"
30
31 #include "DNA_curve_types.h"
32 #include "DNA_object_types.h"
33
34 #include "BLI_listbase.h"
35 #include "BLI_math.h"
36 #include "BLI_string.h"
37
38 #include "BKE_curve.h"
39 #include "BKE_object.h"
40 }
41
42 using Alembic::AbcGeom::bool_t;
43 using Alembic::AbcGeom::FloatArraySample;
44 using Alembic::AbcGeom::FloatArraySamplePtr;
45 using Alembic::AbcGeom::MetaData;
46 using Alembic::AbcGeom::P3fArraySamplePtr;
47 using Alembic::AbcGeom::kWrapExisting;
48
49 using Alembic::AbcGeom::IBoolProperty;
50 using Alembic::AbcGeom::ICompoundProperty;
51 using Alembic::AbcGeom::INuPatch;
52 using Alembic::AbcGeom::INuPatchSchema;
53 using Alembic::AbcGeom::IObject;
54 using Alembic::AbcGeom::ISampleSelector;
55
56 using Alembic::AbcGeom::OBoolProperty;
57 using Alembic::AbcGeom::OCompoundProperty;
58 using Alembic::AbcGeom::ONuPatch;
59 using Alembic::AbcGeom::ONuPatchSchema;
60
61 /* ************************************************************************** */
62
63 AbcNurbsWriter::AbcNurbsWriter(Object *ob,
64                                AbcTransformWriter *parent,
65                                uint32_t time_sampling,
66                                ExportSettings &settings)
67     : AbcObjectWriter(ob, time_sampling, settings, parent)
68 {
69         m_is_animated = isAnimated();
70
71         /* if the object is static, use the default static time sampling */
72         if (!m_is_animated) {
73                 m_time_sampling = 0;
74         }
75
76         Curve *curve = static_cast<Curve *>(m_object->data);
77         size_t numNurbs = BLI_listbase_count(&curve->nurb);
78
79         for (size_t i = 0; i < numNurbs; ++i) {
80                 std::stringstream str;
81                 str << m_name << '_' << i;
82
83                 while (parent->alembicXform().getChildHeader(str.str())) {
84                         str << "_";
85                 }
86
87                 ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling);
88                 m_nurbs_schema.push_back(nurbs.getSchema());
89         }
90 }
91
92 bool AbcNurbsWriter::isAnimated() const
93 {
94         /* check if object has shape keys */
95         Curve *cu = static_cast<Curve *>(m_object->data);
96         return (cu->key != NULL);
97 }
98
99 static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots)
100 {
101         if (num_knots <= 1) {
102                 return;
103         }
104
105         /* Add an extra knot at the beggining and end of the array since most apps
106          * require/expect them. */
107         knots.reserve(num_knots + 2);
108
109         knots.push_back(0.0f);
110
111         for (int i = 0; i < num_knots; ++i) {
112                 knots.push_back(nu_knots[i]);
113         }
114
115         knots[0] = 2.0f * knots[1] - knots[2];
116         knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]);
117 }
118
119 void AbcNurbsWriter::do_write()
120 {
121         /* we have already stored a sample for this object. */
122         if (!m_first_frame && !m_is_animated) {
123                 return;
124         }
125
126         if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) {
127                 return;
128         }
129
130         Curve *curve = static_cast<Curve *>(m_object->data);
131         ListBase *nulb;
132
133         if (m_object->runtime.curve_cache->deformed_nurbs.first != NULL) {
134                 nulb = &m_object->runtime.curve_cache->deformed_nurbs;
135         }
136         else {
137                 nulb = BKE_curve_nurbs_get(curve);
138         }
139
140         size_t count = 0;
141         for (Nurb *nu = static_cast<Nurb *>(nulb->first); nu; nu = nu->next, ++count) {
142                 std::vector<float> knotsU;
143                 get_knots(knotsU, KNOTSU(nu), nu->knotsu);
144
145                 std::vector<float> knotsV;
146                 get_knots(knotsV, KNOTSV(nu), nu->knotsv);
147
148                 const int size = nu->pntsu * nu->pntsv;
149                 std::vector<Imath::V3f> positions(size);
150                 std::vector<float> weights(size);
151
152                 const BPoint *bp = nu->bp;
153
154                 for (int i = 0; i < size; ++i, ++bp) {
155                         copy_yup_from_zup(positions[i].getValue(), bp->vec);
156                         weights[i] = bp->vec[3];
157                 }
158
159                 ONuPatchSchema::Sample sample;
160                 sample.setUOrder(nu->orderu + 1);
161                 sample.setVOrder(nu->orderv + 1);
162                 sample.setPositions(positions);
163                 sample.setPositionWeights(weights);
164                 sample.setUKnot(FloatArraySample(knotsU));
165                 sample.setVKnot(FloatArraySample(knotsV));
166                 sample.setNu(nu->pntsu);
167                 sample.setNv(nu->pntsv);
168
169                 /* TODO(kevin): to accomodate other software we should duplicate control
170                  * points to indicate that a NURBS is cyclic. */
171                 OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties();
172
173                 if ((nu->flagu & CU_NURB_ENDPOINT) != 0) {
174                         OBoolProperty prop(user_props, "endpoint_u");
175                         prop.set(true);
176                 }
177
178                 if ((nu->flagv & CU_NURB_ENDPOINT) != 0) {
179                         OBoolProperty prop(user_props, "endpoint_v");
180                         prop.set(true);
181                 }
182
183                 if ((nu->flagu & CU_NURB_CYCLIC) != 0) {
184                         OBoolProperty prop(user_props, "cyclic_u");
185                         prop.set(true);
186                 }
187
188                 if ((nu->flagv & CU_NURB_CYCLIC) != 0) {
189                         OBoolProperty prop(user_props, "cyclic_v");
190                         prop.set(true);
191                 }
192
193                 m_nurbs_schema[count].set(sample);
194         }
195 }
196
197 /* ************************************************************************** */
198
199 AbcNurbsReader::AbcNurbsReader(const IObject &object, ImportSettings &settings)
200     : AbcObjectReader(object, settings)
201 {
202         getNurbsPatches(m_iobject);
203         get_min_max_time(m_iobject, m_schemas[0].first, m_min_time, m_max_time);
204 }
205
206 bool AbcNurbsReader::valid() const
207 {
208         if (m_schemas.empty()) {
209                 return false;
210         }
211
212         std::vector< std::pair<INuPatchSchema, IObject> >::const_iterator it;
213         for (it = m_schemas.begin(); it != m_schemas.end(); ++it) {
214                 const INuPatchSchema &schema = it->first;
215
216                 if (!schema.valid()) {
217                         return false;
218                 }
219         }
220
221         return true;
222 }
223
224 static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots)
225 {
226         if (!knots || knots->size() == 0) {
227                 return false;
228         }
229
230         /* Skip first and last knots, as they are used for padding. */
231         const size_t num_knots = knots->size() - 2;
232         nu_knots = static_cast<float *>(MEM_callocN(num_knots * sizeof(float), "abc_setsplineknotsu"));
233
234         for (size_t i = 0; i < num_knots; ++i) {
235                 nu_knots[i] = (*knots)[i + 1];
236         }
237
238         return true;
239 }
240
241 void AbcNurbsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
242 {
243         Curve *cu = static_cast<Curve *>(BKE_curve_add(bmain, "abc_curve", OB_SURF));
244         cu->actvert = CU_ACT_NONE;
245
246         std::vector< std::pair<INuPatchSchema, IObject> >::iterator it;
247
248         for (it = m_schemas.begin(); it != m_schemas.end(); ++it) {
249                 Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb"));
250                 nu->flag  = CU_SMOOTH;
251                 nu->type = CU_NURBS;
252                 nu->resolu = cu->resolu;
253                 nu->resolv = cu->resolv;
254
255                 const INuPatchSchema &schema = it->first;
256                 INuPatchSchema::Sample smp;
257                 try {
258                         smp = schema.getValue(sample_sel);
259                 }
260                 catch(Alembic::Util::Exception &ex) {
261                         printf("Alembic: error reading nurbs sample for '%s/%s' at time %f: %s\n",
262                                    m_iobject.getFullName().c_str(),
263                                    schema.getName().c_str(),
264                                    sample_sel.getRequestedTime(),
265                                    ex.what());
266                         return;
267                 }
268
269
270                 nu->orderu = smp.getUOrder() - 1;
271                 nu->orderv = smp.getVOrder() - 1;
272                 nu->pntsu = smp.getNumU();
273                 nu->pntsv = smp.getNumV();
274
275                 /* Read positions and weights. */
276
277                 const P3fArraySamplePtr positions = smp.getPositions();
278                 const FloatArraySamplePtr weights = smp.getPositionWeights();
279
280                 const size_t num_points = positions->size();
281
282                 nu->bp = static_cast<BPoint *>(MEM_callocN(num_points * sizeof(BPoint), "abc_setsplinetype"));
283
284                 BPoint *bp = nu->bp;
285                 float posw_in = 1.0f;
286
287                 for (int i = 0; i < num_points; ++i, ++bp) {
288                         const Imath::V3f &pos_in = (*positions)[i];
289
290                         if (weights) {
291                                 posw_in = (*weights)[i];
292                         }
293
294                         copy_zup_from_yup(bp->vec, pos_in.getValue());
295                         bp->vec[3] = posw_in;
296                         bp->f1 = SELECT;
297                         bp->radius = 1.0f;
298                         bp->weight = 1.0f;
299                 }
300
301                 /* Read knots. */
302
303                 if (!set_knots(smp.getUKnot(), nu->knotsu)) {
304                         BKE_nurb_knot_calc_u(nu);
305                 }
306
307                 if (!set_knots(smp.getVKnot(), nu->knotsv)) {
308                         BKE_nurb_knot_calc_v(nu);
309                 }
310
311                 /* Read flags. */
312
313                 ICompoundProperty user_props = schema.getUserProperties();
314
315                 if (has_property(user_props, "enpoint_u")) {
316                         nu->flagu |= CU_NURB_ENDPOINT;
317                 }
318
319                 if (has_property(user_props, "enpoint_v")) {
320                         nu->flagv |= CU_NURB_ENDPOINT;
321                 }
322
323                 if (has_property(user_props, "cyclic_u")) {
324                         nu->flagu |= CU_NURB_CYCLIC;
325                 }
326
327                 if (has_property(user_props, "cyclic_v")) {
328                         nu->flagv |= CU_NURB_CYCLIC;
329                 }
330
331                 BLI_addtail(BKE_curve_nurbs_get(cu), nu);
332         }
333
334         BLI_strncpy(cu->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1);
335
336         m_object = BKE_object_add_only_object(bmain, OB_SURF, m_object_name.c_str());
337         m_object->data = cu;
338 }
339
340 void AbcNurbsReader::getNurbsPatches(const IObject &obj)
341 {
342         if (!obj.valid()) {
343                 return;
344         }
345
346         const int num_children = obj.getNumChildren();
347
348         if (num_children == 0) {
349                 INuPatch abc_nurb(obj, kWrapExisting);
350                 INuPatchSchema schem = abc_nurb.getSchema();
351                 m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, obj));
352                 return;
353         }
354
355         for (int i = 0; i < num_children; ++i) {
356                 bool ok = true;
357                 IObject child(obj, obj.getChildHeader(i).getName());
358
359                 if (!m_name.empty() && child.valid() && !begins_with(child.getFullName(), m_name)) {
360                         ok = false;
361                 }
362
363                 if (!child.valid()) {
364                         continue;
365                 }
366
367                 const MetaData &md = child.getMetaData();
368
369                 if (INuPatch::matches(md) && ok) {
370                         INuPatch abc_nurb(child, kWrapExisting);
371                         INuPatchSchema schem = abc_nurb.getSchema();
372                         m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, child));
373                 }
374
375                 getNurbsPatches(child);
376         }
377 }