1
1

Compare commits

...

51 Commits

Author SHA1 Message Date
277117826f properly interpolate selection between some domains 2021-08-10 10:39:07 +02:00
71e3f18b4a add Store Local Attribute node 2021-08-09 12:54:02 +02:00
92b8d2f7e4 support accessing index and normal 2021-08-09 12:17:19 +02:00
d4223da817 support list input in Point Separate node 2021-08-09 11:50:30 +02:00
1ee857b4e7 Merge branch 'master' into temp-geometry-nodes-expandable-geometry-socket-prototype 2021-08-09 11:31:24 +02:00
1a22c90583 better split 2021-08-06 17:04:20 +02:00
cb38233f6c improve automatic array source detection in expander 2021-08-06 16:46:24 +02:00
14103793de support lists in point translate node 2021-08-06 16:14:42 +02:00
b6c0c06420 support outputting attribute to modifier 2021-08-06 16:00:48 +02:00
bfa882c2f4 improve extrude node 2021-08-06 14:36:17 +02:00
e9ab6ff164 support array in fill node for debugging purposes 2021-08-06 13:37:29 +02:00
8d4791d92d support array input/output in extrude node 2021-08-06 13:28:49 +02:00
c50478674c add initial Extrude node by Fabian 2021-08-06 12:56:13 +02:00
adbeabb13c support array input in point distribute node 2021-08-06 12:40:46 +02:00
4a248986d0 add geometry output to geometry expander 2021-08-06 12:18:45 +02:00
f9b948d4fe get input attribute names from modifier 2021-08-06 11:56:11 +02:00
9eb238db2d add remove button 2021-08-06 11:45:08 +02:00
c596e6b277 workaround for attribute inputs: use string sockets 2021-08-06 11:36:28 +02:00
a2a2f7c95b output more attributes from point distribute node 2021-08-06 10:40:41 +02:00
bcb74106a0 add more inputst to Point Instance node 2021-08-06 10:29:09 +02:00
f204b3d16c Merge branch 'master' into temp-geometry-nodes-expandable-geometry-socket-prototype 2021-08-06 10:21:02 +02:00
6343cd913d support execution geometry expander 2021-08-05 17:27:00 +02:00
d5dc3501b5 show warning when expander output is not available 2021-08-05 16:14:51 +02:00
fa37d09040 reuse GeometryExpanderOutput struct 2021-08-05 15:53:48 +02:00
0ce5b5be3b Merge branch 'master' into temp-geometry-nodes-expandable-geometry-socket-prototype 2021-08-05 14:24:26 +02:00
4fc03dd96b add noise node 2021-08-04 17:22:49 +02:00
7fcf2ad543 handle node rename 2021-08-04 17:09:20 +02:00
3e413d9289 improve expander attribute search 2021-08-04 16:41:09 +02:00
5afddf257d add plus icon to rotation output in point distribute node 2021-08-04 15:16:41 +02:00
297f7062a8 progress 2021-08-04 13:59:44 +02:00
f2b51d960d support rotation input in instance node 2021-08-04 13:41:33 +02:00
65283828ea output rotations from point distribute node 2021-08-04 13:17:53 +02:00
1cad21295e improve search 2021-08-04 13:10:56 +02:00
503114993f quiet warnings 2021-08-04 12:40:20 +02:00
3064492272 Merge branch 'master' into temp-geometry-nodes-expandable-geometry-socket-prototype 2021-08-04 12:34:52 +02:00
df115dec05 show domain dropdown in socket 2021-08-03 14:47:19 +02:00
f8a23fb05a fix passing values from modifier 2021-08-03 13:45:34 +02:00
3f1603a8f8 initial socket draw override 2021-08-03 13:41:53 +02:00
5d0409c25c initial array output 2021-08-03 13:12:18 +02:00
b78014af97 initial search when adding expander output 2021-08-03 12:39:45 +02:00
ef611546d6 keep already existing links when adding new sockets 2021-08-03 12:08:23 +02:00
4bc27fcdf0 initial support for adding geometry expander outputs 2021-08-03 11:48:51 +02:00
4e8935c7b7 Merge branch 'master' into temp-geometry-nodes-expandable-geometry-socket-prototype 2021-08-03 10:47:36 +02:00
5a5f5a1350 initial geometry expander node storage 2021-08-02 16:37:22 +02:00
60adfd93af initial geometry expander node 2021-08-02 15:59:58 +02:00
ac8fbb2768 support socket inspection 2021-08-02 15:50:32 +02:00
3c4869bf48 support implicit conversion for arrays 2021-08-02 15:39:15 +02:00
379344b636 support function nodes on arrays 2021-08-02 15:04:02 +02:00
dd71f59d35 handle array in GeoNodeExecParams 2021-08-02 14:21:01 +02:00
9d9395ef42 load arrays from sockets 2021-08-02 13:43:45 +02:00
92518a7057 initial array cpp type 2021-08-02 13:29:18 +02:00
38 changed files with 2374 additions and 236 deletions

View File

@@ -493,6 +493,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeAttributeRemove"),
NodeItem("GeometryNodeAttributeMapRange"),
NodeItem("GeometryNodeAttributeTransfer"),
NodeItem("GeometryNodeAttributeStoreLocal"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeRGBCurve"),
@@ -531,6 +532,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeJoinGeometry"),
NodeItem("GeometryNodeSeparateComponents"),
NodeItem("GeometryNodeRaycast"),
NodeItem("GeometryNodeGeometryExpander"),
]),
GeometryNodeCategory("GEO_INPUT", "Input", items=[
NodeItem("GeometryNodeObjectInfo"),
@@ -553,6 +555,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeEdgeSplit"),
NodeItem("GeometryNodeSubdivisionSurface"),
NodeItem("GeometryNodeMeshSubdivide"),
NodeItem("GeometryNodeExtrude"),
]),
GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[
NodeItem("GeometryNodeMeshCircle"),
@@ -582,6 +585,7 @@ geometry_node_categories = [
NodeItem("FunctionNodeFloatCompare"),
NodeItem("FunctionNodeFloatToInt"),
NodeItem("GeometryNodeSwitch"),
NodeItem("ShaderNodeTexNoise"),
]),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
NodeItem("ShaderNodeVectorCurve"),

View File

@@ -345,6 +345,10 @@ class MeshComponent : public GeometryComponent {
const AttributeDomain from_domain,
const AttributeDomain to_domain) const final;
blender::VArrayPtr<bool> adapt_selection(blender::VArrayPtr<bool> selection,
AttributeDomain from_domain,
AttributeDomain to_domain) const;
bool is_empty() const final;
bool owns_direct_data() const override;

View File

@@ -223,6 +223,11 @@ typedef int (*NodeGPUExecFunction)(struct GPUMaterial *mat,
struct bNodeExecData *execdata,
struct GPUNodeStack *in,
struct GPUNodeStack *out);
typedef bool (*NodeDrawSocketFunction)(const struct bContext *C,
struct uiLayout *layout,
struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocket *socket);
/**
* \brief Defines a node type.
@@ -339,6 +344,10 @@ typedef struct bNodeType {
NodeGeometryExecFunction geometry_node_execute;
bool geometry_node_execute_supports_laziness;
/* Draws a socket in a way specific to this node. If false is returned, the socket will be drawn
* as usual. */
NodeDrawSocketFunction draw_socket;
/* RNA integration */
ExtensionRNA rna_ext;
} bNodeType;
@@ -726,6 +735,9 @@ void nodeSetSocketAvailability(struct bNodeSocket *sock, bool is_available);
int nodeSocketLinkLimit(const struct bNodeSocket *sock);
void nodeGeometryExpanderUpdateOutputNameCache(struct GeometryExpanderOutput *expander_output,
const bNodeTree *ntree);
/* Node Clipboard */
void BKE_node_clipboard_init(const struct bNodeTree *ntree);
void BKE_node_clipboard_clear(void);
@@ -1477,6 +1489,9 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_CURVE_SET_HANDLES 1072
#define GEO_NODE_CURVE_SPLINE_TYPE 1073
#define GEO_NODE_CURVE_SELECT_HANDLES 1074
#define GEO_NODE_GEOMETRY_EXPANDER 1075
#define GEO_NODE_EXTRUDE 1076
#define GEO_NODE_ATTRIBUTE_STORE_LOCAL 1077
/** \} */

View File

@@ -988,9 +988,25 @@ std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_r
std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray);
if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) {
varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain);
if (!varray) {
return {};
if (this->type() == GEO_COMPONENT_TYPE_MESH && data_type == CD_PROP_BOOL &&
varray->type().is<bool>()) {
/* TODO: Not all boolean attributes are selections. */
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(*this);
blender::VArrayPtr<bool> varray_bool =
std::make_unique<blender::fn::VArray_For_OwnedGVArray<bool>>(std::move(varray));
varray_bool = mesh_component.adapt_selection(
std::move(varray_bool), attribute.domain, domain);
if (!varray_bool) {
return {};
}
varray = std::make_unique<blender::fn::GVArray_For_OwnedVArray<bool>>(
std::move(varray_bool));
}
else {
varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain);
if (!varray) {
return {};
}
}
}

View File

@@ -651,6 +651,207 @@ blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
return {};
}
namespace blender::bke::adapt_selection_domain {
static VArrayPtr<bool> varray_from_array(Array<bool> array)
{
return std::make_unique<VArray_For_ArrayContainer<Array<bool>>>(std::move(array));
}
static VArrayPtr<bool> adapt_selection_point_to_face(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totpoly);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
bool poly_is_selected = true;
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
if (!selection->get(loop.v)) {
poly_is_selected = false;
break;
}
}
new_selection[poly_index] = poly_is_selected;
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_point_to_corner(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totloop);
for (const int loop_index : IndexRange(mesh.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
new_selection[loop_index] = selection->get(loop.v);
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_point_to_edge(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totedge);
for (const int edge_index : IndexRange(mesh.totedge)) {
const MEdge &edge = mesh.medge[edge_index];
const bool edge_is_selected = selection->get(edge.v1) && selection->get(edge.v2);
new_selection[edge_index] = edge_is_selected;
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_edge_to_point(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totvert, false);
for (const int edge_index : IndexRange(mesh.totedge)) {
if (selection->get(edge_index)) {
const MEdge &edge = mesh.medge[edge_index];
new_selection[edge.v1] = true;
new_selection[edge.v2] = true;
}
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_edge_to_face(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totpoly);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
bool poly_is_selected = true;
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
if (!selection->get(loop.e)) {
poly_is_selected = false;
break;
}
}
new_selection[poly_index] = poly_is_selected;
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_face_to_point(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totvert, false);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
if (selection->get(poly_index)) {
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
BLI_assert(loop.v < mesh.totvert);
new_selection[loop.v] = true;
}
}
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_face_to_edge(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totedge, false);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
if (selection->get(poly_index)) {
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
new_selection[loop.e] = true;
}
}
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_face_to_corner(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totloop);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
const bool is_selected = selection->get(poly_index);
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
new_selection[loop_index] = is_selected;
}
}
return varray_from_array(std::move(new_selection));
}
} // namespace blender::bke::adapt_selection_domain
blender::VArrayPtr<bool> MeshComponent::adapt_selection(blender::VArrayPtr<bool> selection,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
{
using namespace blender::bke::adapt_selection_domain;
const int from_domain_size = this->attribute_domain_size(from_domain);
BLI_assert(selection->size() == from_domain_size);
if (from_domain == to_domain) {
return selection;
}
if (from_domain_size == 0) {
return selection;
}
if (selection->is_single()) {
return selection;
}
switch (from_domain) {
case ATTR_DOMAIN_CORNER: {
switch (to_domain) {
case ATTR_DOMAIN_POINT:
break;
case ATTR_DOMAIN_FACE:
break;
case ATTR_DOMAIN_EDGE:
break;
default:
break;
}
break;
}
case ATTR_DOMAIN_POINT: {
switch (to_domain) {
case ATTR_DOMAIN_CORNER:
return adapt_selection_point_to_corner(*mesh_, std::move(selection));
case ATTR_DOMAIN_FACE:
return adapt_selection_point_to_face(*mesh_, std::move(selection));
case ATTR_DOMAIN_EDGE:
return adapt_selection_point_to_edge(*mesh_, std::move(selection));
default:
break;
}
break;
}
case ATTR_DOMAIN_FACE: {
switch (to_domain) {
case ATTR_DOMAIN_POINT:
return adapt_selection_face_to_point(*mesh_, std::move(selection));
case ATTR_DOMAIN_CORNER:
return adapt_selection_face_to_corner(*mesh_, std::move(selection));
case ATTR_DOMAIN_EDGE:
return adapt_selection_face_to_edge(*mesh_, std::move(selection));
default:
break;
}
break;
}
case ATTR_DOMAIN_EDGE: {
switch (to_domain) {
case ATTR_DOMAIN_CORNER:
break;
case ATTR_DOMAIN_POINT:
return adapt_selection_edge_to_point(*mesh_, std::move(selection));
case ATTR_DOMAIN_FACE:
return adapt_selection_edge_to_face(*mesh_, std::move(selection));
default:
break;
}
break;
}
default:
break;
}
return {};
}
static Mesh *get_mesh_from_component_for_write(GeometryComponent &component)
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);

View File

@@ -71,7 +71,9 @@
#include "BKE_node.h"
#include "BLI_ghash.h"
#include "BLI_string_ref.hh"
#include "BLI_threads.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -90,6 +92,8 @@
#include "MOD_nodes.h"
using blender::StringRef;
#define NODE_DEFAULT_MAX_WIDTH 700
/* Fallback types for undefined tree, nodes, sockets */
@@ -572,6 +576,11 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
}
BLO_write_struct_by_name(writer, node->typeinfo->storagename, storage);
}
else if (node->type == GEO_NODE_GEOMETRY_EXPANDER) {
NodeGeometryGeometryExpander *storage = (NodeGeometryGeometryExpander *)node->storage;
BLO_write_struct(writer, NodeGeometryGeometryExpander, storage);
BLO_write_struct_list(writer, GeometryExpanderOutput, &storage->outputs);
}
else if (node->typeinfo != &NodeTypeUndefined) {
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
}
@@ -751,6 +760,11 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
BLO_read_data_address(reader, &storage->string);
break;
}
case GEO_NODE_GEOMETRY_EXPANDER: {
NodeGeometryGeometryExpander *storage = (NodeGeometryGeometryExpander *)node->storage;
BLO_read_list(reader, &storage->outputs);
break;
}
default:
break;
}
@@ -3916,6 +3930,42 @@ int nodeSocketLinkLimit(const bNodeSocket *sock)
return sock->limit;
}
static std::string expander_output_to_name(const GeometryExpanderOutput &expander_output,
const bNodeTree &ntree)
{
switch (expander_output.type) {
case GEOMETRY_EXPANDER_OUTPUT_TYPE_LOCAL: {
if (expander_output.local_socket_identifier[0] == '\0') {
return expander_output.local_node_name;
}
return expander_output.local_node_name + StringRef("") +
expander_output.local_socket_identifier;
}
case GEOMETRY_EXPANDER_OUTPUT_TYPE_INPUT: {
LISTBASE_FOREACH (const bNodeSocket *, socket, &ntree.inputs) {
if (socket->identifier == StringRef(expander_output.input_identifier)) {
return StringRef("Input ▶ ") + socket->name;
}
}
return "Unkown input";
}
case GEOMETRY_EXPANDER_OUTPUT_TYPE_BUILTIN: {
return StringRef("Built-in ▶ ") + expander_output.builtin_identifier;
}
case GEOMETRY_EXPANDER_OUTPUT_TYPE_DERIVED: {
return StringRef("Derived ▶ ") + expander_output.derived_identifier;
}
}
return "";
}
void nodeGeometryExpanderUpdateOutputNameCache(struct GeometryExpanderOutput *expander_output,
const bNodeTree *ntree)
{
const std::string name = expander_output_to_name(*expander_output, *ntree);
STRNCPY(expander_output->display_name_cache, name.c_str());
}
/* ************** Node Clipboard *********** */
#define USE_NODE_CB_VALIDATE
@@ -5130,6 +5180,7 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_remove();
register_node_type_geo_attribute_separate_xyz();
register_node_type_geo_attribute_store_local();
register_node_type_geo_attribute_transfer();
register_node_type_geo_attribute_vector_math();
register_node_type_geo_attribute_vector_rotate();
@@ -5156,6 +5207,8 @@ static void registerGeometryNodes()
register_node_type_geo_curve_trim();
register_node_type_geo_delete_geometry();
register_node_type_geo_edge_split();
register_node_type_geo_extrude();
register_node_type_geo_geometry_expander();
register_node_type_geo_input_material();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();

View File

@@ -1446,4 +1446,121 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm,
}
}
/**
* Use to select bmesh vertex data based on an array of bool.
*/
void BM_select_vertices(BMesh *bm, const bool *mask)
{
BMIter iter;
BMVert *v;
int i = 0;
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
BM_elem_flag_set(v, BM_ELEM_SELECT, mask[i]);
i++;
}
}
/**
* Use to select bmesh edge data based on an array of bool.
*/
void BM_select_edges(BMesh *bm, const bool *mask)
{
BMIter iter;
BMEdge *e;
int i = 0;
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
BM_elem_flag_set(e, BM_ELEM_SELECT, mask[i]);
i++;
}
}
/**
* Use to select bmesh face data based on an array of bool.
*/
void BM_select_faces(BMesh *bm, const bool *mask)
{
BMIter iter;
BMFace *f;
int i = 0;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
BM_elem_flag_set(f, BM_ELEM_SELECT, mask[i]);
i++;
}
}
void BM_get_selected_faces(BMesh *bm, bool **selection)
{
BMIter iter;
BMFace *f;
int i = 0;
*selection = MEM_malloc_arrayN((size_t)bm->totface, sizeof(bool), "bm faces");
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
(*selection)[i] = BM_elem_flag_test(f, BM_ELEM_SELECT);
i++;
}
// BMO_slot_map_elem_get()
}
void BM_get_tagged_faces(BMesh *bm, bool **selection)
{
BMIter iter;
BMFace *f;
int i = 0;
*selection = MEM_malloc_arrayN((size_t)bm->totface, sizeof(bool), "bm faces");
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
(*selection)[i] = BM_elem_flag_test(f, BM_ELEM_TAG);
i++;
}
}
void BM_tag_new_faces(BMesh *bm, BMOperator *b_mesh_operator)
{
BMOIter iter;
BMFace *f;
//*selection = MEM_malloc_arrayN((size_t)bm->totface, sizeof(bool), "bm faces");
BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
BMO_ITER (f, &iter, b_mesh_operator->slots_out, "faces.out", BM_FACE) {
BM_elem_flag_enable(f, BM_ELEM_TAG);
}
// BMO_slot_map_elem_get()
}
void BM_tag_vertices(BMesh *bm, const bool *mask)
{
BMIter iter;
BMVert *v;
int i = 0;
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
BM_elem_flag_set(v, BM_ELEM_TAG, mask[i]);
i++;
}
}
/**
* Use to temporary tag bmesh edge data based on an array of bool.
*/
void BM_tag_edges(BMesh *bm, const bool *mask)
{
BMIter iter;
BMEdge *e;
int i = 0;
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
BM_elem_flag_set(e, BM_ELEM_TAG, mask[i]);
i++;
}
}
/**
* Use to temporary tag bmesh face data based on an array of bool.
*/
void BM_tag_faces(BMesh *bm, const bool *mask)
{
BMIter iter;
BMFace *f;
int i = 0;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
BM_elem_flag_set(f, BM_ELEM_TAG, mask[i]);
i++;
}
}
/** \} */

View File

@@ -134,3 +134,13 @@ void BM_mesh_vert_coords_apply(BMesh *bm, const float (*vert_coords)[3]);
void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm,
const float (*vert_coords)[3],
const float mat[4][4]);
void BM_select_vertices(BMesh *bm, const bool *mask);
void BM_select_edges(BMesh *bm, const bool *mask);
void BM_select_faces(BMesh *bm, const bool *mask);
void BM_get_selected_faces(BMesh *bm, bool **selection);
void BM_tag_vertices(BMesh *bm, const bool *mask);
void BM_tag_edges(BMesh *bm, const bool *mask);
void BM_tag_faces(BMesh *bm, const bool *mask);
void BM_get_tagged_faces(BMesh *bm, bool **selection);
void BM_tag_new_faces(BMesh *bm, BMOperator *b_mesh_operator);

View File

@@ -1898,6 +1898,9 @@ static BMOpDefine bmo_inset_individual_def = {
{{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* input faces */
{"thickness", BMO_OP_SLOT_FLT}, /* thickness */
{"depth", BMO_OP_SLOT_FLT}, /* depth */
{"thickness_array", BMO_OP_SLOT_PTR}, /* thickness */
{"depth_array", BMO_OP_SLOT_PTR}, /* depth */
{"use_attributes", BMO_OP_SLOT_BOOL}, /* Use spans for thickness and depth */
{"use_even_offset", BMO_OP_SLOT_BOOL}, /* scale the offset to give more even thickness */
{"use_interpolate", BMO_OP_SLOT_BOOL}, /* blend face data across the inset */
{"use_relative_offset", BMO_OP_SLOT_BOOL}, /* scale the offset by surrounding geometry */
@@ -1929,6 +1932,9 @@ static BMOpDefine bmo_inset_region_def = {
{"use_edge_rail", BMO_OP_SLOT_BOOL}, /* inset the region along existing edges */
{"thickness", BMO_OP_SLOT_FLT}, /* thickness */
{"depth", BMO_OP_SLOT_FLT}, /* depth */
{"thickness_array", BMO_OP_SLOT_PTR}, /* thickness */
{"depth_array", BMO_OP_SLOT_PTR}, /* depth */
{"use_attributes", BMO_OP_SLOT_BOOL}, /* Use spans for thickness and depth */
{"use_outset", BMO_OP_SLOT_BOOL}, /* outset rather than inset */
{{'\0'}},
},

View File

@@ -419,6 +419,9 @@ void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
BMOIter oiter;
MemArena *interp_arena = NULL;
const bool use_attributes = BMO_slot_bool_get(op->slots_in, "use_attributes");
const float *thickness_array = BMO_slot_ptr_get(op->slots_in, "thickness_array");
const float *depth_array = BMO_slot_ptr_get(op->slots_in, "depth_array");
const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
const float depth = BMO_slot_float_get(op->slots_in, "depth");
const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
@@ -433,19 +436,37 @@ void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
if (use_interpolate) {
interp_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
}
int i = 0;
if (use_attributes) {
BMO_ITER_INDEX (f, &oiter, op->slots_in, "faces", BM_FACE, i) {
bmo_face_inset_individual(bm,
f,
interp_arena,
thickness_array[i],
depth_array[i],
use_even_offset,
use_relative_offset,
use_interpolate);
BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
bmo_face_inset_individual(bm,
f,
interp_arena,
thickness,
depth,
use_even_offset,
use_relative_offset,
use_interpolate);
if (use_interpolate) {
BLI_memarena_clear(interp_arena);
}
}
}
else {
BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
bmo_face_inset_individual(bm,
f,
interp_arena,
thickness,
depth,
use_even_offset,
use_relative_offset,
use_interpolate);
if (use_interpolate) {
BLI_memarena_clear(interp_arena);
if (use_interpolate) {
BLI_memarena_clear(interp_arena);
}
}
}
@@ -677,12 +698,15 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
const bool use_outset = BMO_slot_bool_get(op->slots_in, "use_outset");
const bool use_boundary = BMO_slot_bool_get(op->slots_in, "use_boundary") &&
(use_outset == false);
const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
const bool use_even_boundary = use_even_offset; /* could make own option */
const bool use_relative_offset = BMO_slot_bool_get(op->slots_in, "use_relative_offset");
const bool use_edge_rail = BMO_slot_bool_get(op->slots_in, "use_edge_rail");
const bool use_interpolate = BMO_slot_bool_get(op->slots_in, "use_interpolate");
const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
const bool use_attributes = BMO_slot_bool_get(op->slots_in, "use_attributes");
const float *thickness_array = BMO_slot_ptr_get(op->slots_in, "thickness_array");
const float depth = BMO_slot_float_get(op->slots_in, "depth");
#ifdef USE_LOOP_CUSTOMDATA_MERGE
const bool has_math_ldata = (use_interpolate && CustomData_has_math(&bm->ldata));
@@ -1096,7 +1120,12 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
}
/* apply the offset */
madd_v3_v3fl(v_split->co, tvec, thickness);
if (use_attributes) {
madd_v3_v3fl(v_split->co, tvec, thickness_array[v_split->head.index]);
}
else {
madd_v3_v3fl(v_split->co, tvec, thickness);
}
}
/* this saves expensive/slow glue check for common cases */

View File

@@ -78,6 +78,9 @@
#include "NOD_geometry_nodes_eval_log.hh"
#include "FN_array_cpp_type.hh"
#include "FN_generic_span.hh"
#include "node_intern.h" /* own include */
#ifdef WITH_COMPOSITOR
@@ -88,6 +91,7 @@ using blender::Map;
using blender::Set;
using blender::Span;
using blender::Vector;
using blender::fn::ArrayCPPType;
using blender::fn::CPPType;
using blender::fn::GPointer;
namespace geo_log = blender::nodes::geometry_nodes_eval_log;
@@ -349,6 +353,31 @@ void node_from_view(const bNode *node, float x, float y, float *rx, float *ry)
nodeFromView(node, x, y, rx, ry);
}
static void draw_socket_layout(
const bContext *C, uiLayout *layout, bNodeTree *ntree, bNode *node, bNodeSocket *socket)
{
if (node->typeinfo->draw_socket) {
if (node->typeinfo->draw_socket(C, layout, ntree, node, socket)) {
return;
}
}
uiLayout *row = uiLayoutRow(layout, true);
if (socket->in_out == SOCK_OUT) {
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
}
PointerRNA nodeptr;
RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr);
PointerRNA sockptr;
RNA_pointer_create(&ntree->id, &RNA_NodeSocket, socket, &sockptr);
const char *socket_label = nodeSocketLabel(socket);
socket->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label));
if (socket->flag & SOCK_IS_ATTRIBUTE_OUTPUT) {
uiItemR(row, &sockptr, "add_to_geometry", 0, "", ICON_ADD);
}
}
/**
* Based on settings and sockets in node, set drawing rect info.
*/
@@ -400,11 +429,7 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
uiLayoutSetContextPointer(layout, "node", &nodeptr);
uiLayoutSetContextPointer(layout, "socket", &sockptr);
/* Align output buttons to the right. */
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
const char *socket_label = nodeSocketLabel(nsock);
nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label));
draw_socket_layout(C, layout, ntree, node, nsock);
UI_block_align_end(node->block);
UI_block_layout_resolve(node->block, nullptr, &buty);
@@ -537,10 +562,7 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
uiLayoutSetContextPointer(layout, "node", &nodeptr);
uiLayoutSetContextPointer(layout, "socket", &sockptr);
uiLayout *row = uiLayoutRow(layout, true);
const char *socket_label = nodeSocketLabel(nsock);
nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label));
draw_socket_layout(C, layout, ntree, node, nsock);
UI_block_align_end(node->block);
UI_block_layout_resolve(node->block, nullptr, &buty);
@@ -842,17 +864,21 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal
};
const GPointer value = value_log.value();
if (value.is_type<int>()) {
ss << *value.get<int>() << TIP_(" (Integer)");
}
else if (value.is_type<float>()) {
ss << *value.get<float>() << TIP_(" (Float)");
}
else if (value.is_type<blender::float3>()) {
ss << *value.get<blender::float3>() << TIP_(" (Vector)");
}
else if (value.is_type<bool>()) {
ss << (*value.get<bool>() ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)");
const ArrayCPPType *array_cpp_type = dynamic_cast<const ArrayCPPType *>(value.type());
if (array_cpp_type != nullptr) {
const CPPType &element_type = array_cpp_type->element_type();
blender::fn::GSpan span = array_cpp_type->array_span(value.get());
const int max_elements = 10;
const int num_elements = std::min<int>(max_elements, span.size());
for (const int i : blender::IndexRange(num_elements)) {
ss << "\u2022 ";
element_type.print(span[i], ss);
ss << "\n";
}
if (num_elements < span.size()) {
ss << "\u2022 ...\n";
}
ss << "Length: " << span.size();
}
else if (value.is_type<std::string>()) {
ss << *value.get<std::string>() << TIP_(" (String)");

View File

@@ -22,6 +22,7 @@
*/
#include <algorithm>
#include <string>
#include "MEM_guardedalloc.h"
@@ -32,9 +33,12 @@
#include "DNA_world_types.h"
#include "BLI_blenlib.h"
#include "BLI_function_ref.hh"
#include "BLI_math.h"
#include "BLI_string_ref.hh"
#include "BKE_context.h"
#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_lib_id.h"
@@ -77,6 +81,13 @@
#include "NOD_texture.h"
#include "node_intern.h" /* own include */
using blender::FunctionRef;
using blender::MutableSpan;
using blender::Span;
using blender::StringRef;
using blender::StringRefNull;
using blender::Vector;
#define USE_ESC_COMPO
/* ***************** composite job manager ********************** */
@@ -2976,3 +2987,258 @@ void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ****************** Geometry Expander Add Output ******************* */
static eNodeSocketDatatype custom_data_type_to_socket_type(const CustomDataType type)
{
switch (type) {
case CD_PROP_FLOAT:
return SOCK_FLOAT;
case CD_PROP_FLOAT2:
case CD_PROP_FLOAT3:
return SOCK_VECTOR;
case CD_PROP_COLOR:
return SOCK_RGBA;
case CD_PROP_INT32:
return SOCK_INT;
case CD_PROP_BOOL:
return SOCK_BOOLEAN;
default:
return SOCK_FLOAT;
}
}
static void foreach_available_attribute(
bNodeTree *ntree,
bNode *UNUSED(current_node),
FunctionRef<void(const GeometryExpanderOutput &attribute)> callback)
{
LISTBASE_FOREACH (bNodeSocket *, group_input, &ntree->inputs) {
if (group_input->type == SOCK_STRING) {
GeometryExpanderOutput attribute;
attribute.type = GEOMETRY_EXPANDER_OUTPUT_TYPE_INPUT;
attribute.array_source = (eGeometryExpanderArraySource)group_input->output_array_source;
STRNCPY(attribute.input_identifier, group_input->identifier);
attribute.socket_type = SOCK_FLOAT;
callback(attribute);
}
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
LISTBASE_FOREACH (bNodeSocket *, node_output, &node->outputs) {
if ((node_output->flag & SOCK_ADD_ATTRIBUTE_TO_GEOMETRY) &&
ELEM(node_output->type, SOCK_INT, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN)) {
GeometryExpanderOutput attribute;
attribute.type = GEOMETRY_EXPANDER_OUTPUT_TYPE_LOCAL;
attribute.socket_type = (eNodeSocketDatatype)node_output->type;
attribute.array_source = (eGeometryExpanderArraySource)node_output->output_array_source;
STRNCPY(attribute.local_node_name, node->name);
STRNCPY(attribute.local_socket_identifier, node_output->identifier);
callback(attribute);
}
}
if (node->type == GEO_NODE_ATTRIBUTE_STORE_LOCAL) {
GeometryExpanderOutput attribute;
attribute.type = GEOMETRY_EXPANDER_OUTPUT_TYPE_LOCAL;
attribute.socket_type = custom_data_type_to_socket_type((CustomDataType)node->custom1);
attribute.array_source = GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_VERTICES;
STRNCPY(attribute.local_node_name, node->name);
STRNCPY(attribute.local_socket_identifier, "");
callback(attribute);
}
}
{
GeometryExpanderOutput attribute;
attribute.type = GEOMETRY_EXPANDER_OUTPUT_TYPE_BUILTIN;
attribute.socket_type = SOCK_VECTOR;
attribute.array_source = GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_VERTICES;
STRNCPY(attribute.builtin_identifier, "position");
callback(attribute);
}
{
GeometryExpanderOutput attribute;
attribute.type = GEOMETRY_EXPANDER_OUTPUT_TYPE_BUILTIN;
attribute.socket_type = SOCK_INT;
attribute.array_source = GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_FACES;
STRNCPY(attribute.builtin_identifier, "material_index");
callback(attribute);
}
{
GeometryExpanderOutput attribute;
attribute.type = GEOMETRY_EXPANDER_OUTPUT_TYPE_DERIVED;
attribute.socket_type = SOCK_VECTOR;
attribute.array_source = GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_VERTICES;
STRNCPY(attribute.derived_identifier, "normal");
callback(attribute);
}
{
GeometryExpanderOutput attribute;
attribute.type = GEOMETRY_EXPANDER_OUTPUT_TYPE_DERIVED;
attribute.socket_type = SOCK_INT;
attribute.array_source = GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_VERTICES;
STRNCPY(attribute.derived_identifier, "index");
callback(attribute);
}
}
static MutableSpan<GeometryExpanderOutput> get_updated_cached_available_attributes(
bNodeTree *ntree, bNode *node)
{
static Vector<GeometryExpanderOutput> cached_available_attributes;
cached_available_attributes.clear();
foreach_available_attribute(ntree, node, [&](const GeometryExpanderOutput &attribute) {
cached_available_attributes.append(attribute);
});
return cached_available_attributes;
}
static const EnumPropertyItem *node_geometry_expander_output_add_items(bContext *C,
PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
EnumPropertyItem *items = nullptr;
int totitem = 0;
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
char *node_name = RNA_string_get_alloc(ptr, "node_name", nullptr, 0);
bNode *node = nodeFindNodebyName(ntree, node_name);
MEM_freeN(node_name);
MutableSpan<GeometryExpanderOutput> attributes = get_updated_cached_available_attributes(ntree,
node);
for (const int i : attributes.index_range()) {
GeometryExpanderOutput &attribute = attributes[i];
nodeGeometryExpanderUpdateOutputNameCache(&attribute, ntree);
EnumPropertyItem item = {0};
item.value = i;
item.name = attribute.display_name_cache;
item.identifier = "test";
RNA_enum_item_add(&items, &totitem, &item);
}
RNA_enum_item_end(&items, &totitem);
*r_free = true;
return items;
}
static int node_geometry_expander_output_add_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
char *node_name = RNA_string_get_alloc(op->ptr, "node_name", nullptr, 0);
bNode *node = nodeFindNodebyName(ntree, node_name);
MEM_freeN(node_name);
if (node == nullptr) {
return OPERATOR_CANCELLED;
}
NodeGeometryGeometryExpander *storage = (NodeGeometryGeometryExpander *)node->storage;
const int item_value = RNA_enum_get(op->ptr, "item");
Span<GeometryExpanderOutput> attributes = get_updated_cached_available_attributes(ntree, node);
const GeometryExpanderOutput &attribute = attributes[item_value];
auto to_identifier = [](int id) { return "out_" + std::to_string(id); };
int id = 0;
while (nodeFindSocket(node, SOCK_OUT, to_identifier(id).c_str()) != nullptr) {
id++;
}
const std::string identifier = to_identifier(id);
GeometryExpanderOutput *expander_output = (GeometryExpanderOutput *)MEM_callocN(
sizeof(GeometryExpanderOutput), __func__);
*expander_output = attribute;
STRNCPY(expander_output->socket_identifier, identifier.c_str());
expander_output->is_outdated = false;
BLI_addtail(&storage->outputs, expander_output);
nodeUpdate(ntree, node);
ntreeUpdateTree(CTX_data_main(C), ntree);
snode_notify(C, snode);
snode_dag_update(C, snode);
return OPERATOR_FINISHED;
}
static int node_geometry_expander_output_add_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
PointerRNA ptr = CTX_data_pointer_get(C, "node");
bNode *node = (bNode *)ptr.data;
RNA_string_set(op->ptr, "node_name", node->name);
return WM_enum_search_invoke(C, op, event);
}
void NODE_OT_geometry_expander_output_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Geometry Expander Output";
ot->description = "Add geometry expander output";
ot->idname = "NODE_OT_geometry_expander_output_add";
/* callbacks */
ot->invoke = node_geometry_expander_output_add_invoke;
ot->exec = node_geometry_expander_output_add_exec;
ot->poll = ED_operator_node_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
PropertyRNA *prop;
prop = RNA_def_string(ot->srna, "node_name", nullptr, 0, "Node Name", "Node name");
prop = RNA_def_enum(ot->srna, "item", DummyRNA_NULL_items, 0, "Item", "");
RNA_def_enum_funcs(prop, node_geometry_expander_output_add_items);
ot->prop = prop;
}
static int node_geometry_expander_output_remove_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
PointerRNA ptr = CTX_data_pointer_get(C, "node");
bNode *node = (bNode *)ptr.data;
if (node == nullptr) {
return OPERATOR_CANCELLED;
}
NodeGeometryGeometryExpander *storage = (NodeGeometryGeometryExpander *)node->storage;
const int output_index = RNA_int_get(op->ptr, "output_index");
GeometryExpanderOutput *expander_output = (GeometryExpanderOutput *)BLI_findlink(
&storage->outputs, output_index);
BLI_remlink(&storage->outputs, expander_output);
MEM_freeN(expander_output);
nodeUpdate(ntree, node);
ntreeUpdateTree(CTX_data_main(C), ntree);
snode_notify(C, snode);
snode_dag_update(C, snode);
return OPERATOR_FINISHED;
}
void NODE_OT_geometry_expander_output_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Geometry Expander Output";
ot->description = "Remove geometry expander output";
ot->idname = "NODE_OT_geometry_expander_output_remove";
/* callbacks */
ot->exec = node_geometry_expander_output_remove_exec;
ot->poll = ED_operator_node_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_int(
ot->srna, "output_index", 0, 0, 1000, "Output Index", "Output index to remove", 0, 1000);
}

View File

@@ -312,6 +312,9 @@ void NODE_GGT_backdrop_corner_pin(struct wmGizmoGroupType *gzgt);
void NODE_OT_cryptomatte_layer_add(struct wmOperatorType *ot);
void NODE_OT_cryptomatte_layer_remove(struct wmOperatorType *ot);
void NODE_OT_geometry_expander_output_add(struct wmOperatorType *ot);
void NODE_OT_geometry_expander_output_remove(struct wmOperatorType *ot);
/* node_geometry_attribute_search.cc */
void node_geometry_add_attribute_search_button(const struct bContext *C,
const struct bNodeTree *node_tree,

View File

@@ -124,6 +124,9 @@ void node_operatortypes(void)
WM_operatortype_append(NODE_OT_cryptomatte_layer_add);
WM_operatortype_append(NODE_OT_cryptomatte_layer_remove);
WM_operatortype_append(NODE_OT_geometry_expander_output_add);
WM_operatortype_append(NODE_OT_geometry_expander_output_remove);
}
void ED_operatormacros_node(void)

View File

@@ -0,0 +1,90 @@
/*
* 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 "BLI_array.hh"
#include "FN_cpp_type.hh"
#include "FN_generic_span.hh"
namespace blender::fn {
template<typename T> struct ArrayCPPTypeParam {
};
class ArrayCPPType : public CPPType {
private:
const CPPType &element_type_;
GSpan (*get_span_)(const void *value);
void (*construct_uninitialized_)(void *r_value, int64_t size);
public:
template<typename ElementT, int64_t InlineBufferCapacity, typename Allocator>
ArrayCPPType(ArrayCPPTypeParam<Array<ElementT, InlineBufferCapacity, Allocator>> /* unused */,
StringRef debug_name)
: CPPType(
CPPTypeParam<Array<ElementT, InlineBufferCapacity, Allocator>, CPPTypeFlags::None>(),
debug_name),
element_type_(CPPType::get<ElementT>())
{
using ArrayT = Array<ElementT, InlineBufferCapacity, Allocator>;
get_span_ = [](const void *value) {
Span<ElementT> span = *(const ArrayT *)value;
return GSpan(span);
};
construct_uninitialized_ = [](void *r_value, const int64_t size) {
new (r_value) ArrayT(size, NoInitialization());
};
}
const CPPType &element_type() const
{
return element_type_;
}
int64_t array_size(const void *value) const
{
return get_span_(value).size();
}
GSpan array_span(const void *value) const
{
return get_span_(value);
}
GMutableSpan array_span(void *value) const
{
GSpan span = get_span_(value);
return GMutableSpan(span.type(), (void *)span.data(), span.size());
}
GMutableSpan array_construct_uninitialized(void *r_value, const int64_t size) const
{
construct_uninitialized_(r_value, size);
return this->array_span(r_value);
}
};
} // namespace blender::fn
#define MAKE_ARRAY_CPP_TYPE(IDENTIFIER, TYPE_NAME) \
template<> const blender::fn::CPPType &blender::fn::CPPType::get_impl<TYPE_NAME>() \
{ \
static blender::fn::ArrayCPPType cpp_type{blender::fn::ArrayCPPTypeParam<TYPE_NAME>(), \
STRINGIFY(IDENTIFIER)}; \
return cpp_type; \
}

View File

@@ -41,7 +41,8 @@ class GMutablePointer {
{
}
template<typename T> GMutablePointer(T *data) : GMutablePointer(&CPPType::get<T>(), data)
template<typename T, typename std::enable_if_t<!std::is_void_v<T>> * = nullptr>
GMutablePointer(T *data) : GMutablePointer(&CPPType::get<T>(), data)
{
}

View File

@@ -588,6 +588,32 @@ template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableAr
}
};
class GVArray_For_RepeatedGSpan : public GVArray {
private:
GSpan span_;
const void *default_value_;
public:
GVArray_For_RepeatedGSpan(int64_t size, GSpan span, const void *default_value = nullptr)
: GVArray(span.type(), size),
span_(span),
default_value_(default_value ? default_value : span.type().default_value())
{
}
private:
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
{
if (span_.is_empty()) {
type_->copy_construct(default_value_, r_value);
}
else {
const int64_t repeated_index = index % span_.size();
type_->copy_construct(span_[repeated_index], r_value);
}
}
};
/* A generic version of VArray_Span. */
class GVArray_GSpan : public GSpan {
private:

View File

@@ -149,6 +149,10 @@ typedef struct bNodeSocket {
* kept for forward compatibility */
/** Custom data for inputs, only UI writes in this. */
bNodeStack ns DNA_DEPRECATED;
/* eGeometryExpanderArraySource. */
int output_array_source;
char _pad2[4];
} bNodeSocket;
/* sock->type */
@@ -214,6 +218,8 @@ typedef enum eNodeSocketFlag {
* type is obvious and the name takes up too much space.
*/
SOCK_HIDE_LABEL = (1 << 12),
SOCK_ADD_ATTRIBUTE_TO_GEOMETRY = (1 << 13),
SOCK_IS_ATTRIBUTE_OUTPUT = (1 << 14),
} eNodeSocketFlag;
/* TODO: Limit data in bNode to what we want to see saved. */
@@ -1430,6 +1436,61 @@ typedef struct NodeGeometryRaycast {
char _pad[1];
} NodeGeometryRaycast;
typedef enum eGeometryExpanderOutputType {
GEOMETRY_EXPANDER_OUTPUT_TYPE_LOCAL,
GEOMETRY_EXPANDER_OUTPUT_TYPE_INPUT,
GEOMETRY_EXPANDER_OUTPUT_TYPE_BUILTIN,
GEOMETRY_EXPANDER_OUTPUT_TYPE_DERIVED,
} eGeometryExpanderOutputType;
typedef enum eGeometryExpanderArraySource {
GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_VERTICES = 0,
GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_EDGES = 1,
GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_FACES = 2,
GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_FACE_CORNERS = 3,
GEOMETRY_EXPANDER_ARRAY_SOURCE_POINT_CLOUD_POINTS = 4,
GEOMETRY_EXPANDER_ARRAY_SOURCE_CURVE_POINTS = 5,
GEOMETRY_EXPANDER_ARRAY_SOURCE_CURVE_SPLINES = 6,
} eGeometryExpanderArraySource;
typedef struct GeometryExpanderOutput {
struct GeometryExpanderOutput *next, *prev;
/* eGeometryExpanderOutputType. */
int type;
uint8_t is_outdated;
char _pad[3];
/* Identifier of the corresponding socket in the geometry expander. */
char socket_identifier[64];
/* Derived from the other data. */
char display_name_cache[64];
/* eGeometryExpanderAttributeSource. */
int8_t array_source;
/* eNodeSocketDatatype. */
int8_t socket_type;
char _pad2[6];
/* Local attribute data. */
char local_node_name[64];
char local_socket_identifier[64];
/* Input attribute data. */
char input_identifier[64];
/* Builtin attribute data. */
char builtin_identifier[64];
/* Derived data. */
char derived_identifier[64];
} GeometryExpanderOutput;
typedef struct NodeGeometryGeometryExpander {
ListBase outputs;
} NodeGeometryGeometryExpander;
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1

View File

@@ -283,6 +283,7 @@ extern StructRNA RNA_FreestyleModuleSettings;
extern StructRNA RNA_FreestyleSettings;
extern StructRNA RNA_Function;
extern StructRNA RNA_FunctionNode;
extern StructRNA RNA_GeometryExpanderOutput;
extern StructRNA RNA_GPencilFrame;
extern StructRNA RNA_GPencilInterpolateSettings;
extern StructRNA RNA_GPencilLayer;

View File

@@ -39,6 +39,7 @@
#include "BKE_animsys.h"
#include "BKE_attribute.h"
#include "BKE_cryptomatte.h"
#include "BKE_geometry_set.h"
#include "BKE_image.h"
#include "BKE_node.h"
#include "BKE_texture.h"
@@ -2484,10 +2485,56 @@ static void rna_Node_name_set(PointerRNA *ptr, const char *value)
nodeUniqueName(ntree, node);
LISTBASE_FOREACH (bNode *, other_node, &ntree->nodes) {
if (other_node->type != GEO_NODE_GEOMETRY_EXPANDER) {
continue;
}
NodeGeometryGeometryExpander *storage = (NodeGeometryGeometryExpander *)other_node->storage;
LISTBASE_FOREACH (GeometryExpanderOutput *, expander_output, &storage->outputs) {
if (expander_output->type != GEOMETRY_EXPANDER_OUTPUT_TYPE_LOCAL) {
continue;
}
if (STREQ(expander_output->local_node_name, oldname)) {
STRNCPY(expander_output->local_node_name, node->name);
nodeGeometryExpanderUpdateOutputNameCache(expander_output, ntree);
}
}
}
/* fix all the animation data which may link to this */
BKE_animdata_fix_paths_rename_all(NULL, "nodes", oldname, node->name);
}
void rna_NodeSocket_add_to_geometry_set(PointerRNA *ptr, const bool value)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNodeSocket *socket = (bNodeSocket *)ptr->data;
bNode *node;
nodeFindNode(ntree, socket, &node, NULL);
if (value) {
socket->flag |= SOCK_ADD_ATTRIBUTE_TO_GEOMETRY;
}
else {
socket->flag &= ~SOCK_ADD_ATTRIBUTE_TO_GEOMETRY;
}
LISTBASE_FOREACH (bNode *, other_node, &ntree->nodes) {
if (other_node->type != GEO_NODE_GEOMETRY_EXPANDER) {
continue;
}
NodeGeometryGeometryExpander *storage = (NodeGeometryGeometryExpander *)other_node->storage;
LISTBASE_FOREACH (GeometryExpanderOutput *, expander_output, &storage->outputs) {
if (expander_output->type != GEOMETRY_EXPANDER_OUTPUT_TYPE_LOCAL) {
continue;
}
if (STREQ(expander_output->local_node_name, node->name) &&
STREQ(expander_output->local_socket_identifier, socket->identifier)) {
expander_output->is_outdated = !value;
}
}
}
}
static bNodeSocket *rna_Node_inputs_new(ID *id,
bNode *node,
Main *bmain,
@@ -9140,6 +9187,26 @@ static void def_geo_attribute_fill(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_attribute_store_local(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_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf");
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_GeometryNode_socket_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_with_auto_items);
RNA_def_property_enum_default(prop, ATTR_DOMAIN_AUTO);
RNA_def_property_ui_text(prop, "Domain", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_attribute_convert(StructRNA *srna)
{
PropertyRNA *prop;
@@ -10178,6 +10245,24 @@ static void def_geo_attribute_transfer(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_extrude(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "distance_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float);
RNA_def_property_ui_text(
prop, "Distance", "Changes the Distance input between Float and Attribute");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "inset_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom2");
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float);
RNA_def_property_ui_text(prop, "Inset", "Changes the Inset input between Float and Attribute");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_input_material(StructRNA *srna)
{
PropertyRNA *prop;
@@ -10227,6 +10312,55 @@ static void def_geo_raycast(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_geometry_expander_output(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static const EnumPropertyItem array_source_items[] = {
{GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_VERTICES, "MESH_VERTICES", ICON_NONE, "Vertices", ""},
{GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_EDGES, "MESH_EDGES", ICON_NONE, "Edges", ""},
{GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_FACES, "MESH_FACES", ICON_NONE, "Faces", ""},
{GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_FACE_CORNERS,
"MESH_FACE_CORNERS",
ICON_NONE,
"Face Corners",
""},
{GEOMETRY_EXPANDER_ARRAY_SOURCE_POINT_CLOUD_POINTS,
"POINT_CLOUD_POINTS",
ICON_NONE,
"Points",
""},
{GEOMETRY_EXPANDER_ARRAY_SOURCE_CURVE_POINTS,
"CURVE_POINTS",
ICON_NONE,
"Control Points",
""},
{GEOMETRY_EXPANDER_ARRAY_SOURCE_CURVE_SPLINES, "CURVE_SPLINES", ICON_NONE, "Splines", ""},
{0, NULL, 0, NULL, NULL},
};
srna = RNA_def_struct(brna, "GeometryExpanderOutput", NULL);
prop = RNA_def_property(srna, "array_source", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, array_source_items);
RNA_def_property_ui_text(prop, "Array Source", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTree_update");
}
static void def_geo_geometry_expander(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryGeometryExpander", "storage");
prop = RNA_def_property(srna, "outputs", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "outputs", NULL);
RNA_def_property_struct_type(prop, "GeometryExpanderOutput");
RNA_def_property_ui_text(prop, "Expander Outputs", "");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
@@ -10274,6 +10408,8 @@ static void rna_def_geometry_node(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Geometry Node", "");
RNA_def_struct_sdna(srna, "bNode");
RNA_def_struct_register_funcs(srna, "rna_GeometryNode_register", "rna_Node_unregister", NULL);
def_geo_geometry_expander_output(brna);
}
static void rna_def_function_node(BlenderRNA *brna)
@@ -10372,6 +10508,15 @@ static void rna_def_node_socket(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Hide Value", "Hide the socket input value");
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, NULL);
prop = RNA_def_property(srna, "add_to_geometry", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SOCK_ADD_ATTRIBUTE_TO_GEOMETRY);
RNA_def_property_boolean_funcs(prop, NULL, "rna_NodeSocket_add_to_geometry_set");
RNA_def_property_ui_text(
prop,
"Add to Geometry",
"Add attribute output to the geometry so that it can be accessed later");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocket_update");
prop = RNA_def_property(srna, "node", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_funcs(prop, "rna_NodeSocket_node_get", NULL, NULL, NULL);
RNA_def_property_struct_type(prop, "Node");

View File

@@ -79,6 +79,8 @@
#include "MOD_nodes_evaluator.hh"
#include "MOD_ui_common.h"
#include "FN_array_cpp_type.hh"
#include "ED_spreadsheet.h"
#include "NOD_derived_node_tree.hh"
@@ -86,6 +88,7 @@
#include "NOD_geometry_nodes_eval_log.hh"
#include "NOD_node_tree_multi_function.hh"
using blender::Array;
using blender::destruct_ptr;
using blender::float3;
using blender::FunctionRef;
@@ -401,12 +404,14 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
},
[](const IDProperty &property) { return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); },
[](const IDProperty &property, void *r_value) {
float value;
if (property.type == IDP_FLOAT) {
*(float *)r_value = IDP_Float(&property);
value = IDP_Float(&property);
}
else if (property.type == IDP_DOUBLE) {
*(float *)r_value = (float)IDP_Double(&property);
value = (float)IDP_Double(&property);
}
new (r_value) Array<float>({value});
},
};
return &float_type;
@@ -441,7 +446,10 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
return (PropertyType)((bNodeSocketValueInt *)socket.default_value)->subtype;
},
[](const IDProperty &property) { return property.type == IDP_INT; },
[](const IDProperty &property, void *r_value) { *(int *)r_value = IDP_Int(&property); },
[](const IDProperty &property, void *r_value) {
int value = IDP_Int(&property);
new (r_value) Array<int>({value});
},
};
return &int_type;
}
@@ -485,7 +493,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
property.len == 3;
},
[](const IDProperty &property, void *r_value) {
copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
float3 value;
copy_v3_v3(value, (const float *)IDP_Array(&property));
new (r_value) Array<float3>({value});
},
};
return &vector_type;
@@ -517,8 +527,10 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
nullptr,
[](const IDProperty &property) { return property.type == IDP_INT; },
[](const IDProperty &property, void *r_value) {
*(bool *)r_value = IDP_Int(&property) != 0;
bool value = IDP_Int(&property) != 0;
new (r_value) Array<bool>({value});
},
};
return &boolean_type;
}
@@ -675,6 +687,22 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
}
}
}
LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) {
if (!ELEM(socket->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_INT, SOCK_RGBA)) {
continue;
}
IDPropertyTemplate prop_template = {0};
IDProperty *new_prop = IDP_New(IDP_STRING, &prop_template, socket->identifier);
IDP_AddToGroup(nmd->settings.properties, new_prop);
if (old_properties != nullptr) {
IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, socket->identifier);
if (old_prop != nullptr) {
IDP_CopyPropertyContent(new_prop, old_prop);
}
}
}
if (old_properties != nullptr) {
IDP_FreeProperty(old_properties);
@@ -713,7 +741,8 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd)
static void initialize_group_input(NodesModifierData &nmd,
const bNodeSocket &socket,
const CPPType &cpp_type,
void *r_value)
void *r_value,
Map<std::string, std::string> &r_group_input_attribute_names)
{
const SocketPropertyType *property_type = get_socket_property_type(socket);
if (property_type == nullptr) {
@@ -735,6 +764,9 @@ static void initialize_group_input(NodesModifierData &nmd,
return;
}
property_type->init_cpp_value(*property, r_value);
if (property->type == IDP_STRING) {
r_group_input_attribute_names.add(socket.identifier, IDP_String(property));
}
}
static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain)
@@ -851,7 +883,7 @@ static void clear_runtime_data(NodesModifierData *nmd)
*/
static GeometrySet compute_geometry(const DerivedNodeTree &tree,
Span<const NodeRef *> group_input_nodes,
const InputSocketRef &socket_to_compute,
const NodeRef *group_output_node,
GeometrySet input_geometry_set,
NodesModifierData *nmd,
const ModifierEvalContext *ctx)
@@ -862,6 +894,8 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
Map<DOutputSocket, GMutablePointer> group_inputs;
Map<std::string, std::string> group_input_attribute_names;
const DTreeContext *root_context = &tree.root_context();
for (const NodeRef *group_input_node : group_input_nodes) {
Span<const OutputSocketRef *> group_input_sockets = group_input_node->outputs().drop_back(1);
@@ -885,7 +919,8 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
for (const OutputSocketRef *socket : remaining_input_sockets) {
const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type();
void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment());
initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in);
initialize_group_input(
*nmd, *socket->bsocket(), cpp_type, value_in, group_input_attribute_names);
group_inputs.add_new({root_context, socket}, {cpp_type, value_in});
}
}
@@ -894,7 +929,9 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
input_geometry_set.clear();
Vector<DInputSocket> group_outputs;
group_outputs.append({root_context, &socket_to_compute});
for (const InputSocketRef *socket_ref : group_output_node->inputs().drop_back(1)) {
group_outputs.append({root_context, socket_ref});
}
std::optional<geo_log::GeoLogger> geo_logger;
@@ -914,6 +951,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
eval_params.depsgraph = ctx->depsgraph;
eval_params.self_object = ctx->object;
eval_params.geo_logger = geo_logger.has_value() ? &*geo_logger : nullptr;
eval_params.group_input_attribute_names = group_input_attribute_names;
blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params);
if (geo_logger.has_value()) {
@@ -921,10 +959,43 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
clear_runtime_data(nmd_orig);
nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger);
}
BLI_assert(eval_params.r_output_values.size() >= 1);
BLI_assert(eval_params.r_output_values.size() == 1);
GMutablePointer result = eval_params.r_output_values[0];
return result.relocate_out<GeometrySet>();
GMutablePointer result_geometry_ptr = eval_params.r_output_values[0];
GeometrySet result_geometry = result_geometry_ptr.relocate_out<GeometrySet>();
if (result_geometry.has_mesh()) {
const GeometryComponentType component_type = GEO_COMPONENT_TYPE_MESH;
const AttributeDomain attribute_domain = ATTR_DOMAIN_POINT;
GeometryComponent &result_component = result_geometry.get_component_for_write(component_type);
const int domain_size = result_component.attribute_domain_size(attribute_domain);
int output_index = 0;
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->outputs, output_index) {
if (!ELEM(socket->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_INT, SOCK_BOOLEAN)) {
continue;
}
IDProperty *prop = IDP_GetPropertyFromGroup(nmd->settings.properties, socket->identifier);
if (prop == nullptr) {
continue;
}
const char *attribute_name = IDP_String(prop);
if (StringRef(attribute_name).is_empty()) {
continue;
}
GMutablePointer array_ptr = eval_params.r_output_values[output_index];
const blender::fn::ArrayCPPType *array_cpp_type =
dynamic_cast<const blender::fn::ArrayCPPType *>(array_ptr.type());
blender::fn::GVArray_For_RepeatedGSpan array_repeated{
domain_size, array_cpp_type->array_span(array_ptr.get())};
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(
array_cpp_type->element_type());
result_component.attribute_try_create(
attribute_name, attribute_domain, data_type, AttributeInitVArray{&array_repeated});
}
}
return result_geometry;
}
/**
@@ -1000,19 +1071,18 @@ static void modifyGeometry(ModifierData *md,
return;
}
Span<const InputSocketRef *> group_outputs = output_nodes[0]->inputs().drop_back(1);
if (group_outputs.size() == 0) {
const NodeRef *output_node = output_nodes[0];
if (output_node->inputs().size() <= 1) {
return;
}
const InputSocketRef *group_output = group_outputs[0];
if (group_output->idname() != "NodeSocketGeometry") {
const InputSocketRef &geometry_output_socket = output_node->input(0);
if (geometry_output_socket.idname() != "NodeSocketGeometry") {
return;
}
geometry_set = compute_geometry(
tree, input_nodes, *group_outputs[0], std::move(geometry_set), nmd, ctx);
tree, input_nodes, output_node, std::move(geometry_set), nmd, ctx);
}
static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
@@ -1131,6 +1201,21 @@ static void panel_draw(const bContext *C, Panel *panel)
LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) {
draw_property_for_socket(layout, &bmain_ptr, ptr, nmd->settings.properties, *socket);
}
uiItemS(layout);
LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) {
IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties,
socket->identifier);
if (property == nullptr) {
continue;
}
char socket_id_esc[sizeof(socket->identifier) * 2];
BLI_str_escape(socket_id_esc, socket->identifier, sizeof(socket_id_esc));
char rna_path[1024];
BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc);
uiItemR(layout, ptr, rna_path, 0, socket->name, ICON_NONE);
}
}
/* Draw node warnings. */

View File

@@ -21,6 +21,7 @@
#include "DEG_depsgraph_query.h"
#include "FN_array_cpp_type.hh"
#include "FN_generic_value_map.hh"
#include "FN_multi_function.hh"
@@ -32,6 +33,7 @@
namespace blender::modifiers::geometry_nodes {
using fn::ArrayCPPType;
using fn::CPPType;
using fn::GValueMap;
using nodes::GeoNodeExecParams;
@@ -858,11 +860,8 @@ class GeometryNodesEvaluator {
const MultiFunction &fn,
NodeState &node_state)
{
MFContextBuilder fn_context;
MFParamsBuilder fn_params{fn, 1};
LinearAllocator<> &allocator = local_allocators_.local();
/* Prepare the inputs for the multi function. */
/* Compute output array length. */
int output_size = -1;
for (const int i : node->inputs().index_range()) {
const InputSocketRef &socket_ref = node->input(i);
if (!socket_ref.is_available()) {
@@ -873,8 +872,37 @@ class GeometryNodesEvaluator {
BLI_assert(input_state.was_ready_for_execution);
SingleInputValue &single_value = *input_state.value.single;
BLI_assert(single_value.value != nullptr);
fn_params.add_readonly_single_input(GPointer{*input_state.type, single_value.value});
const ArrayCPPType *array_cpp_type = dynamic_cast<const ArrayCPPType *>(input_state.type);
BLI_assert(array_cpp_type != nullptr);
const int input_size = array_cpp_type->array_size(single_value.value);
output_size = std::max(output_size, input_size);
}
/* If there is no input, output a single value. */
if (output_size == -1) {
output_size = 1;
}
MFContextBuilder fn_context;
MFParamsBuilder fn_params{fn, output_size};
LinearAllocator<> &allocator = local_allocators_.local();
Vector<std::unique_ptr<fn::GVArray>> input_varrays;
/* Prepare the inputs for the multi function. */
for (const int i : node->inputs().index_range()) {
const InputSocketRef &socket_ref = node->input(i);
if (!socket_ref.is_available()) {
continue;
}
InputState &input_state = node_state.inputs[i];
SingleInputValue &single_value = *input_state.value.single;
const ArrayCPPType *array_cpp_type = dynamic_cast<const ArrayCPPType *>(input_state.type);
GSpan span = array_cpp_type->array_span(single_value.value);
auto varray = std::make_unique<fn::GVArray_For_RepeatedGSpan>(output_size, span);
fn_params.add_readonly_single_input(*varray);
input_varrays.append(std::move(varray));
}
/* Prepare the outputs for the multi function. */
Vector<GMutablePointer> outputs;
for (const int i : node->outputs().index_range()) {
@@ -882,13 +910,16 @@ class GeometryNodesEvaluator {
if (!socket_ref.is_available()) {
continue;
}
const CPPType &type = *get_socket_cpp_type(socket_ref);
void *buffer = allocator.allocate(type.size(), type.alignment());
fn_params.add_uninitialized_single_output(GMutableSpan{type, buffer, 1});
const ArrayCPPType *type = dynamic_cast<const ArrayCPPType *>(
get_socket_cpp_type(socket_ref));
BLI_assert(type != nullptr);
void *buffer = allocator.allocate(type->size(), type->alignment());
GMutableSpan span = type->array_construct_uninitialized(buffer, output_size);
fn_params.add_uninitialized_single_output(span);
outputs.append({type, buffer});
}
fn.call(IndexRange(1), fn_params, fn_context);
fn.call(IndexRange(output_size), fn_params, fn_context);
/* Forward the computed outputs. */
int output_index = 0;
@@ -1389,7 +1420,26 @@ class GeometryNodesEvaluator {
return;
}
if (conversions_.is_convertible(from_type, to_type)) {
const ArrayCPPType *from_array_type = dynamic_cast<const ArrayCPPType *>(&from_type);
const ArrayCPPType *to_array_type = dynamic_cast<const ArrayCPPType *>(&to_type);
if (from_array_type != nullptr && to_array_type != nullptr &&
conversions_.is_convertible(from_array_type->element_type(),
to_array_type->element_type())) {
const MultiFunction &fn = *conversions_.get_conversion_multi_function(
MFDataType::ForSingle(from_array_type->element_type()),
MFDataType::ForSingle(to_array_type->element_type()));
GSpan from_span = from_array_type->array_span(from_value);
const int array_size = from_span.size();
GMutableSpan to_span = to_array_type->array_construct_uninitialized(to_value, array_size);
MFParamsBuilder params{fn, array_size};
params.add_readonly_single_input(from_span);
params.add_uninitialized_single_output(to_span);
MFContextBuilder context;
fn.call(IndexRange(array_size), params, context);
}
else if (conversions_.is_convertible(from_type, to_type)) {
/* Do the conversion if possible. */
conversions_.convert_to_uninitialized(from_type, to_type, from_value, to_value);
}
@@ -1464,6 +1514,7 @@ NodeParamsProvider::NodeParamsProvider(GeometryNodesEvaluator &evaluator,
this->modifier = &evaluator.params_.modifier_->modifier;
this->depsgraph = evaluator.params_.depsgraph;
this->logger = evaluator.params_.geo_logger;
this->group_attribute_input_names = &evaluator.params_.group_input_attribute_names;
}
bool NodeParamsProvider::can_get_input(StringRef identifier) const

View File

@@ -50,6 +50,7 @@ struct GeometryNodesEvaluationParams {
Depsgraph *depsgraph;
Object *self_object;
geo_log::GeoLogger *geo_logger;
Map<std::string, std::string> group_input_attribute_names;
Vector<GMutablePointer> r_output_values;
};

View File

@@ -156,6 +156,7 @@ set(SRC
geometry/nodes/node_geo_attribute_remove.cc
geometry/nodes/node_geo_attribute_sample_texture.cc
geometry/nodes/node_geo_attribute_separate_xyz.cc
geometry/nodes/node_geo_attribute_store_local.cc
geometry/nodes/node_geo_attribute_transfer.cc
geometry/nodes/node_geo_attribute_vector_math.cc
geometry/nodes/node_geo_attribute_vector_rotate.cc
@@ -184,6 +185,8 @@ set(SRC
geometry/nodes/node_geo_curve_trim.cc
geometry/nodes/node_geo_delete_geometry.cc
geometry/nodes/node_geo_edge_split.cc
geometry/nodes/node_geo_extrude.cc
geometry/nodes/node_geo_geometry_expander.cc
geometry/nodes/node_geo_input_material.cc
geometry/nodes/node_geo_is_viewport.cc
geometry/nodes/node_geo_join_geometry.cc
@@ -289,7 +292,7 @@ set(SRC
shader/nodes/node_shader_tex_image.c
shader/nodes/node_shader_tex_magic.c
shader/nodes/node_shader_tex_musgrave.c
shader/nodes/node_shader_tex_noise.c
shader/nodes/node_shader_tex_noise.cc
shader/nodes/node_shader_tex_pointdensity.c
shader/nodes/node_shader_tex_sky.c
shader/nodes/node_shader_tex_voronoi.c

View File

@@ -44,6 +44,7 @@ void register_node_type_geo_attribute_proximity(void);
void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_remove(void);
void register_node_type_geo_attribute_separate_xyz(void);
void register_node_type_geo_attribute_store_local(void);
void register_node_type_geo_attribute_transfer(void);
void register_node_type_geo_attribute_vector_math(void);
void register_node_type_geo_attribute_vector_rotate(void);
@@ -70,6 +71,8 @@ void register_node_type_geo_curve_to_points(void);
void register_node_type_geo_curve_trim(void);
void register_node_type_geo_delete_geometry(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_extrude(void);
void register_node_type_geo_geometry_expander(void);
void register_node_type_geo_input_material(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);

View File

@@ -16,6 +16,7 @@
#pragma once
#include "FN_array_cpp_type.hh"
#include "FN_generic_value_map.hh"
#include "BKE_attribute_access.hh"
@@ -37,6 +38,7 @@ using bke::OutputAttribute;
using bke::OutputAttribute_Typed;
using bke::ReadAttributeLookup;
using bke::WriteAttributeLookup;
using fn::ArrayCPPType;
using fn::CPPType;
using fn::GMutablePointer;
using fn::GMutableSpan;
@@ -65,6 +67,7 @@ class GeoNodeExecParamsProvider {
const ModifierData *modifier = nullptr;
Depsgraph *depsgraph = nullptr;
geometry_nodes_eval_log::GeoLogger *logger = nullptr;
const Map<std::string, std::string> *group_attribute_input_names = nullptr;
/**
* Returns true when the node is allowed to get/extract the input value. The identifier is
@@ -142,11 +145,23 @@ class GeoNodeExecParams {
*/
template<typename T> T extract_input(StringRef identifier)
{
if constexpr (std::is_same_v<T, int> || std::is_same_v<T, float> ||
std::is_same_v<T, float3> || std::is_same_v<T, ColorGeometry4f> ||
std::is_same_v<T, bool>) {
Array<T> array = this->extract_input<Array<T>>(identifier);
if (array.is_empty()) {
static T default_value{};
return default_value;
}
return array[0];
}
else {
#ifdef DEBUG
this->check_input_access(identifier, &CPPType::get<T>());
this->check_input_access(identifier, &CPPType::get<T>());
#endif
GMutablePointer gvalue = this->extract_input(identifier);
return gvalue.relocate_out<T>();
GMutablePointer gvalue = this->extract_input(identifier);
return gvalue.relocate_out<T>();
}
}
/**
@@ -169,12 +184,24 @@ class GeoNodeExecParams {
*/
template<typename T> const T &get_input(StringRef identifier) const
{
if constexpr (std::is_same_v<T, int> || std::is_same_v<T, float> ||
std::is_same_v<T, float3> || std::is_same_v<T, ColorGeometry4f> ||
std::is_same_v<T, bool>) {
const Array<T> &array = this->get_input<Array<T>>(identifier);
if (array.is_empty()) {
static const T default_value{};
return default_value;
}
return array[0];
}
else {
#ifdef DEBUG
this->check_input_access(identifier, &CPPType::get<T>());
this->check_input_access(identifier, &CPPType::get<T>());
#endif
GPointer gvalue = provider_->get_input(identifier);
BLI_assert(gvalue.is_type<T>());
return *(const T *)gvalue.get();
GPointer gvalue = provider_->get_input(identifier);
BLI_assert(gvalue.is_type<T>());
return *(const T *)gvalue.get();
}
}
/**
@@ -183,13 +210,31 @@ class GeoNodeExecParams {
template<typename T> void set_output(StringRef identifier, T &&value)
{
using StoredT = std::decay_t<T>;
const CPPType &type = CPPType::get<std::decay_t<T>>();
if constexpr (std::is_same_v<StoredT, int> || std::is_same_v<StoredT, float> ||
std::is_same_v<StoredT, float3> || std::is_same_v<StoredT, ColorGeometry4f> ||
std::is_same_v<StoredT, bool>) {
this->set_output(identifier, Array<StoredT>({value}));
}
else {
const CPPType &type = CPPType::get<std::decay_t<T>>();
#ifdef DEBUG
this->check_output_access(identifier, type);
#endif
GMutablePointer gvalue = provider_->alloc_output_value(type);
new (gvalue.get()) StoredT(std::forward<T>(value));
provider_->set_output(identifier, gvalue);
}
}
void set_output_by_move(StringRef identifier, GMutablePointer value)
{
const CPPType &type = *value.type();
#ifdef DEBUG
this->check_output_access(identifier, type);
#endif
GMutablePointer gvalue = provider_->alloc_output_value(type);
new (gvalue.get()) StoredT(std::forward<T>(value));
provider_->set_output(identifier, gvalue);
GMutablePointer stored_value = provider_->alloc_output_value(type);
type.move_construct(value.get(), stored_value.get());
provider_->set_output(identifier, stored_value);
}
/**
@@ -239,6 +284,11 @@ class GeoNodeExecParams {
return *provider_->dnode->bnode();
}
const bNodeTree &ntree() const
{
return *provider_->dnode->tree().btree();
}
const Object *self_object() const
{
return provider_->self_object;
@@ -291,6 +341,11 @@ class GeoNodeExecParams {
const GeometryComponent &component,
const AttributeDomain default_domain) const;
std::string get_group_input_attribute_name(const StringRef input_identifier) const
{
return provider_->group_attribute_input_names->lookup_default(input_identifier, "");
}
private:
/* Utilities for detecting common errors at when using this class. */
void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;

View File

@@ -284,6 +284,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize,
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, 0, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STORE_LOCAL, def_geo_attribute_store_local, "ATTRIBUTE_STORE_LOCAL", AttributeStoreLocal, "Attribute Store Local", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "")
@@ -311,6 +312,8 @@ DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_
DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "")
DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
DefNode(GeometryNode, GEO_NODE_EXTRUDE, def_geo_extrude, "EXTRUDE", Extrude, "Extrude", "")
DefNode(GeometryNode, GEO_NODE_GEOMETRY_EXPANDER, def_geo_geometry_expander, "GEOMETRY_EXPANDER", GeometryExpander, "Geometry Expander", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")

View File

@@ -92,4 +92,27 @@ void curve_create_default_rotation_attribute(Span<float3> tangents,
Span<float3> normals,
MutableSpan<float3> rotations);
inline bool should_add_output_attribute(const bNode &node, StringRef output_name)
{
LISTBASE_FOREACH (bNodeSocket *, sock, &node.outputs) {
if (sock->name == output_name) {
return (sock->flag & SOCK_ADD_ATTRIBUTE_TO_GEOMETRY);
}
}
return false;
}
inline std::string get_local_attribute_name(const StringRef tree_name,
const StringRef node_name,
const StringRef socket_identifier)
{
return "local_" + tree_name + "_" + node_name + "_" + socket_identifier;
}
inline std::string get_input_attribute_name(const StringRef tree_name,
const StringRef input_identifier)
{
return "input_" + tree_name + "_" + input_identifier;
}
} // namespace blender::nodes

View File

@@ -100,28 +100,38 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
switch (data_type) {
case CD_PROP_FLOAT: {
const float value = params.get_input<float>("Value_001");
attribute->fill(&value);
const Array<float> values = params.get_input<Array<float>>("Value_001");
fn::GVArray_For_RepeatedGSpan values_repeated{attribute->size(), values.as_span()};
fn::GVArray_GSpan values_span{values_repeated};
attribute->set_all(values_span.data());
break;
}
case CD_PROP_FLOAT3: {
const float3 value = params.get_input<float3>("Value");
attribute->fill(&value);
const Array<float3> values = params.get_input<Array<float3>>("Value");
fn::GVArray_For_RepeatedGSpan values_repeated{attribute->size(), values.as_span()};
fn::GVArray_GSpan values_span{values_repeated};
attribute->set_all(values_span.data());
break;
}
case CD_PROP_COLOR: {
const ColorGeometry4f value = params.get_input<ColorGeometry4f>("Value_002");
attribute->fill(&value);
const Array<ColorGeometry4f> values = params.get_input<Array<ColorGeometry4f>>("Value_002");
fn::GVArray_For_RepeatedGSpan values_repeated{attribute->size(), values.as_span()};
fn::GVArray_GSpan values_span{values_repeated};
attribute->set_all(values_span.data());
break;
}
case CD_PROP_BOOL: {
const bool value = params.get_input<bool>("Value_003");
attribute->fill(&value);
const Array<bool> values = params.get_input<Array<bool>>("Value_003");
fn::GVArray_For_RepeatedGSpan values_repeated{attribute->size(), values.as_span()};
fn::GVArray_GSpan values_span{values_repeated};
attribute->set_all(values_span.data());
break;
}
case CD_PROP_INT32: {
const int value = params.get_input<int>("Value_004");
attribute->fill(&value);
const Array<int> values = params.get_input<Array<int>>("Value_004");
fn::GVArray_For_RepeatedGSpan values_repeated{attribute->size(), values.as_span()};
fn::GVArray_GSpan values_span{values_repeated};
attribute->set_all(values_span.data());
break;
}
default:

View File

@@ -0,0 +1,177 @@
/*
* 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 "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_attribute_store_local_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_VECTOR, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_INT, N_("Value"), 0, 0, 0, 0, -10000000.0f, 10000000.0f},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_store_local_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_node_attribute_store_local_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
}
static void geo_node_attribute_store_local_init(bNodeTree *UNUSED(tree), bNode *node)
{
node->custom1 = CD_PROP_FLOAT;
node->custom2 = ATTR_DOMAIN_POINT;
}
static void geo_node_attribute_store_local_update(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *socket_value_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
bNodeSocket *socket_value_float = socket_value_vector->next;
bNodeSocket *socket_value_color4f = socket_value_float->next;
bNodeSocket *socket_value_boolean = socket_value_color4f->next;
bNodeSocket *socket_value_int32 = socket_value_boolean->next;
const CustomDataType data_type = static_cast<CustomDataType>(node->custom1);
nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3);
nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT);
nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR);
nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL);
nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32);
}
namespace blender::nodes {
static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name)
{
/* Use the domain of the result attribute if it already exists. */
std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
if (result_info) {
return result_info->domain;
}
return ATTR_DOMAIN_POINT;
}
static void store_local_attribute(GeometryComponent &component, const GeoNodeExecParams &params)
{
const bNode &node = params.node();
const bNodeTree &tree = params.ntree();
const std::string attribute_name = get_local_attribute_name(tree.id.name, node.name, "");
const CustomDataType data_type = static_cast<CustomDataType>(node.custom1);
const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2);
const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ?
get_result_domain(component, attribute_name) :
domain;
OutputAttribute attribute = component.attribute_try_get_for_output_only(
attribute_name, result_domain, data_type);
if (!attribute) {
return;
}
switch (data_type) {
case CD_PROP_FLOAT: {
const Array<float> values = params.get_input<Array<float>>("Value_001");
fn::GVArray_For_RepeatedGSpan values_repeated{attribute->size(), values.as_span()};
fn::GVArray_GSpan values_span{values_repeated};
attribute->set_all(values_span.data());
break;
}
case CD_PROP_FLOAT3: {
const Array<float3> values = params.get_input<Array<float3>>("Value");
fn::GVArray_For_RepeatedGSpan values_repeated{attribute->size(), values.as_span()};
fn::GVArray_GSpan values_span{values_repeated};
attribute->set_all(values_span.data());
break;
}
case CD_PROP_COLOR: {
const Array<ColorGeometry4f> values = params.get_input<Array<ColorGeometry4f>>("Value_002");
fn::GVArray_For_RepeatedGSpan values_repeated{attribute->size(), values.as_span()};
fn::GVArray_GSpan values_span{values_repeated};
attribute->set_all(values_span.data());
break;
}
case CD_PROP_BOOL: {
const Array<bool> values = params.get_input<Array<bool>>("Value_003");
fn::GVArray_For_RepeatedGSpan values_repeated{attribute->size(), values.as_span()};
fn::GVArray_GSpan values_span{values_repeated};
attribute->set_all(values_span.data());
break;
}
case CD_PROP_INT32: {
const Array<int> values = params.get_input<Array<int>>("Value_004");
fn::GVArray_For_RepeatedGSpan values_repeated{attribute->size(), values.as_span()};
fn::GVArray_GSpan values_span{values_repeated};
attribute->set_all(values_span.data());
break;
}
default:
break;
}
attribute.save();
}
static void geo_node_attribute_store_local_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
if (geometry_set.has<MeshComponent>()) {
store_local_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
}
if (geometry_set.has<PointCloudComponent>()) {
store_local_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
if (geometry_set.has<CurveComponent>()) {
store_local_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_attribute_store_local()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_ATTRIBUTE_STORE_LOCAL, "Store Local Attribute", NODE_CLASS_ATTRIBUTE, 0);
node_type_socket_templates(
&ntype, geo_node_attribute_store_local_in, geo_node_attribute_store_local_out);
node_type_init(&ntype, geo_node_attribute_store_local_init);
node_type_update(&ntype, geo_node_attribute_store_local_update);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_store_local_exec;
ntype.draw_buttons = geo_node_attribute_store_local_layout;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,213 @@
/*
* 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 "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_mesh.h"
#include "BKE_node.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "bmesh.h"
#include "bmesh_tools.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_extrude_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_BOOLEAN, N_("Individual")},
{SOCK_BOOLEAN, N_("Selection"), 1, 0, 0, 0, 0, 1, PROP_NONE, SOCK_HIDE_VALUE},
{SOCK_FLOAT, N_("Distance"), 0.0f, 0, 0, 0, FLT_MIN, FLT_MAX, PROP_DISTANCE},
{SOCK_FLOAT, N_("Inset"), 0.0f, 0, 0, 0, FLT_MIN, FLT_MAX, PROP_DISTANCE},
{-1, ""},
};
static bNodeSocketTemplate geo_node_extrude_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_BOOLEAN, N_("Top Face"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_IS_ATTRIBUTE_OUTPUT},
{SOCK_BOOLEAN, N_("Side Face"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_IS_ATTRIBUTE_OUTPUT},
{-1, ""},
};
using blender::Span;
static void geo_node_extrude_init(bNodeTree *UNUSED(ntree), bNode *node)
{
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
if (socket->type != SOCK_GEOMETRY) {
socket->output_array_source = GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_FACES;
}
}
}
static Mesh *extrude_mesh(const Mesh *mesh,
const Span<bool> selection,
const Span<float> distance,
const Span<float> inset,
const bool inset_individual_faces,
bool **selection_top_faces_out,
bool **selection_all_faces_out)
{
const BMeshCreateParams bmesh_create_params = {true};
const BMeshFromMeshParams bmesh_from_mesh_params = {
true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}};
BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params);
BM_select_faces(bm, selection.data());
BMOperator op;
if (inset_individual_faces) {
BMO_op_initf(bm,
&op,
0,
"inset_individual faces=%hf use_even_offset=%b thickness=%f depth=%f "
"thickness_array=%p depth_array=%p use_attributes=%b",
BM_ELEM_SELECT,
true,
inset[0],
distance[0],
inset.data(),
distance.data(),
true);
}
else {
BMO_op_initf(bm,
&op,
0,
"inset_region faces=%hf use_boundary=%b use_even_offset=%b thickness=%f depth=%f "
"thickness_array=%p depth_array=%p use_attributes=%b",
BM_ELEM_SELECT,
true,
true,
inset[0],
distance[0],
inset.data(),
distance.data(),
true);
}
BM_tag_new_faces(bm, &op);
BMO_op_exec(bm, &op);
BMO_op_finish(bm, &op);
CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX};
Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh);
BM_get_selected_faces(bm, selection_top_faces_out);
BM_get_tagged_faces(bm, selection_all_faces_out);
BM_mesh_free(bm);
BKE_mesh_normals_tag_dirty(result);
return result;
}
namespace blender::nodes {
static void geo_node_extrude_exec(GeoNodeExecParams params)
{
const bNode &node = params.node();
const bNodeTree &ntree = params.ntree();
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
Array<bool> top_faces;
Array<bool> side_faces;
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
if (mesh_component.has_mesh()) {
const Mesh *input_mesh = mesh_component.get_for_read();
Array<bool> selection_array = params.extract_input<Array<bool>>("Selection");
fn::GVArray_For_RepeatedGSpan selection_repeated{input_mesh->totpoly,
selection_array.as_span()};
fn::GVArray_Span<bool> selection{selection_repeated};
AttributeDomain attribute_domain = ATTR_DOMAIN_POINT;
const bool inset_individual_faces = params.extract_input<bool>("Individual");
if (inset_individual_faces) {
attribute_domain = ATTR_DOMAIN_FACE;
}
const int domain_size = mesh_component.attribute_domain_size(attribute_domain);
Array<float> distances_array = params.extract_input<Array<float>>("Distance");
fn::GVArray_For_RepeatedGSpan distances_repeated{domain_size, distances_array.as_span()};
fn::GVArray_Span<float> distance{distances_repeated};
Array<float> insets_array = params.extract_input<Array<float>>("Inset");
fn::GVArray_For_RepeatedGSpan insets_repeated{domain_size, insets_array.as_span()};
fn::GVArray_Span<float> inset{insets_repeated};
bool *selection_top_faces_out = nullptr;
bool *selection_all_faces_out = nullptr;
Mesh *result = extrude_mesh(input_mesh,
selection,
distance,
inset,
inset_individual_faces,
&selection_top_faces_out,
&selection_all_faces_out);
geometry_set.replace_mesh(result);
MeshComponent &result_mesh_component = geometry_set.get_component_for_write<MeshComponent>();
for (const int i : IndexRange(result->totpoly)) {
if (selection_top_faces_out[i]) {
selection_all_faces_out[i] = false;
}
}
top_faces = Span(selection_top_faces_out, result->totpoly);
side_faces = Span(selection_all_faces_out, result->totpoly);
if (should_add_output_attribute(node, "Top Face")) {
std::string attribute_name = get_local_attribute_name(ntree.id.name, node.name, "Top Face");
fn::GVArray_For_Span varray{top_faces.as_span()};
result_mesh_component.attribute_try_create(
attribute_name, ATTR_DOMAIN_FACE, CD_PROP_BOOL, AttributeInitVArray{&varray});
}
if (should_add_output_attribute(node, "Side Face")) {
std::string attribute_name = get_local_attribute_name(ntree.id.name, node.name, "Side Face");
fn::GVArray_For_Span varray{side_faces.as_span()};
result_mesh_component.attribute_try_create(
attribute_name, ATTR_DOMAIN_FACE, CD_PROP_BOOL, AttributeInitVArray{&varray});
}
MEM_freeN(selection_top_faces_out);
MEM_freeN(selection_all_faces_out);
}
params.set_output("Geometry", std::move(geometry_set));
params.set_output("Top Face", std::move(top_faces));
params.set_output("Side Face", std::move(side_faces));
}
} // namespace blender::nodes
void register_node_type_geo_extrude()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_EXTRUDE, "Extrude", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_extrude_in, geo_node_extrude_out);
node_type_init(&ntype, geo_node_extrude_init);
ntype.geometry_node_execute = blender::nodes::geo_node_extrude_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,329 @@
/*
* 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 "UI_interface.h"
#include "UI_resources.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_material.h"
static bNodeSocketTemplate geo_node_geometry_expander_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void geo_node_geometry_expander_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *UNUSED(ptr))
{
uiItemO(layout, "Add", ICON_ADD, "node.geometry_expander_output_add");
}
static bool geo_node_geometry_expander_socket_layout(const bContext *UNUSED(C),
uiLayout *layout,
bNodeTree *ntree,
bNode *node,
bNodeSocket *socket)
{
if (socket->in_out == SOCK_IN) {
return false;
}
if (socket->type == SOCK_GEOMETRY) {
return false;
}
const NodeGeometryGeometryExpander *storage = (const NodeGeometryGeometryExpander *)
node->storage;
const int socket_index = BLI_findindex(&node->outputs, socket);
const int expander_output_index = socket_index - 1;
GeometryExpanderOutput *expander_output = (GeometryExpanderOutput *)BLI_findlink(
&storage->outputs, expander_output_index);
nodeGeometryExpanderUpdateOutputNameCache(expander_output, ntree);
PointerRNA expander_output_ptr;
RNA_pointer_create(
&ntree->id, &RNA_GeometryExpanderOutput, expander_output, &expander_output_ptr);
uiLayout *row = uiLayoutRow(layout, true);
uiLayout *split = uiLayoutSplit(row, 0.5, false);
uiItemL(split, expander_output->display_name_cache, ICON_NONE);
uiLayout *subrow = uiLayoutRow(split, true);
if (expander_output->is_outdated) {
uiItemL(subrow, "", ICON_ERROR);
}
else {
uiItemR(subrow, &expander_output_ptr, "array_source", 0, "", ICON_NONE);
}
uiItemIntO(subrow,
"",
ICON_X,
"node.geometry_expander_output_remove",
"output_index",
expander_output_index);
return true;
}
static GeometryComponentType array_source_to_component_type(
const eGeometryExpanderArraySource array_source)
{
switch (array_source) {
case GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_VERTICES:
return GEO_COMPONENT_TYPE_MESH;
case GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_EDGES:
return GEO_COMPONENT_TYPE_MESH;
case GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_FACES:
return GEO_COMPONENT_TYPE_MESH;
case GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_FACE_CORNERS:
return GEO_COMPONENT_TYPE_MESH;
case GEOMETRY_EXPANDER_ARRAY_SOURCE_POINT_CLOUD_POINTS:
return GEO_COMPONENT_TYPE_POINT_CLOUD;
case GEOMETRY_EXPANDER_ARRAY_SOURCE_CURVE_POINTS:
return GEO_COMPONENT_TYPE_CURVE;
case GEOMETRY_EXPANDER_ARRAY_SOURCE_CURVE_SPLINES:
return GEO_COMPONENT_TYPE_CURVE;
}
BLI_assert_unreachable();
return GEO_COMPONENT_TYPE_MESH;
}
static AttributeDomain array_source_to_domain(const eGeometryExpanderArraySource array_source)
{
switch (array_source) {
case GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_VERTICES:
return ATTR_DOMAIN_POINT;
case GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_EDGES:
return ATTR_DOMAIN_EDGE;
case GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_FACES:
return ATTR_DOMAIN_FACE;
case GEOMETRY_EXPANDER_ARRAY_SOURCE_MESH_FACE_CORNERS:
return ATTR_DOMAIN_CORNER;
case GEOMETRY_EXPANDER_ARRAY_SOURCE_POINT_CLOUD_POINTS:
return ATTR_DOMAIN_POINT;
case GEOMETRY_EXPANDER_ARRAY_SOURCE_CURVE_POINTS:
return ATTR_DOMAIN_POINT;
case GEOMETRY_EXPANDER_ARRAY_SOURCE_CURVE_SPLINES:
return ATTR_DOMAIN_CURVE;
}
BLI_assert_unreachable();
return ATTR_DOMAIN_POINT;
}
static void geo_node_geometry_expander_exec(GeoNodeExecParams params)
{
const bNode &bnode = params.node();
const bNodeTree &ntree = params.ntree();
const NodeGeometryGeometryExpander *storage = (const NodeGeometryGeometryExpander *)
bnode.storage;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
int expander_output_index;
LISTBASE_FOREACH_INDEX (
GeometryExpanderOutput *, expander_output, &storage->outputs, expander_output_index) {
bNodeSocket &socket = *(bNodeSocket *)BLI_findlink(&bnode.outputs, expander_output_index + 1);
const ArrayCPPType *array_cpp_type = dynamic_cast<const ArrayCPPType *>(
socket.typeinfo->get_geometry_nodes_cpp_type());
BLI_assert(array_cpp_type != nullptr);
const CustomDataType data_type = bke::cpp_type_to_custom_data_type(
array_cpp_type->element_type());
BUFFER_FOR_CPP_TYPE_VALUE(*array_cpp_type, buffer);
const eGeometryExpanderArraySource array_source = (eGeometryExpanderArraySource)
expander_output->array_source;
const GeometryComponentType component_type = array_source_to_component_type(array_source);
const AttributeDomain domain = array_source_to_domain(array_source);
const GeometryComponent *component = geometry_set.get_component_for_read(component_type);
if (component == nullptr) {
array_cpp_type->default_construct(buffer);
}
else {
const int domain_size = component->attribute_domain_size(domain);
switch (expander_output->type) {
case GEOMETRY_EXPANDER_OUTPUT_TYPE_BUILTIN: {
GVArrayPtr attribute = component->attribute_try_get_for_read(
expander_output->builtin_identifier, domain, data_type);
if (attribute) {
array_cpp_type->array_construct_uninitialized(buffer, domain_size);
attribute->materialize_to_uninitialized(array_cpp_type->array_span(buffer).data());
}
else {
array_cpp_type->default_construct(buffer);
}
break;
}
case GEOMETRY_EXPANDER_OUTPUT_TYPE_INPUT: {
const std::string attribute_name = params.get_group_input_attribute_name(
expander_output->input_identifier);
GVArrayPtr attribute = component->attribute_try_get_for_read(
attribute_name, domain, data_type);
if (attribute) {
array_cpp_type->array_construct_uninitialized(buffer, domain_size);
attribute->materialize_to_uninitialized(array_cpp_type->array_span(buffer).data());
}
else {
array_cpp_type->default_construct(buffer);
}
break;
}
case GEOMETRY_EXPANDER_OUTPUT_TYPE_LOCAL: {
const std::string attribute_name = get_local_attribute_name(
ntree.id.name,
expander_output->local_node_name,
expander_output->local_socket_identifier);
GVArrayPtr attribute = component->attribute_try_get_for_read(
attribute_name, domain, data_type);
if (attribute) {
array_cpp_type->array_construct_uninitialized(buffer, domain_size);
attribute->materialize_to_uninitialized(array_cpp_type->array_span(buffer).data());
}
else {
array_cpp_type->default_construct(buffer);
}
break;
}
case GEOMETRY_EXPANDER_OUTPUT_TYPE_DERIVED: {
const StringRef identifier = expander_output->derived_identifier;
if (identifier == "index") {
array_cpp_type->array_construct_uninitialized(buffer, domain_size);
MutableSpan<int> indices = array_cpp_type->array_span(buffer).typed<int>();
for (int i : indices.index_range()) {
indices[i] = i;
}
}
else if (identifier == "normal") {
if (component->attribute_exists("normal")) {
GVArray_Typed<float3> normals = component->attribute_get_for_read<float3>(
"normal", domain, {0, 0, 0});
array_cpp_type->array_construct_uninitialized(buffer, domain_size);
normals->materialize_to_uninitialized(
array_cpp_type->array_span(buffer).typed<float3>());
}
else {
array_cpp_type->default_construct(buffer);
}
}
else {
array_cpp_type->default_construct(buffer);
}
break;
}
}
}
params.set_output_by_move(socket.identifier, {array_cpp_type, buffer});
}
params.set_output("Geometry", geometry_set);
}
static void geo_node_geometry_expander_init(bNodeTree *ntree, bNode *node)
{
NodeGeometryGeometryExpander *storage = (NodeGeometryGeometryExpander *)MEM_callocN(
sizeof(NodeGeometryGeometryExpander), __func__);
node->storage = storage;
nodeAddSocket(ntree, node, SOCK_OUT, "NodeSocketGeometry", "Geometry", "Geometry");
}
static void geo_node_geometry_expander_update(bNodeTree *ntree, bNode *node)
{
NodeGeometryGeometryExpander *storage = (NodeGeometryGeometryExpander *)node->storage;
Map<StringRef, bNodeSocket *> old_outputs;
bNodeSocket *geometry_socket = (bNodeSocket *)node->outputs.first;
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
if (socket == geometry_socket) {
continue;
}
old_outputs.add(socket->identifier, socket);
}
VectorSet<bNodeSocket *> new_sockets;
LISTBASE_FOREACH (GeometryExpanderOutput *, expander_output, &storage->outputs) {
bNodeSocket *socket = old_outputs.lookup_default(expander_output->socket_identifier, nullptr);
if (socket == nullptr) {
const char *idname = nodeStaticSocketType(expander_output->socket_type, PROP_NONE);
socket = nodeAddSocket(
ntree, node, SOCK_OUT, idname, expander_output->socket_identifier, "name");
}
new_sockets.add_new(socket);
}
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &node->outputs) {
if (socket == geometry_socket) {
continue;
}
if (!new_sockets.contains(socket)) {
nodeRemoveSocket(ntree, node, socket);
}
}
BLI_listbase_clear(&node->outputs);
BLI_addtail(&node->outputs, geometry_socket);
for (bNodeSocket *socket : new_sockets) {
BLI_addtail(&node->outputs, socket);
}
}
static void geo_node_geometry_expander_storage_free(bNode *node)
{
NodeGeometryGeometryExpander *storage = (NodeGeometryGeometryExpander *)node->storage;
LISTBASE_FOREACH_MUTABLE (GeometryExpanderOutput *, output, &storage->outputs) {
MEM_freeN(output);
}
MEM_freeN(storage);
}
static void geo_node_geometry_expander_storage_copy(bNodeTree *UNUSED(dest_ntree),
bNode *dst_node,
const bNode *src_node)
{
NodeGeometryGeometryExpander *src_storage = (NodeGeometryGeometryExpander *)src_node->storage;
NodeGeometryGeometryExpander *dst_storage = (NodeGeometryGeometryExpander *)MEM_callocN(
sizeof(NodeGeometryGeometryExpander), __func__);
LISTBASE_FOREACH (GeometryExpanderOutput *, src_output, &src_storage->outputs) {
GeometryExpanderOutput *dst_output = (GeometryExpanderOutput *)MEM_callocN(
sizeof(GeometryExpanderOutput), __func__);
*dst_output = *src_output;
BLI_addtail(&dst_storage->outputs, dst_output);
}
dst_node->storage = dst_storage;
}
} // namespace blender::nodes
void register_node_type_geo_geometry_expander()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_GEOMETRY_EXPANDER, "Geometry Expander", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_geometry_expander_in, nullptr);
node_type_init(&ntype, blender::nodes::geo_node_geometry_expander_init);
node_type_storage(&ntype,
"NodeGeometryGeometryExpander",
blender::nodes::geo_node_geometry_expander_storage_free,
blender::nodes::geo_node_geometry_expander_storage_copy);
node_type_update(&ntype, blender::nodes::geo_node_geometry_expander_update);
ntype.geometry_node_execute = blender::nodes::geo_node_geometry_expander_exec;
ntype.draw_buttons = blender::nodes::geo_node_geometry_expander_layout;
ntype.draw_socket = blender::nodes::geo_node_geometry_expander_socket_layout;
nodeRegisterType(&ntype);
}

View File

@@ -42,14 +42,16 @@ using blender::bke::GeometryInstanceGroup;
static bNodeSocketTemplate geo_node_point_distribute_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_FLOAT, N_("Distance Min"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_DISTANCE},
{SOCK_FLOAT, N_("Density Max"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
{SOCK_STRING, N_("Density Attribute")},
{SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};
static bNodeSocketTemplate geo_node_point_distribute_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_VECTOR, N_("Rotation"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_IS_ATTRIBUTE_OUTPUT},
{SOCK_VECTOR, N_("Normal"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_IS_ATTRIBUTE_OUTPUT},
{SOCK_INT, N_("ID"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_IS_ATTRIBUTE_OUTPUT},
{-1, ""},
};
@@ -60,6 +62,15 @@ static void geo_node_point_distribute_layout(uiLayout *layout,
uiItemR(layout, ptr, "distribute_method", 0, "", ICON_NONE);
}
static void geo_node_point_distribute_init(bNodeTree *UNUSED(ntree), bNode *node)
{
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
if (socket->type != SOCK_GEOMETRY) {
socket->output_array_source = GEOMETRY_EXPANDER_ARRAY_SOURCE_POINT_CLOUD_POINTS;
}
}
}
static void node_point_distribute_update(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *sock_min_dist = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
@@ -84,12 +95,15 @@ static float3 normal_to_euler_rotation(const float3 normal)
static void sample_mesh_surface(const Mesh &mesh,
const float4x4 &transform,
const float base_density,
const VArray<float> *density_factors,
const Array<float> &density_factors,
const int seed,
Vector<float3> &r_positions,
Vector<float3> &r_bary_coords,
Vector<int> &r_looptri_indices)
{
fn::GVArray_For_RepeatedGSpan density_factors_repeated{mesh.totloop, density_factors.as_span()};
GVArray_Typed<float> density_factors_varray{density_factors_repeated};
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
@@ -105,19 +119,16 @@ static void sample_mesh_surface(const Mesh &mesh,
const float3 v1_pos = transform * float3(mesh.mvert[v1_index].co);
const float3 v2_pos = transform * float3(mesh.mvert[v2_index].co);
float looptri_density_factor = 1.0f;
if (density_factors != nullptr) {
const float v0_density_factor = std::max(0.0f, density_factors->get(v0_loop));
const float v1_density_factor = std::max(0.0f, density_factors->get(v1_loop));
const float v2_density_factor = std::max(0.0f, density_factors->get(v2_loop));
looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
}
const float v0_density = std::max(0.0f, density_factors_varray[v0_loop]);
const float v1_density = std::max(0.0f, density_factors_varray[v1_loop]);
const float v2_density = std::max(0.0f, density_factors_varray[v2_loop]);
float looptri_density = (v0_density + v1_density + v2_density) / 3.0f;
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
const int looptri_seed = BLI_hash_int(looptri_index + seed);
RandomNumberGenerator looptri_rng(looptri_seed);
const float points_amount_fl = area * base_density * looptri_density_factor;
const float points_amount_fl = area * base_density * looptri_density;
const float add_point_probability = fractf(points_amount_fl);
const bool add_point = add_point_probability > looptri_rng.get_float();
const int point_amount = (int)points_amount_fl + (int)add_point;
@@ -338,23 +349,20 @@ BLI_NOINLINE static void interpolate_existing_attributes(
}
}
namespace {
struct SpecialAttributeOutputs {
Vector<float3> rotations;
Vector<float3> normals;
Vector<int> ids;
};
} // namespace
BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup> sets,
Span<int> instance_start_offsets,
GeometryComponent &component,
Span<int> UNUSED(instance_start_offsets),
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
Span<Vector<int>> looptri_indices_array,
SpecialAttributeOutputs &r_special_attributes)
{
OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
"id", ATTR_DOMAIN_POINT);
OutputAttribute_Typed<float3> normal_attribute =
component.attribute_try_get_for_output_only<float3>("normal", ATTR_DOMAIN_POINT);
OutputAttribute_Typed<float3> rotation_attribute =
component.attribute_try_get_for_output_only<float3>("rotation", ATTR_DOMAIN_POINT);
MutableSpan<int> result_ids = id_attribute.as_span();
MutableSpan<float3> result_normals = normal_attribute.as_span();
MutableSpan<float3> result_rotations = rotation_attribute.as_span();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : sets) {
const GeometrySet &set = set_group.geometry_set;
@@ -364,13 +372,8 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
BKE_mesh_runtime_looptri_len(&mesh)};
for (const float4x4 &transform : set_group.transforms) {
const int offset = instance_start_offsets[i_instance];
Span<float3> bary_coords = bary_coords_array[i_instance];
Span<int> looptri_indices = looptri_indices_array[i_instance];
MutableSpan<int> ids = result_ids.slice(offset, bary_coords.size());
MutableSpan<float3> normals = result_normals.slice(offset, bary_coords.size());
MutableSpan<float3> rotations = result_rotations.slice(offset, bary_coords.size());
/* Use one matrix multiplication per point instead of three (for each triangle corner). */
float rotation_matrix[3][3];
@@ -388,19 +391,17 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
const float3 v1_pos = float3(mesh.mvert[v1_index].co);
const float3 v2_pos = float3(mesh.mvert[v2_index].co);
ids[i] = (int)(bary_coord.hash() + (uint64_t)looptri_index);
normal_tri_v3(normals[i], v0_pos, v1_pos, v2_pos);
mul_m3_v3(rotation_matrix, normals[i]);
rotations[i] = normal_to_euler_rotation(normals[i]);
r_special_attributes.ids.append((int)(bary_coord.hash() + (uint64_t)looptri_index));
float3 normal;
normal_tri_v3(normal, v0_pos, v1_pos, v2_pos);
mul_m3_v3(rotation_matrix, normal);
r_special_attributes.normals.append(normal);
r_special_attributes.rotations.append(normal_to_euler_rotation(normal));
}
i_instance++;
}
}
id_attribute.save();
normal_attribute.save();
rotation_attribute.save();
}
BLI_NOINLINE static void add_remaining_point_attributes(
@@ -409,7 +410,8 @@ BLI_NOINLINE static void add_remaining_point_attributes(
const Map<std::string, AttributeKind> &attributes,
GeometryComponent &component,
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
Span<Vector<int>> looptri_indices_array,
SpecialAttributeOutputs &r_special_attributes)
{
interpolate_existing_attributes(set_groups,
instance_start_offsets,
@@ -417,50 +419,41 @@ BLI_NOINLINE static void add_remaining_point_attributes(
component,
bary_coords_array,
looptri_indices_array);
compute_special_attributes(
set_groups, instance_start_offsets, component, bary_coords_array, looptri_indices_array);
compute_special_attributes(set_groups,
instance_start_offsets,
bary_coords_array,
looptri_indices_array,
r_special_attributes);
}
static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
const StringRef density_attribute_name,
const float density,
const float base_density,
const Array<float> &density,
const int seed,
MutableSpan<Vector<float3>> positions_all,
MutableSpan<Vector<float3>> bary_coords_all,
MutableSpan<Vector<int>> looptri_indices_all)
{
/* If there is an attribute name, the default value for the densities should be zero so that
* points are only scattered where the attribute exists. Otherwise, just "ignore" the density
* factors. */
const bool use_one_default = density_attribute_name.is_empty();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
const Mesh &mesh = *component.get_for_read();
for (const float4x4 &transform : set_group.transforms) {
Vector<float3> &positions = positions_all[i_instance];
Vector<float3> &bary_coords = bary_coords_all[i_instance];
Vector<int> &looptri_indices = looptri_indices_all[i_instance];
sample_mesh_surface(mesh,
transform,
density,
&*density_factors,
seed,
positions,
bary_coords,
looptri_indices);
sample_mesh_surface(
mesh, transform, base_density, density, seed, positions, bary_coords, looptri_indices);
i_instance++;
}
}
}
static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_groups,
const StringRef density_attribute_name,
const float density,
const float max_density,
const Array<float> &density_factors,
const int seed,
const float minimum_distance,
MutableSpan<Vector<float3>> positions_all,
@@ -479,7 +472,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
Vector<float3> &bary_coords = bary_coords_all[i_instance];
Vector<int> &looptri_indices = looptri_indices_all[i_instance];
sample_mesh_surface(
mesh, transform, density, nullptr, seed, positions, bary_coords, looptri_indices);
mesh, transform, max_density, {1.0f}, seed, positions, bary_coords, looptri_indices);
instance_start_offsets[i_instance] = initial_points_len;
initial_points_len += positions.size();
@@ -487,11 +480,6 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
}
}
/* If there is an attribute name, the default value for the densities should be zero so that
* points are only scattered where the attribute exists. Otherwise, just "ignore" the density
* factors. */
const bool use_one_default = density_attribute_name.is_empty();
/* Unlike the other result arrays, the elimination mask in stored as a flat array for every
* point, in order to simplify culling points from the KDTree (which needs to know about all
* points at once). */
@@ -507,8 +495,10 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *component.get_for_read();
const GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
fn::GVArray_For_RepeatedGSpan density_factors_repeated{mesh.totloop,
density_factors.as_span()};
GVArray_Typed<float> density_factors_varray{density_factors_repeated};
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
Vector<float3> &positions = positions_all[i_instance];
@@ -518,7 +508,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
const int offset = instance_start_offsets[i_instance];
update_elimination_mask_based_on_density_factors(
mesh,
density_factors,
density_factors_varray,
bary_coords,
looptri_indices,
elimination_mask.as_mutable_span().slice(offset, positions.size()));
@@ -533,6 +523,14 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
}
}
static void set_fallback_output(GeoNodeExecParams &params)
{
params.set_output("Geometry", GeometrySet());
params.set_output("Rotation", Array<float3>());
params.set_output("Normal", Array<float3>());
params.set_output("ID", Array<int>());
}
static void geo_node_point_distribute_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -541,19 +539,18 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
static_cast<GeometryNodePointDistributeMode>(params.node().custom1);
const int seed = params.get_input<int>("Seed") * 5383843;
const float density = params.extract_input<float>("Density Max");
const std::string density_attribute_name = params.extract_input<std::string>(
"Density Attribute");
const float density_max = 1.0f;
const Array<float> density_factors = params.extract_input<Array<float>>("Density");
if (density <= 0.0f) {
params.set_output("Geometry", GeometrySet());
if (density_max <= 0.0f) {
set_fallback_output(params);
return;
}
Vector<GeometryInstanceGroup> set_groups;
geometry_set_gather_instances(geometry_set, set_groups);
if (set_groups.is_empty()) {
params.set_output("Geometry", GeometrySet());
set_fallback_output(params);
return;
}
@@ -567,7 +564,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
if (set_groups.is_empty()) {
params.error_message_add(NodeWarningType::Error, TIP_("Input geometry must contain a mesh"));
params.set_output("Geometry", GeometrySet());
set_fallback_output(params);
return;
}
@@ -586,8 +583,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
switch (distribute_method) {
case GEO_NODE_POINT_DISTRIBUTE_RANDOM: {
distribute_points_random(set_groups,
density_attribute_name,
density,
density_max,
density_factors,
seed,
positions_all,
bary_coords_all,
@@ -597,8 +594,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
case GEO_NODE_POINT_DISTRIBUTE_POISSON: {
const float minimum_distance = params.extract_input<float>("Distance Min");
distribute_points_poisson_disk(set_groups,
density_attribute_name,
density,
density_max,
density_factors,
seed,
minimum_distance,
positions_all,
@@ -632,14 +629,46 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
Map<std::string, AttributeKind> attributes;
bke::geometry_set_gather_instances_attribute_info(
set_groups, {GEO_COMPONENT_TYPE_MESH}, {"position", "normal", "id"}, attributes);
SpecialAttributeOutputs special_attributes;
add_remaining_point_attributes(set_groups,
instance_start_offsets,
attributes,
point_component,
bary_coords_all,
looptri_indices_all);
looptri_indices_all,
special_attributes);
if (should_add_output_attribute(params.node(), "Rotation")) {
const std::string rotation_attribute_name = get_local_attribute_name(
params.ntree().id.name, params.node().name, "Rotation");
fn::GVArray_For_Span rotations_varray{special_attributes.rotations.as_span()};
point_component.attribute_try_create(rotation_attribute_name,
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT3,
AttributeInitVArray{&rotations_varray});
}
if (should_add_output_attribute(params.node(), "Normal")) {
const std::string normal_attribute_name = get_local_attribute_name(
params.ntree().id.name, params.node().name, "Normal");
fn::GVArray_For_Span normals_varray{special_attributes.normals.as_span()};
point_component.attribute_try_create(normal_attribute_name,
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT3,
AttributeInitVArray{&normals_varray});
}
if (should_add_output_attribute(params.node(), "ID")) {
const std::string id_attribute_name = get_local_attribute_name(
params.ntree().id.name, params.node().name, "ID");
fn::GVArray_For_Span ids_varray{special_attributes.ids.as_span()};
point_component.attribute_try_create(
id_attribute_name, ATTR_DOMAIN_POINT, CD_PROP_INT32, AttributeInitVArray{&ids_varray});
}
params.set_output("Geometry", std::move(geometry_set_out));
params.set_output("Rotation", Array<float3>(special_attributes.rotations.as_span()));
params.set_output("Normal", Array<float3>(special_attributes.normals.as_span()));
params.set_output("ID", Array<int>(special_attributes.ids.as_span()));
}
} // namespace blender::nodes
@@ -652,6 +681,7 @@ void register_node_type_geo_point_distribute()
&ntype, GEO_NODE_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_point_distribute_in, geo_node_point_distribute_out);
node_type_update(&ntype, node_point_distribute_update);
node_type_init(&ntype, geo_node_point_distribute_init);
ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_exec;
ntype.draw_buttons = geo_node_point_distribute_layout;
nodeRegisterType(&ntype);

View File

@@ -38,6 +38,9 @@ static bNodeSocketTemplate geo_node_point_instance_in[] = {
PROP_NONE,
SOCK_HIDE_LABEL},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_EULER},
{SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 0.0f, -10000.0f, 10000.0f},
{SOCK_INT, N_("ID"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
{-1, ""},
};
@@ -172,11 +175,19 @@ static void add_instances_from_component(InstancesComponent &instances,
GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>(
"position", domain, {0, 0, 0});
GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>(
"rotation", domain, {0, 0, 0});
GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>(
"scale", domain, {1, 1, 1});
GVArray_Typed<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1);
Array<float3> rotations_array = params.get_input<Array<float3>>("Rotation");
fn::GVArray_For_RepeatedGSpan rotations_repeated{domain_size, rotations_array.as_span()};
GVArray_Typed<float3> rotations{rotations_repeated};
Array<float3> scales_array = params.get_input<Array<float3>>("Scale");
static float3 ones = {1.0f, 1.0f, 1.0f};
fn::GVArray_For_RepeatedGSpan scales_repeated{domain_size, scales_array.as_span(), &ones};
GVArray_Typed<float3> scales{scales_repeated};
Array<int> ids_array = params.get_input<Array<int>>("ID");
fn::GVArray_For_RepeatedGSpan ids_repeated{domain_size, ids_array.as_span()};
GVArray_Typed<int> ids{ids_repeated};
/* The initial size of the component might be non-zero if there are two component types. */
const int start_len = instances.instances_amount();
@@ -193,7 +204,7 @@ static void add_instances_from_component(InstancesComponent &instances,
for (const int i : range) {
handles[i] = handle;
transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
instance_ids[i] = id_attribute[i];
instance_ids[i] = ids[i];
}
});
}
@@ -206,7 +217,7 @@ static void add_instances_from_component(InstancesComponent &instances,
const int handle = possible_handles[index];
handles[i] = handle;
transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
instance_ids[i] = id_attribute[i];
instance_ids[i] = ids[i];
}
});
}

View File

@@ -25,7 +25,7 @@
static bNodeSocketTemplate geo_node_point_instance_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Mask")},
{SOCK_BOOLEAN, N_("Mask"), 1, 0, 0, 0, 0, 1, PROP_NONE, SOCK_HIDE_VALUE},
{-1, ""},
};
@@ -99,17 +99,16 @@ static void create_component_points(GeometryComponent &component, const int tota
static void separate_points_from_component(const GeometryComponent &in_component,
GeometryComponent &out_component,
const StringRef mask_name,
const Span<bool> mask,
const bool invert)
{
if (!in_component.attribute_domain_supported(ATTR_DOMAIN_POINT) ||
in_component.attribute_domain_size(ATTR_DOMAIN_POINT) == 0) {
return;
}
const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>(
mask_name, ATTR_DOMAIN_POINT, false);
VArray_Span<bool> masks{mask_attribute};
const int tot_in_point = in_component.attribute_domain_size(ATTR_DOMAIN_POINT);
fn::GVArray_For_RepeatedGSpan mask_repeated{tot_in_point, mask};
GVArray_Span<bool> masks{mask_repeated};
const int total = masks.count(!invert);
if (total == 0) {
@@ -122,7 +121,7 @@ static void separate_points_from_component(const GeometryComponent &in_component
}
static GeometrySet separate_geometry_set(const GeometrySet &set_in,
const StringRef mask_name,
const Span<bool> mask,
const bool invert)
{
GeometrySet set_out;
@@ -132,7 +131,7 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in,
continue;
}
GeometryComponent &out_component = set_out.get_component_for_write(component->type());
separate_points_from_component(*component, out_component, mask_name, invert);
separate_points_from_component(*component, out_component, mask, invert);
}
return set_out;
}
@@ -145,7 +144,7 @@ static void geo_node_point_separate_exec(GeoNodeExecParams params)
if (wait_for_inputs) {
return;
}
const std::string mask_attribute_name = params.get_input<std::string>("Mask");
const Array<bool> mask = params.get_input<Array<bool>>("Mask");
GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry");
/* TODO: This is not necessary-- the input geometry set can be read only,
@@ -153,12 +152,10 @@ static void geo_node_point_separate_exec(GeoNodeExecParams params)
geometry_set = geometry_set_realize_instances(geometry_set);
if (params.lazy_output_is_required("Geometry 1")) {
params.set_output("Geometry 1",
separate_geometry_set(geometry_set, mask_attribute_name, true));
params.set_output("Geometry 1", separate_geometry_set(geometry_set, mask, true));
}
if (params.lazy_output_is_required("Geometry 2")) {
params.set_output("Geometry 2",
separate_geometry_set(geometry_set, mask_attribute_name, false));
params.set_output("Geometry 2", separate_geometry_set(geometry_set, mask, false));
}
}

View File

@@ -21,7 +21,6 @@
static bNodeSocketTemplate geo_node_point_translate_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Translation")},
{SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{-1, ""},
};
@@ -31,13 +30,6 @@ static bNodeSocketTemplate geo_node_point_translate_out[] = {
{-1, ""},
};
static void geo_node_point_translate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE);
}
namespace blender::nodes {
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
@@ -47,11 +39,15 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
if (!position_attribute) {
return;
}
GVArray_Typed<float3> attribute = params.get_input_attribute<float3>(
"Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
for (const int i : IndexRange(attribute.size())) {
position_attribute->set(i, position_attribute->get(i) + attribute[i]);
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
const Array<float3> &translations_array = params.get_input<Array<float3>>("Translation");
fn::GVArray_For_RepeatedGSpan repeated_translation{domain_size, translations_array.as_span()};
fn::GVArray_Typed<float3> translations{repeated_translation};
for (const int i : IndexRange(domain_size)) {
position_attribute->set(i, position_attribute->get(i) + translations[i]);
}
position_attribute.save();
@@ -76,23 +72,6 @@ static void geo_node_point_translate_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
static void geo_node_point_translate_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryPointTranslate *data = (NodeGeometryPointTranslate *)MEM_callocN(
sizeof(NodeGeometryPointTranslate), __func__);
data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
node->storage = data;
}
static void geo_node_point_translate_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage;
update_attribute_input_socket_availabilities(
*node, "Translation", (GeometryNodeAttributeInputMode)node_storage.input_type);
}
} // namespace blender::nodes
void register_node_type_geo_point_translate()
@@ -101,13 +80,6 @@ void register_node_type_geo_point_translate()
geo_node_type_base(&ntype, GEO_NODE_POINT_TRANSLATE, "Point Translate", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_point_translate_in, geo_node_point_translate_out);
node_type_init(&ntype, blender::nodes::geo_node_point_translate_init);
node_type_update(&ntype, blender::nodes::geo_node_point_translate_update);
node_type_storage(&ntype,
"NodeGeometryPointTranslate",
node_free_standard_storage,
node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_point_translate_exec;
ntype.draw_buttons = geo_node_point_translate_layout;
nodeRegisterType(&ntype);
}

View File

@@ -47,6 +47,7 @@
#include "NOD_node_tree_multi_function.hh"
#include "NOD_socket.h"
#include "FN_array_cpp_type.hh"
#include "FN_cpp_type_make.hh"
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
@@ -604,6 +605,9 @@ static bNodeSocketType *make_socket_type_virtual()
return stype;
}
using blender::Array;
using blender::fn::CPPType;
static bNodeSocketType *make_socket_type_bool()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE);
@@ -611,8 +615,12 @@ static bNodeSocketType *make_socket_type_bool()
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value;
};
socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
socktype->get_geometry_nodes_cpp_type = []() { return &CPPType::get<Array<bool>>(); };
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
bool value;
socket.typeinfo->get_base_cpp_value(socket, &value);
new (r_value) Array<bool>({value});
};
return socktype;
}
@@ -623,8 +631,12 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value;
};
socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
socktype->get_geometry_nodes_cpp_type = []() { return &CPPType::get<Array<float>>(); };
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
float value;
socket.typeinfo->get_base_cpp_value(socket, &value);
new (r_value) Array<float>({value});
};
return socktype;
}
@@ -635,8 +647,12 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value;
};
socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
socktype->get_geometry_nodes_cpp_type = []() { return &CPPType::get<Array<int>>(); };
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
int value;
socket.typeinfo->get_base_cpp_value(socket, &value);
new (r_value) Array<int>({value});
};
return socktype;
}
@@ -647,8 +663,12 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value;
};
socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
socktype->get_geometry_nodes_cpp_type = []() { return &CPPType::get<Array<blender::float3>>(); };
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
blender::float3 value;
socket.typeinfo->get_base_cpp_value(socket, &value);
new (r_value) Array<blender::float3>({value});
};
return socktype;
}
@@ -661,8 +681,14 @@ static bNodeSocketType *make_socket_type_rgba()
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
};
socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
socktype->get_geometry_nodes_cpp_type = []() {
return &CPPType::get<Array<blender::ColorGeometry4f>>();
};
socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) {
blender::ColorGeometry4f value;
socket.typeinfo->get_base_cpp_value(socket, &value);
new (r_value) Array<blender::ColorGeometry4f>({value});
};
return socktype;
}
@@ -678,6 +704,12 @@ static bNodeSocketType *make_socket_type_string()
return socktype;
}
MAKE_ARRAY_CPP_TYPE(IntArray, blender::Array<int>)
MAKE_ARRAY_CPP_TYPE(FloatArray, blender::Array<float>)
MAKE_ARRAY_CPP_TYPE(Float3Array, blender::Array<blender::float3>)
MAKE_ARRAY_CPP_TYPE(BoolArray, blender::Array<bool>)
MAKE_ARRAY_CPP_TYPE(ColorArray, blender::Array<blender::ColorGeometry4f>)
MAKE_CPP_TYPE(Object, Object *, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(Collection, Collection *, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(Texture, Tex *, CPPTypeFlags::BasicType)

View File

@@ -17,6 +17,8 @@
* All rights reserved.
*/
#include "BLI_noise.h"
#include "../node_shader_util.h"
/* **************** NOISE ******************** */
@@ -48,7 +50,7 @@ static bNodeSocketTemplate sh_node_tex_noise_out[] = {
static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeTexNoise *tex = MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise");
NodeTexNoise *tex = (NodeTexNoise *)MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise");
BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT);
BKE_texture_colormapping_default(&tex->base.color_mapping);
tex->dimensions = 3;
@@ -86,18 +88,82 @@ static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node)
nodeSetSocketAvailability(sockW, tex->dimensions == 1 || tex->dimensions == 4);
}
class NoiseTextureFunction : public blender::fn::MultiFunction {
public:
NoiseTextureFunction()
{
static blender::fn::MFSignature signature = create_signature();
this->set_signature(&signature);
}
static blender::fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Noise Texture"};
signature.single_input<blender::float3>("Vector");
signature.single_input<float>("Scale");
signature.single_input<float>("Detail");
signature.single_input<float>("Roughness");
signature.single_input<float>("Distortion");
signature.single_output<float>("Fac");
signature.single_output<blender::ColorGeometry4f>("Color");
return signature.build();
}
void call(blender::IndexMask mask,
blender::fn::MFParams params,
blender::fn::MFContext UNUSED(context)) const override
{
const blender::VArray<blender::float3> &vectors =
params.readonly_single_input<blender::float3>(0, "Vector");
const blender::VArray<float> &scales = params.readonly_single_input<float>(1, "Scale");
const blender::VArray<float> &details = params.readonly_single_input<float>(2, "Detail");
blender::MutableSpan<float> r_values = params.uninitialized_single_output<float>(5, "Fac");
blender::MutableSpan<blender::ColorGeometry4f> r_colors =
params.uninitialized_single_output<blender::ColorGeometry4f>(6, "Color");
for (int i : mask) {
const blender::float3 vector = vectors[i];
const float scale = scales[i];
const float noise_size = safe_divide(1.0f, scale);
const float detail = details[i];
const float noise1 = BLI_noise_generic_turbulence(
noise_size, vector.x, vector.y, vector.z, detail, false, 1);
const float noise2 = BLI_noise_generic_turbulence(
noise_size, vector.y, vector.x + 100.0f, vector.z, detail, false, 1);
const float noise3 = BLI_noise_generic_turbulence(
noise_size, vector.z + 100.0f, vector.y, vector.x, detail, false, 1);
r_values[i] = noise1;
r_colors[i] = {noise1, noise2, noise3, 1.0f};
}
}
};
static void sh_node_tex_noise_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
/* TODO: Not only support 3D. */
NodeTexNoise *tex = builder.dnode()->storage<NodeTexNoise>();
if (tex->dimensions != 3) {
builder.set_not_implemented();
return;
}
static NoiseTextureFunction fn;
builder.set_matching_fn(fn);
}
/* node type definition */
void register_node_type_sh_tex_noise(void)
{
static bNodeType ntype;
sh_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0);
sh_fn_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0);
node_type_socket_templates(&ntype, sh_node_tex_noise_in, sh_node_tex_noise_out);
node_type_init(&ntype, node_shader_init_tex_noise);
node_type_storage(
&ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage);
node_type_gpu(&ntype, node_shader_gpu_tex_noise);
node_type_update(&ntype, node_shader_update_tex_noise);
ntype.expand_in_mf_network = sh_node_tex_noise_expand_in_mf_network;
nodeRegisterType(&ntype);
}