Alembic procedural: add support for instancing

Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.

In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.

Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.

As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
This commit is contained in:
Kévin Dietrich 2021-03-12 01:30:12 +01:00
parent 7017844c5e
commit d72fc36ec5
2 changed files with 89 additions and 15 deletions

View File

@ -719,6 +719,11 @@ void AlembicObject::load_all_data(AlembicProcedural *proc,
{
cached_data.clear();
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
const TimeSamplingPtr time_sampling = schema.getTimeSampling();
cached_data.set_time_sampling(*time_sampling);
@ -784,6 +789,11 @@ void AlembicObject::load_all_data(AlembicProcedural *proc, ISubDSchema &schema,
{
cached_data.clear();
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
AttributeRequestSet requested_attributes = get_requested_attributes();
const TimeSamplingPtr time_sampling = schema.getTimeSampling();
@ -918,6 +928,11 @@ void AlembicObject::load_all_data(AlembicProcedural *proc,
{
cached_data.clear();
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
const TimeSamplingPtr time_sampling = schema.getTimeSampling();
cached_data.set_time_sampling(*time_sampling);
@ -1480,22 +1495,24 @@ void AlembicProcedural::load_objects(Progress &progress)
Geometry *geometry = nullptr;
if (abc_object->schema_type == AlembicObject::CURVES) {
geometry = scene_->create_node<Hair>();
}
else if (abc_object->schema_type == AlembicObject::POLY_MESH ||
abc_object->schema_type == AlembicObject::SUBD) {
geometry = scene_->create_node<Mesh>();
}
else {
continue;
}
if (!abc_object->instance_of) {
if (abc_object->schema_type == AlembicObject::CURVES) {
geometry = scene_->create_node<Hair>();
}
else if (abc_object->schema_type == AlembicObject::POLY_MESH ||
abc_object->schema_type == AlembicObject::SUBD) {
geometry = scene_->create_node<Mesh>();
}
else {
continue;
}
geometry->set_owner(this);
geometry->name = abc_object->iobject.getName();
geometry->set_owner(this);
geometry->name = abc_object->iobject.getName();
array<Node *> used_shaders = abc_object->get_used_shaders();
geometry->set_used_shaders(used_shaders);
array<Node *> used_shaders = abc_object->get_used_shaders();
geometry->set_used_shaders(used_shaders);
}
Object *object = scene_->create_node<Object>();
object->set_owner(this);
@ -1504,6 +1521,17 @@ void AlembicProcedural::load_objects(Progress &progress)
abc_object->set_object(object);
}
/* Share geometries between instances. */
foreach (Node *node, objects) {
AlembicObject *abc_object = static_cast<AlembicObject *>(node);
if (abc_object->instance_of) {
abc_object->get_object()->set_geometry(
abc_object->instance_of->get_object()->get_geometry());
abc_object->schema_type = abc_object->instance_of->schema_type;
}
}
}
void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame_time)
@ -1519,6 +1547,11 @@ void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame
object->tag_update(scene_);
}
/* Only update sockets for the original Geometry. */
if (abc_object->instance_of) {
return;
}
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket());
@ -1581,6 +1614,11 @@ void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame
object->tag_update(scene_);
}
/* Only update sockets for the original Geometry. */
if (abc_object->instance_of) {
return;
}
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
/* Cycles overwrites the original triangles when computing displacement, so we always have to
@ -1666,6 +1704,11 @@ void AlembicProcedural::read_curves(AlembicObject *abc_object, Abc::chrono_t fra
object->tag_update(scene_);
}
/* Only update sockets for the original Geometry. */
if (abc_object->instance_of) {
return;
}
Hair *hair = static_cast<Hair *>(object->get_geometry());
cached_data.curve_keys.copy_to_socket(frame_time, hair, hair->get_curve_keys_socket());
@ -1806,9 +1849,38 @@ void AlembicProcedural::walk_hierarchy(
else if (IFaceSet::matches(header)) {
// ignore the face set, it will be read along with the data
}
else if (IPoints::matches(header)) {
// unsupported for now
}
else if (INuPatch::matches(header)) {
// unsupported for now
}
else {
// unsupported type for now (Points, NuPatch)
next_object = parent.getChild(header.getName());
if (next_object.isInstanceRoot()) {
unordered_map<std::string, AlembicObject *>::const_iterator iter;
/* Was this object asked to be rendered? */
iter = object_map.find(next_object.getFullName());
if (iter != object_map.end()) {
AlembicObject *abc_object = iter->second;
/* Only try to render an instance if the original object is also rendered. */
iter = object_map.find(next_object.instanceSourcePath());
if (iter != object_map.end()) {
abc_object->iobject = next_object;
abc_object->instance_of = iter->second;
if (matrix_samples_data.samples) {
abc_object->xform_samples = *matrix_samples_data.samples;
abc_object->xform_time_sampling = matrix_samples_data.time_sampling;
}
}
}
}
}
if (next_object.valid()) {

View File

@ -276,6 +276,8 @@ class AlembicObject : public Node {
bool need_shader_update = true;
AlembicObject *instance_of = nullptr;
Alembic::AbcCoreAbstract::TimeSamplingPtr xform_time_sampling;
MatrixSampleMap xform_samples;
Alembic::AbcGeom::IObject iobject;