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