2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2016 Kévin Dietrich.
19 * All rights reserved.
21 * ***** END GPL LICENSE BLOCK *****
25 #include "abc_customdata.h"
27 #include <Alembic/AbcGeom/All.h>
31 #include "DNA_customdata_types.h"
32 #include "DNA_meshdata_types.h"
34 #include "BKE_customdata.h"
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. */
43 using Alembic::AbcGeom::kVertexScope;
44 using Alembic::AbcGeom::kFacevaryingScope;
46 using Alembic::Abc::C4fArraySample;
47 using Alembic::Abc::UInt32ArraySample;
48 using Alembic::Abc::V2fArraySample;
50 using Alembic::AbcGeom::OV2fGeomParam;
51 using Alembic::AbcGeom::OC4fGeomParam;
53 static void get_uvs(const CDStreamConfig &config,
54 std::vector<Imath::V2f> &uvs,
55 std::vector<uint32_t> &uvidx,
58 MLoopUV *mloopuv_array = static_cast<MLoopUV *>(cd_data);
64 const int num_poly = config.totpoly;
65 MPoly *polygons = config.mpoly;
67 if (!config.pack_uvs) {
69 uvidx.resize(config.totloop);
70 uvs.resize(config.totloop);
72 for (int i = 0; i < num_poly; ++i) {
73 MPoly ¤t_poly = polygons[i];
74 MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop;
76 for (int j = 0; j < current_poly.totloop; ++j, ++cnt) {
80 uvs[cnt][0] = loopuvpoly->uv[0];
81 uvs[cnt][1] = loopuvpoly->uv[1];
86 for (int i = 0; i < num_poly; ++i) {
87 MPoly ¤t_poly = polygons[i];
88 MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop;
90 for (int j = 0; j < current_poly.totloop; ++j) {
92 Imath::V2f uv(loopuvpoly->uv[0], loopuvpoly->uv[1]);
94 std::vector<Imath::V2f>::iterator it = std::find(uvs.begin(), uvs.end(), uv);
96 if (it == uvs.end()) {
97 uvidx.push_back(uvs.size());
101 uvidx.push_back(std::distance(uvs.begin(), it));
108 const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data)
110 const int active_uvlayer = CustomData_get_active_layer(data, CD_MLOOPUV);
112 if (active_uvlayer < 0) {
116 void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer);
118 get_uvs(config, sample.uvs, sample.indices, cd_data);
120 return CustomData_get_layer_name(data, CD_MLOOPUV, active_uvlayer);
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
128 static void write_uv(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name)
130 std::vector<uint32_t> indices;
131 std::vector<Imath::V2f> uvs;
133 get_uvs(config, uvs, indices, data);
135 if (indices.empty() || uvs.empty()) {
139 OV2fGeomParam param(prop, name, true, kFacevaryingScope, 1);
141 OV2fGeomParam::Sample sample(
142 V2fArraySample(&uvs.front(), uvs.size()),
143 UInt32ArraySample(&indices.front(), indices.size()),
149 /* Convention to write Vertex Colors:
150 * - C3fGeomParam/C4fGeomParam on the arbGeomParam
151 * - set scope as vertex varying
153 static void write_mcol(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name)
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);
160 std::vector<Imath::C4f> buffer(config.totvert);
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];
169 for (int j = 0; j < p->totloop; ++j) {
173 col[0] = cface->a * cscale;
174 col[1] = cface->r * cscale;
175 col[2] = cface->g * cscale;
176 col[3] = cface->b * cscale;
178 buffer[mloop->v] = col;
182 OC4fGeomParam param(prop, name, true, kFacevaryingScope, 1);
184 OC4fGeomParam::Sample sample(
185 C4fArraySample(&buffer.front(), buffer.size()),
191 void write_custom_data(const OCompoundProperty &prop, const CDStreamConfig &config, CustomData *data, int data_type)
193 CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
195 if (!CustomData_has_layer(data, cd_data_type)) {
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);
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);
206 if (cd_data_type == CD_MLOOPUV) {
207 /* Already exported. */
208 if (i == active_layer) {
212 write_uv(prop, config, cd_data, name);
214 else if (cd_data_type == CD_MLOOPCOL) {
215 write_mcol(prop, config, cd_data, name);
220 /* ************************************************************************** */
222 using Alembic::Abc::C3fArraySamplePtr;
223 using Alembic::Abc::C4fArraySamplePtr;
224 using Alembic::Abc::PropertyHeader;
226 using Alembic::AbcGeom::IC3fGeomParam;
227 using Alembic::AbcGeom::IC4fGeomParam;
228 using Alembic::AbcGeom::IV2fGeomParam;
230 static void read_mcols(const CDStreamConfig &config, void *data,
231 const C3fArraySamplePtr &c3f_ptr, const C4fArraySamplePtr &c4f_ptr)
233 MCol *cfaces = static_cast<MCol *>(data);
234 MPoly *polys = config.mpoly;
235 MLoop *mloops = config.mloop;
238 for (int i = 0; i < config.totpoly; ++i) {
239 MPoly *p = &polys[i];
240 MCol *cface = &cfaces[p->loopstart + p->totloop];
241 MLoop *mloop = &mloops[p->loopstart + p->totloop];
243 for (int j = 0; j < p->totloop; ++j) {
246 const Imath::C3f &color = (*c3f_ptr)[mloop->v];
247 cface->a = FTOCHAR(color[0]);
248 cface->r = FTOCHAR(color[1]);
249 cface->g = FTOCHAR(color[2]);
255 for (int i = 0; i < config.totpoly; ++i) {
256 MPoly *p = &polys[i];
257 MCol *cface = &cfaces[p->loopstart + p->totloop];
258 MLoop *mloop = &mloops[p->loopstart + p->totloop];
260 for (int j = 0; j < p->totloop; ++j) {
263 const Imath::C4f &color = (*c4f_ptr)[mloop->v];
264 cface->a = FTOCHAR(color[0]);
265 cface->r = FTOCHAR(color[1]);
266 cface->g = FTOCHAR(color[2]);
267 cface->b = FTOCHAR(color[3]);
273 static void read_uvs(const CDStreamConfig &config, void *data,
274 const Alembic::AbcGeom::V2fArraySamplePtr &uvs,
275 const Alembic::AbcGeom::UInt32ArraySamplePtr &indices)
277 MPoly *mpolys = config.mpoly;
278 MLoopUV *mloopuvs = static_cast<MLoopUV *>(data);
280 unsigned int uv_index, loop_index;
282 for (int i = 0; i < config.totpoly; ++i) {
283 MPoly &poly = mpolys[i];
285 for (int f = 0; f < poly.totloop; ++f) {
286 loop_index = poly.loopstart + f;
287 uv_index = (*indices)[loop_index];
288 const Imath::V2f &uv = (*uvs)[uv_index];
290 MLoopUV &loopuv = mloopuvs[loop_index];
291 loopuv.uv[0] = uv[0];
292 loopuv.uv[1] = uv[1];
297 static void read_custom_data_ex(const ICompoundProperty &prop,
298 const PropertyHeader &prop_header,
299 const CDStreamConfig &config,
300 const Alembic::Abc::ISampleSelector &iss,
303 if (data_type == CD_MLOOPCOL) {
304 C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr();
305 C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr();
307 if (IC3fGeomParam::matches(prop_header)) {
308 IC3fGeomParam color_param(prop, prop_header.getName());
309 IC3fGeomParam::Sample sample;
310 color_param.getIndexed(sample, iss);
312 c3f_ptr = sample.getVals();
314 else if (IC4fGeomParam::matches(prop_header)) {
315 IC4fGeomParam color_param(prop, prop_header.getName());
316 IC4fGeomParam::Sample sample;
317 color_param.getIndexed(sample, iss);
319 c4f_ptr = sample.getVals();
322 void *cd_data = config.add_customdata_cb(config.user_data,
323 prop_header.getName().c_str(),
326 read_mcols(config, cd_data, c3f_ptr, c4f_ptr);
328 else if (data_type == CD_MLOOPUV) {
329 IV2fGeomParam uv_param(prop, prop_header.getName());
331 if (!uv_param.isIndexed()) {
335 IV2fGeomParam::Sample sample;
336 uv_param.getIndexed(sample, iss);
338 if (uv_param.getScope() != kFacevaryingScope) {
342 void *cd_data = config.add_customdata_cb(config.user_data,
343 prop_header.getName().c_str(),
346 read_uvs(config, cd_data, sample.getVals(), sample.getIndices());
350 void read_custom_data(const ICompoundProperty &prop, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss)
359 const size_t num_props = prop.getNumProperties();
361 for (size_t i = 0; i < num_props; ++i) {
362 const Alembic::Abc::PropertyHeader &prop_header = prop.getPropertyHeader(i);
364 /* Read UVs according to convention. */
365 if (IV2fGeomParam::matches(prop_header) && Alembic::AbcGeom::isUV(prop_header)) {
366 if (++num_uvs > MAX_MTFACE) {
370 read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPUV);
374 /* Read vertex colors according to convention. */
375 if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) {
376 if (++num_colors > MAX_MCOL) {
380 read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPCOL);