Geometry Nodes: move realize-instances code to blenkernel

I need to access this functionality from modifier code. Therefore it should
not be in the nodes module.
This commit is contained in:
Jacques Lucke 2021-02-16 12:30:42 +01:00
parent 21de1f9148
commit 5688f791f5
5 changed files with 268 additions and 261 deletions

View File

@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "BKE_geometry_set.hh"
namespace blender::bke {
@ -39,4 +41,21 @@ struct GeometryInstanceGroup {
Vector<GeometryInstanceGroup> geometry_set_gather_instances(const GeometrySet &geometry_set);
GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set);
struct AttributeInfo {
CustomDataType data_type;
AttributeDomain domain;
};
/**
* Add information about all the attributes on every component of the type. The resulting info
* will contain the highest complexity data type and the highest priority domain among every
* attribute with the given name on all of the input components.
*/
void gather_attribute_info(Map<std::string, AttributeInfo> &attributes,
const GeometryComponentType component_type,
Span<bke::GeometryInstanceGroup> set_groups,
const Set<std::string> &ignored_attributes);
} // namespace blender::bke

View File

@ -15,10 +15,14 @@
*/
#include "BKE_geometry_set_instances.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
#include "DNA_collection_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
namespace blender::bke {
@ -160,4 +164,247 @@ Vector<GeometryInstanceGroup> geometry_set_gather_instances(const GeometrySet &g
return result_vector;
}
void gather_attribute_info(Map<std::string, AttributeInfo> &attributes,
const GeometryComponentType component_type,
Span<GeometryInstanceGroup> set_groups,
const Set<std::string> &ignored_attributes)
{
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (!set.has(component_type)) {
continue;
}
const GeometryComponent &component = *set.get_component_for_read(component_type);
for (const std::string &name : component.attribute_names()) {
if (ignored_attributes.contains(name)) {
continue;
}
const ReadAttributePtr read_attribute = component.attribute_try_get_for_read(name);
if (!read_attribute) {
continue;
}
const AttributeDomain domain = read_attribute->domain();
const CustomDataType data_type = read_attribute->custom_data_type();
auto add_info = [&, data_type, domain](AttributeInfo *info) {
info->domain = domain;
info->data_type = data_type;
};
auto modify_info = [&, data_type, domain](AttributeInfo *info) {
info->domain = domain; /* TODO: Use highest priority domain. */
info->data_type = bke::attribute_data_type_highest_complexity(
{info->data_type, data_type});
};
attributes.add_or_modify(name, add_info, modify_info);
}
}
}
static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGroup> set_groups)
{
int totverts = 0;
int totloops = 0;
int totedges = 0;
int totpolys = 0;
int64_t cd_dirty_vert = 0;
int64_t cd_dirty_poly = 0;
int64_t cd_dirty_edge = 0;
int64_t cd_dirty_loop = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (set.has_mesh()) {
const Mesh &mesh = *set.get_mesh_for_read();
totverts += mesh.totvert * set_group.transforms.size();
totloops += mesh.totloop * set_group.transforms.size();
totedges += mesh.totedge * set_group.transforms.size();
totpolys += mesh.totpoly * set_group.transforms.size();
cd_dirty_vert |= mesh.runtime.cd_dirty_vert;
cd_dirty_poly |= mesh.runtime.cd_dirty_poly;
cd_dirty_edge |= mesh.runtime.cd_dirty_edge;
cd_dirty_loop |= mesh.runtime.cd_dirty_loop;
}
}
Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
/* Copy settings from the first input geometry set with a mesh. */
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (set.has_mesh()) {
const Mesh &mesh = *set.get_mesh_for_read();
BKE_mesh_copy_settings(new_mesh, &mesh);
break;
}
}
new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
new_mesh->runtime.cd_dirty_loop = cd_dirty_loop;
int vert_offset = 0;
int loop_offset = 0;
int edge_offset = 0;
int poly_offset = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (set.has_mesh()) {
const Mesh &mesh = *set.get_mesh_for_read();
for (const float4x4 &transform : set_group.transforms) {
for (const int i : IndexRange(mesh.totvert)) {
const MVert &old_vert = mesh.mvert[i];
MVert &new_vert = new_mesh->mvert[vert_offset + i];
new_vert = old_vert;
const float3 new_position = transform * float3(old_vert.co);
copy_v3_v3(new_vert.co, new_position);
}
for (const int i : IndexRange(mesh.totedge)) {
const MEdge &old_edge = mesh.medge[i];
MEdge &new_edge = new_mesh->medge[edge_offset + i];
new_edge = old_edge;
new_edge.v1 += vert_offset;
new_edge.v2 += vert_offset;
}
for (const int i : IndexRange(mesh.totloop)) {
const MLoop &old_loop = mesh.mloop[i];
MLoop &new_loop = new_mesh->mloop[loop_offset + i];
new_loop = old_loop;
new_loop.v += vert_offset;
new_loop.e += edge_offset;
}
for (const int i : IndexRange(mesh.totpoly)) {
const MPoly &old_poly = mesh.mpoly[i];
MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
new_poly = old_poly;
new_poly.loopstart += loop_offset;
}
vert_offset += mesh.totvert;
loop_offset += mesh.totloop;
edge_offset += mesh.totedge;
poly_offset += mesh.totpoly;
}
}
}
return new_mesh;
}
static void join_attributes(Span<GeometryInstanceGroup> set_groups,
const GeometryComponentType component_type,
const Map<std::string, AttributeInfo> &attribute_info,
GeometryComponent &result)
{
for (Map<std::string, AttributeInfo>::Item entry : attribute_info.items()) {
StringRef name = entry.key;
const AttributeDomain domain_output = entry.value.domain;
const CustomDataType data_type_output = entry.value.data_type;
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output);
BLI_assert(cpp_type != nullptr);
result.attribute_try_create(entry.key, domain_output, data_type_output);
WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name);
if (!write_attribute || &write_attribute->cpp_type() != cpp_type ||
write_attribute->domain() != domain_output) {
continue;
}
fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
int offset = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (set.has(component_type)) {
const GeometryComponent &component = *set.get_component_for_read(component_type);
const int domain_size = component.attribute_domain_size(domain_output);
if (domain_size == 0) {
continue; /* Domain size is 0, so no need to increment the offset. */
}
ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
name, domain_output, data_type_output);
if (source_attribute) {
fn::GSpan src_span = source_attribute->get_span();
const void *src_buffer = src_span.data();
for (const int UNUSED(i) : set_group.transforms.index_range()) {
void *dst_buffer = dst_span[offset];
cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size);
offset += domain_size;
}
}
else {
offset += domain_size * set_group.transforms.size();
}
}
}
write_attribute->apply_span();
}
}
static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, GeometrySet &result)
{
Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups);
MeshComponent &dst_component = result.get_component_for_write<MeshComponent>();
dst_component.replace(new_mesh);
/* The position attribute is handled above already. */
Map<std::string, AttributeInfo> attributes;
gather_attribute_info(attributes, GeometryComponentType::Mesh, set_groups, {"position"});
join_attributes(set_groups,
GeometryComponentType::Mesh,
attributes,
static_cast<GeometryComponent &>(dst_component));
}
static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_groups,
GeometrySet &result)
{
int totpoint = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (set.has<PointCloudComponent>()) {
const PointCloudComponent &component = *set.get_component_for_read<PointCloudComponent>();
totpoint += component.attribute_domain_size(ATTR_DOMAIN_POINT);
}
}
PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>();
PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint);
dst_component.replace(pointcloud);
Map<std::string, AttributeInfo> attributes;
gather_attribute_info(attributes, GeometryComponentType::Mesh, set_groups, {});
join_attributes(set_groups,
GeometryComponentType::PointCloud,
attributes,
static_cast<GeometryComponent &>(dst_component));
}
static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups,
GeometrySet &result)
{
/* Not yet supported. Joining volume grids with the same name requires resampling of at least
* one of the grids. The cell size of the resulting volume has to be determined somehow. */
VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>();
UNUSED_VARS(set_groups, dst_component);
}
GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set)
{
if (!geometry_set.has_instances()) {
return geometry_set;
}
GeometrySet new_geometry_set;
Vector<GeometryInstanceGroup> set_groups = bke::geometry_set_gather_instances(geometry_set);
join_instance_groups_mesh(set_groups, new_geometry_set);
join_instance_groups_pointcloud(set_groups, new_geometry_set);
join_instance_groups_volume(set_groups, new_geometry_set);
return new_geometry_set;
}
} // namespace blender::bke

View File

@ -20,6 +20,7 @@
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.hh"
#include "BKE_geometry_set_instances.hh"
#include "BKE_persistent_data_handle.hh"
#include "DNA_node_types.h"
@ -38,6 +39,7 @@ using bke::Float3ReadAttribute;
using bke::Float3WriteAttribute;
using bke::FloatReadAttribute;
using bke::FloatWriteAttribute;
using bke::geometry_set_realize_instances;
using bke::Int32ReadAttribute;
using bke::Int32WriteAttribute;
using bke::PersistentDataHandleMap;

View File

@ -28,249 +28,6 @@ namespace blender::nodes {
using bke::GeometryInstanceGroup;
void gather_attribute_info(Map<std::string, AttributeInfo> &attributes,
const GeometryComponentType component_type,
Span<GeometryInstanceGroup> set_groups,
const Set<std::string> &ignored_attributes)
{
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (!set.has(component_type)) {
continue;
}
const GeometryComponent &component = *set.get_component_for_read(component_type);
for (const std::string &name : component.attribute_names()) {
if (ignored_attributes.contains(name)) {
continue;
}
const ReadAttributePtr read_attribute = component.attribute_try_get_for_read(name);
if (!read_attribute) {
continue;
}
const AttributeDomain domain = read_attribute->domain();
const CustomDataType data_type = read_attribute->custom_data_type();
auto add_info = [&, data_type, domain](AttributeInfo *info) {
info->domain = domain;
info->data_type = data_type;
};
auto modify_info = [&, data_type, domain](AttributeInfo *info) {
info->domain = domain; /* TODO: Use highest priority domain. */
info->data_type = bke::attribute_data_type_highest_complexity(
{info->data_type, data_type});
};
attributes.add_or_modify(name, add_info, modify_info);
}
}
}
static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGroup> set_groups)
{
int totverts = 0;
int totloops = 0;
int totedges = 0;
int totpolys = 0;
int64_t cd_dirty_vert = 0;
int64_t cd_dirty_poly = 0;
int64_t cd_dirty_edge = 0;
int64_t cd_dirty_loop = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (set.has_mesh()) {
const Mesh &mesh = *set.get_mesh_for_read();
totverts += mesh.totvert * set_group.transforms.size();
totloops += mesh.totloop * set_group.transforms.size();
totedges += mesh.totedge * set_group.transforms.size();
totpolys += mesh.totpoly * set_group.transforms.size();
cd_dirty_vert |= mesh.runtime.cd_dirty_vert;
cd_dirty_poly |= mesh.runtime.cd_dirty_poly;
cd_dirty_edge |= mesh.runtime.cd_dirty_edge;
cd_dirty_loop |= mesh.runtime.cd_dirty_loop;
}
}
Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
/* Copy settings from the first input geometry set with a mesh. */
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (set.has_mesh()) {
const Mesh &mesh = *set.get_mesh_for_read();
BKE_mesh_copy_settings(new_mesh, &mesh);
break;
}
}
new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
new_mesh->runtime.cd_dirty_loop = cd_dirty_loop;
int vert_offset = 0;
int loop_offset = 0;
int edge_offset = 0;
int poly_offset = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (set.has_mesh()) {
const Mesh &mesh = *set.get_mesh_for_read();
for (const float4x4 &transform : set_group.transforms) {
for (const int i : IndexRange(mesh.totvert)) {
const MVert &old_vert = mesh.mvert[i];
MVert &new_vert = new_mesh->mvert[vert_offset + i];
new_vert = old_vert;
const float3 new_position = transform * float3(old_vert.co);
copy_v3_v3(new_vert.co, new_position);
}
for (const int i : IndexRange(mesh.totedge)) {
const MEdge &old_edge = mesh.medge[i];
MEdge &new_edge = new_mesh->medge[edge_offset + i];
new_edge = old_edge;
new_edge.v1 += vert_offset;
new_edge.v2 += vert_offset;
}
for (const int i : IndexRange(mesh.totloop)) {
const MLoop &old_loop = mesh.mloop[i];
MLoop &new_loop = new_mesh->mloop[loop_offset + i];
new_loop = old_loop;
new_loop.v += vert_offset;
new_loop.e += edge_offset;
}
for (const int i : IndexRange(mesh.totpoly)) {
const MPoly &old_poly = mesh.mpoly[i];
MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
new_poly = old_poly;
new_poly.loopstart += loop_offset;
}
vert_offset += mesh.totvert;
loop_offset += mesh.totloop;
edge_offset += mesh.totedge;
poly_offset += mesh.totpoly;
}
}
}
return new_mesh;
}
static void join_attributes(Span<GeometryInstanceGroup> set_groups,
const GeometryComponentType component_type,
const Map<std::string, AttributeInfo> &attribute_info,
GeometryComponent &result)
{
for (Map<std::string, AttributeInfo>::Item entry : attribute_info.items()) {
StringRef name = entry.key;
const AttributeDomain domain_output = entry.value.domain;
const CustomDataType data_type_output = entry.value.data_type;
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output);
BLI_assert(cpp_type != nullptr);
result.attribute_try_create(entry.key, domain_output, data_type_output);
WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name);
if (!write_attribute || &write_attribute->cpp_type() != cpp_type ||
write_attribute->domain() != domain_output) {
continue;
}
fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
int offset = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (set.has(component_type)) {
const GeometryComponent &component = *set.get_component_for_read(component_type);
const int domain_size = component.attribute_domain_size(domain_output);
if (domain_size == 0) {
continue; /* Domain size is 0, so no need to increment the offset. */
}
ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
name, domain_output, data_type_output);
if (source_attribute) {
fn::GSpan src_span = source_attribute->get_span();
const void *src_buffer = src_span.data();
for (const int UNUSED(i) : set_group.transforms.index_range()) {
void *dst_buffer = dst_span[offset];
cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size);
offset += domain_size;
}
}
else {
offset += domain_size * set_group.transforms.size();
}
}
}
write_attribute->apply_span();
}
}
static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, GeometrySet &result)
{
Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups);
MeshComponent &dst_component = result.get_component_for_write<MeshComponent>();
dst_component.replace(new_mesh);
/* The position attribute is handled above already. */
Map<std::string, AttributeInfo> attributes;
gather_attribute_info(attributes, GeometryComponentType::Mesh, set_groups, {"position"});
join_attributes(set_groups,
GeometryComponentType::Mesh,
attributes,
static_cast<GeometryComponent &>(dst_component));
}
static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_groups,
GeometrySet &result)
{
int totpoint = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (set.has<PointCloudComponent>()) {
const PointCloudComponent &component = *set.get_component_for_read<PointCloudComponent>();
totpoint += component.attribute_domain_size(ATTR_DOMAIN_POINT);
}
}
PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>();
PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint);
dst_component.replace(pointcloud);
Map<std::string, AttributeInfo> attributes;
gather_attribute_info(attributes, GeometryComponentType::Mesh, set_groups, {});
join_attributes(set_groups,
GeometryComponentType::PointCloud,
attributes,
static_cast<GeometryComponent &>(dst_component));
}
static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups,
GeometrySet &result)
{
/* Not yet supported. Joining volume grids with the same name requires resampling of at least
* one of the grids. The cell size of the resulting volume has to be determined somehow. */
VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>();
UNUSED_VARS(set_groups, dst_component);
}
GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set)
{
if (!geometry_set.has_instances()) {
return geometry_set;
}
GeometrySet new_geometry_set;
Vector<GeometryInstanceGroup> set_groups = bke::geometry_set_gather_instances(geometry_set);
join_instance_groups_mesh(set_groups, new_geometry_set);
join_instance_groups_pointcloud(set_groups, new_geometry_set);
join_instance_groups_volume(set_groups, new_geometry_set);
return new_geometry_set;
}
/**
* Update the availability of a group of input sockets with the same name,
* used for switching between attribute inputs or single values.

View File

@ -25,7 +25,6 @@
#include "DNA_node_types.h"
#include "BKE_geometry_set_instances.hh"
#include "BKE_node.h"
#include "BLT_translation.h"
@ -48,21 +47,4 @@ void update_attribute_input_socket_availabilities(bNode &node,
Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
const AttributeDomain domain);
GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set);
struct AttributeInfo {
CustomDataType data_type;
AttributeDomain domain;
};
/**
* Add information about all the attributes on every component of the type. The resulting info
* will contain the highest complexity data type and the highest priority domain among every
* attribute with the given name on all of the input components.
*/
void gather_attribute_info(Map<std::string, AttributeInfo> &attributes,
const GeometryComponentType component_type,
Span<bke::GeometryInstanceGroup> set_groups,
const Set<std::string> &ignored_attributes);
} // namespace blender::nodes