Merge branch 'master' into blender2.8
[blender.git] / source / blender / alembic / intern / abc_customdata.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  * The Original Code is Copyright (C) 2016 K√©vin Dietrich.
19  * All rights reserved.
20  *
21  * ***** END GPL LICENSE BLOCK *****
22  *
23  */
24
25 #include "abc_customdata.h"
26
27 #include <Alembic/AbcGeom/All.h>
28 #include <algorithm>
29
30 extern "C" {
31 #include "DNA_customdata_types.h"
32 #include "DNA_meshdata_types.h"
33
34 #include "BLI_math_base.h"
35 #include "BKE_customdata.h"
36 }
37
38 /* NOTE: for now only UVs and Vertex Colors are supported for streaming.
39  * Although Alembic only allows for a single UV layer per {I|O}Schema, and does
40  * not have a vertex color concept, there is a convention between DCCs to write
41  * such data in a way that lets other DCC know what they are for. See comments
42  * in the write code for the conventions. */
43
44 using Alembic::AbcGeom::kVertexScope;
45 using Alembic::AbcGeom::kFacevaryingScope;
46
47 using Alembic::Abc::C4fArraySample;
48 using Alembic::Abc::UInt32ArraySample;
49 using Alembic::Abc::V2fArraySample;
50
51 using Alembic::AbcGeom::OV2fGeomParam;
52 using Alembic::AbcGeom::OC4fGeomParam;
53
54 static void get_uvs(const CDStreamConfig &config,
55                     std::vector<Imath::V2f> &uvs,
56                     std::vector<uint32_t> &uvidx,
57                     void *cd_data)
58 {
59         MLoopUV *mloopuv_array = static_cast<MLoopUV *>(cd_data);
60
61         if (!mloopuv_array) {
62                 return;
63         }
64
65         const int num_poly = config.totpoly;
66         MPoly *polygons = config.mpoly;
67
68         if (!config.pack_uvs) {
69                 int cnt = 0;
70                 uvidx.resize(config.totloop);
71                 uvs.resize(config.totloop);
72
73                 for (int i = 0; i < num_poly; ++i) {
74                         MPoly &current_poly = polygons[i];
75                         MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop;
76
77                         for (int j = 0; j < current_poly.totloop; ++j, ++cnt) {
78                                 --loopuvpoly;
79
80                                 uvidx[cnt] = cnt;
81                                 uvs[cnt][0] = loopuvpoly->uv[0];
82                                 uvs[cnt][1] = loopuvpoly->uv[1];
83                         }
84                 }
85         }
86         else {
87                 for (int i = 0; i < num_poly; ++i) {
88                         MPoly &current_poly = polygons[i];
89                         MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop;
90
91                         for (int j = 0; j < current_poly.totloop; ++j) {
92                                 loopuvpoly--;
93                                 Imath::V2f uv(loopuvpoly->uv[0], loopuvpoly->uv[1]);
94
95                                 std::vector<Imath::V2f>::iterator it = std::find(uvs.begin(), uvs.end(), uv);
96
97                                 if (it == uvs.end()) {
98                                         uvidx.push_back(uvs.size());
99                                         uvs.push_back(uv);
100                                 }
101                                 else {
102                                         uvidx.push_back(std::distance(uvs.begin(), it));
103                                 }
104                         }
105                 }
106         }
107 }
108
109 const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data)
110 {
111         const int active_uvlayer = CustomData_get_active_layer(data, CD_MLOOPUV);
112
113         if (active_uvlayer < 0) {
114                 return "";
115         }
116
117         void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer);
118
119         get_uvs(config, sample.uvs, sample.indices, cd_data);
120
121         return CustomData_get_layer_name(data, CD_MLOOPUV, active_uvlayer);
122 }
123
124 /* Convention to write UVs:
125  * - V2fGeomParam on the arbGeomParam
126  * - set scope as face varying
127  * - (optional due to its behaviour) tag as UV using Alembic::AbcGeom::SetIsUV
128  */
129 static void write_uv(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name)
130 {
131         std::vector<uint32_t> indices;
132         std::vector<Imath::V2f> uvs;
133
134         get_uvs(config, uvs, indices, data);
135
136         if (indices.empty() || uvs.empty()) {
137                 return;
138         }
139
140         OV2fGeomParam param(prop, name, true, kFacevaryingScope, 1);
141
142         OV2fGeomParam::Sample sample(
143                 V2fArraySample(&uvs.front(), uvs.size()),
144                 UInt32ArraySample(&indices.front(), indices.size()),
145                 kFacevaryingScope);
146
147         param.set(sample);
148 }
149
150 /* Convention to write Vertex Colors:
151  * - C3fGeomParam/C4fGeomParam on the arbGeomParam
152  * - set scope as vertex varying
153  */
154 static void write_mcol(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name)
155 {
156         const float cscale = 1.0f / 255.0f;
157         MPoly *polys = config.mpoly;
158         MLoop *mloops = config.mloop;
159         MCol *cfaces = static_cast<MCol *>(data);
160
161         std::vector<Imath::C4f> buffer(config.totvert);
162
163         Imath::C4f col;
164
165         for (int i = 0; i < config.totpoly; ++i) {
166                 MPoly *p = &polys[i];
167                 MCol *cface = &cfaces[p->loopstart + p->totloop];
168                 MLoop *mloop = &mloops[p->loopstart + p->totloop];
169
170                 for (int j = 0; j < p->totloop; ++j) {
171                         cface--;
172                         mloop--;
173
174                         col[0] = cface->a * cscale;
175                         col[1] = cface->r * cscale;
176                         col[2] = cface->g * cscale;
177                         col[3] = cface->b * cscale;
178
179                         buffer[mloop->v] = col;
180                 }
181         }
182
183         OC4fGeomParam param(prop, name, true, kFacevaryingScope, 1);
184
185         OC4fGeomParam::Sample sample(
186                 C4fArraySample(&buffer.front(), buffer.size()),
187                 kVertexScope);
188
189         param.set(sample);
190 }
191
192 void write_custom_data(const OCompoundProperty &prop, const CDStreamConfig &config, CustomData *data, int data_type)
193 {
194         CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
195
196         if (!CustomData_has_layer(data, cd_data_type)) {
197                 return;
198         }
199
200         const int active_layer = CustomData_get_active_layer(data, cd_data_type);
201         const int tot_layers = CustomData_number_of_layers(data, cd_data_type);
202
203         for (int i = 0; i < tot_layers; ++i) {
204                 void *cd_data = CustomData_get_layer_n(data, cd_data_type, i);
205                 const char *name = CustomData_get_layer_name(data, cd_data_type, i);
206
207                 if (cd_data_type == CD_MLOOPUV) {
208                         /* Already exported. */
209                         if (i == active_layer) {
210                                 continue;
211                         }
212
213                         write_uv(prop, config, cd_data, name);
214                 }
215                 else if (cd_data_type == CD_MLOOPCOL) {
216                         write_mcol(prop, config, cd_data, name);
217                 }
218         }
219 }
220
221 /* ************************************************************************** */
222
223 using Alembic::Abc::C3fArraySamplePtr;
224 using Alembic::Abc::C4fArraySamplePtr;
225 using Alembic::Abc::PropertyHeader;
226
227 using Alembic::AbcGeom::IC3fGeomParam;
228 using Alembic::AbcGeom::IC4fGeomParam;
229 using Alembic::AbcGeom::IV2fGeomParam;
230
231
232 static void read_uvs(const CDStreamConfig &config, void *data,
233                      const Alembic::AbcGeom::V2fArraySamplePtr &uvs,
234                      const Alembic::AbcGeom::UInt32ArraySamplePtr &indices)
235 {
236         MPoly *mpolys = config.mpoly;
237         MLoopUV *mloopuvs = static_cast<MLoopUV *>(data);
238
239         unsigned int uv_index, loop_index, rev_loop_index;
240
241         for (int i = 0; i < config.totpoly; ++i) {
242                 MPoly &poly = mpolys[i];
243                 unsigned int rev_loop_offset = poly.loopstart + poly.totloop - 1;
244
245                 for (int f = 0; f < poly.totloop; ++f) {
246                         loop_index = poly.loopstart + f;
247                         rev_loop_index = rev_loop_offset - f;
248                         uv_index = (*indices)[loop_index];
249                         const Imath::V2f &uv = (*uvs)[uv_index];
250
251                         MLoopUV &loopuv = mloopuvs[rev_loop_index];
252                         loopuv.uv[0] = uv[0];
253                         loopuv.uv[1] = uv[1];
254                 }
255         }
256 }
257
258 static size_t mcols_out_of_bounds_check(
259         const size_t color_index,
260         const size_t array_size,
261         const std::string & iobject_full_name,
262         const PropertyHeader &prop_header,
263         bool &r_bounds_warning_given)
264 {
265         if (color_index < array_size) {
266                 return color_index;
267         }
268
269         if (!r_bounds_warning_given) {
270                 std::cerr << "Alembic: color index out of bounds "
271                              "reading face colors for object "
272                           << iobject_full_name
273                           << ", property "
274                           << prop_header.getName() << std::endl;
275                 r_bounds_warning_given = true;
276         }
277
278         return 0;
279 }
280
281 static void read_custom_data_mcols(const std::string & iobject_full_name,
282                                    const ICompoundProperty &arbGeomParams,
283                                    const PropertyHeader &prop_header,
284                                    const CDStreamConfig &config,
285                                    const Alembic::Abc::ISampleSelector &iss)
286 {
287         C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr();
288         C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr();
289         Alembic::Abc::UInt32ArraySamplePtr indices;
290         bool use_c3f_ptr;
291         bool is_facevarying;
292
293         /* Find the correct interpretation of the data */
294         if (IC3fGeomParam::matches(prop_header)) {
295                 IC3fGeomParam color_param(arbGeomParams, prop_header.getName());
296                 IC3fGeomParam::Sample sample;
297                 BLI_assert(!strcmp("rgb", color_param.getInterpretation()));
298
299                 color_param.getIndexed(sample, iss);
300                 is_facevarying = sample.getScope() == kFacevaryingScope &&
301                                  config.totloop == sample.getIndices()->size();
302
303                 c3f_ptr = sample.getVals();
304                 indices = sample.getIndices();
305                 use_c3f_ptr = true;
306         }
307         else if (IC4fGeomParam::matches(prop_header)) {
308                 IC4fGeomParam color_param(arbGeomParams, prop_header.getName());
309                 IC4fGeomParam::Sample sample;
310                 BLI_assert(!strcmp("rgba", color_param.getInterpretation()));
311
312                 color_param.getIndexed(sample, iss);
313                 is_facevarying = sample.getScope() == kFacevaryingScope &&
314                                  config.totloop == sample.getIndices()->size();
315
316                 c4f_ptr = sample.getVals();
317                 indices = sample.getIndices();
318                 use_c3f_ptr = false;
319         }
320         else {
321                 /* this won't happen due to the checks in read_custom_data() */
322                 return;
323         }
324         BLI_assert(c3f_ptr || c4f_ptr);
325
326         /* Read the vertex colors */
327         void *cd_data = config.add_customdata_cb(config.user_data,
328                                                  prop_header.getName().c_str(),
329                                                  CD_MLOOPCOL);
330         MCol *cfaces = static_cast<MCol *>(cd_data);
331         MPoly *mpolys = config.mpoly;
332         MLoop *mloops = config.mloop;
333
334         size_t face_index = 0;
335         size_t color_index;
336         bool bounds_warning_given = false;
337
338         /* The colors can go through two layers of indexing. Often the 'indices'
339          * array doesn't do anything (i.e. indices[n] = n), but when it does, it's
340          * important. Blender 2.79 writes indices incorrectly (see T53745), which
341          * is why we have to check for indices->size() > 0 */
342         bool use_dual_indexing = is_facevarying && indices->size() > 0;
343
344         for (int i = 0; i < config.totpoly; ++i) {
345                 MPoly *poly = &mpolys[i];
346                 MCol *cface = &cfaces[poly->loopstart + poly->totloop];
347                 MLoop *mloop = &mloops[poly->loopstart + poly->totloop];
348
349                 for (int j = 0; j < poly->totloop; ++j, ++face_index) {
350                         --cface;
351                         --mloop;
352
353                         color_index = is_facevarying ? face_index : mloop->v;
354                         if (use_dual_indexing) {
355                                 color_index = (*indices)[color_index];
356                         }
357                         if (use_c3f_ptr) {
358                                 color_index = mcols_out_of_bounds_check(
359                                                   color_index,
360                                                   c3f_ptr->size(),
361                                                   iobject_full_name, prop_header,
362                                                   bounds_warning_given);
363
364                                 const Imath::C3f &color = (*c3f_ptr)[color_index];
365                                 cface->a = FTOCHAR(color[0]);
366                                 cface->r = FTOCHAR(color[1]);
367                                 cface->g = FTOCHAR(color[2]);
368                                 cface->b = 255;
369                         }
370                         else {
371                                 color_index = mcols_out_of_bounds_check(
372                                                   color_index,
373                                                   c4f_ptr->size(),
374                                                   iobject_full_name, prop_header,
375                                                   bounds_warning_given);
376
377                                 const Imath::C4f &color = (*c4f_ptr)[color_index];
378                                 cface->a = FTOCHAR(color[0]);
379                                 cface->r = FTOCHAR(color[1]);
380                                 cface->g = FTOCHAR(color[2]);
381                                 cface->b = FTOCHAR(color[3]);
382                         }
383                 }
384         }
385 }
386
387 static void read_custom_data_uvs(const ICompoundProperty &prop,
388                                  const PropertyHeader &prop_header,
389                                  const CDStreamConfig &config,
390                                  const Alembic::Abc::ISampleSelector &iss)
391 {
392         IV2fGeomParam uv_param(prop, prop_header.getName());
393
394         if (!uv_param.isIndexed()) {
395                 return;
396         }
397
398         IV2fGeomParam::Sample sample;
399         uv_param.getIndexed(sample, iss);
400
401         if (uv_param.getScope() != kFacevaryingScope) {
402                 return;
403         }
404
405         void *cd_data = config.add_customdata_cb(config.user_data,
406                                                  prop_header.getName().c_str(),
407                                                  CD_MLOOPUV);
408
409         read_uvs(config, cd_data, sample.getVals(), sample.getIndices());
410 }
411
412 void read_custom_data(const std::string & iobject_full_name,
413                       const ICompoundProperty &prop,
414                       const CDStreamConfig &config,
415                       const Alembic::Abc::ISampleSelector &iss)
416 {
417         if (!prop.valid()) {
418                 return;
419         }
420
421         int num_uvs = 0;
422         int num_colors = 0;
423
424         const size_t num_props = prop.getNumProperties();
425
426         for (size_t i = 0; i < num_props; ++i) {
427                 const Alembic::Abc::PropertyHeader &prop_header = prop.getPropertyHeader(i);
428
429                 /* Read UVs according to convention. */
430                 if (IV2fGeomParam::matches(prop_header) && Alembic::AbcGeom::isUV(prop_header)) {
431                         if (++num_uvs > MAX_MTFACE) {
432                                 continue;
433                         }
434
435                         read_custom_data_uvs(prop, prop_header, config, iss);
436                         continue;
437                 }
438
439                 /* Read vertex colors according to convention. */
440                 if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) {
441                         if (++num_colors > MAX_MCOL) {
442                                 continue;
443                         }
444
445                         read_custom_data_mcols(iobject_full_name, prop, prop_header, config, iss);
446                         continue;
447                 }
448         }
449 }