mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Alembic: remember default and active color attributes
Prior to version 3.5, when importing an Alembic file the default and active color attributes were set to the first color layer. Since then, as the default and active color attributes are stored as strings on the Mesh object, importing an Alembic file leaves those uninitialized. Note that this only worked prior to 3.5 as the indices for the active and default layers were initialized by default to zero. Remembering those default and active attributes is necessary after an import for e.g. being able to visualize the colors using viewport shading overlays. As Alembic does not have a concept of default or active color attribute, this writes the name of those attributes in the mesh user properties during export. During import, we lookup those properties and use them to set the default and active color layers. If the properties do not exist, we default to the first color layer. This adds a test for the new export and import of the default and active names, and updates old tests to check that the fallback to the first layer works. Pull Request: https://projects.blender.org/blender/blender/pulls/164075
This commit is contained in:
parent
c6447e69c6
commit
cc44ebbd24
7 changed files with 138 additions and 2 deletions
|
|
@ -51,6 +51,7 @@ using Alembic::AbcGeom::OInt32Property;
|
|||
using Alembic::AbcGeom::ON3fGeomParam;
|
||||
using Alembic::AbcGeom::OPolyMesh;
|
||||
using Alembic::AbcGeom::OPolyMeshSchema;
|
||||
using Alembic::AbcGeom::OStringProperty;
|
||||
using Alembic::AbcGeom::OSubD;
|
||||
using Alembic::AbcGeom::OSubDSchema;
|
||||
using Alembic::AbcGeom::OV2fGeomParam;
|
||||
|
|
@ -137,10 +138,23 @@ void ABCGenericMeshWriter::create_alembic_objects(const HierarchyContext *contex
|
|||
abc_poly_mesh_ = OPolyMesh(args_.abc_parent, args_.abc_name, timesample_index_);
|
||||
abc_poly_mesh_schema_ = abc_poly_mesh_.getSchema();
|
||||
|
||||
OCompoundProperty typeContainer = abc_poly_mesh_.getSchema().getUserProperties();
|
||||
OBoolProperty type(typeContainer, "meshtype");
|
||||
abc_custom_data_container_ = abc_poly_mesh_.getSchema().getUserProperties();
|
||||
OBoolProperty type(abc_custom_data_container_, "meshtype");
|
||||
type.set(subsurf_modifier_ == nullptr);
|
||||
}
|
||||
|
||||
if (context->object->data->id_type() == ID_ME) {
|
||||
Mesh *mesh = id_cast<Mesh *>(context->object->data);
|
||||
if (mesh->active_color_attribute && mesh->default_color_attribute) {
|
||||
OStringProperty active_color_attribute(abc_custom_data_container_,
|
||||
ABC_ACTIVE_COLOR_ATTRIBUTE_PROPNAME);
|
||||
active_color_attribute.set(mesh->active_color_attribute);
|
||||
|
||||
OStringProperty default_color_attribute(abc_custom_data_container_,
|
||||
ABC_DEFAULT_COLOR_ATTRIBUTE_PROPNAME);
|
||||
default_color_attribute.set(mesh->default_color_attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Alembic::Abc::OObject ABCGenericMeshWriter::get_alembic_object() const
|
||||
|
|
|
|||
|
|
@ -559,5 +559,70 @@ AbcUvScope get_uv_scope(const Alembic::AbcGeom::GeometryScope scope,
|
|||
return ABC_UV_SCOPE_NONE;
|
||||
}
|
||||
|
||||
static std::string get_string_property(const ICompoundProperty &prop,
|
||||
const StringRefNull name,
|
||||
const Alembic::AbcGeom::ISampleSelector &sample_sel)
|
||||
{
|
||||
if (!prop.valid()) {
|
||||
return "";
|
||||
}
|
||||
const PropertyHeader *header = prop.getPropertyHeader(name);
|
||||
if (header && header->isScalar() && Alembic::AbcGeom::IStringProperty::matches(*header)) {
|
||||
Alembic::AbcGeom::IStringProperty active_color_attribute(prop, header->getName());
|
||||
return active_color_attribute.getValue(sample_sel);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void read_active_and_default_color_attributes(Mesh &mesh,
|
||||
const ICompoundProperty &user_props,
|
||||
const Alembic::AbcGeom::ISampleSelector &sample_sel)
|
||||
{
|
||||
std::string active_color_attribute = get_string_property(
|
||||
user_props, ABC_ACTIVE_COLOR_ATTRIBUTE_PROPNAME, sample_sel);
|
||||
std::string default_color_attribute = get_string_property(
|
||||
user_props, ABC_DEFAULT_COLOR_ATTRIBUTE_PROPNAME, sample_sel);
|
||||
|
||||
StringRefNull first_color_attribute;
|
||||
StringRefNull final_active_color_attribute;
|
||||
StringRefNull final_default_color_attribute;
|
||||
|
||||
mesh.attributes().foreach_attribute([&](const bke::AttributeIter &iter) {
|
||||
if (iter.domain != bke::AttrDomain::Corner && iter.domain != bke::AttrDomain::Point) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (iter.data_type != bke::AttrType::ColorByte && iter.data_type != bke::AttrType::ColorFloat)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (first_color_attribute.is_empty()) {
|
||||
first_color_attribute = iter.name;
|
||||
}
|
||||
|
||||
if (iter.name == active_color_attribute) {
|
||||
final_active_color_attribute = active_color_attribute;
|
||||
}
|
||||
if (iter.name == default_color_attribute) {
|
||||
final_default_color_attribute = default_color_attribute;
|
||||
}
|
||||
});
|
||||
|
||||
if (final_active_color_attribute.is_empty()) {
|
||||
final_active_color_attribute = first_color_attribute;
|
||||
}
|
||||
if (final_default_color_attribute.is_empty()) {
|
||||
final_default_color_attribute = first_color_attribute;
|
||||
}
|
||||
|
||||
if (!final_default_color_attribute.is_empty()) {
|
||||
BKE_id_attributes_default_color_set(&mesh.id, final_default_color_attribute);
|
||||
}
|
||||
if (!final_active_color_attribute.is_empty()) {
|
||||
BKE_id_attributes_active_color_set(&mesh.id, final_active_color_attribute);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace io::alembic
|
||||
} // namespace blender
|
||||
|
|
|
|||
|
|
@ -123,5 +123,14 @@ AbcUvScope get_uv_scope(const Alembic::AbcGeom::GeometryScope scope,
|
|||
const CDStreamConfig &config,
|
||||
const Alembic::AbcGeom::UInt32ArraySamplePtr &indices);
|
||||
|
||||
/**
|
||||
* Sets the active and default color attribute names on the mesh. Defaults to the first color
|
||||
* attribute layer if the #user_props do not have the Blender-specific properties which are set
|
||||
* by the exporter.
|
||||
*/
|
||||
void read_active_and_default_color_attributes(Mesh &mesh,
|
||||
const ICompoundProperty &user_props,
|
||||
const Alembic::AbcGeom::ISampleSelector &sample_sel);
|
||||
|
||||
} // namespace io::alembic
|
||||
} // namespace blender
|
||||
|
|
|
|||
|
|
@ -811,6 +811,9 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
|
|||
if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) {
|
||||
addCacheModifier();
|
||||
}
|
||||
|
||||
ICompoundProperty user_props = m_schema.getUserProperties();
|
||||
read_active_and_default_color_attributes(*mesh, user_props, sample_sel);
|
||||
}
|
||||
|
||||
bool AbcMeshReader::accepts_object_type(
|
||||
|
|
@ -1153,6 +1156,9 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
|
|||
}
|
||||
|
||||
add_subdiv_modifier();
|
||||
|
||||
ICompoundProperty user_props = m_schema.getUserProperties();
|
||||
read_active_and_default_color_attributes(*mesh, user_props, sample_sel);
|
||||
}
|
||||
|
||||
bool AbcSubDReader::topology_changed(const Mesh *existing_mesh, const ISampleSelector &sample_sel)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@ class AbcObjectReader;
|
|||
struct AbcReaderConstructorArgs;
|
||||
|
||||
static inline const std::string ABC_CURVE_RESOLUTION_U_PROPNAME = "blender:resolution";
|
||||
static inline const std::string ABC_ACTIVE_COLOR_ATTRIBUTE_PROPNAME =
|
||||
"blender:active_color_attribute";
|
||||
static inline const std::string ABC_DEFAULT_COLOR_ATTRIBUTE_PROPNAME =
|
||||
"blender:default_color_attribute";
|
||||
|
||||
std::string get_valid_abc_name(const char *name);
|
||||
|
||||
|
|
|
|||
3
tests/files/alembic/vertex-colours-default-active.blend
Normal file
3
tests/files/alembic/vertex-colours-default-active.blend
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7e8c4f4e4ca4559330562bc6283cb14e4b1985935cb516b40d04545e37208217
|
||||
size 98444
|
||||
|
|
@ -220,6 +220,10 @@ class VertexColourImportTest(AbstractAlembicTest):
|
|||
self.assertAlmostEqualFloatArray(layer.data[98].color, (0.9019607, 0.4745098, 0.2666666, 1.0))
|
||||
self.assertAlmostEqualFloatArray(layer.data[99].color, (0.8941176, 0.4705882, 0.2627451, 1.0))
|
||||
|
||||
mesh = ob.data
|
||||
self.assertEqual(mesh.color_attributes.active_color_name, 'Cf')
|
||||
self.assertEqual(mesh.color_attributes.default_color_name, 'Cf')
|
||||
|
||||
def test_import_from_blender(self):
|
||||
# Blender saved per-vertex, and as RGBA.
|
||||
res = bpy.ops.wm.alembic_import(
|
||||
|
|
@ -235,6 +239,37 @@ class VertexColourImportTest(AbstractAlembicTest):
|
|||
self.assertAlmostEqualFloatArray(layer.data[98].color, (0.0941176, 0.1215686, 0.9137254, 1.0))
|
||||
self.assertAlmostEqualFloatArray(layer.data[99].color, (0.1294117, 0.3529411, 0.7529411, 1.0))
|
||||
|
||||
mesh = ob.data
|
||||
self.assertEqual(mesh.color_attributes.active_color_name, 'Cf')
|
||||
self.assertEqual(mesh.color_attributes.default_color_name, 'Cf')
|
||||
|
||||
|
||||
class VertexColourImportExportTest(AbstractAlembicTest):
|
||||
def setUp(self):
|
||||
self._tempdir = tempfile.TemporaryDirectory()
|
||||
self.tempdir = pathlib.Path(self._tempdir.name)
|
||||
|
||||
def test_export_import_default_active(self):
|
||||
"""Test export and import of the default and active color attribute names"""
|
||||
|
||||
abc_path = str(self.tempdir / "vertex-colours-default-active.abc")
|
||||
|
||||
# Export
|
||||
bpy.ops.wm.open_mainfile(filepath=str(args.testdir / "vertex-colours-default-active.blend"))
|
||||
self.assertIn('FINISHED', bpy.ops.wm.alembic_export(
|
||||
filepath=abc_path,
|
||||
vcolors=True
|
||||
))
|
||||
|
||||
# Import back
|
||||
res = bpy.ops.wm.alembic_import(filepath=abc_path, as_background_job=False)
|
||||
self.assertEqual({'FINISHED'}, res)
|
||||
|
||||
ob = bpy.context.active_object
|
||||
mesh = ob.data
|
||||
self.assertEqual(mesh.color_attributes.active_color_name, 'Active')
|
||||
self.assertEqual(mesh.color_attributes.default_color_name, 'Default')
|
||||
|
||||
|
||||
class CameraExportImportTest(unittest.TestCase):
|
||||
names = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue