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