Geometry Nodes: support for geometry instancing

Previously, the Point Instance node in geometry nodes could only instance
existing objects or collections. The reason was that large parts of Blender
worked under the assumption that objects are the main unit of instancing.
Now we also want to instance geometry within an object, so a slightly larger
refactor was necessary.

This should not affect files that do not use the new kind of instances.

The main change is a redefinition of what "instanced data" is. Now, an
instances is a cow-object + object-data (the geometry). This can be nicely
seen in `struct DupliObject`. This allows the same object to generate
multiple geometries of different types which can be instanced individually.

A nice side effect of this refactor is that having multiple geometry components
is not a special case in the depsgraph object iterator anymore, because those
components are integrated with the `DupliObject` system.

Unfortunately, different systems that work with instances in Blender (e.g.
render engines and exporters) often work under the assumption that objects are
the main unit of instancing. So those have to be updated as well to be able to
handle the new instances. This patch updates Cycles, EEVEE and other viewport
engines. Exporters have not been updated yet. Some minimal (not master-ready)
changes to update the obj and alembic exporters can be found in P2336 and P2335.
Different file formats may want to handle these new instances in different ways.

For users, the only thing that changed is that the Point Instance node now
has a geometry mode.

This also fixes T88454.

Differential Revision: https://developer.blender.org/D11841
This commit is contained in:
Jacques Lucke 2021-09-06 18:22:24 +02:00
parent d9ad77fa58
commit 5a9a16334c
Notes: blender-bot 2023-02-14 10:48:33 +01:00
Referenced by commit 217e0a2ce6, Fix T95262: instances ignored in Frame Selected operator
Referenced by commit eaa35b27e0, Fix T91311: incorrect batch generation for instances
Referenced by commit 2b4afcbb4c, Fix T91236: AssetBrowser crash with certain collections
Referenced by issue #91311, Unable to open spring production file (020_02A.blend)
Referenced by issue #91236, Asset Browser: crash when linking/appending an asset made from a collection containing certain types of objects like a camera, a light...
Referenced by issue #90566, Mesh to Curve node  setup crashes Cycles then Blender (EXCEPTION_ACCESS_VIOLATION)
Referenced by issue #89657, Support geometry in Point Instance node
Referenced by issue #88454, Blender Collapses with Geometry Nodes Applied To Second Object
30 changed files with 501 additions and 314 deletions

View File

@ -526,8 +526,13 @@ bool BlenderSync::object_has_particle_hair(BL::Object b_ob)
/* Old particle hair. */
void BlenderSync::sync_particle_hair(
Hair *hair, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step)
Hair *hair, BL::Mesh &b_mesh, BObjectInfo &b_ob_info, bool motion, int motion_step)
{
if (!b_ob_info.is_real_object_data()) {
return;
}
BL::Object b_ob = b_ob_info.real_object;
/* obtain general settings */
if (b_ob.mode() == b_ob.mode_PARTICLE_EDIT || b_ob.mode() == b_ob.mode_EDIT) {
return;
@ -788,10 +793,10 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
}
/* Hair object. */
void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step)
void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int motion_step)
{
/* Convert Blender hair to Cycles curves. */
BL::Hair b_hair(b_ob.data());
BL::Hair b_hair(b_ob_info.object_data);
if (motion) {
export_hair_curves_motion(hair, b_hair, motion_step);
}
@ -800,16 +805,16 @@ void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motio
}
}
#else
void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step)
void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int motion_step)
{
(void)hair;
(void)b_ob;
(void)b_ob_info;
(void)motion;
(void)motion_step;
}
#endif
void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *hair)
void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Hair *hair)
{
/* make a copy of the shaders as the caller in the main thread still need them for syncing the
* attributes */
@ -819,19 +824,19 @@ void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *ha
new_hair.set_used_shaders(used_shaders);
if (view_layer.use_hair) {
if (b_ob.type() == BL::Object::type_HAIR) {
if (b_ob_info.object_data.is_a(&RNA_Hair)) {
/* Hair object. */
sync_hair(&new_hair, b_ob, false);
sync_hair(&new_hair, b_ob_info, false);
}
else {
/* Particle hair. */
bool need_undeformed = new_hair.need_attribute(scene, ATTR_STD_GENERATED);
BL::Mesh b_mesh = object_to_mesh(
b_data, b_ob, b_depsgraph, need_undeformed, Mesh::SUBDIVISION_NONE);
b_data, b_ob_info, b_depsgraph, need_undeformed, Mesh::SUBDIVISION_NONE);
if (b_mesh) {
sync_particle_hair(&new_hair, b_mesh, b_ob, false);
free_object_to_mesh(b_data, b_ob, b_mesh);
sync_particle_hair(&new_hair, b_mesh, b_ob_info, false);
free_object_to_mesh(b_data, b_ob_info, b_mesh);
}
}
}
@ -859,7 +864,7 @@ void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *ha
}
void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph,
BL::Object b_ob,
BObjectInfo &b_ob_info,
Hair *hair,
int motion_step)
{
@ -869,18 +874,19 @@ void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph,
}
/* Export deformed coordinates. */
if (ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) {
if (b_ob.type() == BL::Object::type_HAIR) {
if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) {
if (b_ob_info.object_data.is_a(&RNA_Hair)) {
/* Hair object. */
sync_hair(hair, b_ob, true, motion_step);
sync_hair(hair, b_ob_info, true, motion_step);
return;
}
else {
/* Particle hair. */
BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_depsgraph, false, Mesh::SUBDIVISION_NONE);
BL::Mesh b_mesh = object_to_mesh(
b_data, b_ob_info, b_depsgraph, false, Mesh::SUBDIVISION_NONE);
if (b_mesh) {
sync_particle_hair(hair, b_mesh, b_ob, true, motion_step);
free_object_to_mesh(b_data, b_ob, b_mesh);
sync_particle_hair(hair, b_mesh, b_ob_info, true, motion_step);
free_object_to_mesh(b_data, b_ob_info, b_mesh);
return;
}
}

View File

@ -29,13 +29,15 @@
CCL_NAMESPACE_BEGIN
static Geometry::Type determine_geom_type(BL::Object &b_ob, bool use_particle_hair)
static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_particle_hair)
{
if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) {
if (b_ob_info.object_data.is_a(&RNA_Hair) || use_particle_hair) {
return Geometry::HAIR;
}
if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) {
if (b_ob_info.object_data.is_a(&RNA_Volume) ||
(b_ob_info.object_data == b_ob_info.real_object.data() &&
object_fluid_gas_domain_find(b_ob_info.real_object))) {
return Geometry::VOLUME;
}
@ -71,20 +73,17 @@ array<Node *> BlenderSync::find_used_shaders(BL::Object &b_ob)
}
Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
BL::Object &b_ob,
BL::Object &b_ob_instance,
BObjectInfo &b_ob_info,
bool object_updated,
bool use_particle_hair,
TaskPool *task_pool)
{
/* Test if we can instance or if the object is modified. */
BL::ID b_ob_data = b_ob.data();
BL::ID b_key_id = (BKE_object_is_modified(b_ob)) ? b_ob_instance : b_ob_data;
Geometry::Type geom_type = determine_geom_type(b_ob, use_particle_hair);
GeometryKey key(b_key_id.ptr.data, geom_type);
Geometry::Type geom_type = determine_geom_type(b_ob_info, use_particle_hair);
GeometryKey key(b_ob_info.object_data, geom_type);
/* Find shader indices. */
array<Node *> used_shaders = find_used_shaders(b_ob);
array<Node *> used_shaders = find_used_shaders(b_ob_info.iter_object);
/* Ensure we only sync instanced geometry once. */
Geometry *geom = geometry_map.find(key);
@ -111,7 +110,7 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
}
else {
/* Test if we need to update existing geometry. */
sync = geometry_map.update(geom, b_key_id);
sync = geometry_map.update(geom, b_ob_info.object_data);
}
if (!sync) {
@ -144,7 +143,7 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
geometry_synced.insert(geom);
geom->name = ustring(b_ob_data.name().c_str());
geom->name = ustring(b_ob_info.object_data.name().c_str());
/* Store the shaders immediately for the object attribute code. */
geom->set_used_shaders(used_shaders);
@ -153,19 +152,19 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
if (progress.get_cancel())
return;
progress.set_sync_status("Synchronizing object", b_ob.name());
progress.set_sync_status("Synchronizing object", b_ob_info.real_object.name());
if (geom_type == Geometry::HAIR) {
Hair *hair = static_cast<Hair *>(geom);
sync_hair(b_depsgraph, b_ob, hair);
sync_hair(b_depsgraph, b_ob_info, hair);
}
else if (geom_type == Geometry::VOLUME) {
Volume *volume = static_cast<Volume *>(geom);
sync_volume(b_ob, volume);
sync_volume(b_ob_info, volume);
}
else {
Mesh *mesh = static_cast<Mesh *>(geom);
sync_mesh(b_depsgraph, b_ob, mesh);
sync_mesh(b_depsgraph, b_ob_info, mesh);
}
};
@ -181,7 +180,7 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
}
void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph,
BL::Object &b_ob,
BObjectInfo &b_ob_info,
Object *object,
float motion_time,
bool use_particle_hair,
@ -210,16 +209,17 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph,
if (progress.get_cancel())
return;
if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) {
if (b_ob_info.object_data.is_a(&RNA_Hair) || use_particle_hair) {
Hair *hair = static_cast<Hair *>(geom);
sync_hair_motion(b_depsgraph, b_ob, hair, motion_step);
sync_hair_motion(b_depsgraph, b_ob_info, hair, motion_step);
}
else if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) {
else if (b_ob_info.object_data.is_a(&RNA_Volume) ||
object_fluid_gas_domain_find(b_ob_info.real_object)) {
/* No volume motion blur support yet. */
}
else {
Mesh *mesh = static_cast<Mesh *>(geom);
sync_mesh_motion(b_depsgraph, b_ob, mesh, motion_step);
sync_mesh_motion(b_depsgraph, b_ob_info, mesh, motion_step);
}
};

View File

@ -27,15 +27,14 @@ CCL_NAMESPACE_BEGIN
void BlenderSync::sync_light(BL::Object &b_parent,
int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
BL::Object &b_ob,
BL::Object &b_ob_instance,
BObjectInfo &b_ob_info,
int random_id,
Transform &tfm,
bool *use_portal)
{
/* test if we need to sync */
ObjectKey key(b_parent, persistent_id, b_ob_instance, false);
BL::Light b_light(b_ob.data());
ObjectKey key(b_parent, persistent_id, b_ob_info.real_object, false);
BL::Light b_light(b_ob_info.object_data);
Light *light = light_map.find(key);
@ -44,7 +43,7 @@ void BlenderSync::sync_light(BL::Object &b_parent,
const bool tfm_updated = (light && light->get_tfm() != tfm);
/* Update if either object or light data changed. */
if (!light_map.add_or_update(&light, b_ob, b_parent, key) && !tfm_updated) {
if (!light_map.add_or_update(&light, b_ob_info.real_object, b_parent, key) && !tfm_updated) {
Shader *shader;
if (!shader_map.add_or_update(&shader, b_light)) {
if (light->get_is_portal())
@ -139,11 +138,11 @@ void BlenderSync::sync_light(BL::Object &b_parent,
light->set_max_bounces(get_int(clight, "max_bounces"));
if (b_ob != b_ob_instance) {
if (b_ob_info.real_object != b_ob_info.iter_object) {
light->set_random_id(random_id);
}
else {
light->set_random_id(hash_uint2(hash_string(b_ob.name().c_str()), 0));
light->set_random_id(hash_uint2(hash_string(b_ob_info.real_object.name().c_str()), 0));
}
if (light->get_light_type() == LIGHT_AREA)
@ -155,7 +154,7 @@ void BlenderSync::sync_light(BL::Object &b_parent,
*use_portal = true;
/* visibility */
uint visibility = object_ray_visibility(b_ob);
uint visibility = object_ray_visibility(b_ob_info.real_object);
light->set_use_diffuse((visibility & PATH_RAY_DIFFUSE) != 0);
light->set_use_glossy((visibility & PATH_RAY_GLOSSY) != 0);
light->set_use_transmission((visibility & PATH_RAY_TRANSMIT) != 0);

View File

@ -999,12 +999,14 @@ static void create_mesh(Scene *scene,
static void create_subd_mesh(Scene *scene,
Mesh *mesh,
BL::Object &b_ob,
BObjectInfo &b_ob_info,
BL::Mesh &b_mesh,
const array<Node *> &used_shaders,
float dicing_rate,
int max_subdivisions)
{
BL::Object b_ob = b_ob_info.real_object;
BL::SubsurfModifier subsurf_mod(b_ob.modifiers[b_ob.modifiers.length() - 1]);
bool subdivide_uvs = subsurf_mod.uv_smooth() != BL::SubsurfModifier::uv_smooth_NONE;
@ -1043,7 +1045,7 @@ static void create_subd_mesh(Scene *scene,
*
* NOTE: This code is run prior to object motion blur initialization. so can not access properties
* set by `sync_object_motion_init()`. */
static bool mesh_need_motion_attribute(BL::Object &b_ob, Scene *scene)
static bool mesh_need_motion_attribute(BObjectInfo &b_ob_info, Scene *scene)
{
const Scene::MotionType need_motion = scene->need_motion();
if (need_motion == Scene::MOTION_NONE) {
@ -1060,7 +1062,7 @@ static bool mesh_need_motion_attribute(BL::Object &b_ob, Scene *scene)
* - Motion attribute expects non-zero time steps.
*
* Avoid adding motion attributes if the motion blur will enforce 0 motion steps. */
PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
PointerRNA cobject = RNA_pointer_get(&b_ob_info.real_object.ptr, "cycles");
const bool use_motion = get_boolean(cobject, "use_motion_blur");
if (!use_motion) {
return false;
@ -1072,12 +1074,13 @@ static bool mesh_need_motion_attribute(BL::Object &b_ob, Scene *scene)
return true;
}
static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *mesh)
static void sync_mesh_cached_velocities(BObjectInfo &b_ob_info, Scene *scene, Mesh *mesh)
{
if (!mesh_need_motion_attribute(b_ob, scene)) {
if (!mesh_need_motion_attribute(b_ob_info, scene)) {
return;
}
BL::Object b_ob = b_ob_info.real_object;
BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob, true, nullptr);
if (!b_mesh_cache) {
@ -1118,12 +1121,16 @@ static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *me
}
}
static void sync_mesh_fluid_motion(BL::Object &b_ob, Scene *scene, Mesh *mesh)
static void sync_mesh_fluid_motion(BObjectInfo &b_ob_info, Scene *scene, Mesh *mesh)
{
if (!mesh_need_motion_attribute(b_ob, scene)) {
if (!b_ob_info.is_real_object_data()) {
return;
}
if (!mesh_need_motion_attribute(b_ob_info, scene)) {
return;
}
BL::Object b_ob = b_ob_info.real_object;
BL::FluidDomainSettings b_fluid_domain = object_fluid_liquid_domain_find(b_ob);
if (!b_fluid_domain)
@ -1157,7 +1164,7 @@ static void sync_mesh_fluid_motion(BL::Object &b_ob, Scene *scene, Mesh *mesh)
}
}
void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *mesh)
void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Mesh *mesh)
{
/* make a copy of the shaders as the caller in the main thread still need them for syncing the
* attributes */
@ -1170,20 +1177,21 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *me
/* Adaptive subdivision setup. Not for baking since that requires
* exact mapping to the Blender mesh. */
if (!scene->bake_manager->get_baking()) {
new_mesh.set_subdivision_type(object_subdivision_type(b_ob, preview, experimental));
new_mesh.set_subdivision_type(
object_subdivision_type(b_ob_info.real_object, preview, experimental));
}
/* For some reason, meshes do not need this... */
bool need_undeformed = new_mesh.need_attribute(scene, ATTR_STD_GENERATED);
BL::Mesh b_mesh = object_to_mesh(
b_data, b_ob, b_depsgraph, need_undeformed, new_mesh.get_subdivision_type());
b_data, b_ob_info, b_depsgraph, need_undeformed, new_mesh.get_subdivision_type());
if (b_mesh) {
/* Sync mesh itself. */
if (new_mesh.get_subdivision_type() != Mesh::SUBDIVISION_NONE)
create_subd_mesh(scene,
&new_mesh,
b_ob,
b_ob_info,
b_mesh,
new_mesh.get_used_shaders(),
dicing_rate,
@ -1191,15 +1199,15 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *me
else
create_mesh(scene, &new_mesh, b_mesh, new_mesh.get_used_shaders(), false);
free_object_to_mesh(b_data, b_ob, b_mesh);
free_object_to_mesh(b_data, b_ob_info, b_mesh);
}
}
/* cached velocities (e.g. from alembic archive) */
sync_mesh_cached_velocities(b_ob, scene, &new_mesh);
sync_mesh_cached_velocities(b_ob_info, scene, &new_mesh);
/* mesh fluid motion mantaflow */
sync_mesh_fluid_motion(b_ob, scene, &new_mesh);
sync_mesh_fluid_motion(b_ob_info, scene, &new_mesh);
/* update original sockets */
@ -1230,18 +1238,19 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *me
}
void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph,
BL::Object b_ob,
BObjectInfo &b_ob_info,
Mesh *mesh,
int motion_step)
{
/* Fluid motion blur already exported. */
BL::FluidDomainSettings b_fluid_domain = object_fluid_liquid_domain_find(b_ob);
BL::FluidDomainSettings b_fluid_domain = object_fluid_liquid_domain_find(b_ob_info.real_object);
if (b_fluid_domain) {
return;
}
/* Cached motion blur already exported. */
BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob, true, nullptr);
BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(
b_ob_info.real_object, true, nullptr);
if (mesh_cache) {
return;
}
@ -1255,11 +1264,13 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph,
/* Skip objects without deforming modifiers. this is not totally reliable,
* would need a more extensive check to see which objects are animated. */
BL::Mesh b_mesh(PointerRNA_NULL);
if (ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) {
if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) {
/* get derived mesh */
b_mesh = object_to_mesh(b_data, b_ob, b_depsgraph, false, Mesh::SUBDIVISION_NONE);
b_mesh = object_to_mesh(b_data, b_ob_info, b_depsgraph, false, Mesh::SUBDIVISION_NONE);
}
const std::string ob_name = b_ob_info.real_object.name();
/* TODO(sergey): Perform preliminary check for number of vertices. */
if (b_mesh) {
/* Export deformed coordinates. */
@ -1295,17 +1306,17 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph,
memcmp(mP, &mesh->get_verts()[0], sizeof(float3) * numverts) == 0) {
/* no motion, remove attributes again */
if (b_mesh.vertices.length() != numverts) {
VLOG(1) << "Topology differs, disabling motion blur for object " << b_ob.name();
VLOG(1) << "Topology differs, disabling motion blur for object " << ob_name;
}
else {
VLOG(1) << "No actual deformation motion for object " << b_ob.name();
VLOG(1) << "No actual deformation motion for object " << ob_name;
}
mesh->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr_mN)
mesh->attributes.remove(ATTR_STD_MOTION_VERTEX_NORMAL);
}
else if (motion_step > 0) {
VLOG(1) << "Filling deformation motion for object " << b_ob.name();
VLOG(1) << "Filling deformation motion for object " << ob_name;
/* motion, fill up previous steps that we might have skipped because
* they had no motion, but we need them anyway now */
float3 *P = &mesh->get_verts()[0];
@ -1319,8 +1330,8 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph,
}
else {
if (b_mesh.vertices.length() != numverts) {
VLOG(1) << "Topology differs, discarding motion blur for object " << b_ob.name()
<< " at time " << motion_step;
VLOG(1) << "Topology differs, discarding motion blur for object " << ob_name << " at time "
<< motion_step;
memcpy(mP, &mesh->get_verts()[0], sizeof(float3) * numverts);
if (mN != NULL) {
memcpy(mN, attr_N->data_float3(), sizeof(float3) * numverts);
@ -1328,7 +1339,7 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph,
}
}
free_object_to_mesh(b_data, b_ob, b_mesh);
free_object_to_mesh(b_data, b_ob_info, b_mesh);
return;
}

View File

@ -154,7 +154,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
const bool is_instance = b_instance.is_instance();
BL::Object b_ob = b_instance.object();
BL::Object b_parent = is_instance ? b_instance.parent() : b_instance.object();
BL::Object b_ob_instance = is_instance ? b_instance.instance_object() : b_ob;
BObjectInfo b_ob_info{b_ob, is_instance ? b_instance.instance_object() : b_ob, b_ob.data()};
const bool motion = motion_time != 0.0f;
/*const*/ Transform tfm = get_transform(b_ob.matrix_world());
int *persistent_id = NULL;
@ -178,8 +178,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
{
sync_light(b_parent,
persistent_id,
b_ob,
b_ob_instance,
b_ob_info,
is_instance ? b_instance.random_id() : 0,
tfm,
use_portal);
@ -231,7 +230,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
TaskPool *object_geom_task_pool = (is_instance) ? NULL : geom_task_pool;
/* key to lookup object */
ObjectKey key(b_parent, persistent_id, b_ob_instance, use_particle_hair);
ObjectKey key(b_parent, persistent_id, b_ob_info.real_object, use_particle_hair);
Object *object;
/* motion vector case */
@ -249,12 +248,8 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
/* mesh deformation */
if (object->get_geometry())
sync_geometry_motion(b_depsgraph,
b_ob_instance,
object,
motion_time,
use_particle_hair,
object_geom_task_pool);
sync_geometry_motion(
b_depsgraph, b_ob_info, object, motion_time, use_particle_hair, object_geom_task_pool);
}
return object;
@ -265,15 +260,8 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
(tfm != object->get_tfm());
/* mesh sync */
/* b_ob is owned by the iterator and will go out of scope at the end of the block.
* b_ob_instance is the original object and will remain valid for deferred geometry
* sync. */
Geometry *geometry = sync_geometry(b_depsgraph,
b_ob_instance,
b_ob_instance,
object_updated,
use_particle_hair,
object_geom_task_pool);
Geometry *geometry = sync_geometry(
b_depsgraph, b_ob_info, object_updated, use_particle_hair, object_geom_task_pool);
object->set_geometry(geometry);
/* special case not tracked by object update flags */

View File

@ -23,6 +23,7 @@
#include "RNA_types.h"
#include "blender/blender_id_map.h"
#include "blender/blender_util.h"
#include "blender/blender_viewport.h"
#include "render/scene.h"
@ -158,18 +159,24 @@ class BlenderSync {
bool sync_object_attributes(BL::DepsgraphObjectInstance &b_instance, Object *object);
/* Volume */
void sync_volume(BL::Object &b_ob, Volume *volume);
void sync_volume(BObjectInfo &b_ob_info, Volume *volume);
/* Mesh */
void sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *mesh);
void sync_mesh_motion(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *mesh, int motion_step);
void sync_mesh(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Mesh *mesh);
void sync_mesh_motion(BL::Depsgraph b_depsgraph,
BObjectInfo &b_ob_info,
Mesh *mesh,
int motion_step);
/* Hair */
void sync_hair(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *hair);
void sync_hair_motion(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *hair, int motion_step);
void sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step = 0);
void sync_hair(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Hair *hair);
void sync_hair_motion(BL::Depsgraph b_depsgraph,
BObjectInfo &b_ob_info,
Hair *hair,
int motion_step);
void sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int motion_step = 0);
void sync_particle_hair(
Hair *hair, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step = 0);
Hair *hair, BL::Mesh &b_mesh, BObjectInfo &b_ob_info, bool motion, int motion_step = 0);
bool object_has_particle_hair(BL::Object b_ob);
/* Camera */
@ -178,14 +185,13 @@ class BlenderSync {
/* Geometry */
Geometry *sync_geometry(BL::Depsgraph &b_depsgrpah,
BL::Object &b_ob,
BL::Object &b_ob_instance,
BObjectInfo &b_ob_info,
bool object_updated,
bool use_particle_hair,
TaskPool *task_pool);
void sync_geometry_motion(BL::Depsgraph &b_depsgraph,
BL::Object &b_ob,
BObjectInfo &b_ob_info,
Object *object,
float motion_time,
bool use_particle_hair,
@ -194,8 +200,7 @@ class BlenderSync {
/* Light */
void sync_light(BL::Object &b_parent,
int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
BL::Object &b_ob,
BL::Object &b_ob_instance,
BObjectInfo &b_ob_info,
int random_id,
Transform &tfm,
bool *use_portal);

View File

@ -40,6 +40,28 @@ float *BKE_image_get_float_pixels_for_frame(void *image, int frame, int tile);
CCL_NAMESPACE_BEGIN
struct BObjectInfo {
/* Object directly provided by the depsgraph iterator. This object is only valid during one
* iteration and must not be accessed afterwards. Transforms and visibility should be checked on
* this object. */
BL::Object iter_object;
/* This object remains alive even after the object iterator is done. It corresponds to one
* original object. It is the object that owns the object data below. */
BL::Object real_object;
/* The object-data referenced by the iter object. This is still valid after the depsgraph
* iterator is done. It might have a different type compared to real_object.data(). */
BL::ID object_data;
/* True when the current geometry is the data of the referenced object. False when it is a
* geometry instance that does not have a 1-to-1 relationship with an object. */
bool is_real_object_data() const
{
return const_cast<BL::Object &>(real_object).data() == object_data;
}
};
typedef BL::ShaderNodeAttribute::attribute_type_enum BlenderAttributeType;
BlenderAttributeType blender_attribute_name_split_type(ustring name, string *r_real_name);
@ -47,7 +69,7 @@ void python_thread_state_save(void **python_thread_state);
void python_thread_state_restore(void **python_thread_state);
static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/,
BL::Object &object,
BObjectInfo &b_ob_info,
BL::Depsgraph & /*depsgraph*/,
bool /*calc_undeformed*/,
Mesh::SubdivisionType subdivision_type)
@ -69,9 +91,9 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/,
#endif
BL::Mesh mesh(PointerRNA_NULL);
if (object.type() == BL::Object::type_MESH) {
if (b_ob_info.object_data.is_a(&RNA_Mesh)) {
/* TODO: calc_undeformed is not used. */
mesh = BL::Mesh(object.data());
mesh = BL::Mesh(b_ob_info.object_data);
/* Make a copy to split faces if we use autosmooth, otherwise not needed.
* Also in edit mode do we need to make a copy, to ensure data layers like
@ -79,12 +101,15 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/,
if (mesh.is_editmode() ||
(mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE)) {
BL::Depsgraph depsgraph(PointerRNA_NULL);
mesh = object.to_mesh(false, depsgraph);
assert(b_ob_info.is_real_object_data());
mesh = b_ob_info.real_object.to_mesh(false, depsgraph);
}
}
else {
BL::Depsgraph depsgraph(PointerRNA_NULL);
mesh = object.to_mesh(false, depsgraph);
if (b_ob_info.is_real_object_data()) {
mesh = b_ob_info.real_object.to_mesh(false, depsgraph);
}
}
#if 0
@ -108,10 +133,14 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/,
}
static inline void free_object_to_mesh(BL::BlendData & /*data*/,
BL::Object &object,
BObjectInfo &b_ob_info,
BL::Mesh &mesh)
{
if (!b_ob_info.is_real_object_data()) {
return;
}
/* Free mesh if we didn't just use the existing one. */
BL::Object object = b_ob_info.real_object;
if (object.data().ptr.data != mesh.ptr.data) {
object.to_mesh_clear();
}
@ -219,9 +248,13 @@ static inline bool BKE_object_is_modified(BL::Object &self, BL::Scene &scene, bo
return self.is_modified(scene, (preview) ? (1 << 0) : (1 << 1)) ? true : false;
}
static inline bool BKE_object_is_deform_modified(BL::Object &self, BL::Scene &scene, bool preview)
static inline bool BKE_object_is_deform_modified(BObjectInfo &self, BL::Scene &scene, bool preview)
{
return self.is_deform_modified(scene, (preview) ? (1 << 0) : (1 << 1)) ? true : false;
if (!self.is_real_object_data()) {
return false;
}
return self.real_object.is_deform_modified(scene, (preview) ? (1 << 0) : (1 << 1)) ? true :
false;
}
static inline int render_resolution_x(BL::RenderSettings &b_render)

View File

@ -181,9 +181,12 @@ class BlenderSmokeLoader : public ImageLoader {
AttributeStandard attribute;
};
static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Volume *volume, float frame)
static void sync_smoke_volume(Scene *scene, BObjectInfo &b_ob_info, Volume *volume, float frame)
{
BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob);
if (!b_ob_info.is_real_object_data()) {
return;
}
BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob_info.real_object);
if (!b_domain) {
return;
}
@ -206,7 +209,7 @@ static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Volume *volume, fl
Attribute *attr = volume->attributes.add(std);
ImageLoader *loader = new BlenderSmokeLoader(b_ob, std);
ImageLoader *loader = new BlenderSmokeLoader(b_ob_info.real_object, std);
ImageParams params;
params.frame = frame;
@ -244,11 +247,11 @@ class BlenderVolumeLoader : public VDBImageLoader {
};
static void sync_volume_object(BL::BlendData &b_data,
BL::Object &b_ob,
BObjectInfo &b_ob_info,
Scene *scene,
Volume *volume)
{
BL::Volume b_volume(b_ob.data());
BL::Volume b_volume(b_ob_info.object_data);
b_volume.grids.load(b_data.ptr.data);
BL::VolumeRender b_render(b_volume.render());
@ -296,19 +299,19 @@ static void sync_volume_object(BL::BlendData &b_data,
}
}
void BlenderSync::sync_volume(BL::Object &b_ob, Volume *volume)
void BlenderSync::sync_volume(BObjectInfo &b_ob_info, Volume *volume)
{
volume->clear(true);
if (view_layer.use_volumes) {
if (b_ob.type() == BL::Object::type_VOLUME) {
if (b_ob_info.object_data.is_a(&RNA_Volume)) {
/* Volume object. Create only attributes, bounding mesh will then
* be automatically generated later. */
sync_volume_object(b_data, b_ob, scene, volume);
sync_volume_object(b_data, b_ob_info, scene, volume);
}
else {
/* Smoke domain. */
sync_smoke_volume(scene, b_ob, volume, b_scene.frame_current());
sync_smoke_volume(scene, b_ob_info, volume, b_scene.frame_current());
}
}

View File

@ -31,6 +31,7 @@ struct ListBase;
struct Object;
struct ParticleSystem;
struct Scene;
struct ID;
/* ---------------------------------------------------- */
/* Dupli-Geometry */
@ -42,7 +43,10 @@ void free_object_duplilist(struct ListBase *lb);
typedef struct DupliObject {
struct DupliObject *next, *prev;
/* Object whose geometry is instanced. */
struct Object *ob;
/* Data owned by the object above that is instanced. This might not be the same as `ob->data`. */
struct ID *ob_data;
float mat[4][4];
float orco[3], uv[2];

View File

@ -41,7 +41,7 @@ typedef enum GeometryComponentType {
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set);
bool BKE_object_has_geometry_set_instances(const struct Object *ob);
#ifdef __cplusplus
}

View File

@ -283,6 +283,7 @@ struct GeometrySet {
void clear();
bool owns_direct_data() const;
void ensure_owns_direct_data();
/* Utility methods for creation. */
@ -447,12 +448,14 @@ class InstanceReference {
None,
Object,
Collection,
GeometrySet,
};
private:
Type type_ = Type::None;
/** Depending on the type this is either null, an Object or Collection pointer. */
void *data_ = nullptr;
std::unique_ptr<GeometrySet> geometry_set_;
public:
InstanceReference() = default;
@ -465,6 +468,19 @@ class InstanceReference {
{
}
InstanceReference(GeometrySet geometry_set)
: type_(Type::GeometrySet),
geometry_set_(std::make_unique<GeometrySet>(std::move(geometry_set)))
{
}
InstanceReference(const InstanceReference &src) : type_(src.type_), data_(src.data_)
{
if (src.type_ == Type::GeometrySet) {
geometry_set_ = std::make_unique<GeometrySet>(*src.geometry_set_);
}
}
Type type() const
{
return type_;
@ -482,14 +498,37 @@ class InstanceReference {
return *(Collection *)data_;
}
const GeometrySet &geometry_set() const
{
BLI_assert(type_ == Type::GeometrySet);
return *geometry_set_;
}
bool owns_direct_data() const
{
if (type_ != Type::GeometrySet) {
/* The object and collection instances are not direct data. */
return true;
}
return geometry_set_->owns_direct_data();
}
void ensure_owns_direct_data()
{
if (type_ != Type::GeometrySet) {
return;
}
geometry_set_->ensure_owns_direct_data();
}
uint64_t hash() const
{
return blender::get_default_hash(data_);
return blender::get_default_hash_2(data_, geometry_set_.get());
}
friend bool operator==(const InstanceReference &a, const InstanceReference &b)
{
return a.data_ == b.data_;
return a.data_ == b.data_ && a.geometry_set_.get() == b.geometry_set_.get();
}
};
@ -529,7 +568,7 @@ class InstancesComponent : public GeometryComponent {
void reserve(int min_capacity);
void resize(int capacity);
int add_reference(InstanceReference reference);
int add_reference(const InstanceReference &reference);
void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1);
blender::Span<InstanceReference> references() const;

View File

@ -458,6 +458,8 @@ void BKE_object_modifiers_lib_link_common(void *userData,
struct ID **idpoin,
int cb_flag);
void BKE_object_replace_data_on_shallow_copy(struct Object *ob, struct ID *new_data);
struct PartEff;
struct PartEff *BKE_object_do_version_give_parteff_245(struct Object *ob);

View File

@ -122,7 +122,7 @@ blender::Span<int> InstancesComponent::instance_ids() const
* If the reference exists already, the handle of the existing reference is returned.
* Otherwise a new handle is added.
*/
int InstancesComponent::add_reference(InstanceReference reference)
int InstancesComponent::add_reference(const InstanceReference &reference)
{
return references_.index_of_or_add_as(reference);
}
@ -144,14 +144,23 @@ bool InstancesComponent::is_empty() const
bool InstancesComponent::owns_direct_data() const
{
/* The object and collection instances are not direct data. Instance transforms are direct data
* and are always owned. Therefore, instance components always own all their direct data. */
for (const InstanceReference &reference : references_) {
if (!reference.owns_direct_data()) {
return false;
}
}
return true;
}
void InstancesComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
for (const InstanceReference &const_reference : references_) {
/* Const cast is fine because we are not changing anything that would change the hash of the
* reference. */
InstanceReference &reference = const_cast<InstanceReference &>(const_reference);
reference.ensure_owns_direct_data();
}
}
static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)

View File

@ -218,6 +218,16 @@ void GeometrySet::ensure_owns_direct_data()
}
}
bool GeometrySet::owns_direct_data() const
{
for (const GeometryComponentPtr &component : components_.values()) {
if (!component->owns_direct_data()) {
return false;
}
}
return true;
}
/* Returns a read-only mesh or null. */
const Mesh *GeometrySet::get_mesh_for_read() const
{
@ -376,9 +386,32 @@ void BKE_geometry_set_free(GeometrySet *geometry_set)
delete geometry_set;
}
bool BKE_geometry_set_has_instances(const GeometrySet *geometry_set)
bool BKE_object_has_geometry_set_instances(const Object *ob)
{
return geometry_set->get_component_for_read<InstancesComponent>() != nullptr;
const GeometrySet *geometry_set = ob->runtime.geometry_set_eval;
if (geometry_set == nullptr) {
return false;
}
if (geometry_set->has_instances()) {
return true;
}
const bool has_mesh = geometry_set->has_mesh();
const bool has_pointcloud = geometry_set->has_pointcloud();
const bool has_volume = geometry_set->has_volume();
const bool has_curve = geometry_set->has_curve();
if (ob->type == OB_MESH) {
return has_pointcloud || has_volume || has_curve;
}
if (ob->type == OB_POINTCLOUD) {
return has_mesh || has_volume || has_curve;
}
if (ob->type == OB_VOLUME) {
return has_mesh || has_pointcloud || has_curve;
}
if (ob->type == OB_CURVE) {
return has_mesh || has_pointcloud || has_volume;
}
return false;
}
/** \} */

View File

@ -168,6 +168,11 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set,
collection, instance_transform, r_sets);
break;
}
case InstanceReference::Type::GeometrySet: {
const GeometrySet &geometry_set = reference.geometry_set();
geometry_set_collect_recursive(geometry_set, instance_transform, r_sets);
break;
}
case InstanceReference::Type::None: {
break;
}
@ -290,6 +295,13 @@ static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_se
}
break;
}
case InstanceReference::Type::GeometrySet: {
const GeometrySet &geometry_set = reference.geometry_set();
if (!instances_attribute_foreach_recursive(geometry_set, callback, limit, count)) {
return false;
}
break;
}
case InstanceReference::Type::None: {
break;
}

View File

@ -1979,8 +1979,7 @@ int BKE_object_visibility(const Object *ob, const int dag_eval_mode)
visibility |= OB_VISIBLE_INSTANCES;
}
if (ob->runtime.geometry_set_eval != NULL &&
BKE_geometry_set_has_instances(ob->runtime.geometry_set_eval)) {
if (BKE_object_has_geometry_set_instances(ob)) {
visibility |= OB_VISIBLE_INSTANCES;
}
@ -5737,3 +5736,13 @@ void BKE_object_modifiers_lib_link_common(void *userData,
id_us_plus_no_lib(*idpoin);
}
}
void BKE_object_replace_data_on_shallow_copy(Object *ob, ID *new_data)
{
ob->type = BKE_object_obdata_to_type(new_data);
ob->data = new_data;
ob->runtime.geometry_set_eval = NULL;
ob->runtime.data_eval = NULL;
ob->runtime.bb->flag |= BOUNDBOX_DIRTY;
ob->id.py_instance = NULL;
}

View File

@ -194,6 +194,7 @@ static DupliObject *make_dupli(const DupliContext *ctx,
}
dob->ob = ob;
dob->ob_data = (ID *)ob->data;
mul_m4_m4m4(dob->mat, (float(*)[4])ctx->space_mat, mat);
dob->type = ctx->gen->type;
@ -834,14 +835,59 @@ static const DupliGenerator gen_dupli_verts_pointcloud = {
/** \name Instances Geometry Component Implementation
* \{ */
static void make_duplis_instances_component(const DupliContext *ctx)
static void make_duplis_geometry_set_impl(const DupliContext *ctx,
const GeometrySet &geometry_set,
const float parent_transform[4][4],
bool geometry_set_is_instance)
{
const InstancesComponent *component =
ctx->object->runtime.geometry_set_eval->get_component_for_read<InstancesComponent>();
int component_index = 0;
if (ctx->object->type != OB_MESH || geometry_set_is_instance) {
const Mesh *mesh = geometry_set.get_mesh_for_read();
if (mesh != nullptr) {
DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
dupli->ob_data = (ID *)mesh;
}
}
if (ctx->object->type != OB_VOLUME || geometry_set_is_instance) {
const Volume *volume = geometry_set.get_volume_for_read();
if (volume != nullptr) {
DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
dupli->ob_data = (ID *)volume;
}
}
if (ctx->object->type != OB_CURVE || geometry_set_is_instance) {
const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
if (curve_component != nullptr) {
const Curve *curve = curve_component->get_curve_for_render();
if (curve != nullptr) {
DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
dupli->ob_data = (ID *)curve;
}
}
}
if (ctx->object->type != OB_POINTCLOUD || geometry_set_is_instance) {
const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read();
if (pointcloud != nullptr) {
DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
dupli->ob_data = (ID *)pointcloud;
}
}
const bool creates_duplis_for_components = component_index >= 1;
const InstancesComponent *component = geometry_set.get_component_for_read<InstancesComponent>();
if (component == nullptr) {
return;
}
const DupliContext *instances_ctx = ctx;
/* Create a sub-context if some duplis were created above. This is to avoid dupli id collisions
* between the instances component below and the other components above. */
DupliContext new_instances_ctx;
if (creates_duplis_for_components) {
copy_dupli_context(&new_instances_ctx, ctx, ctx->object, nullptr, component_index);
instances_ctx = &new_instances_ctx;
}
Span<float4x4> instance_offset_matrices = component->instance_transforms();
Span<int> instance_reference_handles = component->instance_reference_handles();
Span<int> almost_unique_ids = component->almost_unique_ids();
@ -855,13 +901,13 @@ static void make_duplis_instances_component(const DupliContext *ctx)
case InstanceReference::Type::Object: {
Object &object = reference.object();
float matrix[4][4];
mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrices[i].values);
make_dupli(ctx, &object, matrix, id);
mul_m4_m4m4(matrix, parent_transform, instance_offset_matrices[i].values);
make_dupli(instances_ctx, &object, matrix, id);
float space_matrix[4][4];
mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object.imat);
mul_m4_m4_pre(space_matrix, ctx->object->obmat);
make_recursive_duplis(ctx, &object, space_matrix, id);
mul_m4_m4_pre(space_matrix, parent_transform);
make_recursive_duplis(instances_ctx, &object, space_matrix, id);
break;
}
case InstanceReference::Type::Collection: {
@ -870,23 +916,36 @@ static void make_duplis_instances_component(const DupliContext *ctx)
unit_m4(collection_matrix);
sub_v3_v3(collection_matrix[3], collection.instance_offset);
mul_m4_m4_pre(collection_matrix, instance_offset_matrices[i].values);
mul_m4_m4_pre(collection_matrix, ctx->object->obmat);
mul_m4_m4_pre(collection_matrix, parent_transform);
eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
DupliContext sub_ctx;
copy_dupli_context(&sub_ctx, instances_ctx, instances_ctx->object, nullptr, id);
eEvaluationMode mode = DEG_get_mode(instances_ctx->depsgraph);
int object_id = 0;
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (&collection, object, mode) {
if (object == ctx->object) {
if (object == instances_ctx->object) {
continue;
}
float instance_matrix[4][4];
mul_m4_m4m4(instance_matrix, collection_matrix, object->obmat);
make_dupli(ctx, object, instance_matrix, id);
make_recursive_duplis(ctx, object, collection_matrix, id);
make_dupli(&sub_ctx, object, instance_matrix, object_id++);
make_recursive_duplis(&sub_ctx, object, collection_matrix, object_id++);
}
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
break;
}
case InstanceReference::Type::GeometrySet: {
float new_transform[4][4];
mul_m4_m4m4(new_transform, parent_transform, instance_offset_matrices[i].values);
DupliContext sub_ctx;
copy_dupli_context(&sub_ctx, instances_ctx, instances_ctx->object, nullptr, id);
make_duplis_geometry_set_impl(&sub_ctx, reference.geometry_set(), new_transform, true);
break;
}
case InstanceReference::Type::None: {
break;
}
@ -894,9 +953,15 @@ static void make_duplis_instances_component(const DupliContext *ctx)
}
}
static const DupliGenerator gen_dupli_instances_component = {
static void make_duplis_geometry_set(const DupliContext *ctx)
{
const GeometrySet *geometry_set = ctx->object->runtime.geometry_set_eval;
make_duplis_geometry_set_impl(ctx, *geometry_set, ctx->object->obmat, false);
}
static const DupliGenerator gen_dupli_geometry_set = {
0,
make_duplis_instances_component,
make_duplis_geometry_set,
};
/** \} */
@ -1567,8 +1632,8 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
}
if (ctx->object->runtime.geometry_set_eval != nullptr) {
if (BKE_geometry_set_has_instances(ctx->object->runtime.geometry_set_eval)) {
return &gen_dupli_instances_component;
if (BKE_object_has_geometry_set_instances(ctx->object)) {
return &gen_dupli_geometry_set;
}
}

View File

@ -84,12 +84,24 @@ template<typename T> class UserCounter {
return data_;
}
const T *operator->() const
{
BLI_assert(data_ != nullptr);
return data_;
}
T &operator*()
{
BLI_assert(data_ != nullptr);
return *data_;
}
const T &operator*() const
{
BLI_assert(data_ != nullptr);
return *data_;
}
operator bool() const
{
return data_ != nullptr;

View File

@ -145,15 +145,7 @@ typedef struct DEGObjectIterData {
eEvaluationMode eval_mode;
/* **** Iteration over geometry components **** */
/* The object whose components we currently iterate over.
* This might point to #temp_dupli_object. */
struct Object *geometry_component_owner;
/* Some identifier that is used to determine which geometry component should be returned next. */
int geometry_component_id;
/* Temporary storage for an object that is created from a component. */
struct Object temp_geometry_component_object;
struct Object *next_object;
/* **** Iteration over dupli-list. *** */

View File

@ -120,130 +120,6 @@ bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, DupliObject
return false;
}
void deg_iterator_components_init(DEGObjectIterData *data, Object *object)
{
data->geometry_component_owner = object;
data->geometry_component_id = 0;
}
/* Returns false when iterator is exhausted. */
bool deg_iterator_components_step(BLI_Iterator *iter)
{
DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
if (data->geometry_component_owner == nullptr) {
return false;
}
if (data->geometry_component_owner->runtime.geometry_set_eval == nullptr) {
/* Return the object itself, if it does not have a geometry set yet. */
iter->current = data->geometry_component_owner;
data->geometry_component_owner = nullptr;
return true;
}
GeometrySet *geometry_set = data->geometry_component_owner->runtime.geometry_set_eval;
if (geometry_set == nullptr) {
data->geometry_component_owner = nullptr;
return false;
}
/* The mesh component. */
if (data->geometry_component_id == 0) {
data->geometry_component_id++;
/* Don't use a temporary object for this component, when the owner is a mesh object. */
if (data->geometry_component_owner->type == OB_MESH) {
iter->current = data->geometry_component_owner;
return true;
}
const Mesh *mesh = geometry_set->get_mesh_for_read();
if (mesh != nullptr) {
Object *temp_object = &data->temp_geometry_component_object;
*temp_object = *data->geometry_component_owner;
temp_object->type = OB_MESH;
temp_object->data = (void *)mesh;
temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id;
iter->current = temp_object;
return true;
}
}
/* The pointcloud component. */
if (data->geometry_component_id == 1) {
data->geometry_component_id++;
/* Don't use a temporary object for this component, when the owner is a point cloud object. */
if (data->geometry_component_owner->type == OB_POINTCLOUD) {
iter->current = data->geometry_component_owner;
return true;
}
const PointCloud *pointcloud = geometry_set->get_pointcloud_for_read();
if (pointcloud != nullptr) {
Object *temp_object = &data->temp_geometry_component_object;
*temp_object = *data->geometry_component_owner;
temp_object->type = OB_POINTCLOUD;
temp_object->data = (void *)pointcloud;
temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id;
iter->current = temp_object;
return true;
}
}
/* The volume component. */
if (data->geometry_component_id == 2) {
data->geometry_component_id++;
/* Don't use a temporary object for this component, when the owner is a volume object. */
if (data->geometry_component_owner->type == OB_VOLUME) {
iter->current = data->geometry_component_owner;
return true;
}
const VolumeComponent *component = geometry_set->get_component_for_read<VolumeComponent>();
if (component != nullptr) {
const Volume *volume = component->get_for_read();
if (volume != nullptr) {
Object *temp_object = &data->temp_geometry_component_object;
*temp_object = *data->geometry_component_owner;
temp_object->type = OB_VOLUME;
temp_object->data = (void *)volume;
temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id;
iter->current = temp_object;
return true;
}
}
}
/* The curve component. */
if (data->geometry_component_id == 3) {
data->geometry_component_id++;
const CurveComponent *component = geometry_set->get_component_for_read<CurveComponent>();
if (component != nullptr) {
const Curve *curve = component->get_curve_for_render();
if (curve != nullptr) {
Object *temp_object = &data->temp_geometry_component_object;
*temp_object = *data->geometry_component_owner;
temp_object->type = OB_CURVE;
temp_object->data = (void *)curve;
/* Assign data_eval here too, because curve rendering code tries
* to use a mesh if it can find one in this pointer. */
temp_object->runtime.data_eval = (ID *)curve;
temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id;
iter->current = temp_object;
return true;
}
}
}
data->geometry_component_owner = nullptr;
return false;
}
void deg_iterator_duplis_init(DEGObjectIterData *data, Object *object)
{
if ((data->flag & DEG_ITER_OBJECT_FLAG_DUPLI) &&
@ -292,6 +168,9 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data)
temp_dupli_object->dt = MIN2(temp_dupli_object->dt, dupli_parent->dt);
copy_v4_v4(temp_dupli_object->color, dupli_parent->color);
temp_dupli_object->runtime.select_id = dupli_parent->runtime.select_id;
if (dob->ob->data != dob->ob_data) {
BKE_object_replace_data_on_shallow_copy(temp_dupli_object, dob->ob_data);
}
/* Duplicated elements shouldn't care whether their original collection is visible or not. */
temp_dupli_object->base_flag |= BASE_VISIBLE_DEPSGRAPH;
@ -308,7 +187,7 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data)
copy_m4_m4(data->temp_dupli_object.obmat, dob->mat);
invert_m4_m4(data->temp_dupli_object.imat, data->temp_dupli_object.obmat);
deg_iterator_components_init(data, &data->temp_dupli_object);
data->next_object = &data->temp_dupli_object;
BLI_assert(deg::deg_validate_copy_on_write_datablock(&data->temp_dupli_object.id));
return true;
}
@ -377,7 +256,7 @@ bool deg_iterator_objects_step(DEGObjectIterData *data)
}
if (ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) {
deg_iterator_components_init(data, object);
data->next_object = object;
}
data->id_node_index++;
return true;
@ -400,6 +279,7 @@ void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data)
return;
}
data->next_object = nullptr;
data->dupli_parent = nullptr;
data->dupli_list = nullptr;
data->dupli_object_next = nullptr;
@ -408,8 +288,6 @@ void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data)
data->id_node_index = 0;
data->num_id_nodes = num_id_nodes;
data->eval_mode = DEG_get_mode(depsgraph);
data->geometry_component_id = 0;
data->geometry_component_owner = nullptr;
deg_invalidate_iterator_work_data(data);
DEG_iterator_objects_next(iter);
@ -419,7 +297,9 @@ void DEG_iterator_objects_next(BLI_Iterator *iter)
{
DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
while (true) {
if (deg_iterator_components_step(iter)) {
if (data->next_object != nullptr) {
iter->current = data->next_object;
data->next_object = nullptr;
return;
}
if (deg_iterator_duplis_step(data)) {

View File

@ -632,14 +632,29 @@ void DRW_viewport_request_redraw(void)
/** \name Duplis
* \{ */
static void drw_duplidata_load(DupliObject *dupli)
static uint dupli_key_hash(const void *key)
{
const DupliKey *dupli_key = (const DupliKey *)key;
return BLI_ghashutil_ptrhash(dupli_key->ob) ^ BLI_ghashutil_ptrhash(dupli_key->ob_data);
}
static bool dupli_key_cmp(const void *key1, const void *key2)
{
const DupliKey *dupli_key1 = (const DupliKey *)key1;
const DupliKey *dupli_key2 = (const DupliKey *)key2;
return dupli_key1->ob != dupli_key2->ob || dupli_key1->ob_data != dupli_key2->ob_data;
}
static void drw_duplidata_load(Object *ob)
{
DupliObject *dupli = DST.dupli_source;
if (dupli == NULL) {
return;
}
if (DST.dupli_origin != dupli->ob) {
if (DST.dupli_origin != dupli->ob || (DST.dupli_origin_data != dupli->ob_data)) {
DST.dupli_origin = dupli->ob;
DST.dupli_origin_data = dupli->ob_data;
}
else {
/* Same data as previous iter. No need to poll ghash for this. */
@ -647,16 +662,23 @@ static void drw_duplidata_load(DupliObject *dupli)
}
if (DST.dupli_ghash == NULL) {
DST.dupli_ghash = BLI_ghash_ptr_new(__func__);
DST.dupli_ghash = BLI_ghash_new(dupli_key_hash, dupli_key_cmp, __func__);
}
DupliKey *key = MEM_callocN(sizeof(DupliKey), __func__);
key->ob = dupli->ob;
key->ob_data = dupli->ob_data;
void **value;
if (!BLI_ghash_ensure_p(DST.dupli_ghash, DST.dupli_origin, &value)) {
if (!BLI_ghash_ensure_p(DST.dupli_ghash, key, &value)) {
*value = MEM_callocN(sizeof(void *) * DST.enabled_engine_count, __func__);
/* TODO: Meh a bit out of place but this is nice as it is
* only done once per "original" object. */
drw_batch_cache_validate(DST.dupli_origin);
* only done once per instance type. */
drw_batch_cache_validate(ob);
}
else {
MEM_freeN(key);
}
DST.dupli_datas = *(void ***)value;
}
@ -670,12 +692,24 @@ static void duplidata_value_free(void *val)
MEM_freeN(val);
}
static void duplidata_key_free(void *key)
{
DupliKey *dupli_key = (DupliKey *)key;
if (dupli_key->ob_data == NULL) {
drw_batch_cache_generate_requested(dupli_key->ob);
}
else {
Object temp_object = *dupli_key->ob;
BKE_object_replace_data_on_shallow_copy(&temp_object, dupli_key->ob_data);
drw_batch_cache_generate_requested(&temp_object);
}
MEM_freeN(key);
}
static void drw_duplidata_free(void)
{
if (DST.dupli_ghash != NULL) {
BLI_ghash_free(DST.dupli_ghash,
(void (*)(void *key))drw_batch_cache_generate_requested,
duplidata_value_free);
BLI_ghash_free(DST.dupli_ghash, duplidata_key_free, duplidata_value_free);
DST.dupli_ghash = NULL;
}
}
@ -1523,6 +1557,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
/* Only iterate over objects for internal engines or when overlays are enabled */
if (do_populate_loop) {
DST.dupli_origin = NULL;
DST.dupli_origin_data = NULL;
DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) {
if ((object_type_exclude_viewport & (1 << ob->type)) != 0) {
continue;
@ -1532,7 +1567,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
}
DST.dupli_parent = data_.dupli_parent;
DST.dupli_source = data_.dupli_object_current;
drw_duplidata_load(DST.dupli_source);
drw_duplidata_load(ob);
drw_engines_cache_populate(ob);
}
DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END;
@ -1881,12 +1916,13 @@ void DRW_render_object_iter(
draw_ctx->v3d->object_type_exclude_viewport :
0;
DST.dupli_origin = NULL;
DST.dupli_origin_data = NULL;
DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) {
if ((object_type_exclude_viewport & (1 << ob->type)) == 0) {
DST.dupli_parent = data_.dupli_parent;
DST.dupli_source = data_.dupli_object_current;
DST.ob_handle = 0;
drw_duplidata_load(DST.dupli_source);
drw_duplidata_load(ob);
if (!DST.dupli_source) {
drw_batch_cache_validate(ob);
@ -2330,6 +2366,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
v3d->object_type_exclude_select);
bool filter_exclude = false;
DST.dupli_origin = NULL;
DST.dupli_origin_data = NULL;
DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) {
if (!BKE_object_is_visible_in_viewport(v3d, ob)) {
continue;
@ -2362,7 +2399,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
DRW_select_load_id(ob->runtime.select_id);
DST.dupli_parent = data_.dupli_parent;
DST.dupli_source = data_.dupli_object_current;
drw_duplidata_load(DST.dupli_source);
drw_duplidata_load(ob);
drw_engines_cache_populate(ob);
}
}
@ -2475,6 +2512,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph,
const int object_type_exclude_viewport = v3d->object_type_exclude_viewport;
DST.dupli_origin = NULL;
DST.dupli_origin_data = NULL;
DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (DST.draw_ctx.depsgraph, ob) {
if ((object_type_exclude_viewport & (1 << ob->type)) != 0) {
continue;
@ -2484,7 +2522,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph,
}
DST.dupli_parent = data_.dupli_parent;
DST.dupli_source = data_.dupli_object_current;
drw_duplidata_load(DST.dupli_source);
drw_duplidata_load(ob);
drw_engines_cache_populate(ob);
}
DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END;

View File

@ -497,6 +497,11 @@ typedef struct DRWDebugSphere {
/* ------------- DRAW MANAGER ------------ */
typedef struct DupliKey {
struct Object *ob;
struct ID *ob_data;
} DupliKey;
#define DST_MAX_SLOTS 64 /* Cannot be changed without modifying RST.bound_tex_slots */
#define MAX_CLIP_PLANES 6 /* GL_MAX_CLIP_PLANES is at least 6 */
#define STENCIL_UNDEFINED 256
@ -515,15 +520,19 @@ typedef struct DRWManager {
/** Handle of next DRWPass to be allocated. */
DRWResourceHandle pass_handle;
/** Dupli state. NULL if not dupli. */
/** Dupli object that corresponds to the current object. */
struct DupliObject *dupli_source;
/** Object that created the dupli-list the current object is part of. */
struct Object *dupli_parent;
/** Object referenced by the current dupli object. */
struct Object *dupli_origin;
/** Ghash containing original objects. */
/** Object-data referenced by the current dupli object. */
struct ID *dupli_origin_data;
/** Ghash: #DupliKey -> void pointer for each enabled engine. */
struct GHash *dupli_ghash;
/** TODO(fclem): try to remove usage of this. */
DRWInstanceData *object_instance_data[MAX_INSTANCE_DATA_SIZE];
/* Array of dupli_data (one for each enabled engine) to handle duplis. */
/* Dupli data for the current dupli for each enabled engine. */
void **dupli_datas;
/* Rendering state */

View File

@ -2246,13 +2246,6 @@ static bool dupliobject_instancer_cmp(const void *a_, const void *b_)
return false;
}
static bool object_has_geometry_set_instances(const Object *object_eval)
{
struct GeometrySet *geometry_set = object_eval->runtime.geometry_set_eval;
return (geometry_set != NULL) && BKE_geometry_set_has_instances(geometry_set);
}
static void make_object_duplilist_real(bContext *C,
Depsgraph *depsgraph,
Scene *scene,
@ -2266,7 +2259,8 @@ static void make_object_duplilist_real(bContext *C,
Object *object_eval = DEG_get_evaluated_object(depsgraph, base->object);
if (!(base->object->transflag & OB_DUPLI) && !object_has_geometry_set_instances(object_eval)) {
if (!(base->object->transflag & OB_DUPLI) &&
!BKE_object_has_geometry_set_instances(object_eval)) {
return;
}

View File

@ -35,6 +35,10 @@ struct CollectionCellValue {
const Collection *collection;
};
struct GeometrySetCellValue {
const GeometrySet *geometry_set;
};
/**
* This is a type that can hold the value of a cell in a spreadsheet. This type allows us to
* decouple the drawing of individual cells from the code that generates the data to be displayed.
@ -53,6 +57,7 @@ class CellValue {
std::optional<ColorGeometry4f> value_color;
std::optional<ObjectCellValue> value_object;
std::optional<CollectionCellValue> value_collection;
std::optional<GeometrySetCellValue> value_geometry_set;
};
} // namespace blender::ed::spreadsheet

View File

@ -332,6 +332,11 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
r_cell_value.value_collection = CollectionCellValue{&collection};
break;
}
case InstanceReference::Type::GeometrySet: {
const GeometrySet &geometry_set = reference.geometry_set();
r_cell_value.value_geometry_set = GeometrySetCellValue{&geometry_set};
break;
}
case InstanceReference::Type::None: {
break;
}

View File

@ -209,6 +209,23 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
0,
nullptr);
}
else if (cell_value.value_geometry_set.has_value()) {
uiDefIconTextBut(params.block,
UI_BTYPE_LABEL,
0,
ICON_MESH_DATA,
"Geometry",
params.xmin,
params.ymin,
params.width,
params.height,
nullptr,
0,
0,
0,
0,
nullptr);
}
}
void draw_float_vector(const CellDrawParams &params, const Span<float> values) const

View File

@ -501,9 +501,7 @@ static void iter_snap_objects(SnapObjectContext *sctx,
}
Object *obj_eval = DEG_get_evaluated_object(depsgraph, base->object);
if (obj_eval->transflag & OB_DUPLI ||
(obj_eval->runtime.geometry_set_eval != NULL &&
BKE_geometry_set_has_instances(obj_eval->runtime.geometry_set_eval))) {
if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) {
ListBase *lb = object_duplilist(depsgraph, sctx->scene, obj_eval);
for (DupliObject *dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) {
BLI_assert(DEG_is_evaluated_object(dupli_ob->ob));

View File

@ -1889,6 +1889,7 @@ typedef enum GeometryNodeTriangulateQuads {
typedef enum GeometryNodePointInstanceType {
GEO_NODE_POINT_INSTANCE_TYPE_OBJECT = 0,
GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION = 1,
GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY = 2,
} GeometryNodePointInstanceType;
typedef enum GeometryNodePointInstanceFlag {

View File

@ -9295,6 +9295,11 @@ static void def_geo_point_instance(StructRNA *srna)
ICON_NONE,
"Collection",
"Instance an entire collection on all points"},
{GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY,
"GEOMETRY",
ICON_NONE,
"Geometry",
"Copy geometry to all points"},
{0, NULL, 0, NULL, NULL},
};

View File

@ -31,13 +31,14 @@ static void geo_node_point_instance_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Geometry>("Geometry");
b.add_input<decl::Object>("Object").hide_label(true);
b.add_input<decl::Collection>("Collection").hide_label(true);
b.add_input<decl::Geometry>("Instance Geometry");
b.add_input<decl::Int>("Seed").min(-10000).max(10000);
b.add_output<decl::Geometry>("Geometry");
}
static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "instance_type", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiItemR(layout, ptr, "instance_type", 0, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "instance_type") == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION) {
uiItemR(layout, ptr, "use_whole_collection", 0, nullptr, ICON_NONE);
}
@ -56,7 +57,8 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
{
bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
bNodeSocket *collection_socket = object_socket->next;
bNodeSocket *seed_socket = collection_socket->next;
bNodeSocket *instance_geometry_socket = collection_socket->next;
bNodeSocket *seed_socket = instance_geometry_socket->next;
NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node->storage;
GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node_storage->instance_type;
@ -65,6 +67,8 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
nodeSetSocketAvailability(object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT);
nodeSetSocketAvailability(collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION);
nodeSetSocketAvailability(instance_geometry_socket,
type == GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY);
nodeSetSocketAvailability(
seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection);
}
@ -114,6 +118,12 @@ static Vector<InstanceReference> get_instance_references__collection(GeoNodeExec
return references;
}
static Vector<InstanceReference> get_instance_references__geometry(GeoNodeExecParams &params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Instance Geometry");
return {std::move(geometry_set)};
}
static Vector<InstanceReference> get_instance_references(GeoNodeExecParams &params)
{
const bNode &node = params.node();
@ -128,6 +138,9 @@ static Vector<InstanceReference> get_instance_references(GeoNodeExecParams &para
case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: {
return get_instance_references__collection(params);
}
case GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY: {
return get_instance_references__geometry(params);
}
}
return {};
}