Compare commits
51 Commits
tmp-vfx-pl
...
temp-geome
Author | SHA1 | Date | |
---|---|---|---|
277117826f | |||
71e3f18b4a | |||
92b8d2f7e4 | |||
d4223da817 | |||
1ee857b4e7 | |||
1a22c90583 | |||
cb38233f6c | |||
14103793de | |||
b6c0c06420 | |||
bfa882c2f4 | |||
e9ab6ff164 | |||
8d4791d92d | |||
c50478674c | |||
adbeabb13c | |||
4a248986d0 | |||
f9b948d4fe | |||
9eb238db2d | |||
c596e6b277 | |||
a2a2f7c95b | |||
bcb74106a0 | |||
f204b3d16c | |||
6343cd913d | |||
d5dc3501b5 | |||
fa37d09040 | |||
0ce5b5be3b | |||
4fc03dd96b | |||
7fcf2ad543 | |||
3e413d9289 | |||
5afddf257d | |||
297f7062a8 | |||
f2b51d960d | |||
65283828ea | |||
1cad21295e | |||
503114993f | |||
3064492272 | |||
df115dec05 | |||
f8a23fb05a | |||
3f1603a8f8 | |||
5d0409c25c | |||
b78014af97 | |||
ef611546d6 | |||
4bc27fcdf0 | |||
4e8935c7b7 | |||
5a5f5a1350 | |||
60adfd93af | |||
ac8fbb2768 | |||
3c4869bf48 | |||
379344b636 | |||
dd71f59d35 | |||
9d9395ef42 | |||
92518a7057 |
@@ -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"),
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -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 {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
|
@@ -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++;
|
||||
}
|
||||
}
|
||||
/** \} */
|
||||
|
@@ -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);
|
@@ -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'}},
|
||||
},
|
||||
|
@@ -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 */
|
||||
|
@@ -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)");
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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)
|
||||
|
90
source/blender/functions/FN_array_cpp_type.hh
Normal file
90
source/blender/functions/FN_array_cpp_type.hh
Normal 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; \
|
||||
}
|
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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");
|
||||
|
@@ -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. */
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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", "")
|
||||
|
@@ -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
|
||||
|
@@ -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:
|
||||
|
@@ -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 ¶ms)
|
||||
{
|
||||
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);
|
||||
}
|
213
source/blender/nodes/geometry/nodes/node_geo_extrude.cc
Normal file
213
source/blender/nodes/geometry/nodes/node_geo_extrude.cc
Normal 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);
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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 ¶ms)
|
||||
{
|
||||
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);
|
||||
|
@@ -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];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
}
|
Reference in New Issue
Block a user