Geometry Nodes: support evaluating mesh object to geometry set

This implements the design proposed in T83357.

The goal is to allow the geometry nodes modifier on mesh objects to
output instances and potentially other geometry types. Both problems
are tackled by allowing mesh objects to evaluate to a geometry set,
instead of just a single mesh id data block. The geometry set can
contain a mesh but also other data like instances and a point cloud.

I can't say that I'm sure that this commit won't introduce bugs. Mainly
the temporary object creation during rendering seems a bit brittle.
BUT, we can be reasonably sure that this commit will not introduce
regressions (at least not ones, that are hard to fix). This is because
the code has been written in a way that minimizes changes for existing
functionality.

Given that we intend to hide the point cloud object for the next release,
we won't even have to worry about temporary object creation for now.

An important part of the technical design is to make sure that
`ObjectRuntime->data_eval` contains the same data before and after this
patch. This helps to make sure, that existing code paths are impacted as
little as possible.

Instead of fully replacing `data_eval`, there is `geometry_set_eval`,
which contains all the geometry components an object evaluated to
(including the data referenced by `data_eval`).

For now, not much code has to be aware of `geometry_set_eval`. Mainly
the depsgraph object iterator and the instances system have to know
about it.

Reviewers: brecht

Differential Revision: https://developer.blender.org/D9851
This commit is contained in:
Jacques Lucke 2020-12-15 12:42:10 +01:00
parent d8db5cb600
commit 0d58eabee6
Notes: blender-bot 2023-02-14 08:10:10 +01:00
Referenced by issue #85492, Particles System not rendering in Blender 2.93 / 2.92 beta
Referenced by issue #83357, Design to support instancing (GeometrySet) for mesh objects
4 changed files with 143 additions and 21 deletions

View File

@ -50,6 +50,7 @@
#include "BKE_colorband.h"
#include "BKE_deform.h"
#include "BKE_editmesh.h"
#include "BKE_geometry_set.hh"
#include "BKE_key.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
@ -881,6 +882,56 @@ void BKE_mesh_wrapper_deferred_finalize(Mesh *me_eval,
BLI_assert(me_eval->runtime.wrapper_type_finalize == 0);
}
/**
* Modifies the given mesh and geometry set. The geometry set is expect to have NO mesh component.
* After this function ends, the geometry set will still have NO mesh component. Instead, an input
* mesh is passed separately and is returned separately.
*
* The purpose of the geometry set is to store all non-mesh geometry components that are generated
* by modifiers.
*/
static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md,
const ModifierEvalContext &mectx,
Object *ob,
Mesh *input_mesh,
GeometrySet &geometry_set)
{
Mesh *mesh_output = nullptr;
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (mti->modifyGeometrySet == nullptr) {
mesh_output = BKE_modifier_modify_mesh(md, &mectx, input_mesh);
}
else {
/* For performance reasons, this should be called by the modifier and/or nodes themselves at
* some point. */
BKE_mesh_wrapper_ensure_mdata(input_mesh);
/* Adds a new mesh component to the geometry set based on the #input_mesh. */
BLI_assert(!geometry_set.has<MeshComponent>());
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_component.replace(input_mesh, GeometryOwnershipType::Editable);
mesh_component.copy_vertex_group_names_from_object(*ob);
/* Let the modifier change the geometry set. */
mti->modifyGeometrySet(md, &mectx, &geometry_set);
/* Release the mesh from the geometry set again. */
if (geometry_set.has<MeshComponent>()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_output = mesh_component.release();
geometry_set.remove<MeshComponent>();
}
/* Return an empty mesh instead of null. */
if (mesh_output == nullptr) {
mesh_output = BKE_mesh_new_nomain(0, 0, 0, 0, 0);
BKE_mesh_copy_settings(mesh_output, input_mesh);
}
}
return mesh_output;
}
static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
Scene *scene,
Object *ob,
@ -892,7 +943,8 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
const bool allow_shared_mesh,
/* return args */
Mesh **r_deform,
Mesh **r_final)
Mesh **r_final,
GeometrySet **r_geometry_set)
{
/* Input and final mesh. Final mesh is only created the moment the first
* constructive modifier is executed, or a deform modifier needs normals
@ -900,6 +952,8 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
Mesh *mesh_input = (Mesh *)ob->data;
Mesh *mesh_final = nullptr;
Mesh *mesh_deform = nullptr;
/* This geometry set contains the non-mesh data that might be generated by modifiers. */
GeometrySet geometry_set_final;
BLI_assert((mesh_input->id.tag & LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT) == 0);
/* Deformed vertex locations array. Deform only modifier need this type of
@ -1197,7 +1251,8 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
}
}
Mesh *mesh_next = BKE_modifier_modify_mesh(md, &mectx, mesh_final);
Mesh *mesh_next = modifier_modify_mesh_and_geometry_set(
md, mectx, ob, mesh_final, geometry_set_final);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
@ -1375,6 +1430,9 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
if (r_deform) {
*r_deform = mesh_deform;
}
if (r_geometry_set) {
*r_geometry_set = new GeometrySet(std::move(geometry_set_final));
}
}
float (*editbmesh_vert_coords_alloc(BMEditMesh *em, int *r_vert_len))[3]
@ -1483,7 +1541,8 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
const CustomData_MeshMasks *dataMask,
/* return args */
Mesh **r_cage,
Mesh **r_final)
Mesh **r_final,
GeometrySet **r_geometry_set)
{
/* Input and final mesh. Final mesh is only created the moment the first
* constructive modifier is executed, or a deform modifier needs normals
@ -1491,6 +1550,8 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
Mesh *mesh_input = (Mesh *)ob->data;
Mesh *mesh_final = nullptr;
Mesh *mesh_cage = nullptr;
/* This geometry set contains the non-mesh data that might be generated by modifiers. */
GeometrySet geometry_set_final;
/* Deformed vertex locations array. Deform only modifier need this type of
* float array rather than MVert*. Tracked along with mesh_final as an
@ -1655,7 +1716,8 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
}
}
Mesh *mesh_next = BKE_modifier_modify_mesh(md, &mectx, mesh_final);
Mesh *mesh_next = modifier_modify_mesh_and_geometry_set(
md, mectx, ob, mesh_final, geometry_set_final);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
@ -1757,6 +1819,9 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
if (r_cage) {
*r_cage = mesh_cage;
}
if (r_geometry_set) {
*r_geometry_set = new GeometrySet(std::move(geometry_set_final));
}
}
static void mesh_build_extra_data(struct Depsgraph *depsgraph, Object *ob, Mesh *mesh_eval)
@ -1803,6 +1868,7 @@ static void mesh_build_data(struct Depsgraph *depsgraph,
#endif
Mesh *mesh_eval = nullptr, *mesh_deform_eval = nullptr;
GeometrySet *geometry_set_eval = nullptr;
mesh_calc_modifiers(depsgraph,
scene,
ob,
@ -1813,7 +1879,8 @@ static void mesh_build_data(struct Depsgraph *depsgraph,
true,
true,
&mesh_deform_eval,
&mesh_eval);
&mesh_eval,
&geometry_set_eval);
/* The modifier stack evaluation is storing result in mesh->runtime.mesh_eval, but this result
* is not guaranteed to be owned by object.
@ -1825,6 +1892,12 @@ static void mesh_build_data(struct Depsgraph *depsgraph,
const bool is_mesh_eval_owned = (mesh_eval != mesh->runtime.mesh_eval);
BKE_object_eval_assign_data(ob, &mesh_eval->id, is_mesh_eval_owned);
/* Add the final mesh as read-only non-owning component to the geometry set. */
BLI_assert(!geometry_set_eval->has<MeshComponent>());
MeshComponent &mesh_component = geometry_set_eval->get_component_for_write<MeshComponent>();
mesh_component.replace(mesh_eval, GeometryOwnershipType::ReadOnly);
ob->runtime.geometry_set_eval = geometry_set_eval;
ob->runtime.mesh_deform_eval = mesh_deform_eval;
ob->runtime.last_data_mask = *dataMask;
ob->runtime.last_need_mapping = need_mapping;
@ -1864,11 +1937,14 @@ static void editbmesh_build_data(struct Depsgraph *depsgraph,
Mesh *me_cage;
Mesh *me_final;
GeometrySet *non_mesh_components;
editbmesh_calc_modifiers(depsgraph, scene, obedit, em, dataMask, &me_cage, &me_final);
editbmesh_calc_modifiers(
depsgraph, scene, obedit, em, dataMask, &me_cage, &me_final, &non_mesh_components);
em->mesh_eval_final = me_final;
em->mesh_eval_cage = me_cage;
obedit->runtime.geometry_set_eval = non_mesh_components;
BKE_object_boundbox_calc_from_mesh(obedit, em->mesh_eval_final);
@ -2020,7 +2096,8 @@ Mesh *mesh_create_eval_final(Depsgraph *depsgraph,
{
Mesh *final;
mesh_calc_modifiers(depsgraph, scene, ob, 1, false, dataMask, -1, false, false, nullptr, &final);
mesh_calc_modifiers(
depsgraph, scene, ob, 1, false, dataMask, -1, false, false, nullptr, &final, nullptr);
return final;
}
@ -2034,7 +2111,7 @@ Mesh *mesh_create_eval_final_index_render(Depsgraph *depsgraph,
Mesh *final;
mesh_calc_modifiers(
depsgraph, scene, ob, 1, false, dataMask, index, false, false, nullptr, &final);
depsgraph, scene, ob, 1, false, dataMask, index, false, false, nullptr, &final, nullptr);
return final;
}
@ -2046,7 +2123,8 @@ Mesh *mesh_create_eval_no_deform(Depsgraph *depsgraph,
{
Mesh *final;
mesh_calc_modifiers(depsgraph, scene, ob, 0, false, dataMask, -1, false, false, nullptr, &final);
mesh_calc_modifiers(
depsgraph, scene, ob, 0, false, dataMask, -1, false, false, nullptr, &final, nullptr);
return final;
}
@ -2058,7 +2136,8 @@ Mesh *mesh_create_eval_no_deform_render(Depsgraph *depsgraph,
{
Mesh *final;
mesh_calc_modifiers(depsgraph, scene, ob, 0, false, dataMask, -1, false, false, nullptr, &final);
mesh_calc_modifiers(
depsgraph, scene, ob, 0, false, dataMask, -1, false, false, nullptr, &final, nullptr);
return final;
}

View File

@ -371,6 +371,25 @@ static void pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
}
}
static PointCloud *take_pointcloud_ownership_from_geometry_set(GeometrySet &geometry_set)
{
if (!geometry_set.has<PointCloudComponent>()) {
return nullptr;
}
PointCloudComponent &pointcloud_component =
geometry_set.get_component_for_write<PointCloudComponent>();
PointCloud *pointcloud = pointcloud_component.release();
if (pointcloud != nullptr) {
/* Add back, but as read-only non-owning component. */
pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly);
}
else {
/* The component was empty, we can also remove it. */
geometry_set.remove<PointCloudComponent>();
}
return pointcloud;
}
void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
{
/* Free any evaluated data and restore original data. */
@ -382,10 +401,17 @@ void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene
GeometryOwnershipType::ReadOnly);
pointcloud_evaluate_modifiers(depsgraph, scene, object, geometry_set);
PointCloud *pointcloud_eval = take_pointcloud_ownership_from_geometry_set(geometry_set);
/* If the geometry set did not contain a point cloud, we still create an empty one. */
if (pointcloud_eval == nullptr) {
pointcloud_eval = BKE_pointcloud_new_nomain(0);
}
/* Assign evaluated object. */
PointCloud *dummy_pointcloud = BKE_pointcloud_new_nomain(0);
BKE_object_eval_assign_data(object, &dummy_pointcloud->id, true);
object->runtime.geometry_set_eval = new GeometrySet(geometry_set);
const bool eval_is_owned = pointcloud_eval != pointcloud;
BKE_object_eval_assign_data(object, &pointcloud_eval->id, eval_is_owned);
object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
}
/* Draw Cache */

View File

@ -136,8 +136,8 @@ bool deg_iterator_components_step(BLI_Iterator *iter)
return false;
}
if (data->geometry_component_owner->type != OB_POINTCLOUD) {
/* Only point clouds support multiple geometry components currently. */
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;
@ -149,10 +149,16 @@ bool deg_iterator_components_step(BLI_Iterator *iter)
return false;
}
/* The mesh component. */
if (data->geometry_component_id == 0) {
data->geometry_component_id++;
/* The mesh component. */
/* 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;
@ -164,10 +170,17 @@ bool deg_iterator_components_step(BLI_Iterator *iter)
return true;
}
}
/* The pointcloud component. */
if (data->geometry_component_id == 1) {
data->geometry_component_id++;
/* The pointcloud component. */
/* 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;
@ -179,6 +192,7 @@ bool deg_iterator_components_step(BLI_Iterator *iter)
return true;
}
}
data->geometry_component_owner = nullptr;
return false;
}

View File

@ -150,14 +150,17 @@ typedef struct Object_Runtime {
*/
struct ID *data_orig;
/**
* Object data structure created during object evaluation.
* It has all modifiers applied.
* Object data structure created during object evaluation. It has all modifiers applied.
* The type is determined by the type of the original object. For example, for mesh and curve
* objects, this is a mesh. For a volume object, this is a volume.
*/
struct ID *data_eval;
/**
* Some objects support evaluating to a geometry set instead of a single ID. In those cases the
* evaluated geometry will be stored here instead of in #data_eval.
* Objects can evaluate to a geometry set instead of a single ID. In those cases, the evaluated
* geometry set will be stored here. An ID of the correct type is still stored in #data_eval.
* #geometry_set_eval might reference the ID pointed to by #data_eval as well, but does not own
* the data.
*/
struct GeometrySet *geometry_set_eval;