diff options
Diffstat (limited to 'source/blender/alembic/intern/abc_customdata.cc')
-rw-r--r-- | source/blender/alembic/intern/abc_customdata.cc | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/source/blender/alembic/intern/abc_customdata.cc b/source/blender/alembic/intern/abc_customdata.cc new file mode 100644 index 00000000000..ebf1b2ba96e --- /dev/null +++ b/source/blender/alembic/intern/abc_customdata.cc @@ -0,0 +1,379 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#include "abc_customdata.h" + +#include <Alembic/AbcGeom/All.h> +#include <algorithm> + +extern "C" { +#include "DNA_customdata_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +} + +/* NOTE: for now only UVs and Vertex Colors are supported for streaming. + * Although Alembic only allows for a single UV layer per {I|O}Schema, and does + * not have a vertex color concept, there is a convention between DCCs to write + * such data in a way that lets other DCC know what they are for. See comments + * in the write code for the conventions. */ + +using Alembic::AbcGeom::kVertexScope; +using Alembic::AbcGeom::kFacevaryingScope; + +using Alembic::Abc::C4fArraySample; +using Alembic::Abc::UInt32ArraySample; +using Alembic::Abc::V2fArraySample; + +using Alembic::AbcGeom::OV2fGeomParam; +using Alembic::AbcGeom::OC4fGeomParam; + +static void get_uvs(const CDStreamConfig &config, + std::vector<Imath::V2f> &uvs, + std::vector<uint32_t> &uvidx, + void *cd_data) +{ + MLoopUV *mloopuv_array = static_cast<MLoopUV *>(cd_data); + + if (!mloopuv_array) { + return; + } + + const int num_poly = config.totpoly; + MPoly *polygons = config.mpoly; + + if (!config.pack_uvs) { + int cnt = 0; + uvidx.resize(config.totloop); + uvs.resize(config.totloop); + + for (int i = 0; i < num_poly; ++i) { + MPoly ¤t_poly = polygons[i]; + MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop; + + for (int j = 0; j < current_poly.totloop; ++j, ++cnt) { + --loopuvpoly; + + uvidx[cnt] = cnt; + uvs[cnt][0] = loopuvpoly->uv[0]; + uvs[cnt][1] = loopuvpoly->uv[1]; + } + } + } + else { + for (int i = 0; i < num_poly; ++i) { + MPoly ¤t_poly = polygons[i]; + MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop; + + for (int j = 0; j < current_poly.totloop; ++j) { + loopuvpoly--; + Imath::V2f uv(loopuvpoly->uv[0], loopuvpoly->uv[1]); + + std::vector<Imath::V2f>::iterator it = std::find(uvs.begin(), uvs.end(), uv); + + if (it == uvs.end()) { + uvidx.push_back(uvs.size()); + uvs.push_back(uv); + } + else { + uvidx.push_back(std::distance(uvs.begin(), it)); + } + } + } + } +} + +const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data) +{ + const int active_uvlayer = CustomData_get_active_layer(data, CD_MLOOPUV); + + if (active_uvlayer < 0) { + return ""; + } + + void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer); + + get_uvs(config, sample.uvs, sample.indices, cd_data); + + return CustomData_get_layer_name(data, CD_MLOOPUV, active_uvlayer); +} + +/* Convention to write UVs: + * - V2fGeomParam on the arbGeomParam + * - set scope as face varying + * - (optional due to its behaviour) tag as UV using Alembic::AbcGeom::SetIsUV + */ +static void write_uv(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name) +{ + std::vector<uint32_t> indices; + std::vector<Imath::V2f> uvs; + + get_uvs(config, uvs, indices, data); + + if (indices.empty() || uvs.empty()) { + return; + } + + OV2fGeomParam param(prop, name, true, kFacevaryingScope, 1); + + OV2fGeomParam::Sample sample( + V2fArraySample(&uvs.front(), uvs.size()), + UInt32ArraySample(&indices.front(), indices.size()), + kFacevaryingScope); + + param.set(sample); +} + +/* Convention to write Vertex Colors: + * - C3fGeomParam/C4fGeomParam on the arbGeomParam + * - set scope as vertex varying + */ +static void write_mcol(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name) +{ + const float cscale = 1.0f / 255.0f; + MPoly *polys = config.mpoly; + MLoop *mloops = config.mloop; + MCol *cfaces = static_cast<MCol *>(data); + + std::vector<Imath::C4f> buffer(config.totvert); + + Imath::C4f col; + + for (int i = 0; i < config.totpoly; ++i) { + MPoly *p = &polys[i]; + MCol *cface = &cfaces[p->loopstart + p->totloop]; + MLoop *mloop = &mloops[p->loopstart + p->totloop]; + + for (int j = 0; j < p->totloop; ++j) { + cface--; + mloop--; + + col[0] = cface->a * cscale; + col[1] = cface->r * cscale; + col[2] = cface->g * cscale; + col[3] = cface->b * cscale; + + buffer[mloop->v] = col; + } + } + + OC4fGeomParam param(prop, name, true, kFacevaryingScope, 1); + + OC4fGeomParam::Sample sample( + C4fArraySample(&buffer.front(), buffer.size()), + kVertexScope); + + param.set(sample); +} + +void write_custom_data(const OCompoundProperty &prop, const CDStreamConfig &config, CustomData *data, int data_type) +{ + CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); + + if (!CustomData_has_layer(data, cd_data_type)) { + return; + } + + const int active_layer = CustomData_get_active_layer(data, cd_data_type); + const int tot_layers = CustomData_number_of_layers(data, cd_data_type); + + for (int i = 0; i < tot_layers; ++i) { + void *cd_data = CustomData_get_layer_n(data, cd_data_type, i); + const char *name = CustomData_get_layer_name(data, cd_data_type, i); + + if (cd_data_type == CD_MLOOPUV) { + /* Already exported. */ + if (i == active_layer) { + continue; + } + + write_uv(prop, config, cd_data, name); + } + else if (cd_data_type == CD_MLOOPCOL) { + write_mcol(prop, config, cd_data, name); + } + } +} + +/* ************************************************************************** */ + +using Alembic::Abc::C3fArraySamplePtr; +using Alembic::Abc::C4fArraySamplePtr; +using Alembic::Abc::PropertyHeader; + +using Alembic::AbcGeom::IC3fGeomParam; +using Alembic::AbcGeom::IC4fGeomParam; +using Alembic::AbcGeom::IV2fGeomParam; + +static void read_mcols(const CDStreamConfig &config, void *data, + const C3fArraySamplePtr &c3f_ptr, const C4fArraySamplePtr &c4f_ptr) +{ + MCol *cfaces = static_cast<MCol *>(data); + MPoly *polys = config.mpoly; + MLoop *mloops = config.mloop; + + if (c3f_ptr) { + for (int i = 0; i < config.totpoly; ++i) { + MPoly *p = &polys[i]; + MCol *cface = &cfaces[p->loopstart + p->totloop]; + MLoop *mloop = &mloops[p->loopstart + p->totloop]; + + for (int j = 0; j < p->totloop; ++j) { + cface--; + mloop--; + const Imath::C3f &color = (*c3f_ptr)[mloop->v]; + cface->a = FTOCHAR(color[0]); + cface->r = FTOCHAR(color[1]); + cface->g = FTOCHAR(color[2]); + cface->b = 255; + } + } + } + else if (c4f_ptr) { + for (int i = 0; i < config.totpoly; ++i) { + MPoly *p = &polys[i]; + MCol *cface = &cfaces[p->loopstart + p->totloop]; + MLoop *mloop = &mloops[p->loopstart + p->totloop]; + + for (int j = 0; j < p->totloop; ++j) { + cface--; + mloop--; + const Imath::C4f &color = (*c4f_ptr)[mloop->v]; + cface->a = FTOCHAR(color[0]); + cface->r = FTOCHAR(color[1]); + cface->g = FTOCHAR(color[2]); + cface->b = FTOCHAR(color[3]); + } + } + } +} + +static void read_uvs(const CDStreamConfig &config, void *data, + const Alembic::AbcGeom::V2fArraySamplePtr &uvs, + const Alembic::AbcGeom::UInt32ArraySamplePtr &indices) +{ + MPoly *mpolys = config.mpoly; + MLoopUV *mloopuvs = static_cast<MLoopUV *>(data); + + unsigned int uv_index, loop_index; + + for (int i = 0; i < config.totpoly; ++i) { + MPoly &poly = mpolys[i]; + + for (int f = 0; f < poly.totloop; ++f) { + loop_index = poly.loopstart + f; + uv_index = (*indices)[loop_index]; + const Imath::V2f &uv = (*uvs)[uv_index]; + + MLoopUV &loopuv = mloopuvs[loop_index]; + loopuv.uv[0] = uv[0]; + loopuv.uv[1] = uv[1]; + } + } +} + +static void read_custom_data_ex(const ICompoundProperty &prop, + const PropertyHeader &prop_header, + const CDStreamConfig &config, + const Alembic::Abc::ISampleSelector &iss, + int data_type) +{ + if (data_type == CD_MLOOPCOL) { + C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr(); + C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr(); + + if (IC3fGeomParam::matches(prop_header)) { + IC3fGeomParam color_param(prop, prop_header.getName()); + IC3fGeomParam::Sample sample; + color_param.getIndexed(sample, iss); + + c3f_ptr = sample.getVals(); + } + else if (IC4fGeomParam::matches(prop_header)) { + IC4fGeomParam color_param(prop, prop_header.getName()); + IC4fGeomParam::Sample sample; + color_param.getIndexed(sample, iss); + + c4f_ptr = sample.getVals(); + } + + void *cd_data = config.add_customdata_cb(config.user_data, + prop_header.getName().c_str(), + data_type); + + read_mcols(config, cd_data, c3f_ptr, c4f_ptr); + } + else if (data_type == CD_MLOOPUV) { + IV2fGeomParam uv_param(prop, prop_header.getName()); + IV2fGeomParam::Sample sample; + uv_param.getIndexed(sample, iss); + + if (uv_param.getScope() != kFacevaryingScope) { + return; + } + + void *cd_data = config.add_customdata_cb(config.user_data, + prop_header.getName().c_str(), + data_type); + + read_uvs(config, cd_data, sample.getVals(), sample.getIndices()); + } +} + +void read_custom_data(const ICompoundProperty &prop, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss) +{ + if (!prop.valid()) { + return; + } + + int num_uvs = 0; + int num_colors = 0; + + const size_t num_props = prop.getNumProperties(); + + for (size_t i = 0; i < num_props; ++i) { + const Alembic::Abc::PropertyHeader &prop_header = prop.getPropertyHeader(i); + + /* Read UVs according to convention. */ + if (IV2fGeomParam::matches(prop_header) && Alembic::AbcGeom::isUV(prop_header)) { + if (++num_uvs > MAX_MTFACE) { + continue; + } + + read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPUV); + continue; + } + + /* Read vertex colors according to convention. */ + if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) { + if (++num_colors > MAX_MCOL) { + continue; + } + + read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPCOL); + continue; + } + } +} |