/* * ***** 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 #include 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 &uvs, std::vector &uvidx, void *cd_data) { MLoopUV *mloopuv_array = static_cast(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::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 indices; std::vector 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(data); std::vector 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(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(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(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; } } }