diff options
author | Sybren A. Stüvel <sybren@blender.org> | 2021-05-18 20:01:57 +0300 |
---|---|---|
committer | Sybren A. Stüvel <sybren@blender.org> | 2021-05-18 20:01:57 +0300 |
commit | f9567f6c63e75feaf701fa7b78669b9a436f13dd (patch) | |
tree | b2a413a1a8d1b4d5ae71c998734f141947e73534 /source/blender/io/alembic/intern | |
parent | a881b5272b01fe299df5d12aded30515c432bcec (diff) |
Alembic: read/write generated coordinates of meshes
Read and write generated coordinates (also known as "original
coordinates", "reference coordinates", or "orcos") from and to Alembic.
A custom geometry property named "Pref" is used for (hopefully)
interoperability with Maya and Houdini. For now it's only guaranteed for
Blender-to-Blender.
Export: writing generated coordinates is optional (on by default).
Import: generated coordinates are always read whenever the reading of
vertex data is enabled.
Manifest Task: T88081
Diffstat (limited to 'source/blender/io/alembic/intern')
-rw-r--r-- | source/blender/io/alembic/intern/abc_customdata.cc | 72 | ||||
-rw-r--r-- | source/blender/io/alembic/intern/abc_customdata.h | 18 | ||||
-rw-r--r-- | source/blender/io/alembic/intern/abc_reader_mesh.cc | 5 |
3 files changed, 89 insertions, 6 deletions
diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc index 66e05504303..b7a43c339ff 100644 --- a/source/blender/io/alembic/intern/abc_customdata.cc +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -22,12 +22,14 @@ */ #include "abc_customdata.h" +#include "abc_axis_conversion.h" #include <Alembic/AbcGeom/All.h> #include <algorithm> #include <unordered_map> #include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "BLI_math_base.h" @@ -50,8 +52,13 @@ using Alembic::Abc::V2fArraySample; using Alembic::AbcGeom::OC4fGeomParam; using Alembic::AbcGeom::OV2fGeomParam; +using Alembic::AbcGeom::OV3fGeomParam; namespace blender::io::alembic { +/* ORCO, Generated Coordinates, and Reference Points ("Pref") are all terms for the same thing. + * Other applications (Maya, Houdini) write these to a property called "Pref". */ +static const std::string propNameOriginalCoordinates("Pref"); + static void get_uvs(const CDStreamConfig &config, std::vector<Imath::V2f> &uvs, std::vector<uint32_t> &uvidx, @@ -222,6 +229,32 @@ static void write_mcol(const OCompoundProperty &prop, param.set(sample); } +void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig &config) +{ + const void *customdata = CustomData_get_layer(&config.mesh->vdata, CD_ORCO); + if (customdata == nullptr) { + /* Data not available, so don't even bother creating an Alembic property for it. */ + return; + } + const float(*orcodata)[3] = static_cast<const float(*)[3]>(customdata); + + /* Convert 3D vertices from float[3] z=up to V3f y=up. */ + std::vector<Imath::V3f> coords(config.totvert); + float orco_yup[3]; + for (int vertex_idx = 0; vertex_idx < config.totvert; vertex_idx++) { + copy_yup_from_zup(orco_yup, orcodata[vertex_idx]); + coords[vertex_idx].setValue(orco_yup[0], orco_yup[1], orco_yup[2]); + } + + if (!config.abc_ocro.valid()) { + /* Create the Alembic property and keep a reference so future frames can reuse it. */ + config.abc_ocro = OV3fGeomParam(prop, propNameOriginalCoordinates, false, kVertexScope, 1); + } + + OV3fGeomParam::Sample sample(coords, kVertexScope); + config.abc_ocro.set(sample); +} + void write_custom_data(const OCompoundProperty &prop, CDStreamConfig &config, CustomData *data, @@ -263,6 +296,7 @@ using Alembic::Abc::PropertyHeader; using Alembic::AbcGeom::IC3fGeomParam; using Alembic::AbcGeom::IC4fGeomParam; using Alembic::AbcGeom::IV2fGeomParam; +using Alembic::AbcGeom::IV3fGeomParam; static void read_uvs(const CDStreamConfig &config, void *data, @@ -448,6 +482,44 @@ static void read_custom_data_uvs(const ICompoundProperty &prop, read_uvs(config, cd_data, sample.getVals(), sample.getIndices()); } +void read_generated_coordinates(const ICompoundProperty &prop, + const CDStreamConfig &config, + const Alembic::Abc::ISampleSelector &iss) +{ + if (prop.getPropertyHeader(propNameOriginalCoordinates) == nullptr) { + /* The ORCO property isn't there, so don't bother trying to process it. */ + return; + } + + IV3fGeomParam param(prop, propNameOriginalCoordinates); + if (!param.valid() || param.isIndexed()) { + /* Invalid or indexed coordinates aren't supported. */ + return; + } + if (param.getScope() != kVertexScope) { + /* These are original vertex coordinates, so must be vertex-scoped. */ + return; + } + + IV3fGeomParam::Sample sample = param.getExpandedValue(iss); + Alembic::AbcGeom::V3fArraySamplePtr abc_ocro = sample.getVals(); + const size_t totvert = abc_ocro.get()->size(); + + void *cd_data; + if (CustomData_has_layer(&config.mesh->vdata, CD_ORCO)) { + cd_data = CustomData_get_layer(&config.mesh->vdata, CD_ORCO); + } + else { + cd_data = CustomData_add_layer(&config.mesh->vdata, CD_ORCO, CD_CALLOC, NULL, totvert); + } + + float(*orcodata)[3] = static_cast<float(*)[3]>(cd_data); + for (int vertex_idx = 0; vertex_idx < totvert; ++vertex_idx) { + const Imath::V3f &abc_coords = (*abc_ocro)[vertex_idx]; + copy_zup_from_yup(orcodata[vertex_idx], abc_coords.getValue()); + } +} + void read_custom_data(const std::string &iobject_full_name, const ICompoundProperty &prop, const CDStreamConfig &config, diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h index 4eb515f132c..9ee964c0545 100644 --- a/source/blender/io/alembic/intern/abc_customdata.h +++ b/source/blender/io/alembic/intern/abc_customdata.h @@ -72,12 +72,16 @@ struct CDStreamConfig { const char **modifier_error_message; - /* Alembic needs Blender to keep references to C++ objects (the destructors - * finalize the writing to ABC). This map stores OV2fGeomParam objects for the - * 2nd and subsequent UV maps; the primary UV map is kept alive by the Alembic - * mesh sample itself. */ + /* Alembic needs Blender to keep references to C++ objects (the destructors finalize the writing + * to ABC). The following fields are all used to keep these references. */ + + /* Mapping from UV map name to its ABC property, for the 2nd and subsequent UV maps; the primary + * UV map is kept alive by the Alembic mesh sample itself. */ std::map<std::string, Alembic::AbcGeom::OV2fGeomParam> abc_uv_maps; + /* OCRO coordinates, aka Generated Coordinates. */ + Alembic::AbcGeom::OV3fGeomParam abc_ocro; + CDStreamConfig() : mloop(NULL), totloop(0), @@ -102,6 +106,12 @@ struct CDStreamConfig { * For now the active layer is used, maybe needs a better way to choose this. */ const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data); +void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig &config); + +void read_generated_coordinates(const ICompoundProperty &prop, + const CDStreamConfig &config, + const Alembic::Abc::ISampleSelector &iss); + void write_custom_data(const OCompoundProperty &prop, CDStreamConfig &config, CustomData *data, diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 8133f615080..11b6c1c18ca 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -439,6 +439,7 @@ static void read_mesh_sample(const std::string &iobject_full_name, if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) { read_mverts(config, abc_mesh_data); + read_generated_coordinates(schema.getArbGeomParams(), config, selector); } if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) { @@ -558,7 +559,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec /* XXX fixme after 2.80; mesh->flag isn't copied by BKE_mesh_nomain_to_mesh() */ /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); - BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true); mesh->flag |= autosmooth; } @@ -868,7 +869,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, nullptr); if (read_mesh != mesh) { - BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true); + BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true); } ISubDSchema::Sample sample; |