WIP: Volume grid attribute support in geometry nodes #110044

Closed
Lukas Tönne wants to merge 130 commits from LukasTonne/blender:geometry-nodes-flip into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
8 changed files with 193 additions and 37 deletions
Showing only changes of commit 9c42b468f2 - Show all commits

View File

@ -794,6 +794,17 @@ class MutableAttributeAccessor : public AttributeAccessor {
const eCustomDataType data_type,
const AttributeInit &initializer = AttributeInitDefaultValue());
/**
* Find an attribute with the given id, domain and data type. If it does not exist, create a new
* attribute. If the attribute does not exist and can't be created (e.g. because it already
* exists on a different domain or with a different type), none is returned.
*/
GAttributeGridWriter lookup_or_add_grid_for_write(
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer = AttributeInitDefaultValue());
/**
* Same as above, but returns a type that makes it easier to work with the attribute as a span.
* If the caller newly initializes the attribute, it's better to use

View File

@ -79,6 +79,7 @@ const VolumeGrid *BKE_volume_grid_active_get_for_read(const struct Volume *volum
/* Tries to find a grid with the given name. Make sure that the volume has been loaded. */
const VolumeGrid *BKE_volume_grid_find_for_read(const struct Volume *volume, const char *name);
VolumeGrid *BKE_volume_grid_find_for_write(struct Volume *volume, const char *name);
bool BKE_volume_grid_set_active(struct Volume *volume, const struct VolumeGrid *grid);
/* Tries to set the name of the velocity field. If no such grid exists with the given base name,
* this will try common post-fixes in order to detect velocity fields split into multiple grids.

View File

@ -959,6 +959,25 @@ GAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write(
return {};
}
GAttributeGridWriter MutableAttributeAccessor::lookup_or_add_grid_for_write(
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer)
{
std::optional<AttributeMetaData> meta_data = this->lookup_meta_data(attribute_id);
if (meta_data.has_value()) {
if (meta_data->domain == domain && meta_data->data_type == data_type) {
return this->lookup_grid_for_write(attribute_id);
}
return {};
}
if (this->add(attribute_id, domain, data_type, initializer)) {
return this->lookup_grid_for_write(attribute_id);
}
return {};
}
GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_span(
const AttributeIDRef &attribute_id,
const eAttrDomain domain,

View File

@ -919,6 +919,19 @@ VolumeGrid *BKE_volume_grid_find_for_write(Volume *volume, const char *name)
return nullptr;
}
bool BKE_volume_grid_set_active(Volume *volume, const VolumeGrid *grid)
{
int num_grids = BKE_volume_num_grids(volume);
for (int i = 0; i < num_grids; i++) {
if (grid == BKE_volume_grid_get_for_read(volume, i)) {
volume->active_grid = i;
return true;
}
}
volume->active_grid = -1;
return false;
}
/* Grid Loading */
bool BKE_volume_grid_load(const Volume *volume, const VolumeGrid *grid)

View File

@ -156,6 +156,16 @@ GMutableGrid::operator bool() const
return grid_ != nullptr;
}
const CPPType *GMutableGrid::value_type() const
{
const CPPType *type = nullptr;
grid_to_static_type(grid_, [&](auto &grid) {
using GridType = typename std::decay<decltype(grid)>::type;
type = &CPPType::get<typename GridType::ValueType>();
});
return type;
}
template<typename T> MutableGrid<T> MutableGrid<T>::create(const T &background_value)
{
typename GridType::Ptr grid = grid_types::GridCommon<ValueType>::create(background_value);

View File

@ -505,6 +505,8 @@ class VolumeFieldEvaluator : NonMovable, NonCopyable {
void (*set)(void *dst, const GGrid &grid, ResourceScope &scope) = nullptr;
};
static const GridMask empty_mask_;
ResourceScope scope_;
const FieldContext &context_;
const GridMask &mask_;
@ -524,7 +526,7 @@ class VolumeFieldEvaluator : NonMovable, NonCopyable {
{
}
VolumeFieldEvaluator(const FieldContext &context) : context_(context), mask_(GridMask()) {}
VolumeFieldEvaluator(const FieldContext &context) : context_(context), mask_(empty_mask_) {}
~VolumeFieldEvaluator()
{

View File

@ -1059,6 +1059,8 @@ static volume::GridMask grid_mask_from_selection(const volume::GridMask full_mas
return volume::GridMask::from_bools(full_mask, selection);
}
const volume::GridMask VolumeFieldEvaluator::empty_mask_ = volume::GridMask();
int VolumeFieldEvaluator::add_with_destination(GField field, GMutableGrid dst)
{
const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));

View File

@ -6,6 +6,8 @@
* \ingroup nodes
*/
#include "DNA_volume_types.h"
#include "BLI_math_euler.hh"
#include "BLI_math_quaternion.hh"
@ -19,9 +21,11 @@
#include "BKE_idprop.hh"
#include "BKE_node_runtime.hh"
#include "BKE_type_conversions.hh"
#include "BKE_volume.h"
#include "FN_field_cpp_type.hh"
#include "FN_lazy_function_execute.hh"
#include "FN_volume_field.hh"
namespace lf = blender::fn::lazy_function;
namespace geo_log = blender::nodes::geo_eval_log;
@ -385,6 +389,7 @@ struct OutputAttributeToStore {
eAttrDomain domain;
StringRefNull name;
GMutableSpan data;
volume::GMutableGrid grid_data;
};
/**
@ -442,7 +447,8 @@ static Vector<OutputAttributeToStore> compute_attributes_to_store(
for (const auto component_type : {bke::GeometryComponent::Type::Mesh,
bke::GeometryComponent::Type::PointCloud,
bke::GeometryComponent::Type::Curve,
bke::GeometryComponent::Type::Instance})
bke::GeometryComponent::Type::Instance,
bke::GeometryComponent::Type::Volume})
{
if (!geometry.has(component_type)) {
continue;
@ -458,20 +464,28 @@ static Vector<OutputAttributeToStore> compute_attributes_to_store(
const int domain_size = attributes.domain_size(domain);
bke::GeometryFieldContext field_context{component, domain};
fn::FieldEvaluator field_evaluator{field_context, domain_size};
fn::VolumeFieldEvaluator volume_field_evaluator{field_context};
for (const OutputAttributeInfo &output_info : outputs_info) {
const CPPType &type = output_info.field.cpp_type();
const bke::AttributeValidator validator = attributes.lookup_validator(output_info.name);
OutputAttributeToStore store{
component_type,
domain,
output_info.name,
GMutableSpan{
type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size}};
fn::GField field = validator.validate_field_if_necessary(output_info.field);
field_evaluator.add_with_destination(std::move(field), store.data);
OutputAttributeToStore store{component_type, domain, output_info.name};
switch (component.attribute_type()) {
case bke::GeometryComponent::AttributeType::Array:
store.data = GMutableSpan{
type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size};
field_evaluator.add_with_destination(std::move(field), store.data);
break;
case bke::GeometryComponent::AttributeType::Grid:
store.grid_data = volume::GMutableGrid::create(type);
volume_field_evaluator.add_with_destination(std::move(field), store.grid_data);
break;
}
attributes_to_store.append(store);
}
field_evaluator.evaluate();
volume_field_evaluator.evaluate();
}
}
return attributes_to_store;
@ -484,38 +498,73 @@ static void store_computed_output_attributes(
bke::GeometryComponent &component = geometry.get_component_for_write(store.component_type);
bke::MutableAttributeAccessor attributes = *component.attributes_for_write();
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(store.data.type());
const std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(
store.name);
/* Attempt to remove the attribute if it already exists but the domain and type don't match.
* Removing the attribute won't succeed if it is built in and non-removable. */
if (meta_data.has_value() &&
(meta_data->domain != store.domain || meta_data->data_type != data_type))
{
attributes.remove(store.name);
}
/* Try to create the attribute reusing the stored buffer. This will only succeed if the
* attribute didn't exist before, or if it existed but was removed above. */
if (attributes.add(store.name,
store.domain,
bke::cpp_type_to_custom_data_type(store.data.type()),
bke::AttributeInitMoveArray(store.data.data())))
{
continue;
}
switch (component.attribute_type()) {
case bke::GeometryComponent::AttributeType::Array: {
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(store.data.type());
const std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(
store.name);
bke::GAttributeWriter attribute = attributes.lookup_or_add_for_write(
store.name, store.domain, data_type);
if (attribute) {
attribute.varray.set_all(store.data.data());
attribute.finish();
}
/* Attempt to remove the attribute if it already exists but the domain and type don't
* match. Removing the attribute won't succeed if it is built in and non-removable. */
if (meta_data.has_value() &&
(meta_data->domain != store.domain || meta_data->data_type != data_type))
{
attributes.remove(store.name);
}
/* We were unable to reuse the data, so it must be destructed and freed. */
store.data.type().destruct_n(store.data.data(), store.data.size());
MEM_freeN(store.data.data());
if (attributes.add(store.name,
store.domain,
bke::cpp_type_to_custom_data_type(store.data.type()),
bke::AttributeInitMoveArray(store.data.data())))
{
continue;
}
bke::GAttributeWriter attribute = attributes.lookup_or_add_for_write(
store.name, store.domain, data_type);
if (attribute) {
attribute.varray.set_all(store.data.data());
attribute.finish();
}
/* We were unable to reuse the data, so it must be destructed and freed. */
store.data.type().destruct_n(store.data.data(), store.data.size());
MEM_freeN(store.data.data());
break;
}
case bke::GeometryComponent::AttributeType::Grid: {
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
*store.grid_data.value_type());
const std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(
store.name);
/* Attempt to remove the attribute if it already exists but the domain and type don't
* match. Removing the attribute won't succeed if it is built in and non-removable. */
if (meta_data.has_value() &&
(meta_data->domain != store.domain || meta_data->data_type != data_type))
{
attributes.remove(store.name);
}
if (attributes.add(store.name,
store.domain,
bke::cpp_type_to_custom_data_type(*store.grid_data.value_type()),
bke::AttributeInitMoveGrid(store.grid_data)))
{
continue;
}
bke::GAttributeGridWriter attribute = attributes.lookup_or_add_grid_for_write(
store.name, store.domain, data_type);
if (attribute) {
attribute.grid.try_assign(store.grid_data);
attribute.finish();
}
break;
}
}
}
}
@ -533,6 +582,54 @@ static void store_output_attributes(bke::GeometrySet &geometry,
store_computed_output_attributes(geometry, attributes_to_store);
}
static bool set_active_volume_grid(bke::GeometrySet &geometry,
const bNodeTree &tree,
const IDProperty *properties)
{
if (!geometry.has_volume()) {
return false;
}
Volume *volume = geometry.get_volume_for_write();
const bNode &output_node = *tree.group_output_node();
MultiValueMap<eAttrDomain, OutputAttributeInfo> outputs_by_domain;
for (const bNodeSocket *socket : output_node.input_sockets().drop_front(1).drop_back(1)) {
if (!socket_type_has_attribute_toggle(*socket)) {
continue;
}
const std::string prop_name = socket->identifier + input_attribute_name_suffix();
const IDProperty *prop = IDP_GetPropertyFromGroup(properties, prop_name.c_str());
if (prop == nullptr) {
continue;
}
const StringRefNull attribute_name = IDP_String(prop);
if (attribute_name.is_empty()) {
continue;
}
if (!bke::allow_procedural_attribute_access(attribute_name)) {
continue;
}
const int index = socket->index();
const bNodeSocket *interface_socket = (const bNodeSocket *)BLI_findlink(&tree.outputs, index);
const eAttrDomain domain = (eAttrDomain)interface_socket->attribute_domain;
switch (domain) {
case ATTR_DOMAIN_VOXEL: {
const VolumeGrid *grid = BKE_volume_grid_find_for_read(volume, attribute_name.c_str());
if (grid) {
/* First attribute in a volume domain becomes active. */
return BKE_volume_grid_set_active(volume, grid);
}
break;
}
default:
break;
}
}
return false;
}
bke::GeometrySet execute_geometry_nodes_on_geometry(
const bNodeTree &btree,
const IDProperty *properties,
@ -625,6 +722,7 @@ bke::GeometrySet execute_geometry_nodes_on_geometry(
bke::GeometrySet output_geometry = std::move(*param_outputs[0].get<bke::GeometrySet>());
store_output_attributes(output_geometry, btree, properties, param_outputs);
set_active_volume_grid(output_geometry, btree, properties);
for (GMutablePointer &ptr : param_outputs) {
ptr.destruct();