0532191a28dd6324debda0311ec7a5f1c1e094d0
[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(EvaluationContext *eval_ctx,
64                                Scene *scene,
65                                Object *ob,
66                                AbcTransformWriter *parent,
67                                uint32_t time_sampling,
68                                ExportSettings &settings)
69     : AbcObjectWriter(eval_ctx, scene, ob, time_sampling, settings, parent)
70 {
71         m_is_animated = isAnimated();
72
73         /* if the object is static, use the default static time sampling */
74         if (!m_is_animated) {
75                 m_time_sampling = 0;
76         }
77
78         Curve *curve = static_cast<Curve *>(m_object->data);
79         size_t numNurbs = BLI_listbase_count(&curve->nurb);
80
81         for (size_t i = 0; i < numNurbs; ++i) {
82                 std::stringstream str;
83                 str << m_name << '_' << i;
84
85                 while (parent->alembicXform().getChildHeader(str.str())) {
86                         str << "_";
87                 }
88
89                 ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling);
90                 m_nurbs_schema.push_back(nurbs.getSchema());
91         }
92 }
93
94 bool AbcNurbsWriter::isAnimated() const
95 {
96         /* check if object has shape keys */
97         Curve *cu = static_cast<Curve *>(m_object->data);
98         return (cu->key != NULL);
99 }
100
101 static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots)
102 {
103         if (num_knots <= 1) {
104                 return;
105         }
106
107         /* Add an extra knot at the beggining and end of the array since most apps
108          * require/expect them. */
109         knots.reserve(num_knots + 2);
110
111         knots.push_back(0.0f);
112
113         for (int i = 0; i < num_knots; ++i) {
114                 knots.push_back(nu_knots[i]);
115         }
116
117         knots[0] = 2.0f * knots[1] - knots[2];
118         knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]);
119 }
120
121 void AbcNurbsWriter::do_write()
122 {
123         /* we have already stored a sample for this object. */
124         if (!m_first_frame && !m_is_animated) {
125                 return;
126         }
127
128         if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) {
129                 return;
130         }
131
132         Curve *curve = static_cast<Curve *>(m_object->data);
133         ListBase *nulb;
134
135         if (m_object->curve_cache->deformed_nurbs.first != NULL) {
136                 nulb = &m_object->curve_cache->deformed_nurbs;
137         }
138         else {
139                 nulb = BKE_curve_nurbs_get(curve);
140         }
141
142         size_t count = 0;
143         for (Nurb *nu = static_cast<Nurb *>(nulb->first); nu; nu = nu->next, ++count) {
144                 std::vector<float> knotsU;
145                 get_knots(knotsU, KNOTSU(nu), nu->knotsu);
146
147                 std::vector<float> knotsV;
148                 get_knots(knotsV, KNOTSV(nu), nu->knotsv);
149
150                 const int size = nu->pntsu * nu->pntsv;
151                 std::vector<Imath::V3f> positions(size);
152                 std::vector<float> weights(size);
153
154                 const BPoint *bp = nu->bp;
155
156                 for (int i = 0; i < size; ++i, ++bp) {
157                         copy_yup_from_zup(positions[i].getValue(), bp->vec);
158                         weights[i] = bp->vec[3];
159                 }
160
161                 ONuPatchSchema::Sample sample;
162                 sample.setUOrder(nu->orderu + 1);
163                 sample.setVOrder(nu->orderv + 1);
164                 sample.setPositions(positions);
165                 sample.setPositionWeights(weights);
166                 sample.setUKnot(FloatArraySample(knotsU));
167                 sample.setVKnot(FloatArraySample(knotsV));
168                 sample.setNu(nu->pntsu);
169                 sample.setNv(nu->pntsv);
170
171                 /* TODO(kevin): to accomodate other software we should duplicate control
172                  * points to indicate that a NURBS is cyclic. */
173                 OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties();
174
175                 if ((nu->flagu & CU_NURB_ENDPOINT) != 0) {
176                         OBoolProperty prop(user_props, "endpoint_u");
177                         prop.set(true);
178                 }
179
180                 if ((nu->flagv & CU_NURB_ENDPOINT) != 0) {
181                         OBoolProperty prop(user_props, "endpoint_v");
182                         prop.set(true);
183                 }
184
185                 if ((nu->flagu & CU_NURB_CYCLIC) != 0) {
186                         OBoolProperty prop(user_props, "cyclic_u");
187                         prop.set(true);
188                 }
189
190                 if ((nu->flagv & CU_NURB_CYCLIC) != 0) {
191                         OBoolProperty prop(user_props, "cyclic_v");
192                         prop.set(true);
193                 }
194
195                 m_nurbs_schema[count].set(sample);
196         }
197 }
198
199 /* ************************************************************************** */
200
201 AbcNurbsReader::AbcNurbsReader(const IObject &object, ImportSettings &settings)
202     : AbcObjectReader(object, settings)
203 {
204         getNurbsPatches(m_iobject);
205         get_min_max_time(m_iobject, m_schemas[0].first, m_min_time, m_max_time);
206 }
207
208 bool AbcNurbsReader::valid() const
209 {
210         if (m_schemas.empty()) {
211                 return false;
212         }
213
214         std::vector< std::pair<INuPatchSchema, IObject> >::const_iterator it;
215         for (it = m_schemas.begin(); it != m_schemas.end(); ++it) {
216                 const INuPatchSchema &schema = it->first;
217
218                 if (!schema.valid()) {
219                         return false;
220                 }
221         }
222
223         return true;
224 }
225
226 static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots)
227 {
228         if (!knots || knots->size() == 0) {
229                 return false;
230         }
231
232         /* Skip first and last knots, as they are used for padding. */
233         const size_t num_knots = knots->size() - 2;
234         nu_knots = static_cast<float *>(MEM_callocN(num_knots * sizeof(float), "abc_setsplineknotsu"));
235
236         for (size_t i = 0; i < num_knots; ++i) {
237                 nu_knots[i] = (*knots)[i + 1];
238         }
239
240         return true;
241 }
242
243 void AbcNurbsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
244 {
245         Curve *cu = static_cast<Curve *>(BKE_curve_add(bmain, "abc_curve", OB_SURF));
246         cu->actvert = CU_ACT_NONE;
247
248         std::vector< std::pair<INuPatchSchema, IObject> >::iterator it;
249
250         for (it = m_schemas.begin(); it != m_schemas.end(); ++it) {
251                 Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb"));
252                 nu->flag  = CU_SMOOTH;
253                 nu->type = CU_NURBS;
254                 nu->resolu = cu->resolu;
255                 nu->resolv = cu->resolv;
256
257                 const INuPatchSchema &schema = it->first;
258                 const INuPatchSchema::Sample smp = schema.getValue(sample_sel);
259
260                 nu->orderu = smp.getUOrder() - 1;
261                 nu->orderv = smp.getVOrder() - 1;
262                 nu->pntsu = smp.getNumU();
263                 nu->pntsv = smp.getNumV();
264
265                 /* Read positions and weights. */
266
267                 const P3fArraySamplePtr positions = smp.getPositions();
268                 const FloatArraySamplePtr weights = smp.getPositionWeights();
269
270                 const size_t num_points = positions->size();
271
272                 nu->bp = static_cast<BPoint *>(MEM_callocN(num_points * sizeof(BPoint), "abc_setsplinetype"));
273
274                 BPoint *bp = nu->bp;
275                 float posw_in = 1.0f;
276
277                 for (int i = 0; i < num_points; ++i, ++bp) {
278                         const Imath::V3f &pos_in = (*positions)[i];
279
280                         if (weights) {
281                                 posw_in = (*weights)[i];
282                         }
283
284                         copy_zup_from_yup(bp->vec, pos_in.getValue());
285                         bp->vec[3] = posw_in;
286                         bp->f1 = SELECT;
287                         bp->radius = 1.0f;
288                         bp->weight = 1.0f;
289                 }
290
291                 /* Read knots. */
292
293                 if (!set_knots(smp.getUKnot(), nu->knotsu)) {
294                         BKE_nurb_knot_calc_u(nu);
295                 }
296
297                 if (!set_knots(smp.getVKnot(), nu->knotsv)) {
298                         BKE_nurb_knot_calc_v(nu);
299                 }
300
301                 /* Read flags. */
302
303                 ICompoundProperty user_props = schema.getUserProperties();
304
305                 if (has_property(user_props, "enpoint_u")) {
306                         nu->flagu |= CU_NURB_ENDPOINT;
307                 }
308
309                 if (has_property(user_props, "enpoint_v")) {
310                         nu->flagv |= CU_NURB_ENDPOINT;
311                 }
312
313                 if (has_property(user_props, "cyclic_u")) {
314                         nu->flagu |= CU_NURB_CYCLIC;
315                 }
316
317                 if (has_property(user_props, "cyclic_v")) {
318                         nu->flagv |= CU_NURB_CYCLIC;
319                 }
320
321                 BLI_addtail(BKE_curve_nurbs_get(cu), nu);
322         }
323
324         BLI_strncpy(cu->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1);
325
326         m_object = BKE_object_add_only_object(bmain, OB_SURF, m_object_name.c_str());
327         m_object->data = cu;
328 }
329
330 void AbcNurbsReader::getNurbsPatches(const IObject &obj)
331 {
332         if (!obj.valid()) {
333                 return;
334         }
335
336         const int num_children = obj.getNumChildren();
337
338         if (num_children == 0) {
339                 INuPatch abc_nurb(obj, kWrapExisting);
340                 INuPatchSchema schem = abc_nurb.getSchema();
341                 m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, obj));
342                 return;
343         }
344
345         for (int i = 0; i < num_children; ++i) {
346                 bool ok = true;
347                 IObject child(obj, obj.getChildHeader(i).getName());
348
349                 if (!m_name.empty() && child.valid() && !begins_with(child.getFullName(), m_name)) {
350                         ok = false;
351                 }
352
353                 if (!child.valid()) {
354                         continue;
355                 }
356
357                 const MetaData &md = child.getMetaData();
358
359                 if (INuPatch::matches(md) && ok) {
360                         INuPatch abc_nurb(child, kWrapExisting);
361                         INuPatchSchema schem = abc_nurb.getSchema();
362                         m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, child));
363                 }
364
365                 getNurbsPatches(child);
366         }
367 }