Merge branch 'master' into sculpt-dev

This commit is contained in:
Pablo Dobarro 2020-12-16 20:27:13 +01:00
commit 06078a6f92
21 changed files with 603 additions and 17 deletions

View File

@ -264,9 +264,11 @@ template<typename T> class TypedWriteAttribute {
}
};
using BooleanReadAttribute = TypedReadAttribute<bool>;
using FloatReadAttribute = TypedReadAttribute<float>;
using Float3ReadAttribute = TypedReadAttribute<float3>;
using Color4fReadAttribute = TypedReadAttribute<Color4f>;
using BooleanWriteAttribute = TypedWriteAttribute<bool>;
using FloatWriteAttribute = TypedWriteAttribute<float>;
using Float3WriteAttribute = TypedWriteAttribute<float3>;
using Color4fWriteAttribute = TypedWriteAttribute<Color4f>;

View File

@ -392,6 +392,8 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty
return &CPPType::get<int>();
case CD_PROP_COLOR:
return &CPPType::get<Color4f>();
case CD_PROP_BOOL:
return &CPPType::get<bool>();
default:
return nullptr;
}
@ -415,6 +417,9 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type)
if (type.is<Color4f>()) {
return CD_PROP_COLOR;
}
if (type.is<bool>()) {
return CD_PROP_BOOL;
}
return static_cast<CustomDataType>(-1);
}
@ -449,6 +454,9 @@ static ReadAttributePtr read_attribute_from_custom_data(const CustomData &custom
case CD_PROP_COLOR:
return std::make_unique<ArrayReadAttribute<Color4f>>(
domain, Span(static_cast<Color4f *>(layer.data), size));
case CD_PROP_BOOL:
return std::make_unique<ArrayReadAttribute<bool>>(
domain, Span(static_cast<bool *>(layer.data), size));
}
}
}
@ -490,6 +498,9 @@ static WriteAttributePtr write_attribute_from_custom_data(
case CD_PROP_COLOR:
return std::make_unique<ArrayWriteAttribute<Color4f>>(
domain, MutableSpan(static_cast<Color4f *>(layer.data), size));
case CD_PROP_BOOL:
return std::make_unique<ArrayWriteAttribute<bool>>(
domain, MutableSpan(static_cast<bool *>(layer.data), size));
}
}
}
@ -751,6 +762,7 @@ bool PointCloudComponent::attribute_domain_with_type_supported(
const AttributeDomain domain, const CustomDataType data_type) const
{
return domain == ATTR_DOMAIN_POINT && ELEM(data_type,
CD_PROP_BOOL,
CD_PROP_FLOAT,
CD_PROP_FLOAT2,
CD_PROP_FLOAT3,
@ -874,8 +886,13 @@ bool MeshComponent::attribute_domain_with_type_supported(const AttributeDomain d
if (!this->attribute_domain_supported(domain)) {
return false;
}
return ELEM(
data_type, CD_PROP_FLOAT, CD_PROP_FLOAT2, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR);
return ELEM(data_type,
CD_PROP_BOOL,
CD_PROP_FLOAT,
CD_PROP_FLOAT2,
CD_PROP_FLOAT3,
CD_PROP_INT32,
CD_PROP_COLOR);
}
int MeshComponent::attribute_domain_size(const AttributeDomain domain) const

View File

@ -1837,6 +1837,21 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerMultiply_propfloat2,
NULL,
layerAdd_propfloat2},
/* 50: CD_PROP_POOL */
{sizeof(bool),
"bool",
1,
N_("Boolean"),
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL},
};
static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
@ -1892,6 +1907,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDPropCol",
"CDPropFloat3",
"CDPropFloat2",
"CDPropBoolean",
};
const CustomData_MeshMasks CD_MASK_BAREMESH = {

View File

@ -178,6 +178,7 @@ int *BLI_bvhtree_intersect_plane(BVHTree *tree, float plane[4], uint *r_intersec
int BLI_bvhtree_get_len(const BVHTree *tree);
int BLI_bvhtree_get_tree_type(const BVHTree *tree);
float BLI_bvhtree_get_epsilon(const BVHTree *tree);
void BLI_bvhtree_get_bounding_box(BVHTree *tree, float r_bb_min[3], float r_bb_max[3]);
/* find nearest node to the given coordinates
* (if nearest is given it will only search nodes where

View File

@ -1076,6 +1076,25 @@ float BLI_bvhtree_get_epsilon(const BVHTree *tree)
return tree->epsilon;
}
/**
* This function returns the bounding box of the BVH tree.
*/
void BLI_bvhtree_get_bounding_box(BVHTree *tree, float r_bb_min[3], float r_bb_max[3])
{
BVHNode *root = tree->nodes[tree->totleaf];
if (root != NULL) {
const float bb_min[3] = {root->bv[0], root->bv[2], root->bv[4]};
const float bb_max[3] = {root->bv[1], root->bv[3], root->bv[5]};
copy_v3_v3(r_bb_min, bb_min);
copy_v3_v3(r_bb_max, bb_max);
}
else {
BLI_assert(false);
zero_v3(r_bb_min);
zero_v3(r_bb_max);
}
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -3208,6 +3208,13 @@ static void node_geometry_buts_attribute_mix(uiLayout *layout,
uiItemR(col, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("B"), ICON_NONE);
}
static void node_geometry_buts_attribute_point_distribute(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "distribute_method", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_geometry_set_butfunc(bNodeType *ntype)
{
switch (ntype->type) {
@ -3235,6 +3242,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
case GEO_NODE_ATTRIBUTE_MIX:
ntype->draw_buttons = node_geometry_buts_attribute_mix;
break;
case GEO_NODE_POINT_DISTRIBUTE:
ntype->draw_buttons = node_geometry_buts_attribute_point_distribute;
break;
}
}

View File

@ -75,8 +75,7 @@ typedef struct CustomData {
* MUST be >= CD_NUMTYPES, but we cant use a define here.
* Correct size is ensured in CustomData_update_typemap assert().
*/
int typemap[50];
char _pad[4];
int typemap[51];
/** Number of layers, size of layers array. */
int totlayer, maxlayer;
/** In editmode, total size of all data layers. */
@ -156,7 +155,9 @@ typedef enum CustomDataType {
CD_PROP_FLOAT3 = 48,
CD_PROP_FLOAT2 = 49,
CD_NUMTYPES = 50,
CD_PROP_BOOL = 50,
CD_NUMTYPES = 51,
} CustomDataType;
/* Bits for CustomDataMask */
@ -208,6 +209,7 @@ typedef enum CustomDataType {
#define CD_MASK_PROP_COLOR (1ULL << CD_PROP_COLOR)
#define CD_MASK_PROP_FLOAT3 (1ULL << CD_PROP_FLOAT3)
#define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2)
#define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL)
/** Multires loop data. */
#define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK)

View File

@ -1501,6 +1501,11 @@ typedef enum GeometryNodeAttributeInputMode {
GEO_NODE_ATTRIBUTE_INPUT_COLOR = 3,
} GeometryNodeAttributeInputMode;
typedef enum GeometryNodePointDistributeMethod {
GEO_NODE_POINT_DISTRIBUTE_RANDOM = 0,
GEO_NODE_POINT_DISTRIBUTE_POISSON = 1,
} GeometryNodePointDistributeMethod;
#ifdef __cplusplus
}
#endif

View File

@ -44,6 +44,7 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
{CD_PROP_COLOR, "FLOAT_COLOR", 0, "Float Color", "RGBA color with floating-point precisions"},
{CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit precision"},
{CD_PROP_STRING, "STRING", 0, "String", "Text string"},
{CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"},
{0, NULL, 0, NULL, NULL},
};

View File

@ -452,6 +452,20 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[] = {
{GEO_NODE_POINT_DISTRIBUTE_RANDOM,
"RANDOM",
0,
"Random",
"Distribute points randomly on the surface"},
{GEO_NODE_POINT_DISTRIBUTE_POISSON,
"POISSON",
0,
"Poisson Disk",
"Project points on the surface evenly with a Poisson disk distribution"},
{0, NULL, 0, NULL, NULL},
};
#endif
#ifdef RNA_RUNTIME
@ -1876,7 +1890,7 @@ static const EnumPropertyItem *itemf_function_check(
static bool attribute_random_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3);
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL);
}
static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_type_itemf(
bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
@ -1898,7 +1912,7 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_domain_itemf(
static bool attribute_fill_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR);
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR, CD_PROP_BOOL);
}
static const EnumPropertyItem *rna_GeometryNodeAttributeFill_type_itemf(bContext *UNUSED(C),
PointerRNA *UNUSED(ptr),
@ -8481,6 +8495,18 @@ static void def_geo_attribute_mix(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_point_distribute(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "distribute_method", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_node_geometry_point_distribute_method_items);
RNA_def_property_enum_default(prop, GEO_NODE_POINT_DISTRIBUTE_RANDOM);
RNA_def_property_ui_text(prop, "Distribution Method", "Method to use for scattering points");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)

View File

@ -148,6 +148,7 @@ set(SRC
geometry/nodes/node_geo_attribute_mix.cc
geometry/nodes/node_geo_object_info.cc
geometry/nodes/node_geo_point_distribute.cc
geometry/nodes/node_geo_point_distribute_poisson_disk.cc
geometry/nodes/node_geo_point_instance.cc
geometry/nodes/node_geo_subdivision_surface.cc
geometry/nodes/node_geo_transform.cc

View File

@ -26,6 +26,8 @@
namespace blender::nodes {
using bke::BooleanReadAttribute;
using bke::BooleanWriteAttribute;
using bke::Color4fReadAttribute;
using bke::Color4fWriteAttribute;
using bke::Float3ReadAttribute;

View File

@ -271,7 +271,7 @@ DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Spl
DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, 0, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")

View File

@ -42,4 +42,10 @@ namespace blender::nodes {
void update_attribute_input_socket_availabilities(bNode &node,
const StringRef name,
const GeometryNodeAttributeInputMode mode);
}
void poisson_disk_point_elimination(Vector<float3> const *input_points,
Vector<float3> *output_points,
float maximum_distance,
float3 boundbox);
} // namespace blender::nodes

View File

@ -27,6 +27,7 @@ static bNodeSocketTemplate geo_node_attribute_fill_in[] = {
{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},
{-1, ""},
};
@ -45,12 +46,14 @@ static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node
bNodeSocket *socket_value_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
bNodeSocket *socket_value_float = socket_value_vector->next;
bNodeSocket *socket_value_color4f = socket_value_float->next;
bNodeSocket *socket_value_boolean = socket_value_color4f->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);
}
namespace blender::nodes {
@ -96,6 +99,14 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
color4f_attribute.apply_span();
break;
}
case CD_PROP_BOOL: {
BooleanWriteAttribute boolean_attribute = std::move(attribute);
const bool value = params.get_input<bool>("Value_003");
MutableSpan<bool> attribute_span = boolean_attribute.get_span();
attribute_span.fill(value);
boolean_attribute.apply_span();
break;
}
default:
break;
}

View File

@ -59,6 +59,16 @@ static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode
namespace blender::nodes {
static void randomize_attribute(BooleanWriteAttribute &attribute, RandomNumberGenerator &rng)
{
MutableSpan<bool> attribute_span = attribute.get_span();
for (const int i : IndexRange(attribute.size())) {
const bool value = rng.get_float() > 0.5f;
attribute_span[i] = value;
}
attribute.apply_span();
}
static void randomize_attribute(FloatWriteAttribute &attribute,
float min,
float max,
@ -121,6 +131,11 @@ static void randomize_attribute(GeometryComponent &component,
randomize_attribute(float3_attribute, min_value, max_value, rng);
break;
}
case CD_PROP_BOOL: {
BooleanWriteAttribute boolean_attribute = std::move(attribute);
randomize_attribute(boolean_attribute, rng);
break;
}
default:
break;
}

View File

@ -24,6 +24,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
#include "BKE_bvhutils.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
@ -33,8 +34,10 @@
static bNodeSocketTemplate geo_node_point_distribute_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_FLOAT, N_("Density"), 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
{SOCK_FLOAT, N_("Minimum Distance"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
{SOCK_FLOAT, N_("Maximum Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
{SOCK_STRING, N_("Density Attribute")},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};
@ -43,11 +46,19 @@ static bNodeSocketTemplate geo_node_point_distribute_out[] = {
{-1, ""},
};
static void node_point_distribute_update(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *sock_min_dist = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
nodeSetSocketAvailability(sock_min_dist, ELEM(node->custom1, GEO_NODE_POINT_DISTRIBUTE_POISSON));
}
namespace blender::nodes {
static Vector<float3> scatter_points_from_mesh(const Mesh *mesh,
const float density,
const FloatReadAttribute &density_factors)
static Vector<float3> random_scatter_points_from_mesh(const Mesh *mesh,
const float density,
const FloatReadAttribute &density_factors,
const int seed)
{
/* This only updates a cache and can be considered to be logically const. */
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(mesh));
@ -71,7 +82,7 @@ static Vector<float3> scatter_points_from_mesh(const Mesh *mesh,
3.0f;
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
const int looptri_seed = BLI_hash_int(looptri_index);
const int looptri_seed = BLI_hash_int(looptri_index + seed);
RandomNumberGenerator looptri_rng(looptri_seed);
const float points_amount_fl = area * density * looptri_density_factor;
@ -90,17 +101,158 @@ static Vector<float3> scatter_points_from_mesh(const Mesh *mesh,
return points;
}
struct RayCastAll_Data {
void *bvhdata;
BVHTree_RayCastCallback raycast_callback;
const Mesh *mesh;
float base_weight;
FloatReadAttribute *density_factors;
Vector<float3> *projected_points;
float cur_point_weight;
};
static void project_2d_bvh_callback(void *userdata,
int index,
const BVHTreeRay *ray,
BVHTreeRayHit *hit)
{
struct RayCastAll_Data *data = (RayCastAll_Data *)userdata;
data->raycast_callback(data->bvhdata, index, ray, hit);
if (hit->index != -1) {
/* This only updates a cache and can be considered to be logically const. */
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(data->mesh));
const MVert *mvert = data->mesh->mvert;
const MLoopTri &looptri = looptris[index];
const FloatReadAttribute &density_factors = data->density_factors[0];
const int v0_index = data->mesh->mloop[looptri.tri[0]].v;
const int v1_index = data->mesh->mloop[looptri.tri[1]].v;
const int v2_index = data->mesh->mloop[looptri.tri[2]].v;
const float v0_density_factor = std::max(0.0f, density_factors[v0_index]);
const float v1_density_factor = std::max(0.0f, density_factors[v1_index]);
const float v2_density_factor = std::max(0.0f, density_factors[v2_index]);
/* Calculate barycentric weights for hit point. */
float3 weights;
interp_weights_tri_v3(
weights, mvert[v0_index].co, mvert[v1_index].co, mvert[v2_index].co, hit->co);
float point_weight = weights[0] * v0_density_factor + weights[1] * v1_density_factor +
weights[2] * v2_density_factor;
point_weight *= data->base_weight;
if (point_weight >= FLT_EPSILON && data->cur_point_weight <= point_weight) {
data->projected_points->append(hit->co);
}
}
}
static Vector<float3> poisson_scatter_points_from_mesh(const Mesh *mesh,
const float density,
const float minimum_distance,
const FloatReadAttribute &density_factors,
const int seed)
{
Vector<float3> points;
if (minimum_distance <= FLT_EPSILON || density <= FLT_EPSILON) {
return points;
}
/* Scatter points randomly on the mesh with higher density (5-7) times higher than desired for
* good quality possion disk distributions. */
int quality = 5;
const int output_points_target = 1000;
points.resize(output_points_target * quality);
const float required_area = output_points_target *
(2.0f * sqrtf(3.0f) * minimum_distance * minimum_distance);
const float point_scale_multiplier = sqrtf(required_area);
{
const int rnd_seed = BLI_hash_int(seed);
RandomNumberGenerator point_rng(rnd_seed);
for (int i = 0; i < points.size(); i++) {
points[i].x = point_rng.get_float() * point_scale_multiplier;
points[i].y = point_rng.get_float() * point_scale_multiplier;
points[i].z = 0.0f;
}
}
/* Eliminate the scattered points until we get a possion distribution. */
Vector<float3> output_points(output_points_target);
const float3 bounds_max = float3(point_scale_multiplier, point_scale_multiplier, 0);
poisson_disk_point_elimination(&points, &output_points, 2.0f * minimum_distance, bounds_max);
Vector<float3> final_points;
final_points.reserve(output_points_target);
/* Check if we have any points we should remove from the final possion distribition. */
BVHTreeFromMesh treedata;
BKE_bvhtree_from_mesh_get(&treedata, const_cast<Mesh *>(mesh), BVHTREE_FROM_LOOPTRI, 2);
float3 bb_min, bb_max;
BLI_bvhtree_get_bounding_box(treedata.tree, bb_min, bb_max);
struct RayCastAll_Data data;
data.bvhdata = &treedata;
data.raycast_callback = treedata.raycast_callback;
data.mesh = mesh;
data.projected_points = &final_points;
data.density_factors = const_cast<FloatReadAttribute *>(&density_factors);
data.base_weight = std::min(
1.0f, density / (output_points.size() / (point_scale_multiplier * point_scale_multiplier)));
const float max_dist = bb_max[2] - bb_min[2] + 2.0f;
const float3 dir = float3(0, 0, -1);
float3 raystart;
raystart.z = bb_max[2] + 1.0f;
float tile_start_x_coord = bb_min[0];
int tile_repeat_x = ceilf((bb_max[0] - bb_min[0]) / point_scale_multiplier);
float tile_start_y_coord = bb_min[1];
int tile_repeat_y = ceilf((bb_max[1] - bb_min[1]) / point_scale_multiplier);
for (int x = 0; x < tile_repeat_x; x++) {
float tile_curr_x_coord = x * point_scale_multiplier + tile_start_x_coord;
for (int y = 0; y < tile_repeat_y; y++) {
float tile_curr_y_coord = y * point_scale_multiplier + tile_start_y_coord;
for (int idx = 0; idx < output_points.size(); idx++) {
raystart.x = output_points[idx].x + tile_curr_x_coord;
raystart.y = output_points[idx].y + tile_curr_y_coord;
data.cur_point_weight = (float)idx / (float)output_points.size();
BLI_bvhtree_ray_cast_all(
treedata.tree, raystart, dir, 0.0f, max_dist, project_2d_bvh_callback, &data);
}
}
}
return final_points;
}
static void geo_node_point_distribute_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet geometry_set_out;
GeometryNodePointDistributeMethod distribute_method =
static_cast<GeometryNodePointDistributeMethod>(params.node().custom1);
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", std::move(geometry_set_out));
return;
}
const float density = params.extract_input<float>("Density");
const float density = params.extract_input<float>("Maximum Density");
const std::string density_attribute = params.extract_input<std::string>("Density Attribute");
if (density <= 0.0f) {
@ -113,8 +265,19 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
const FloatReadAttribute density_factors = mesh_component.attribute_get_for_read<float>(
density_attribute, ATTR_DOMAIN_POINT, 1.0f);
const int seed = params.get_input<int>("Seed");
Vector<float3> points = scatter_points_from_mesh(mesh_in, density, density_factors);
Vector<float3> points;
switch (distribute_method) {
case GEO_NODE_POINT_DISTRIBUTE_RANDOM:
points = random_scatter_points_from_mesh(mesh_in, density, density_factors, seed);
break;
case GEO_NODE_POINT_DISTRIBUTE_POISSON:
const float min_dist = params.extract_input<float>("Minimum Distance");
points = poisson_scatter_points_from_mesh(mesh_in, density, min_dist, density_factors, seed);
break;
}
PointCloud *pointcloud = BKE_pointcloud_new_nomain(points.size());
memcpy(pointcloud->co, points.data(), sizeof(float3) * points.size());
@ -135,6 +298,7 @@ void register_node_type_geo_point_distribute()
geo_node_type_base(
&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);
ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_exec;
nodeRegisterType(&ntype);
}

View File

@ -0,0 +1,281 @@
/*
* 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.
*/
/*
* Based on Cem Yuksel. 2015. Sample Elimination for Generating Poisson Disk Sample
* ! Sets. Computer Graphics Forum 34, 2 (May 2015), 25-32.
* ! http://www.cemyuksel.com/research/sampleelimination/
* Copyright (c) 2016, Cem Yuksel <cem@cemyuksel.com>
* All rights reserved.
*/
#include "BLI_inplace_priority_queue.hh"
#include "BLI_kdtree.h"
#include "node_geometry_util.hh"
#include <iostream>
#include <string.h>
namespace blender::nodes {
static void tile_point(Vector<float3> *tiled_points,
Vector<size_t> *indices,
const float maximum_distance,
const float3 boundbox,
float3 const &point,
size_t index,
int dimension = 0)
{
for (int dimension_iter = dimension; dimension_iter < 3; dimension_iter++) {
if (boundbox[dimension_iter] - point[dimension_iter] < maximum_distance) {
float3 point_tiled = point;
point_tiled[dimension_iter] -= boundbox[dimension_iter];
tiled_points->append(point_tiled);
indices->append(index);
tile_point(tiled_points,
indices,
maximum_distance,
boundbox,
point_tiled,
index,
dimension_iter + 1);
}
if (point[dimension_iter] < maximum_distance) {
float3 point_tiled = point;
point_tiled[dimension_iter] += boundbox[dimension_iter];
tiled_points->append(point_tiled);
indices->append(index);
tile_point(tiled_points,
indices,
maximum_distance,
boundbox,
point_tiled,
index,
dimension_iter + 1);
}
}
}
/**
* Returns the weight the point gets based on the distance to another point.
*/
static float point_weight_influence_get(const float maximum_distance,
const float minimum_distance,
float distance)
{
const float alpha = 8.0f;
if (distance < minimum_distance) {
distance = minimum_distance;
}
return std::pow(1.0f - distance / maximum_distance, alpha);
}
/**
* Weight each point based on their proximity to its neighbors
*
* For each index in the weight array add a weight based on the proximity the
* corresponding point has with its neighboors.
**/
static void points_distance_weight_calculate(Vector<float> *weights,
const size_t point_id,
const float3 *input_points,
const void *kd_tree,
const float minimum_distance,
const float maximum_distance,
InplacePriorityQueue<float> *heap)
{
KDTreeNearest_3d *nearest_point = nullptr;
int neighbors = BLI_kdtree_3d_range_search(
(KDTree_3d *)kd_tree, input_points[point_id], &nearest_point, maximum_distance);
for (int i = 0; i < neighbors; i++) {
size_t neighbor_point_id = nearest_point[i].index;
if (neighbor_point_id >= weights->size()) {
continue;
}
/* The point should not influence itself. */
if (neighbor_point_id == point_id) {
continue;
}
const float weight_influence = point_weight_influence_get(
maximum_distance, minimum_distance, nearest_point[i].dist);
/* In the first pass we just the weights. */
if (heap == nullptr) {
(*weights)[point_id] += weight_influence;
}
/* When we run again we need to update the weights and the heap. */
else {
(*weights)[neighbor_point_id] -= weight_influence;
heap->priority_decreased(neighbor_point_id);
}
}
if (nearest_point) {
MEM_freeN(nearest_point);
}
}
/**
* Returns the minimum radius fraction used by the default weight function.
*/
static float weight_limit_fraction_get(const size_t input_size, const size_t output_size)
{
const float beta = 0.65f;
const float gamma = 1.5f;
float ratio = float(output_size) / float(input_size);
return (1.0f - std::pow(ratio, gamma)) * beta;
}
/**
* Tile the input points.
*/
static void points_tiling(const float3 *input_points,
const size_t input_size,
void **kd_tree,
const float maximum_distance,
const float3 boundbox)
{
Vector<float3> tiled_points(input_points, input_points + input_size);
Vector<size_t> indices(input_size);
for (size_t i = 0; i < input_size; i++) {
indices[i] = i;
}
/* Tile the tree based on the boundbox. */
for (size_t i = 0; i < input_size; i++) {
tile_point(&tiled_points, &indices, maximum_distance, boundbox, input_points[i], i);
}
/* Build a new tree with the new indices and tiled points. */
*kd_tree = BLI_kdtree_3d_new(tiled_points.size());
for (size_t i = 0; i < tiled_points.size(); i++) {
BLI_kdtree_3d_insert(*(KDTree_3d **)kd_tree, indices[i], tiled_points[i]);
}
BLI_kdtree_3d_balance(*(KDTree_3d **)kd_tree);
}
static void weighted_sample_elimination(const float3 *input_points,
const size_t input_size,
float3 *output_points,
const size_t output_size,
const float maximum_distance,
const float3 boundbox,
const bool do_copy_eliminated)
{
const float minimum_distance = maximum_distance *
weight_limit_fraction_get(input_size, output_size);
void *kd_tree = nullptr;
points_tiling(input_points, input_size, &kd_tree, maximum_distance, boundbox);
/* Assign weights to each sample. */
Vector<float> weights(input_size, 0.0f);
for (size_t point_id = 0; point_id < weights.size(); point_id++) {
points_distance_weight_calculate(
&weights, point_id, input_points, kd_tree, minimum_distance, maximum_distance, nullptr);
}
/* Remove the points based on their weight. */
InplacePriorityQueue<float> heap(weights);
size_t sample_size = input_size;
while (sample_size > output_size) {
/* For each sample around it, remove its weight contribution and update the heap. */
size_t point_id = heap.pop_index();
points_distance_weight_calculate(
&weights, point_id, input_points, kd_tree, minimum_distance, maximum_distance, &heap);
sample_size--;
}
/* Copy the samples to the output array. */
size_t target_size = do_copy_eliminated ? input_size : output_size;
for (size_t i = 0; i < target_size; i++) {
size_t index = heap.all_indices()[i];
output_points[i] = input_points[index];
}
/* Cleanup. */
BLI_kdtree_3d_free((KDTree_3d *)kd_tree);
}
static void progressive_sampling_reorder(Vector<float3> *output_points,
float maximum_density,
float3 boundbox)
{
/* Re-order the points for progressive sampling. */
Vector<float3> temporary_points(output_points->size());
float3 *source_points = output_points->data();
float3 *dest_points = temporary_points.data();
size_t source_size = output_points->size();
size_t dest_size = 0;
while (source_size >= 3) {
dest_size = source_size * 0.5f;
/* Changes the weight function radius using half of the number of samples.
* It is used for progressive sampling. */
maximum_density *= std::sqrt(2.0f);
weighted_sample_elimination(
source_points, source_size, dest_points, dest_size, maximum_density, boundbox, true);
if (dest_points != output_points->data()) {
memcpy((*output_points)[dest_size],
dest_points[dest_size],
(source_size - dest_size) * sizeof(float3));
}
/* Swap the arrays around. */
float3 *points_iter = source_points;
source_points = dest_points;
dest_points = points_iter;
source_size = dest_size;
}
if (source_points != output_points->data()) {
memcpy(output_points->data(), source_points, dest_size * sizeof(float3));
}
}
void poisson_disk_point_elimination(Vector<float3> const *input_points,
Vector<float3> *output_points,
float maximum_density,
float3 boundbox)
{
weighted_sample_elimination(input_points->data(),
input_points->size(),
output_points->data(),
output_points->size(),
maximum_density,
boundbox,
false);
progressive_sampling_reorder(output_points, maximum_density, boundbox);
}
} // namespace blender::nodes

View File

@ -96,6 +96,9 @@ CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
if (found_socket->type == SOCK_RGBA) {
return CD_PROP_COLOR;
}
if (found_socket->type == SOCK_BOOLEAN) {
return CD_PROP_BOOL;
}
BLI_assert(false);
return default_type;

View File

@ -208,6 +208,10 @@ static DataTypeConversions create_implicit_conversions()
conversions, "float to Color4f", [](float a) { return Color4f(a, a, a, 1.0f); });
add_implicit_conversion<Color4f, float>(
conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); });
add_implicit_conversion<float3, bool>(
conversions, "float3 to boolean", [](float3 a) { return a.length_squared() == 0.0f; });
add_implicit_conversion<bool, float3>(
conversions, "boolean to float3", [](bool a) { return (a) ? float3(1.0f) : float3(0.0f); });
return conversions;
}

View File

@ -1204,7 +1204,7 @@ static wmOperator *wm_operator_create(wmWindowManager *wm,
RNA_STRUCT_END;
}
else {
LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &op->opm->type->macro) {
LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &ot->macro) {
wmOperatorType *otm = WM_operatortype_find(macro->idname, 0);
wmOperator *opm = wm_operator_create(wm, otm, macro->ptr, NULL);