1
1

Compare commits

...

12 Commits

Author SHA1 Message Date
04147e2864 use data type and domain enum in random attribute node 2020-11-17 17:20:15 +01:00
9a157d6532 initial attribute creation 2020-11-17 16:58:52 +01:00
7234fd5b31 initial WriteAttribute 2020-11-17 16:39:36 +01:00
6b8a52e2b1 initial random attribute node 2020-11-17 15:25:56 +01:00
2eb00bd479 cleanup 2020-11-17 15:08:49 +01:00
d755281413 simplify typed access 2020-11-17 13:55:23 +01:00
0e9567c63c cleanup 2020-11-17 13:51:33 +01:00
1faab5c82d cleanup naming 2020-11-17 13:42:44 +01:00
d3e05090a8 cleanup 2020-11-17 13:27:58 +01:00
a889254974 use simpler accessor 2020-11-17 12:45:03 +01:00
3c020f9416 initial working version 2020-11-17 12:18:25 +01:00
8786624534 wip 2020-11-16 17:17:45 +01:00
17 changed files with 880 additions and 22 deletions

View File

@@ -499,6 +499,9 @@ geometry_node_categories = [
NodeItem("GeometryNodePointDistribute"),
NodeItem("GeometryNodePointInstance"),
]),
GeometryNodeCategory("GEO_ATTRIBUTES", "Attributes", items=[
NodeItem("GeometryNodeRandomAttribute"),
]),
GeometryNodeCategory("GEO_MATH", "Misc", items=[
NodeItem("ShaderNodeMapRange"),
NodeItem("ShaderNodeClamp"),

View File

@@ -0,0 +1,212 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "FN_cpp_type.hh"
#include "BKE_attribute.h"
#include "BKE_geometry_set.hh"
struct Mesh;
namespace blender::bke {
using fn::CPPType;
class ReadAttribute {
protected:
const AttributeDomain domain_;
const CPPType &cpp_type_;
const int64_t size_;
public:
ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
: domain_(domain), cpp_type_(cpp_type), size_(size)
{
}
virtual ~ReadAttribute();
AttributeDomain domain() const
{
return domain_;
}
const CPPType &cpp_type() const
{
return cpp_type_;
}
int64_t size() const
{
return size_;
}
void get(const int64_t index, void *r_value) const
{
BLI_assert(index < size_);
this->get_internal(index, r_value);
}
protected:
/* r_value is expected to be uninitialized. */
virtual void get_internal(const int64_t index, void *r_value) const = 0;
};
class WriteAttribute {
protected:
const AttributeDomain domain_;
const CPPType &cpp_type_;
const int64_t size_;
public:
WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
: domain_(domain), cpp_type_(cpp_type), size_(size)
{
}
virtual ~WriteAttribute();
AttributeDomain domain() const
{
return domain_;
}
const CPPType &cpp_type() const
{
return cpp_type_;
}
int64_t size() const
{
return size_;
}
void get(const int64_t index, void *r_value) const
{
BLI_assert(index < size_);
this->get_internal(index, r_value);
}
void set(const int64_t index, const void *value)
{
BLI_assert(index < size_);
this->set_internal(index, value);
}
protected:
virtual void get_internal(const int64_t index, void *r_value) const = 0;
virtual void set_internal(const int64_t index, const void *value) = 0;
};
using ReadAttributePtr = std::unique_ptr<ReadAttribute>;
using WriteAttributePtr = std::unique_ptr<WriteAttribute>;
template<typename T> class TypedReadAttribute {
private:
ReadAttributePtr attribute_;
public:
TypedReadAttribute(ReadAttributePtr attribute) : attribute_(std::move(attribute))
{
BLI_assert(attribute_);
BLI_assert(attribute_->cpp_type().is<T>());
}
int64_t size() const
{
return attribute_->size();
}
T operator[](const int64_t index) const
{
BLI_assert(index < attribute_->size());
T value;
value.~T();
attribute_->get(index, &value);
return value;
}
};
template<typename T> class TypedWriteAttribute {
private:
WriteAttributePtr attribute_;
public:
TypedWriteAttribute(WriteAttributePtr attribute) : attribute_(std::move(attribute))
{
BLI_assert(attribute_);
BLI_assert(attribute_->cpp_type().is<T>());
}
int64_t size() const
{
return attribute_->size();
}
T operator[](const int64_t index) const
{
BLI_assert(index < attribute_->size());
T value;
value.~T();
attribute_->get(index, &value);
return value;
}
void set(const int64_t index, const T &value)
{
attribute_->set(index, &value);
}
};
using FloatReadAttribute = TypedReadAttribute<float>;
using Float3ReadAttribute = TypedReadAttribute<float3>;
using FloatWriteAttribute = TypedWriteAttribute<float>;
using Float3WriteAttribute = TypedWriteAttribute<float3>;
ReadAttributePtr mesh_attribute_get_for_read(const MeshComponent &mesh_component,
const StringRef attribute_name);
std::optional<WriteAttributePtr> mesh_attribute_get_for_write(MeshComponent &mesh_component,
const StringRef attribute_name);
ReadAttributePtr mesh_attribute_adapt_domain(const MeshComponent &mesh_component,
ReadAttributePtr attribute,
const AttributeDomain to_domain);
ReadAttributePtr mesh_attribute_get_for_read(const MeshComponent &mesh_component,
const StringRef attribute_name,
const CPPType &cpp_type,
const AttributeDomain domain,
const void *default_value = nullptr);
template<typename T>
TypedReadAttribute<T> mesh_attribute_get_for_read(const MeshComponent &mesh_component,
const StringRef attribute_name,
const AttributeDomain domain,
const T &default_value)
{
ReadAttributePtr attribute = mesh_attribute_get_for_read(
mesh_component,
attribute_name,
CPPType::get<T>(),
domain,
static_cast<const void *>(&default_value));
BLI_assert(attribute);
return attribute;
}
} // namespace blender::bke

View File

@@ -1345,6 +1345,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_POINT_INSTANCE 1005
#define GEO_NODE_SUBDIVISION_SURFACE 1006
#define GEO_NODE_OBJECT_INFO 1007
#define GEO_NODE_RANDOM_ATTRIBUTE 1008
/** \} */

View File

@@ -78,6 +78,7 @@ set(SRC
intern/armature_deform.c
intern/armature_update.c
intern/attribute.c
intern/attribute_accessor.cc
intern/autoexec.c
intern/blender.c
intern/blender_copybuffer.c
@@ -267,6 +268,7 @@ set(SRC
BKE_appdir.h
BKE_armature.h
BKE_attribute.h
BKE_attribute_accessor.hh
BKE_autoexec.h
BKE_blender.h
BKE_blender_copybuffer.h

View File

@@ -0,0 +1,478 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <utility>
#include "BKE_attribute_accessor.hh"
#include "BKE_deform.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_color.hh"
#include "BLI_float2.hh"
#include "BLI_span.hh"
namespace blender::bke {
ReadAttribute::~ReadAttribute() = default;
WriteAttribute::~WriteAttribute() = default;
class VertexWeightWriteAttribute final : public WriteAttribute {
private:
MutableSpan<MDeformVert> dverts_;
const int dvert_index_;
public:
VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
: WriteAttribute(ATTR_DOMAIN_VERTEX, CPPType::get<float>(), totvert),
dverts_(dverts, totvert),
dvert_index_(dvert_index)
{
}
void get_internal(const int64_t index, void *r_value) const override
{
this->get_internal(dverts_, dvert_index_, index, r_value);
}
void set_internal(const int64_t index, const void *value) override
{
MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
weight->weight = *reinterpret_cast<const float *>(value);
}
static void get_internal(const Span<MDeformVert> dverts,
const int dvert_index,
const int64_t index,
void *r_value)
{
const MDeformVert &dvert = dverts[index];
for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
if (weight.def_nr == dvert_index) {
*(float *)r_value = weight.weight;
return;
}
}
*(float *)r_value = 0.0f;
}
};
class VertexWeightReadAttribute final : public ReadAttribute {
private:
const Span<MDeformVert> dverts_;
const int dvert_index_;
public:
VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
: ReadAttribute(ATTR_DOMAIN_VERTEX, CPPType::get<float>(), totvert),
dverts_(dverts, totvert),
dvert_index_(dvert_index)
{
}
void get_internal(const int64_t index, void *r_value) const override
{
VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
}
};
template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
private:
MutableSpan<T> data_;
public:
ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data)
: WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
{
}
void get_internal(const int64_t index, void *r_value) const override
{
new (r_value) T(data_[index]);
}
void set_internal(const int64_t index, const void *value) override
{
data_[index] = *reinterpret_cast<const T *>(value);
}
};
template<typename T> class ArrayReadAttribute final : public ReadAttribute {
private:
Span<T> data_;
public:
ArrayReadAttribute(AttributeDomain domain, Span<T> data)
: ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
{
}
void get_internal(const int64_t index, void *r_value) const override
{
new (r_value) T(data_[index]);
}
};
template<typename StructT, typename ElemT, typename GetFuncT, typename SetFuncT>
class DerivedArrayWriteAttribute final : public WriteAttribute {
private:
MutableSpan<StructT> data_;
GetFuncT get_function_;
SetFuncT set_function_;
public:
DerivedArrayWriteAttribute(AttributeDomain domain,
MutableSpan<StructT> data,
GetFuncT get_function,
SetFuncT set_function)
: WriteAttribute(domain, CPPType::get<ElemT>(), data.size()),
data_(data),
get_function_(std::move(get_function)),
set_function_(std::move(set_function))
{
}
void get_internal(const int64_t index, void *r_value) const override
{
const StructT &struct_value = data_[index];
const ElemT value = get_function_(struct_value);
new (r_value) ElemT(value);
}
void set_internal(const int64_t index, const void *value) override
{
StructT &struct_value = data_[index];
const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
set_function_(struct_value, typed_value);
}
};
template<typename StructT, typename ElemT, typename GetFuncT>
class DerivedArrayReadAttribute final : public ReadAttribute {
private:
Span<StructT> data_;
GetFuncT get_function_;
public:
DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data, GetFuncT get_function)
: ReadAttribute(domain, CPPType::get<ElemT>(), data.size()),
data_(data),
get_function_(std::move(get_function))
{
}
void get_internal(const int64_t index, void *r_value) const override
{
const StructT &struct_value = data_[index];
const ElemT value = get_function_(struct_value);
new (r_value) ElemT(value);
}
};
class ConstantReadAttribute final : public ReadAttribute {
private:
void *value_;
public:
ConstantReadAttribute(AttributeDomain domain,
const int64_t size,
const CPPType &type,
const void *value)
: ReadAttribute(domain, type, size)
{
value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
type.copy_to_uninitialized(value, value_);
}
void get_internal(const int64_t UNUSED(index), void *r_value) const override
{
this->cpp_type_.copy_to_uninitialized(value_, r_value);
}
};
static ReadAttributePtr mesh_attribute_custom_data_read(const CustomData &custom_data,
const int size,
const StringRef attribute_name,
const AttributeDomain domain)
{
for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) {
if (layer.name != nullptr && layer.name == attribute_name) {
switch (layer.type) {
case CD_PROP_FLOAT:
return std::make_unique<ArrayReadAttribute<float>>(
domain, Span(static_cast<float *>(layer.data), size));
case CD_PROP_FLOAT2:
return std::make_unique<ArrayReadAttribute<float2>>(
domain, Span(static_cast<float2 *>(layer.data), size));
case CD_PROP_FLOAT3:
return std::make_unique<ArrayReadAttribute<float3>>(
domain, Span(static_cast<float3 *>(layer.data), size));
case CD_PROP_INT32:
return std::make_unique<ArrayReadAttribute<int>>(
domain, Span(static_cast<int *>(layer.data), size));
case CD_PROP_COLOR:
return std::make_unique<ArrayReadAttribute<Color4f>>(
domain, Span(static_cast<Color4f *>(layer.data), size));
}
}
}
return {};
}
static WriteAttributePtr mesh_attribute_custom_data_write(CustomData custom_data,
const int size,
const StringRef attribute_name,
const AttributeDomain domain)
{
for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) {
if (layer.name != nullptr && layer.name == attribute_name) {
switch (layer.type) {
case CD_PROP_FLOAT:
return std::make_unique<ArrayWriteAttribute<float>>(
domain, MutableSpan(static_cast<float *>(layer.data), size));
case CD_PROP_FLOAT2:
return std::make_unique<ArrayWriteAttribute<float2>>(
domain, MutableSpan(static_cast<float2 *>(layer.data), size));
case CD_PROP_FLOAT3:
return std::make_unique<ArrayWriteAttribute<float3>>(
domain, MutableSpan(static_cast<float3 *>(layer.data), size));
case CD_PROP_INT32:
return std::make_unique<ArrayWriteAttribute<int>>(
domain, MutableSpan(static_cast<int *>(layer.data), size));
case CD_PROP_COLOR:
return std::make_unique<ArrayWriteAttribute<Color4f>>(
domain, MutableSpan(static_cast<Color4f *>(layer.data), size));
}
}
}
return {};
}
ReadAttributePtr mesh_attribute_get_for_read(const MeshComponent &mesh_component,
const StringRef attribute_name)
{
const Mesh *mesh = mesh_component.get_for_read();
if (mesh == nullptr) {
return {};
}
ReadAttributePtr corner_attribute = mesh_attribute_custom_data_read(
mesh->ldata, mesh->totloop, attribute_name, ATTR_DOMAIN_CORNER);
if (corner_attribute) {
return corner_attribute;
}
if (attribute_name == "Position") {
auto get_vertex_position = [](const MVert &vert) { return float3(vert.co); };
return std::make_unique<
DerivedArrayReadAttribute<MVert, float3, decltype(get_vertex_position)>>(
ATTR_DOMAIN_VERTEX, Span(mesh->mvert, mesh->totvert), get_vertex_position);
}
const int vertex_group_index = mesh_component.vertex_group_index(attribute_name);
if (vertex_group_index >= 0) {
return std::make_unique<VertexWeightReadAttribute>(
mesh->dvert, mesh->totvert, vertex_group_index);
}
ReadAttributePtr vertex_attribute = mesh_attribute_custom_data_read(
mesh->vdata, mesh->totvert, attribute_name, ATTR_DOMAIN_VERTEX);
if (vertex_attribute) {
return vertex_attribute;
}
ReadAttributePtr edge_attribute = mesh_attribute_custom_data_read(
mesh->edata, mesh->totedge, attribute_name, ATTR_DOMAIN_EDGE);
if (edge_attribute) {
return edge_attribute;
}
ReadAttributePtr polygon_attribute = mesh_attribute_custom_data_read(
mesh->pdata, mesh->totpoly, attribute_name, ATTR_DOMAIN_POLYGON);
if (polygon_attribute) {
return polygon_attribute;
}
return {};
}
std::optional<WriteAttributePtr> mesh_attribute_get_for_write(MeshComponent &mesh_component,
const StringRef attribute_name)
{
Mesh *mesh = mesh_component.get_for_write();
if (mesh == nullptr) {
return {};
}
WriteAttributePtr corner_attribute = mesh_attribute_custom_data_write(
mesh->ldata, mesh->totloop, attribute_name, ATTR_DOMAIN_CORNER);
if (corner_attribute) {
return corner_attribute;
}
if (attribute_name == "Position") {
auto get_vertex_position = [](const MVert &vert) { return float3(vert.co); };
auto set_vertex_position = [](MVert &vert, const float3 &co) { copy_v3_v3(vert.co, co); };
return std::make_unique<DerivedArrayWriteAttribute<MVert,
float3,
decltype(get_vertex_position),
decltype(set_vertex_position)>>(
ATTR_DOMAIN_VERTEX,
MutableSpan(mesh->mvert, mesh->totvert),
get_vertex_position,
set_vertex_position);
}
const int vertex_group_index = mesh_component.vertex_group_index(attribute_name);
if (vertex_group_index >= 0) {
return std::make_unique<VertexWeightWriteAttribute>(
mesh->dvert, mesh->totvert, vertex_group_index);
}
WriteAttributePtr vertex_attribute = mesh_attribute_custom_data_write(
mesh->vdata, mesh->totvert, attribute_name, ATTR_DOMAIN_VERTEX);
if (vertex_attribute) {
return vertex_attribute;
}
WriteAttributePtr edge_attribute = mesh_attribute_custom_data_write(
mesh->edata, mesh->totedge, attribute_name, ATTR_DOMAIN_EDGE);
if (edge_attribute) {
return edge_attribute;
}
WriteAttributePtr polygon_attribute = mesh_attribute_custom_data_write(
mesh->pdata, mesh->totpoly, attribute_name, ATTR_DOMAIN_POLYGON);
if (polygon_attribute) {
return polygon_attribute;
}
return {};
}
static ReadAttributePtr adapt_mesh_attribute_to_corner(const MeshComponent &UNUSED(mesh_component),
ReadAttributePtr UNUSED(attribute))
{
return {};
}
static ReadAttributePtr adapt_mesh_attribute_to_vertex(const MeshComponent &UNUSED(mesh_component),
ReadAttributePtr UNUSED(attribute))
{
return {};
}
static ReadAttributePtr adapt_mesh_attribute_to_edge(const MeshComponent &UNUSED(mesh_component),
ReadAttributePtr UNUSED(attribute))
{
return {};
}
static ReadAttributePtr adapt_mesh_attribute_to_polygon(
const MeshComponent &UNUSED(mesh_component), ReadAttributePtr UNUSED(attribute))
{
return {};
}
ReadAttributePtr mesh_attribute_adapt_domain(const MeshComponent &mesh_component,
ReadAttributePtr attribute,
const AttributeDomain to_domain)
{
if (!attribute) {
return {};
}
const AttributeDomain from_domain = attribute->domain();
if (from_domain == to_domain) {
return attribute;
}
switch (to_domain) {
case ATTR_DOMAIN_CORNER:
return adapt_mesh_attribute_to_corner(mesh_component, std::move(attribute));
case ATTR_DOMAIN_VERTEX:
return adapt_mesh_attribute_to_vertex(mesh_component, std::move(attribute));
case ATTR_DOMAIN_EDGE:
return adapt_mesh_attribute_to_edge(mesh_component, std::move(attribute));
case ATTR_DOMAIN_POLYGON:
return adapt_mesh_attribute_to_polygon(mesh_component, std::move(attribute));
default:
return {};
}
}
static int get_domain_length(const MeshComponent &mesh_component, const AttributeDomain domain)
{
const Mesh *mesh = mesh_component.get_for_read();
if (mesh == nullptr) {
return 0;
}
switch (domain) {
case ATTR_DOMAIN_CORNER:
return mesh->totloop;
case ATTR_DOMAIN_VERTEX:
return mesh->totvert;
case ATTR_DOMAIN_EDGE:
return mesh->totedge;
case ATTR_DOMAIN_POLYGON:
return mesh->totpoly;
default:
break;
}
return 0;
}
static ReadAttributePtr make_default_attribute(const MeshComponent &mesh_component,
const AttributeDomain domain,
const CPPType &cpp_type,
const void *default_value)
{
const int length = get_domain_length(mesh_component, domain);
return std::make_unique<ConstantReadAttribute>(domain, length, cpp_type, default_value);
}
ReadAttributePtr mesh_attribute_get_for_read(const MeshComponent &mesh_component,
const StringRef attribute_name,
const CPPType &cpp_type,
const AttributeDomain domain,
const void *default_value)
{
ReadAttributePtr attribute = mesh_attribute_get_for_read(mesh_component, attribute_name);
auto get_default_or_empty = [&]() -> ReadAttributePtr {
if (default_value != nullptr) {
return make_default_attribute(mesh_component, domain, cpp_type, default_value);
}
return {};
};
if (!attribute) {
return get_default_or_empty();
}
if (attribute->domain() != domain) {
attribute = mesh_attribute_adapt_domain(mesh_component, std::move(attribute), domain);
}
if (!attribute) {
return get_default_or_empty();
}
if (attribute->cpp_type() != cpp_type) {
/* TODO: Support some type conversions. */
return get_default_or_empty();
}
return attribute;
}
} // namespace blender::bke

View File

@@ -4688,6 +4688,7 @@ static void registerGeometryNodes(void)
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();
register_node_type_geo_object_info();
register_node_type_geo_random_attribute();
}
static void registerFunctionNodes(void)

View File

@@ -80,6 +80,13 @@ struct float2 {
return *this;
}
uint64_t hash() const
{
uint64_t x1 = *reinterpret_cast<const uint32_t *>(&x);
uint64_t x2 = *reinterpret_cast<const uint32_t *>(&y);
return (x1 * 812519) ^ (x2 * 707951);
}
friend float2 operator+(const float2 &a, const float2 &b)
{
return {a.x + b.x, a.y + b.y};

View File

@@ -3163,6 +3163,14 @@ static void node_geometry_buts_triangulate(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "ngon_method", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_geometry_buts_random_attribute(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "domain", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_geometry_set_butfunc(bNodeType *ntype)
{
switch (ntype->type) {
@@ -3175,6 +3183,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
case GEO_NODE_TRIANGULATE:
ntype->draw_buttons = node_geometry_buts_triangulate;
break;
case GEO_NODE_RANDOM_ATTRIBUTE:
ntype->draw_buttons = node_geometry_buts_random_attribute;
break;
}
}

View File

@@ -26,6 +26,7 @@ namespace blender::fn {
MAKE_CPP_TYPE(bool, bool)
MAKE_CPP_TYPE(float, float)
MAKE_CPP_TYPE(float2, blender::float2)
MAKE_CPP_TYPE(float3, blender::float3)
MAKE_CPP_TYPE(float4x4, blender::float4x4)

View File

@@ -36,6 +36,7 @@
#include "DNA_texture_types.h"
#include "BKE_animsys.h"
#include "BKE_attribute.h"
#include "BKE_image.h"
#include "BKE_node.h"
#include "BKE_texture.h"
@@ -8258,6 +8259,30 @@ static void def_geo_triangulate(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_attribute_create_common(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom2");
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
RNA_def_property_enum_default(prop, ATTR_DOMAIN_VERTEX);
RNA_def_property_ui_text(prop, "Domain", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_random_attribute(StructRNA *srna)
{
def_geo_attribute_create_common(srna);
}
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)

View File

@@ -145,6 +145,7 @@ set(SRC
geometry/nodes/node_geo_subdivision_surface.cc
geometry/nodes/node_geo_point_distribute.cc
geometry/nodes/node_geo_point_instance.cc
geometry/nodes/node_geo_random_attribute.cc
geometry/nodes/node_geo_transform.cc
geometry/nodes/node_geo_triangulate.cc
geometry/node_geometry_exec.cc

View File

@@ -34,6 +34,7 @@ void register_node_type_geo_triangulate(void);
void register_node_type_geo_point_distribute(void);
void register_node_type_geo_point_instance(void);
void register_node_type_geo_object_info(void);
void register_node_type_geo_random_attribute(void);
#ifdef __cplusplus
}

View File

@@ -18,6 +18,7 @@
#include "FN_generic_value_map.hh"
#include "BKE_attribute_accessor.hh"
#include "BKE_geometry_set.hh"
#include "BKE_persistent_data_handle.hh"
@@ -25,8 +26,16 @@
namespace blender::nodes {
using bke::Float3ReadAttribute;
using bke::Float3WriteAttribute;
using bke::FloatReadAttribute;
using bke::FloatWriteAttribute;
using bke::PersistentDataHandleMap;
using bke::PersistentObjectHandle;
using bke::ReadAttribute;
using bke::ReadAttributePtr;
using bke::WriteAttribute;
using bke::WriteAttributePtr;
using fn::CPPType;
using fn::GMutablePointer;
using fn::GValueMap;

View File

@@ -274,6 +274,7 @@ DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Bo
DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, 0, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, 0, "POINT_INSTANCE", PointInstance, "Point Instance", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "")
DefNode(GeometryNode, GEO_NODE_RANDOM_ATTRIBUTE, def_geo_random_attribute, "RANDOM_ATTRIBUTE", RandomAttribute, "Random Attribute", "")
/* undefine macros */
#undef DefNode

View File

@@ -47,22 +47,12 @@ namespace blender::nodes {
static Vector<float3> scatter_points_from_mesh(const Mesh *mesh,
const float density,
const int density_attribute_index)
const FloatReadAttribute &density_factors)
{
/* This only updates a cache and can be considered to be logically const. */
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(mesh));
const int looptris_len = BKE_mesh_runtime_looptri_len(mesh);
Array<float> vertex_density_factors(mesh->totvert);
if (density_attribute_index == -1) {
vertex_density_factors.fill(1.0f);
}
else {
MDeformVert *dverts = mesh->dvert;
BKE_defvert_extract_vgroup_to_vertweights(
dverts, density_attribute_index, mesh->totvert, vertex_density_factors.data(), false);
}
Vector<float3> points;
for (const int looptri_index : IndexRange(looptris_len)) {
@@ -73,9 +63,9 @@ static Vector<float3> scatter_points_from_mesh(const Mesh *mesh,
const float3 v0_pos = mesh->mvert[v0_index].co;
const float3 v1_pos = mesh->mvert[v1_index].co;
const float3 v2_pos = mesh->mvert[v2_index].co;
const float v0_density_factor = vertex_density_factors[v0_index];
const float v1_density_factor = vertex_density_factors[v1_index];
const float v2_density_factor = vertex_density_factors[v2_index];
const float v0_density_factor = density_factors[v0_index];
const float v1_density_factor = density_factors[v1_index];
const float v2_density_factor = density_factors[v2_index];
const float looptri_density_factor = (v0_density_factor + v1_density_factor +
v2_density_factor) /
3.0f;
@@ -121,8 +111,11 @@ static void geo_point_distribute_exec(GeoNodeExecParams params)
const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
const Mesh *mesh_in = mesh_component.get_for_read();
const int density_attribute_index = mesh_component.vertex_group_index(density_attribute);
Vector<float3> points = scatter_points_from_mesh(mesh_in, density, density_attribute_index);
const FloatReadAttribute density_factors = bke::mesh_attribute_get_for_read<float>(
mesh_component, density_attribute, ATTR_DOMAIN_VERTEX, 1.0f);
Vector<float3> points = scatter_points_from_mesh(mesh_in, density, density_factors);
PointCloud *pointcloud = BKE_pointcloud_new_nomain(points.size());
memcpy(pointcloud->co, points.data(), sizeof(float3) * points.size());

View File

@@ -38,15 +38,17 @@ static void geo_point_instance_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
Vector<float3> positions;
Vector<float3> instance_positions;
if (geometry_set.has_pointcloud()) {
const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read();
positions.extend((const float3 *)pointcloud->co, pointcloud->totpoint);
instance_positions.extend((const float3 *)pointcloud->co, pointcloud->totpoint);
}
if (geometry_set.has_mesh()) {
const Mesh *mesh = geometry_set.get_mesh_for_read();
for (const int i : IndexRange(mesh->totvert)) {
positions.append(mesh->mvert[i].co);
const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
Float3ReadAttribute positions = bke::mesh_attribute_get_for_read<float3>(
mesh_component, "Position", ATTR_DOMAIN_VERTEX, {0, 0, 0});
for (const int i : IndexRange(positions.size())) {
instance_positions.append(positions[i]);
}
}
@@ -55,7 +57,7 @@ static void geo_point_instance_exec(GeoNodeExecParams params)
Object *object = params.handle_map().lookup(object_handle);
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
instances.replace(std::move(positions), object);
instances.replace(std::move(instance_positions), object);
params.set_output("Geometry", std::move(geometry_set));
}

View File

@@ -0,0 +1,110 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "node_geometry_util.hh"
#include "BLI_rand.hh"
#include "DNA_mesh_types.h"
static bNodeSocketTemplate geo_node_random_attribute_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
{SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_VECTOR, N_("Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};
static bNodeSocketTemplate geo_node_random_attribute_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_attribute_random_init(bNodeTree *UNUSED(tree), bNode *node)
{
node->custom1 = CD_PROP_FLOAT;
}
namespace blender::nodes {
static void geo_random_attribute_exec(GeoNodeExecParams params)
{
const bNode &node = params.node();
const int data_type = node.custom1;
const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2);
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const std::string attribute_name = params.extract_input<std::string>("Attribute");
const float3 min_value = params.extract_input<float3>("Min");
const float3 max_value = params.extract_input<float3>("Max");
const int seed = params.extract_input<int>("Seed");
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
Mesh *mesh = mesh_component.get_for_write();
if (mesh == nullptr) {
params.set_output("Geometry", geometry_set);
return;
}
std::optional<WriteAttributePtr> attribute_opt = bke::mesh_attribute_get_for_write(
mesh_component, attribute_name);
if (!attribute_opt.has_value()) {
BKE_id_attribute_new(&mesh->id, attribute_name.c_str(), data_type, domain, nullptr);
attribute_opt = bke::mesh_attribute_get_for_write(mesh_component, attribute_name);
}
RandomNumberGenerator rng;
rng.seed_random(seed);
if (attribute_opt.has_value()) {
WriteAttributePtr attribute = std::move(*attribute_opt);
const int size = attribute->size();
if (attribute->cpp_type().is<float>()) {
FloatWriteAttribute float_attribute = std::move(attribute);
for (const int i : IndexRange(size)) {
const float value = rng.get_float() * (max_value.x - min_value.x) + min_value.x;
float_attribute.set(i, value);
}
}
else if (attribute->cpp_type().is<float3>()) {
Float3WriteAttribute float3_attribute = std::move(attribute);
for (const int i : IndexRange(size)) {
const float x = rng.get_float();
const float y = rng.get_float();
const float z = rng.get_float();
const float3 value = float3(x, y, z) * (max_value - min_value) + min_value;
float3_attribute.set(i, value);
}
}
}
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_random_attribute()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_RANDOM_ATTRIBUTE, "Random Attribute", 0, 0);
node_type_socket_templates(&ntype, geo_node_random_attribute_in, geo_node_random_attribute_out);
node_type_init(&ntype, geo_attribute_random_init);
ntype.geometry_node_execute = blender::nodes::geo_random_attribute_exec;
nodeRegisterType(&ntype);
}