Geometry Nodes: initial scattering and geometry nodes

This is the initial merge from the geometry-nodes branch.
Nodes:
* Attribute Math
* Boolean
* Edge Split
* Float Compare
* Object Info
* Point Distribute
* Point Instance
* Random Attribute
* Random Float
* Subdivision Surface
* Transform
* Triangulate

It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier.

Notes on the Generic attribute access API

The API adds an indirection for attribute access. That has the following benefits:
* Most code does not have to care about how an attribute is stored internally.
  This is mainly necessary, because we have to deal with "legacy" attributes
  such as vertex weights and attributes that are embedded into other structs
  such as vertex positions.
* When reading from an attribute, we generally don't care what domain the
  attribute is stored on. So we want to abstract away the interpolation that
  that adapts attributes from one domain to another domain (this is not
  actually implemented yet).

Other possible improvements for later iterations include:
* Actually implement interpolation between domains.
* Don't use inheritance for the different attribute types. A single class for read
  access and one for write access might be enough, because we know all the ways
  in which attributes are stored internally. We don't want more different internal
  structures in the future. On the contrary, ideally we can consolidate the different
  storage formats in the future to reduce the need for this indirection.
* Remove the need for heap allocations when creating attribute accessors.

It includes commits from:
* Dalai Felinto
* Hans Goudey
* Jacques Lucke
* Léo Depoix
This commit is contained in:
2020-12-02 13:25:25 +01:00
parent ddbe3274ef
commit 6be56c13e9
103 changed files with 7337 additions and 890 deletions

View File

@@ -31,6 +31,7 @@ _modules = [
"console",
"constraint",
"file",
"geometry_nodes",
"image",
"mesh",
"node",
@@ -42,7 +43,6 @@ _modules = [
"rigidbody",
"screen_play_rendered_anim",
"sequencer",
"simulation",
"userpref",
"uvcalc_follow_active",
"uvcalc_lightmap",

View File

@@ -0,0 +1,60 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# 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.
#
# ##### END GPL LICENSE BLOCK #####
import bpy
def geometry_modifier_poll(context) -> bool:
ob = context.object
# Test object support for geometry node modifier (No volume or hair object support yet)
if not ob or ob.type not in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'POINTCLOUD'}:
return False
return True
class NewGeometryNodeTree(bpy.types.Operator):
"""Create a new geometry node tree"""
bl_idname = "node.new_geometry_node_tree"
bl_label = "New Geometry Node Tree"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return geometry_modifier_poll(context)
def execute(self, context):
group = bpy.data.node_groups.new("Geometry Nodes", 'GeometryNodeTree')
group.inputs.new('NodeSocketGeometry', "Geometry")
group.outputs.new('NodeSocketGeometry', "Geometry")
input_node = group.nodes.new('NodeGroupInput')
output_node = group.nodes.new('NodeGroupOutput')
output_node.is_active_output = True
input_node.location.x = -200 - input_node.width
output_node.location.x = 200
group.links.new(output_node.inputs[0], input_node.outputs[0])
return {'FINISHED'}
classes = (
NewGeometryNodeTree,
)

View File

@@ -1,41 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# 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.
#
# ##### END GPL LICENSE BLOCK #####
import bpy
class NewSimulation(bpy.types.Operator):
"""Create a new simulation data block and edit it in the opened simulation editor"""
bl_idname = "simulation.new"
bl_label = "New Simulation"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return context.area.type == 'NODE_EDITOR' and context.space_data.tree_type == 'SimulationNodeTree'
def execute(self, context):
simulation = bpy.data.simulations.new("Simulation")
context.space_data.simulation = simulation
return {'FINISHED'}
classes = (
NewSimulation,
)

View File

@@ -151,13 +151,10 @@ class NODE_HT_header(Header):
if snode_id:
layout.prop(snode_id, "use_nodes")
elif snode.tree_type == 'SimulationNodeTree':
row = layout.row(align=True)
row.prop(snode, "simulation", text="")
row.operator("simulation.new", text="", icon='ADD')
simulation = snode.simulation
if simulation:
row.prop(snode.simulation, "use_fake_user", text="")
elif snode.tree_type == 'GeometryNodeTree':
NODE_MT_editor_menus.draw_collapsible(context, layout)
layout.separator_spacer()
layout.template_ID(snode, "node_tree", new="node.new_geometry_node_tree")
else:
# Custom node tree is edited as independent ID block

View File

@@ -58,11 +58,11 @@ class TextureNodeCategory(SortedNodeCategory):
context.space_data.tree_type == 'TextureNodeTree')
class SimulationNodeCategory(SortedNodeCategory):
class GeometryNodeCategory(SortedNodeCategory):
@classmethod
def poll(cls, context):
return (context.space_data.type == 'NODE_EDITOR' and
context.space_data.tree_type == 'SimulationNodeTree')
context.space_data.tree_type == 'GeometryNodeTree')
# menu entry for node group tools
@@ -77,11 +77,11 @@ node_tree_group_type = {
'CompositorNodeTree': 'CompositorNodeGroup',
'ShaderNodeTree': 'ShaderNodeGroup',
'TextureNodeTree': 'TextureNodeGroup',
'SimulationNodeTree': 'SimulationNodeGroup',
'GeometryNodeTree': 'GeometryNodeGroup',
}
# generic node group items generator for shader, compositor, simulation and texture node groups
# generic node group items generator for shader, compositor, geometry and texture node groups
def node_group_items(context):
if context is None:
return
@@ -483,13 +483,55 @@ def not_implemented_node(idname):
return NodeItem(idname, label=label)
simulation_node_categories = [
# Simulation Nodes
SimulationNodeCategory("SIM_GROUP", "Group", items=node_group_items),
SimulationNodeCategory("SIM_LAYOUT", "Layout", items=[
geometry_node_categories = [
# Geometry Nodes
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
NodeItem("GeometryNodeRandomAttribute"),
NodeItem("GeometryNodeAttributeMath"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeValToRGB"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
]),
GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[
NodeItem("GeometryNodeTransform"),
NodeItem("GeometryNodeBoolean"),
NodeItem("GeometryNodeJoinGeometry"),
]),
GeometryNodeCategory("GEO_INPUT", "Input", items=[
NodeItem("GeometryNodeObjectInfo"),
NodeItem("FunctionNodeRandomFloat"),
NodeItem("ShaderNodeValue"),
]),
GeometryNodeCategory("GEO_MESH", "Mesh", items=[
NodeItem("GeometryNodeTriangulate"),
NodeItem("GeometryNodeEdgeSplit"),
NodeItem("GeometryNodeSubdivisionSurface"),
]),
GeometryNodeCategory("GEO_POINT", "Point", items=[
NodeItem("GeometryNodePointDistribute"),
NodeItem("GeometryNodePointInstance"),
]),
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
NodeItem("ShaderNodeMapRange"),
NodeItem("ShaderNodeClamp"),
NodeItem("ShaderNodeMath"),
NodeItem("FunctionNodeBooleanMath"),
NodeItem("FunctionNodeFloatCompare"),
]),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
NodeItem("ShaderNodeSeparateXYZ"),
NodeItem("ShaderNodeCombineXYZ"),
NodeItem("ShaderNodeVectorMath"),
]),
GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items),
GeometryNodeCategory("GEO_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),
NodeItem("NodeReroute"),
]),
# NodeItem("FunctionNodeCombineStrings"),
# NodeItem("FunctionNodeGroupInstanceID"),
]
@@ -497,14 +539,14 @@ def register():
nodeitems_utils.register_node_categories('SHADER', shader_node_categories)
nodeitems_utils.register_node_categories('COMPOSITING', compositor_node_categories)
nodeitems_utils.register_node_categories('TEXTURE', texture_node_categories)
nodeitems_utils.register_node_categories('SIMULATION', simulation_node_categories)
nodeitems_utils.register_node_categories('GEOMETRY', geometry_node_categories)
def unregister():
nodeitems_utils.unregister_node_categories('SHADER')
nodeitems_utils.unregister_node_categories('COMPOSITING')
nodeitems_utils.unregister_node_categories('TEXTURE')
nodeitems_utils.unregister_node_categories('SIMULATION')
nodeitems_utils.unregister_node_categories('GEOMETRY')
if __name__ == "__main__":

View File

@@ -40,13 +40,11 @@ struct ReportList;
/* Attribute.domain */
typedef enum AttributeDomain {
ATTR_DOMAIN_VERTEX = 0, /* Mesh Vertex */
ATTR_DOMAIN_POINT = 0, /* Mesh, Hair or PointCloud Point */
ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */
ATTR_DOMAIN_CORNER = 2, /* Mesh Corner */
ATTR_DOMAIN_POLYGON = 3, /* Mesh Polygon */
ATTR_DOMAIN_POINT = 4, /* Hair or PointCloud Point */
ATTR_DOMAIN_CURVE = 5, /* Hair Curve */
ATTR_DOMAIN_CURVE = 4, /* Hair Curve */
ATTR_DOMAIN_NUM
} AttributeDomain;

View File

@@ -0,0 +1,255 @@
/*
* 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 <mutex>
#include "FN_cpp_type.hh"
#include "FN_spans.hh"
#include "BKE_attribute.h"
#include "BLI_float3.hh"
struct Mesh;
namespace blender::bke {
using fn::CPPType;
/**
* This class offers an indirection for reading an attribute.
* This is useful for the following reasons:
* - Blender does not store all attributes the same way.
* The simplest case are custom data layers with primitive types.
* A bit more complex are mesh attributes like the position of vertices,
* which are embedded into the MVert struct.
* Even more complex to access are vertex weights.
* - Sometimes attributes are stored on one domain, but we want to access
* the attribute on a different domain. Therefore, we have to interpolate
* between the domains.
*/
class ReadAttribute {
protected:
const AttributeDomain domain_;
const CPPType &cpp_type_;
const int64_t size_;
/* Protects the span below, so that no two threads initialize it at the same time. */
mutable std::mutex span_mutex_;
/* When it is not null, it points to the attribute array or a temporary array that contains all
* the attribute values. */
mutable void *array_buffer_ = nullptr;
/* Is true when the buffer above is owned by the attribute accessor. */
mutable bool array_is_temporary_ = false;
public:
ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
: domain_(domain), cpp_type_(cpp_type), size_(size)
{
}
virtual ~ReadAttribute();
AttributeDomain domain() const
{
return domain_;
}
const CPPType &cpp_type() const
{
return cpp_type_;
}
int64_t size() const
{
return size_;
}
void get(const int64_t index, void *r_value) const
{
BLI_assert(index < size_);
this->get_internal(index, r_value);
}
/* Get a span that contains all attribute values. */
fn::GSpan get_span() const;
protected:
/* r_value is expected to be uninitialized. */
virtual void get_internal(const int64_t index, void *r_value) const = 0;
virtual void initialize_span() const;
};
/**
* This exists for similar reasons as the ReadAttribute class, except that
* it does not deal with interpolation between domains.
*/
class WriteAttribute {
protected:
const AttributeDomain domain_;
const CPPType &cpp_type_;
const int64_t size_;
/* When not null, this points either to the attribute array or to a temporary array. */
void *array_buffer_ = nullptr;
/* True, when the buffer points to a temporary array. */
bool array_is_temporary_ = false;
/* This helps to protect agains forgetting to apply changes done to the array. */
bool array_should_be_applied_ = false;
public:
WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
: domain_(domain), cpp_type_(cpp_type), size_(size)
{
}
virtual ~WriteAttribute();
AttributeDomain domain() const
{
return domain_;
}
const CPPType &cpp_type() const
{
return cpp_type_;
}
int64_t size() const
{
return size_;
}
void get(const int64_t index, void *r_value) const
{
BLI_assert(index < size_);
this->get_internal(index, r_value);
}
void set(const int64_t index, const void *value)
{
BLI_assert(index < size_);
this->set_internal(index, value);
}
/* Get a span that new attribute values can be written into. When all values have been changed,
* #apply_span has to be called. The span might not contain the original attribute values. */
fn::GMutableSpan get_span();
/* Write the changes to the span into the actual attribute, if they aren't already. */
void apply_span();
protected:
virtual void get_internal(const int64_t index, void *r_value) const = 0;
virtual void set_internal(const int64_t index, const void *value) = 0;
virtual void initialize_span();
virtual void apply_span_if_necessary();
};
using ReadAttributePtr = std::unique_ptr<ReadAttribute>;
using WriteAttributePtr = std::unique_ptr<WriteAttribute>;
/* This provides type safe access to an attribute. */
template<typename T> class TypedReadAttribute {
private:
ReadAttributePtr attribute_;
public:
TypedReadAttribute(ReadAttributePtr attribute) : attribute_(std::move(attribute))
{
BLI_assert(attribute_);
BLI_assert(attribute_->cpp_type().is<T>());
}
int64_t size() const
{
return attribute_->size();
}
T operator[](const int64_t index) const
{
BLI_assert(index < attribute_->size());
T value;
value.~T();
attribute_->get(index, &value);
return value;
}
/* Get a span to that contains all attribute values for faster and more convenient access. */
Span<T> get_span() const
{
return attribute_->get_span().template typed<T>();
}
};
/* This provides type safe access to an attribute. */
template<typename T> class TypedWriteAttribute {
private:
WriteAttributePtr attribute_;
public:
TypedWriteAttribute(WriteAttributePtr attribute) : attribute_(std::move(attribute))
{
BLI_assert(attribute_);
BLI_assert(attribute_->cpp_type().is<T>());
}
int64_t size() const
{
return attribute_->size();
}
T operator[](const int64_t index) const
{
BLI_assert(index < attribute_->size());
T value;
value.~T();
attribute_->get(index, &value);
return value;
}
void set(const int64_t index, const T &value)
{
attribute_->set(index, &value);
}
/* Get a span that new values can be written into. Once all values have been updated #apply_span
* has to be called. The span might *not* contain the initial attribute values, so one should
* generally only write to the span. */
MutableSpan<T> get_span()
{
return attribute_->get_span().typed<T>();
}
/* Write back all changes to the actual attribute, if necessary. */
void apply_span()
{
attribute_->apply_span();
}
};
using FloatReadAttribute = TypedReadAttribute<float>;
using Float3ReadAttribute = TypedReadAttribute<float3>;
using FloatWriteAttribute = TypedWriteAttribute<float>;
using Float3WriteAttribute = TypedWriteAttribute<float3>;
const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
} // namespace blender::bke

View File

@@ -0,0 +1,42 @@
/*
* 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
/** \file
* \ingroup bke
*/
#ifdef __cplusplus
extern "C" {
#endif
struct Object;
struct GeometrySet;
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set);
int BKE_geometry_set_instances(const struct GeometrySet *geometry_set,
float (**r_positions)[3],
float (**r_rotations)[3],
float (**r_scales)[3],
struct Object ***r_objects);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,373 @@
/*
* 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
/** \file
* \ingroup bke
*/
#include <atomic>
#include <iostream>
#include "BLI_float3.hh"
#include "BLI_hash.hh"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_user_counter.hh"
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.h"
struct Mesh;
struct PointCloud;
struct Object;
/* Each geometry component has a specific type. The type determines what kind of data the component
* stores. Functions modifying a geometry will usually just modify a subset of the component types.
*/
enum class GeometryComponentType {
Mesh = 0,
PointCloud = 1,
Instances = 2,
};
enum class GeometryOwnershipType {
/* The geometry is owned. This implies that it can be changed. */
Owned = 0,
/* The geometry can be changed, but someone else is responsible for freeing it. */
Editable = 1,
/* The geometry cannot be changed and someone else is responsible for freeing it. */
ReadOnly = 2,
};
/* Make it possible to use the component type as key in hash tables. */
namespace blender {
template<> struct DefaultHash<GeometryComponentType> {
uint64_t operator()(const GeometryComponentType &value) const
{
return (uint64_t)value;
}
};
} // namespace blender
/**
* This is the base class for specialized geometry component types.
*/
class GeometryComponent {
private:
/* The reference count has two purposes. When it becomes zero, the component is freed. When it is
* larger than one, the component becomes immutable. */
mutable std::atomic<int> users_ = 1;
GeometryComponentType type_;
public:
GeometryComponent(GeometryComponentType type);
virtual ~GeometryComponent();
static GeometryComponent *create(GeometryComponentType component_type);
/* The returned component should be of the same type as the type this is called on. */
virtual GeometryComponent *copy() const = 0;
void user_add() const;
void user_remove() const;
bool is_mutable() const;
GeometryComponentType type() const;
/* Returns true when the geometry component supports this attribute domain. */
virtual bool attribute_domain_supported(const AttributeDomain domain) const;
/* Returns true when the given data type is supported in the given domain. */
virtual bool attribute_domain_with_type_supported(const AttributeDomain domain,
const CustomDataType data_type) const;
/* Can only be used with supported domain types. */
virtual int attribute_domain_size(const AttributeDomain domain) const;
/* Attributes with these names cannot be created or removed via this api. */
virtual bool attribute_is_builtin(const blender::StringRef attribute_name) const;
/* Get read-only access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
virtual blender::bke::ReadAttributePtr attribute_try_get_for_read(
const blender::StringRef attribute_name) const;
/* Get read and write access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
virtual blender::bke::WriteAttributePtr attribute_try_get_for_write(
const blender::StringRef attribute_name);
/* Get a read-only attribute for the domain based on the given attribute. This can be used to
* interpolate from one domain to another.
* Returns null if the interpolation is not implemented. */
virtual blender::bke::ReadAttributePtr attribute_try_adapt_domain(
blender::bke::ReadAttributePtr attribute, const AttributeDomain domain) const;
/* Returns true when the attribute has been deleted. */
virtual bool attribute_try_delete(const blender::StringRef attribute_name);
/* Returns true when the attribute has been created. */
virtual bool attribute_try_create(const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type);
virtual blender::Set<std::string> attribute_names() const;
virtual bool is_empty() const;
/* Get a read-only attribute for the given domain and data type.
* Returns null when it does not exist. */
blender::bke::ReadAttributePtr attribute_try_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) const;
/* Get a read-only attribute for the given domain and data type.
* Returns a constant attribute based on the default value if the attribute does not exist.
* Never returns null. */
blender::bke::ReadAttributePtr attribute_get_for_read(const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value) const;
/* Get a typed read-only attribute for the given domain and type. */
template<typename T>
blender::bke::TypedReadAttribute<T> attribute_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
const T &default_value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_get_for_read(attribute_name, domain, type, &default_value);
}
/* Get a read-only dummy attribute that always returns the same value. */
blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain,
const CustomDataType data_type,
const void *value) const;
/* Get a read-only dummy attribute that always returns the same value. */
template<typename T>
blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain,
const T &value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_get_constant_for_read(domain, type, &value);
}
/**
* Returns the attribute with the given parameters if it exists.
* If an exact match does not exist, other attributes with the same name are deleted and a new
* attribute is created if possible.
*/
blender::bke::WriteAttributePtr attribute_try_ensure_for_write(
const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type);
};
template<typename T>
inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryComponent, T>;
/**
* A geometry set contains zero or more geometry components. There is at most one component of each
* type. Individual components might be shared between multiple geometries. Shared components are
* copied automatically when write access is requested.
*
* Copying a geometry set is a relatively cheap operation, because it does not copy the referenced
* geometry components.
*/
struct GeometrySet {
private:
using GeometryComponentPtr = blender::UserCounter<class GeometryComponent>;
blender::Map<GeometryComponentType, GeometryComponentPtr> components_;
public:
GeometryComponent &get_component_for_write(GeometryComponentType component_type);
template<typename Component> Component &get_component_for_write()
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return static_cast<Component &>(this->get_component_for_write(Component::static_type));
}
const GeometryComponent *get_component_for_read(GeometryComponentType component_type) const;
template<typename Component> const Component *get_component_for_read() const
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return static_cast<const Component *>(get_component_for_read(Component::static_type));
}
bool has(const GeometryComponentType component_type) const;
template<typename Component> bool has() const
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return this->has(Component::static_type);
}
void remove(const GeometryComponentType component_type);
template<typename Component> void remove()
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return this->remove(Component::static_type);
}
void add(const GeometryComponent &component);
void compute_boundbox_without_instances(blender::float3 *r_min, blender::float3 *r_max) const;
friend std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set);
friend bool operator==(const GeometrySet &a, const GeometrySet &b);
uint64_t hash() const;
/* Utility methods for creation. */
static GeometrySet create_with_mesh(
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
static GeometrySet create_with_pointcloud(
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/* Utility methods for access. */
bool has_mesh() const;
bool has_pointcloud() const;
bool has_instances() const;
const Mesh *get_mesh_for_read() const;
const PointCloud *get_pointcloud_for_read() const;
Mesh *get_mesh_for_write();
PointCloud *get_pointcloud_for_write();
/* Utility methods for replacement. */
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
void replace_pointcloud(PointCloud *pointcloud,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
};
/** A geometry component that can store a mesh. */
class MeshComponent : public GeometryComponent {
private:
Mesh *mesh_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
/* Due to historical design choices, vertex group data is stored in the mesh, but the vertex
* group names are stored on an object. Since we don't have an object here, we copy over the
* names into this map. */
blender::Map<std::string, int> vertex_group_names_;
public:
MeshComponent();
~MeshComponent();
GeometryComponent *copy() const override;
void clear();
bool has_mesh() const;
void replace(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
Mesh *release();
void copy_vertex_group_names_from_object(const struct Object &object);
const Mesh *get_for_read() const;
Mesh *get_for_write();
bool attribute_domain_supported(const AttributeDomain domain) const final;
bool attribute_domain_with_type_supported(const AttributeDomain domain,
const CustomDataType data_type) const final;
int attribute_domain_size(const AttributeDomain domain) const final;
bool attribute_is_builtin(const blender::StringRef attribute_name) const final;
blender::bke::ReadAttributePtr attribute_try_get_for_read(
const blender::StringRef attribute_name) const final;
blender::bke::WriteAttributePtr attribute_try_get_for_write(
const blender::StringRef attribute_name) final;
bool attribute_try_delete(const blender::StringRef attribute_name) final;
bool attribute_try_create(const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) final;
blender::Set<std::string> attribute_names() const final;
bool is_empty() const final;
static constexpr inline GeometryComponentType static_type = GeometryComponentType::Mesh;
};
/** A geometry component that stores a point cloud. */
class PointCloudComponent : public GeometryComponent {
private:
PointCloud *pointcloud_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
public:
PointCloudComponent();
~PointCloudComponent();
GeometryComponent *copy() const override;
void clear();
bool has_pointcloud() const;
void replace(PointCloud *pointcloud,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
PointCloud *release();
const PointCloud *get_for_read() const;
PointCloud *get_for_write();
bool attribute_domain_supported(const AttributeDomain domain) const final;
bool attribute_domain_with_type_supported(const AttributeDomain domain,
const CustomDataType data_type) const final;
int attribute_domain_size(const AttributeDomain domain) const final;
bool attribute_is_builtin(const blender::StringRef attribute_name) const final;
blender::bke::ReadAttributePtr attribute_try_get_for_read(
const blender::StringRef attribute_name) const final;
blender::bke::WriteAttributePtr attribute_try_get_for_write(
const blender::StringRef attribute_name) final;
bool attribute_try_delete(const blender::StringRef attribute_name) final;
bool attribute_try_create(const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) final;
blender::Set<std::string> attribute_names() const final;
bool is_empty() const final;
static constexpr inline GeometryComponentType static_type = GeometryComponentType::PointCloud;
};
/** A geometry component that stores instances. */
class InstancesComponent : public GeometryComponent {
private:
blender::Vector<blender::float3> positions_;
blender::Vector<blender::float3> rotations_;
blender::Vector<blender::float3> scales_;
blender::Vector<const Object *> objects_;
public:
InstancesComponent();
~InstancesComponent() = default;
GeometryComponent *copy() const override;
void clear();
void add_instance(const Object *object,
blender::float3 position,
blender::float3 rotation = {0, 0, 0},
blender::float3 scale = {1, 1, 1});
blender::Span<const Object *> objects() const;
blender::Span<blender::float3> positions() const;
blender::Span<blender::float3> rotations() const;
blender::Span<blender::float3> scales() const;
blender::MutableSpan<blender::float3> positions();
int instances_amount() const;
bool is_empty() const final;
static constexpr inline GeometryComponentType static_type = GeometryComponentType::Instances;
};

View File

@@ -43,6 +43,7 @@ struct ModifierData;
struct Object;
struct Scene;
struct bArmature;
struct GeometrySet;
typedef enum {
/* Should not be used, only for None modifier type */
@@ -246,9 +247,9 @@ typedef struct ModifierTypeInfo {
struct Hair *(*modifyHair)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct Hair *hair);
struct PointCloud *(*modifyPointCloud)(struct ModifierData *md,
void (*modifyPointCloud)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct PointCloud *pointcloud);
struct GeometrySet *geometry_set);
struct Volume *(*modifyVolume)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct Volume *volume);

View File

@@ -112,20 +112,26 @@ namespace blender {
namespace nodes {
class SocketMFNetworkBuilder;
class NodeMFNetworkBuilder;
class GeoNodeExecParams;
} // namespace nodes
namespace fn {
class CPPType;
class MFDataType;
}
} // namespace fn
} // namespace blender
using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder);
using SocketGetMFDataTypeFunction = blender::fn::MFDataType (*)();
using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params);
using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)();
using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value);
using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetworkBuilder &builder);
#else
typedef void *NodeExpandInMFNetworkFunction;
typedef void *SocketGetMFDataTypeFunction;
typedef void *SocketExpandInMFNetworkFunction;
typedef void *NodeGeometryExecFunction;
typedef void *SocketGetCPPTypeFunction;
typedef void *SocketGetCPPValueFunction;
#endif
/**
@@ -181,10 +187,12 @@ typedef struct bNodeSocketType {
/* Callback to free the socket type. */
void (*free_self)(struct bNodeSocketType *stype);
/* Returns the multi-function data type of this socket type. */
SocketGetMFDataTypeFunction get_mf_data_type;
/* Expands the socket into a multi-function node that outputs the socket value. */
SocketExpandInMFNetworkFunction expand_in_mf_network;
/* Return the CPPType of this socket. */
SocketGetCPPTypeFunction get_cpp_type;
/* Get the value of this socket in a generic way. */
SocketGetCPPValueFunction get_cpp_value;
} bNodeSocketType;
typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context,
@@ -302,6 +310,9 @@ typedef struct bNodeType {
/* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */
NodeExpandInMFNetworkFunction expand_in_mf_network;
/* Execute a geometry node. */
NodeGeometryExecFunction geometry_node_execute;
/* RNA integration */
ExtensionRNA rna_ext;
} bNodeType;
@@ -438,7 +449,7 @@ bool ntreeHasType(const struct bNodeTree *ntree, int type);
bool ntreeHasTree(const struct bNodeTree *ntree, const struct bNodeTree *lookup);
void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree);
void ntreeUpdateAllNew(struct Main *main);
void ntreeUpdateAllUsers(struct Main *main, struct ID *ngroup);
void ntreeUpdateAllUsers(struct Main *main, struct bNodeTree *ngroup);
void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***deplist, int *totnodes);
@@ -1322,6 +1333,24 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
struct MTex *mtex);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Geometry Nodes
* \{ */
#define GEO_NODE_TRIANGULATE 1000
#define GEO_NODE_EDGE_SPLIT 1001
#define GEO_NODE_TRANSFORM 1002
#define GEO_NODE_BOOLEAN 1003
#define GEO_NODE_POINT_DISTRIBUTE 1004
#define GEO_NODE_POINT_INSTANCE 1005
#define GEO_NODE_SUBDIVISION_SURFACE 1006
#define GEO_NODE_OBJECT_INFO 1007
#define GEO_NODE_RANDOM_ATTRIBUTE 1008
#define GEO_NODE_ATTRIBUTE_MATH 1009
#define GEO_NODE_JOIN_GEOMETRY 1010
/** \} */
/* -------------------------------------------------------------------- */
/** \name Function Nodes
* \{ */

View File

@@ -38,8 +38,10 @@ extern const char *POINTCLOUD_ATTR_RADIUS;
void *BKE_pointcloud_add(struct Main *bmain, const char *name);
void *BKE_pointcloud_add_default(struct Main *bmain, const char *name);
struct PointCloud *BKE_pointcloud_new_nomain(const int totpoint);
struct BoundBox *BKE_pointcloud_boundbox_get(struct Object *ob);
void BKE_pointcloud_minmax(const struct PointCloud *pointcloud, float r_min[3], float r_max[3]);
void BKE_pointcloud_update_customdata_pointers(struct PointCloud *pointcloud);
bool BKE_pointcloud_customdata_required(struct PointCloud *pointcloud,

View File

@@ -78,6 +78,7 @@ set(SRC
intern/armature_deform.c
intern/armature_update.c
intern/attribute.c
intern/attribute_access.cc
intern/autoexec.c
intern/blender.c
intern/blender_copybuffer.c
@@ -124,6 +125,7 @@ set(SRC
intern/fmodifier.c
intern/font.c
intern/freestyle.c
intern/geometry_set.cc
intern/gpencil.c
intern/gpencil_curve.c
intern/gpencil_geom.c
@@ -266,6 +268,7 @@ set(SRC
BKE_appdir.h
BKE_armature.h
BKE_attribute.h
BKE_attribute_access.hh
BKE_autoexec.h
BKE_blender.h
BKE_blender_copybuffer.h
@@ -311,6 +314,8 @@ set(SRC
BKE_fluid.h
BKE_font.h
BKE_freestyle.h
BKE_geometry_set.h
BKE_geometry_set.hh
BKE_global.h
BKE_gpencil.h
BKE_gpencil_curve.h

View File

@@ -63,8 +63,8 @@ static void get_domains(ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
}
case ID_ME: {
Mesh *mesh = (Mesh *)id;
info[ATTR_DOMAIN_VERTEX].customdata = &mesh->vdata;
info[ATTR_DOMAIN_VERTEX].length = mesh->totvert;
info[ATTR_DOMAIN_POINT].customdata = &mesh->vdata;
info[ATTR_DOMAIN_POINT].length = mesh->totvert;
info[ATTR_DOMAIN_EDGE].customdata = &mesh->edata;
info[ATTR_DOMAIN_EDGE].length = mesh->totedge;
info[ATTR_DOMAIN_CORNER].customdata = &mesh->ldata;

File diff suppressed because it is too large Load Diff

View File

@@ -4559,7 +4559,7 @@ void BKE_fluid_particle_system_destroy(struct Object *ob, const int particle_typ
if (psys->part->type == particle_type) {
/* clear modifier */
pfmd = psys_get_modifier(ob, psys);
BLI_remlink(&ob->modifiers, pfmd);
BLI_remlink(&ob->modifiers, (ModifierData *)pfmd);
BKE_modifier_free((ModifierData *)pfmd);
/* clear particle system */

View File

@@ -0,0 +1,554 @@
/*
* 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 "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_pointcloud.h"
#include "DNA_object_types.h"
#include "MEM_guardedalloc.h"
using blender::float3;
using blender::MutableSpan;
using blender::Span;
using blender::StringRef;
using blender::Vector;
/* -------------------------------------------------------------------- */
/** \name Geometry Component
* \{ */
GeometryComponent::GeometryComponent(GeometryComponentType type) : type_(type)
{
}
GeometryComponent ::~GeometryComponent()
{
}
GeometryComponent *GeometryComponent::create(GeometryComponentType component_type)
{
switch (component_type) {
case GeometryComponentType::Mesh:
return new MeshComponent();
case GeometryComponentType::PointCloud:
return new PointCloudComponent();
case GeometryComponentType::Instances:
return new InstancesComponent();
}
BLI_assert(false);
return nullptr;
}
void GeometryComponent::user_add() const
{
users_.fetch_add(1);
}
void GeometryComponent::user_remove() const
{
const int new_users = users_.fetch_sub(1) - 1;
if (new_users == 0) {
delete this;
}
}
bool GeometryComponent::is_mutable() const
{
/* If the item is shared, it is read-only. */
/* The user count can be 0, when this is called from the destructor. */
return users_ <= 1;
}
GeometryComponentType GeometryComponent::type() const
{
return type_;
}
bool GeometryComponent::is_empty() const
{
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Geometry Set
* \{ */
/* This method can only be used when the geometry set is mutable. It returns a mutable geometry
* component of the given type.
*/
GeometryComponent &GeometrySet::get_component_for_write(GeometryComponentType component_type)
{
return components_.add_or_modify(
component_type,
[&](GeometryComponentPtr *value_ptr) -> GeometryComponent & {
/* If the component did not exist before, create a new one. */
new (value_ptr) GeometryComponentPtr(GeometryComponent::create(component_type));
return **value_ptr;
},
[&](GeometryComponentPtr *value_ptr) -> GeometryComponent & {
GeometryComponentPtr &value = *value_ptr;
if (value->is_mutable()) {
/* If the referenced component is already mutable, return it directly. */
return *value;
}
/* If the referenced component is shared, make a copy. The copy is not shared and is
* therefore mutable. */
GeometryComponent *copied_component = value->copy();
value = GeometryComponentPtr{copied_component};
return *copied_component;
});
}
/* Get the component of the given type. Might return null if the component does not exist yet. */
const GeometryComponent *GeometrySet::get_component_for_read(
GeometryComponentType component_type) const
{
const GeometryComponentPtr *component = components_.lookup_ptr(component_type);
if (component != nullptr) {
return component->get();
}
return nullptr;
}
bool GeometrySet::has(const GeometryComponentType component_type) const
{
return components_.contains(component_type);
}
void GeometrySet::remove(const GeometryComponentType component_type)
{
components_.remove(component_type);
}
void GeometrySet::add(const GeometryComponent &component)
{
BLI_assert(!components_.contains(component.type()));
component.user_add();
GeometryComponentPtr component_ptr{const_cast<GeometryComponent *>(&component)};
components_.add_new(component.type(), std::move(component_ptr));
}
void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_max) const
{
const PointCloud *pointcloud = this->get_pointcloud_for_read();
if (pointcloud != nullptr) {
BKE_pointcloud_minmax(pointcloud, *r_min, *r_max);
}
const Mesh *mesh = this->get_mesh_for_read();
if (mesh != nullptr) {
BKE_mesh_wrapper_minmax(mesh, *r_min, *r_max);
}
}
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
{
stream << "<GeometrySet at " << &geometry_set << ", " << geometry_set.components_.size()
<< " components>";
return stream;
}
/* This generally should not be used. It is necessary currently, so that GeometrySet can by used by
* the CPPType system. */
bool operator==(const GeometrySet &UNUSED(a), const GeometrySet &UNUSED(b))
{
return false;
}
/* This generally should not be used. It is necessary currently, so that GeometrySet can by used by
* the CPPType system. */
uint64_t GeometrySet::hash() const
{
return reinterpret_cast<uint64_t>(this);
}
/* Returns a read-only mesh or null. */
const Mesh *GeometrySet::get_mesh_for_read() const
{
const MeshComponent *component = this->get_component_for_read<MeshComponent>();
return (component == nullptr) ? nullptr : component->get_for_read();
}
/* Returns true when the geometry set has a mesh component that has a mesh. */
bool GeometrySet::has_mesh() const
{
const MeshComponent *component = this->get_component_for_read<MeshComponent>();
return component != nullptr && component->has_mesh();
}
/* Returns a read-only point cloud of null. */
const PointCloud *GeometrySet::get_pointcloud_for_read() const
{
const PointCloudComponent *component = this->get_component_for_read<PointCloudComponent>();
return (component == nullptr) ? nullptr : component->get_for_read();
}
/* Returns true when the geometry set has a point cloud component that has a point cloud. */
bool GeometrySet::has_pointcloud() const
{
const PointCloudComponent *component = this->get_component_for_read<PointCloudComponent>();
return component != nullptr && component->has_pointcloud();
}
/* Returns true when the geometry set has an instances component that has at least one instance. */
bool GeometrySet::has_instances() const
{
const InstancesComponent *component = this->get_component_for_read<InstancesComponent>();
return component != nullptr && component->instances_amount() >= 1;
}
/* Create a new geometry set that only contains the given mesh. */
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
GeometrySet geometry_set;
MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>();
component.replace(mesh, ownership);
return geometry_set;
}
/* Create a new geometry set that only contains the given point cloud. */
GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
GeometryOwnershipType ownership)
{
GeometrySet geometry_set;
PointCloudComponent &component = geometry_set.get_component_for_write<PointCloudComponent>();
component.replace(pointcloud, ownership);
return geometry_set;
}
/* Clear the existing mesh and replace it with the given one. */
void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
MeshComponent &component = this->get_component_for_write<MeshComponent>();
component.replace(mesh, ownership);
}
/* Clear the existing point cloud and replace with the given one. */
void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
{
PointCloudComponent &pointcloud_component = this->get_component_for_write<PointCloudComponent>();
pointcloud_component.replace(pointcloud, ownership);
}
/* Returns a mutable mesh or null. No ownership is transferred. */
Mesh *GeometrySet::get_mesh_for_write()
{
MeshComponent &component = this->get_component_for_write<MeshComponent>();
return component.get_for_write();
}
/* Returns a mutable point cloud or null. No ownership is transferred. */
PointCloud *GeometrySet::get_pointcloud_for_write()
{
PointCloudComponent &component = this->get_component_for_write<PointCloudComponent>();
return component.get_for_write();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mesh Component
* \{ */
MeshComponent::MeshComponent() : GeometryComponent(GeometryComponentType::Mesh)
{
}
MeshComponent::~MeshComponent()
{
this->clear();
}
GeometryComponent *MeshComponent::copy() const
{
MeshComponent *new_component = new MeshComponent();
if (mesh_ != nullptr) {
new_component->mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
new_component->ownership_ = GeometryOwnershipType::Owned;
}
return new_component;
}
void MeshComponent::clear()
{
BLI_assert(this->is_mutable());
if (mesh_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, mesh_);
}
mesh_ = nullptr;
}
vertex_group_names_.clear();
}
bool MeshComponent::has_mesh() const
{
return mesh_ != nullptr;
}
/* Clear the component and replace it with the new mesh. */
void MeshComponent::replace(Mesh *mesh, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
mesh_ = mesh;
ownership_ = ownership;
}
/* Return the mesh and clear the component. The caller takes over responsibility for freeing the
* mesh (if the component was responsible before). */
Mesh *MeshComponent::release()
{
BLI_assert(this->is_mutable());
Mesh *mesh = mesh_;
mesh_ = nullptr;
return mesh;
}
void MeshComponent::copy_vertex_group_names_from_object(const Object &object)
{
BLI_assert(this->is_mutable());
vertex_group_names_.clear();
int index = 0;
LISTBASE_FOREACH (const bDeformGroup *, group, &object.defbase) {
vertex_group_names_.add(group->name, index);
index++;
}
}
/* Get the mesh from this component. This method can be used by multiple threads at the same
* time. Therefore, the returned mesh should not be modified. No ownership is transferred. */
const Mesh *MeshComponent::get_for_read() const
{
return mesh_;
}
/* Get the mesh from this component. This method can only be used when the component is mutable,
* i.e. it is not shared. The returned mesh can be modified. No ownership is transferred. */
Mesh *MeshComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
ownership_ = GeometryOwnershipType::Owned;
}
return mesh_;
}
bool MeshComponent::is_empty() const
{
return mesh_ == nullptr;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Pointcloud Component
* \{ */
PointCloudComponent::PointCloudComponent() : GeometryComponent(GeometryComponentType::PointCloud)
{
}
PointCloudComponent::~PointCloudComponent()
{
this->clear();
}
GeometryComponent *PointCloudComponent::copy() const
{
PointCloudComponent *new_component = new PointCloudComponent();
if (pointcloud_ != nullptr) {
new_component->pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
new_component->ownership_ = GeometryOwnershipType::Owned;
}
return new_component;
}
void PointCloudComponent::clear()
{
BLI_assert(this->is_mutable());
if (pointcloud_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, pointcloud_);
}
pointcloud_ = nullptr;
}
}
bool PointCloudComponent::has_pointcloud() const
{
return pointcloud_ != nullptr;
}
/* Clear the component and replace it with the new point cloud. */
void PointCloudComponent::replace(PointCloud *pointcloud, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
pointcloud_ = pointcloud;
ownership_ = ownership;
}
/* Return the point cloud and clear the component. The caller takes over responsibility for freeing
* the point cloud (if the component was responsible before). */
PointCloud *PointCloudComponent::release()
{
BLI_assert(this->is_mutable());
PointCloud *pointcloud = pointcloud_;
pointcloud_ = nullptr;
return pointcloud;
}
/* Get the point cloud from this component. This method can be used by multiple threads at the same
* time. Therefore, the returned point cloud should not be modified. No ownership is transferred.
*/
const PointCloud *PointCloudComponent::get_for_read() const
{
return pointcloud_;
}
/* Get the point cloud from this component. This method can only be used when the component is
* mutable, i.e. it is not shared. The returned point cloud can be modified. No ownership is
* transferred. */
PointCloud *PointCloudComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
ownership_ = GeometryOwnershipType::Owned;
}
return pointcloud_;
}
bool PointCloudComponent::is_empty() const
{
return pointcloud_ == nullptr;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Instances Component
* \{ */
InstancesComponent::InstancesComponent() : GeometryComponent(GeometryComponentType::Instances)
{
}
GeometryComponent *InstancesComponent::copy() const
{
InstancesComponent *new_component = new InstancesComponent();
new_component->positions_ = positions_;
new_component->rotations_ = rotations_;
new_component->scales_ = scales_;
new_component->objects_ = objects_;
return new_component;
}
void InstancesComponent::clear()
{
objects_.clear();
positions_.clear();
rotations_.clear();
scales_.clear();
}
void InstancesComponent::add_instance(const Object *object,
blender::float3 position,
blender::float3 rotation,
blender::float3 scale)
{
objects_.append(object);
positions_.append(position);
rotations_.append(rotation);
scales_.append(scale);
}
Span<const Object *> InstancesComponent::objects() const
{
return objects_;
}
Span<float3> InstancesComponent::positions() const
{
return positions_;
}
blender::Span<blender::float3> InstancesComponent::rotations() const
{
return rotations_;
}
blender::Span<blender::float3> InstancesComponent::scales() const
{
return scales_;
}
MutableSpan<float3> InstancesComponent::positions()
{
return positions_;
}
int InstancesComponent::instances_amount() const
{
BLI_assert(positions_.size() == objects_.size());
return objects_.size();
}
bool InstancesComponent::is_empty() const
{
return positions_.size() == 0;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name C API
* \{ */
void BKE_geometry_set_free(GeometrySet *geometry_set)
{
delete geometry_set;
}
bool BKE_geometry_set_has_instances(const GeometrySet *geometry_set)
{
return geometry_set->get_component_for_read<InstancesComponent>() != nullptr;
}
int BKE_geometry_set_instances(const GeometrySet *geometry_set,
float (**r_positions)[3],
float (**r_rotations)[3],
float (**r_scales)[3],
Object ***r_objects)
{
const InstancesComponent *component = geometry_set->get_component_for_read<InstancesComponent>();
if (component == nullptr) {
return 0;
}
*r_positions = (float(*)[3])component->positions().data();
*r_rotations = (float(*)[3])component->rotations().data();
*r_scales = (float(*)[3])component->scales().data();
*r_objects = (Object **)component->objects().data();
return component->instances_amount();
}
/** \} */

View File

@@ -49,8 +49,8 @@
#include "BLO_read_write.h"
static const char *HAIR_ATTR_POSITION = "Position";
static const char *HAIR_ATTR_RADIUS = "Radius";
static const char *HAIR_ATTR_POSITION = "position";
static const char *HAIR_ATTR_RADIUS = "radius";
/* Hair datablock */

View File

@@ -342,7 +342,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o
static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id)
{
/* Update all group nodes using a node group. */
ntreeUpdateAllUsers(bmain, new_id);
ntreeUpdateAllUsers(bmain, (bNodeTree *)new_id);
}
/**

View File

@@ -39,6 +39,7 @@
#include "DNA_light_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_material_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "DNA_simulation_types.h"
@@ -65,7 +66,6 @@
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_simulation.h"
#include "BLI_ghash.h"
#include "BLI_threads.h"
@@ -75,8 +75,8 @@
#include "NOD_common.h"
#include "NOD_composite.h"
#include "NOD_function.h"
#include "NOD_geometry.h"
#include "NOD_shader.h"
#include "NOD_simulation.h"
#include "NOD_socket.h"
#include "NOD_texture.h"
@@ -85,6 +85,8 @@
#include "BLO_read_write.h"
#include "MOD_nodes.h"
#define NODE_DEFAULT_MAX_WIDTH 700
/* Fallback types for undefined tree, nodes, sockets */
@@ -3958,14 +3960,18 @@ void ntreeUpdateAllNew(Main *main)
FOREACH_NODETREE_END;
}
void ntreeUpdateAllUsers(Main *main, ID *ngroup)
void ntreeUpdateAllUsers(Main *main, bNodeTree *ngroup)
{
if (ngroup == NULL) {
return;
}
/* Update all users of ngroup, to add/remove sockets as needed. */
FOREACH_NODETREE_BEGIN (main, ntree, owner_id) {
bool need_update = false;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->id == ngroup) {
if (node->id == &ngroup->id) {
if (node->typeinfo->group_update_func) {
node->typeinfo->group_update_func(ntree, node);
}
@@ -3979,6 +3985,19 @@ void ntreeUpdateAllUsers(Main *main, ID *ngroup)
}
}
FOREACH_NODETREE_END;
if (ngroup->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH (Object *, object, &main->objects) {
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
NodesModifierData *nmd = (NodesModifierData *)md;
if (nmd->node_group == ngroup) {
MOD_nodes_update_interface(object, nmd);
}
}
}
}
}
}
void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
@@ -4022,7 +4041,7 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
}
if (bmain) {
ntreeUpdateAllUsers(bmain, &ntree->id);
ntreeUpdateAllUsers(bmain, ntree);
}
if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) {
@@ -4659,9 +4678,21 @@ static void registerTextureNodes(void)
register_node_type_tex_proc_distnoise();
}
static void registerSimulationNodes(void)
static void registerGeometryNodes(void)
{
register_node_type_sim_group();
register_node_type_geo_group();
register_node_type_geo_triangulate();
register_node_type_geo_edge_split();
register_node_type_geo_transform();
register_node_type_geo_subdivision_surface();
register_node_type_geo_boolean();
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();
register_node_type_geo_object_info();
register_node_type_geo_random_attribute();
register_node_type_geo_attribute_math();
register_node_type_geo_join_geometry();
}
static void registerFunctionNodes(void)
@@ -4688,7 +4719,7 @@ void BKE_node_system_init(void)
register_node_tree_type_cmp();
register_node_tree_type_sh();
register_node_tree_type_tex();
register_node_tree_type_sim();
register_node_tree_type_geo();
register_node_type_frame();
register_node_type_reroute();
@@ -4698,7 +4729,7 @@ void BKE_node_system_init(void)
registerCompositNodes();
registerShaderNodes();
registerTextureNodes();
registerSimulationNodes();
registerGeometryNodes();
registerFunctionNodes();
}

View File

@@ -94,6 +94,7 @@
#include "BKE_fcurve.h"
#include "BKE_fcurve_driver.h"
#include "BKE_font.h"
#include "BKE_geometry_set.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
@@ -1284,8 +1285,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
}
if (ob->type == OB_POINTCLOUD) {
return (mti->modifyPointCloud != NULL) ||
(mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
return (mti->modifyPointCloud != NULL);
}
if (ob->type == OB_VOLUME) {
return (mti->modifyVolume != NULL);
@@ -1507,6 +1507,9 @@ void BKE_object_eval_assign_data(Object *object_eval, ID *data_eval, bool is_own
object_eval->data = data_eval;
}
}
/* Is set separately currently. */
object_eval->runtime.geometry_set_eval = NULL;
}
/**
@@ -1551,6 +1554,11 @@ void BKE_object_free_derived_caches(Object *ob)
BKE_gpencil_eval_delete(ob->runtime.gpd_eval);
ob->runtime.gpd_eval = NULL;
}
if (ob->runtime.geometry_set_eval != NULL) {
BKE_geometry_set_free(ob->runtime.geometry_set_eval);
ob->runtime.geometry_set_eval = NULL;
}
}
void BKE_object_free_caches(Object *object)
@@ -1771,6 +1779,10 @@ int BKE_object_visibility(const Object *ob, const int dag_eval_mode)
visibility |= OB_VISIBLE_INSTANCES;
}
if (ob->runtime.geometry_set_eval != NULL) {
visibility |= OB_VISIBLE_INSTANCES;
}
/* Optional hiding of self if there are particles or instancers. */
if (visibility & (OB_VISIBLE_PARTICLES | OB_VISIBLE_INSTANCES)) {
switch ((eEvaluationMode)dag_eval_mode) {
@@ -4880,6 +4892,7 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag))
runtime->mesh_deform_eval = NULL;
runtime->curve_cache = NULL;
runtime->object_as_temp_mesh = NULL;
runtime->geometry_set_eval = NULL;
}
/**

View File

@@ -47,6 +47,7 @@
#include "BKE_editmesh.h"
#include "BKE_editmesh_cache.h"
#include "BKE_font.h"
#include "BKE_geometry_set.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_lattice.h"
@@ -806,6 +807,45 @@ static const DupliGenerator gen_dupli_verts_pointcloud = {
/** \} */
/* -------------------------------------------------------------------- */
/** \name Instances Geometry Component Implementation
* \{ */
static void make_duplis_instances_component(const DupliContext *ctx)
{
float(*positions)[3];
float(*rotations)[3];
float(*scales)[3];
Object **objects;
const int amount = BKE_geometry_set_instances(
ctx->object->runtime.geometry_set_eval, &positions, &rotations, &scales, &objects);
for (int i = 0; i < amount; i++) {
Object *object = objects[i];
if (object == NULL) {
continue;
}
float scale_matrix[4][4];
size_to_mat4(scale_matrix, scales[i]);
float rotation_matrix[4][4];
eul_to_mat4(rotation_matrix, rotations[i]);
float matrix[4][4];
mul_m4_m4m4(matrix, rotation_matrix, scale_matrix);
copy_v3_v3(matrix[3], positions[i]);
mul_m4_m4_pre(matrix, ctx->object->obmat);
make_dupli(ctx, object, matrix, i);
make_recursive_duplis(ctx, object, matrix, i);
}
}
static const DupliGenerator gen_dupli_instances_component = {
0,
make_duplis_instances_component,
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Dupli-Faces Implementation (#OB_DUPLIFACES)
* \{ */
@@ -1473,7 +1513,7 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
int transflag = ctx->object->transflag;
int restrictflag = ctx->object->restrictflag;
if ((transflag & OB_DUPLI) == 0) {
if ((transflag & OB_DUPLI) == 0 && ctx->object->runtime.geometry_set_eval == NULL) {
return NULL;
}
@@ -1483,6 +1523,12 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
return NULL;
}
if (ctx->object->runtime.geometry_set_eval != NULL) {
if (BKE_geometry_set_has_instances(ctx->object->runtime.geometry_set_eval)) {
return &gen_dupli_instances_component;
}
}
if (transflag & OB_DUPLIPARTS) {
return &gen_dupli_particles;
}

View File

@@ -1198,9 +1198,6 @@ static bool foreach_object_modifier_ptcache(Object *object,
}
}
}
else if (md->type == eModifierType_Simulation) {
/* TODO(jacques): */
}
}
return true;
}

View File

@@ -33,12 +33,14 @@
#include "BKE_anim_data.h"
#include "BKE_customdata.h"
#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_pointcloud.h"
@@ -53,8 +55,8 @@
static void pointcloud_random(PointCloud *pointcloud);
const char *POINTCLOUD_ATTR_POSITION = "Position";
const char *POINTCLOUD_ATTR_RADIUS = "Radius";
const char *POINTCLOUD_ATTR_POSITION = "position";
const char *POINTCLOUD_ATTR_RADIUS = "radius";
static void pointcloud_init_data(ID *id)
{
@@ -86,6 +88,8 @@ static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_s
alloc_type,
pointcloud_dst->totpoint);
BKE_pointcloud_update_customdata_pointers(pointcloud_dst);
pointcloud_dst->batch_cache = nullptr;
}
static void pointcloud_free_data(ID *id)
@@ -230,21 +234,31 @@ void *BKE_pointcloud_add_default(Main *bmain, const char *name)
return pointcloud;
}
BoundBox *BKE_pointcloud_boundbox_get(Object *ob)
PointCloud *BKE_pointcloud_new_nomain(const int totpoint)
{
BLI_assert(ob->type == OB_POINTCLOUD);
PointCloud *pointcloud = static_cast<PointCloud *>(ob->data);
PointCloud *pointcloud = static_cast<PointCloud *>(BKE_libblock_alloc(
nullptr, ID_PT, BKE_idtype_idcode_to_name(ID_PT), LIB_ID_CREATE_LOCALIZE));
if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
return ob->runtime.bb;
}
pointcloud_init_data(&pointcloud->id);
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = static_cast<BoundBox *>(MEM_callocN(sizeof(BoundBox), "pointcloud boundbox"));
pointcloud->totpoint = totpoint;
float min[3], max[3];
INIT_MINMAX(min, max);
CustomData_add_layer_named(&pointcloud->pdata,
CD_PROP_FLOAT,
CD_CALLOC,
nullptr,
pointcloud->totpoint,
POINTCLOUD_ATTR_RADIUS);
pointcloud->totpoint = totpoint;
CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint);
BKE_pointcloud_update_customdata_pointers(pointcloud);
return pointcloud;
}
void BKE_pointcloud_minmax(const struct PointCloud *pointcloud, float r_min[3], float r_max[3])
{
float(*pointcloud_co)[3] = pointcloud->co;
float *pointcloud_radius = pointcloud->radius;
for (int a = 0; a < pointcloud->totpoint; a++) {
@@ -252,13 +266,34 @@ BoundBox *BKE_pointcloud_boundbox_get(Object *ob)
float radius = (pointcloud_radius) ? pointcloud_radius[a] : 0.0f;
const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius};
const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius};
DO_MIN(co_min, min);
DO_MAX(co_max, max);
DO_MIN(co_min, r_min);
DO_MAX(co_max, r_max);
}
}
BoundBox *BKE_pointcloud_boundbox_get(Object *ob)
{
BLI_assert(ob->type == OB_POINTCLOUD);
if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
return ob->runtime.bb;
}
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = static_cast<BoundBox *>(MEM_callocN(sizeof(BoundBox), "pointcloud boundbox"));
}
blender::float3 min, max;
INIT_MINMAX(min, max);
if (ob->runtime.geometry_set_eval != nullptr) {
ob->runtime.geometry_set_eval->compute_boundbox_without_instances(&min, &max);
}
else {
const PointCloud *pointcloud = static_cast<PointCloud *>(ob->data);
BKE_pointcloud_minmax(pointcloud, min, max);
}
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
return ob->runtime.bb;
}
@@ -306,13 +341,11 @@ PointCloud *BKE_pointcloud_copy_for_eval(struct PointCloud *pointcloud_src, bool
return result;
}
static PointCloud *pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
static void pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
struct Scene *scene,
Object *object,
PointCloud *pointcloud_input)
GeometrySet &geometry_set)
{
PointCloud *pointcloud = pointcloud_input;
/* Modifier evaluation modes. */
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
@@ -332,40 +365,10 @@ static PointCloud *pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
continue;
}
if ((mti->type == eModifierTypeType_OnlyDeform) &&
(mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) {
/* Ensure we are not modifying the input. */
if (pointcloud == pointcloud_input) {
pointcloud = BKE_pointcloud_copy_for_eval(pointcloud, true);
}
/* Ensure we are not overwriting referenced data. */
CustomData_duplicate_referenced_layer_named(
&pointcloud->pdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION, pointcloud->totpoint);
BKE_pointcloud_update_customdata_pointers(pointcloud);
/* Created deformed coordinates array on demand. */
mti->deformVerts(md, &mectx, nullptr, pointcloud->co, pointcloud->totpoint);
}
else if (mti->modifyPointCloud) {
/* Ensure we are not modifying the input. */
if (pointcloud == pointcloud_input) {
pointcloud = BKE_pointcloud_copy_for_eval(pointcloud, true);
}
PointCloud *pointcloud_next = mti->modifyPointCloud(md, &mectx, pointcloud);
if (pointcloud_next && pointcloud_next != pointcloud) {
/* If the modifier returned a new pointcloud, release the old one. */
if (pointcloud != pointcloud_input) {
BKE_id_free(nullptr, pointcloud);
}
pointcloud = pointcloud_next;
if (mti->modifyPointCloud) {
mti->modifyPointCloud(md, &mectx, &geometry_set);
}
}
}
return pointcloud;
}
void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
@@ -375,12 +378,14 @@ void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene
/* Evaluate modifiers. */
PointCloud *pointcloud = static_cast<PointCloud *>(object->data);
PointCloud *pointcloud_eval = pointcloud_evaluate_modifiers(
depsgraph, scene, object, pointcloud);
GeometrySet geometry_set = GeometrySet::create_with_pointcloud(pointcloud,
GeometryOwnershipType::ReadOnly);
pointcloud_evaluate_modifiers(depsgraph, scene, object, geometry_set);
/* Assign evaluated object. */
const bool is_owned = (pointcloud != pointcloud_eval);
BKE_object_eval_assign_data(object, &pointcloud_eval->id, is_owned);
PointCloud *dummy_pointcloud = BKE_pointcloud_new_nomain(0);
BKE_object_eval_assign_data(object, &dummy_pointcloud->id, true);
object->runtime.geometry_set_eval = new GeometrySet(geometry_set);
}
/* Draw Cache */

View File

@@ -48,8 +48,8 @@
#include "BKE_pointcache.h"
#include "BKE_simulation.h"
#include "NOD_geometry.h"
#include "NOD_node_tree_multi_function.hh"
#include "NOD_simulation.h"
#include "BLI_map.hh"
#include "BLT_translation.h"
@@ -70,7 +70,7 @@ static void simulation_init_data(ID *id)
MEMCPY_STRUCT_AFTER(simulation, DNA_struct_default_get(Simulation), id);
bNodeTree *ntree = ntreeAddTree(nullptr, "Simulation Nodetree", ntreeType_Simulation->idname);
bNodeTree *ntree = ntreeAddTree(nullptr, "Geometry Nodetree", ntreeType_Geometry->idname);
simulation->nodetree = ntree;
}

View File

@@ -60,6 +60,12 @@ void BLI_rng_get_tri_sample_float_v2(struct RNG *rng,
const float v2[2],
const float v3[2],
float r_pt[2]) ATTR_NONNULL();
void BLI_rng_get_tri_sample_float_v3(RNG *rng,
const float v1[3],
const float v2[3],
const float v3[3],
float r_pt[3]) ATTR_NONNULL();
void BLI_rng_shuffle_array(struct RNG *rng,
void *data,
unsigned int elem_size_i,

View File

@@ -118,6 +118,7 @@ class RandomNumberGenerator {
float2 get_unit_float2();
float3 get_unit_float3();
float2 get_triangle_sample(float2 v1, float2 v2, float2 v3);
float3 get_triangle_sample_3d(float3 v1, float3 v2, float3 v3);
void get_bytes(MutableSpan<char> r_bytes);
/**

View File

@@ -0,0 +1,158 @@
/*
* 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
/** \file
* \ingroup bli
*/
#include <atomic>
namespace blender {
/**
* A simple automatic reference counter. It is similar to std::shared_ptr, but expects that the
* reference count is inside the object.
*/
template<typename T> class UserCounter {
private:
T *data_ = nullptr;
public:
UserCounter() = default;
UserCounter(T *data) : data_(data)
{
}
UserCounter(const UserCounter &other) : data_(other.data_)
{
this->user_add(data_);
}
UserCounter(UserCounter &&other) : data_(other.data_)
{
other.data_ = nullptr;
}
~UserCounter()
{
this->user_remove(data_);
}
UserCounter &operator=(const UserCounter &other)
{
if (this == &other) {
return *this;
}
this->user_remove(data_);
data_ = other.data_;
this->user_add(data_);
return *this;
}
UserCounter &operator=(UserCounter &&other)
{
if (this == &other) {
return *this;
}
this->user_remove(data_);
data_ = other.data_;
other.data_ = nullptr;
return *this;
}
T *operator->()
{
BLI_assert(data_ != nullptr);
return data_;
}
T &operator*()
{
BLI_assert(data_ != nullptr);
return *data_;
}
operator bool() const
{
return data_ != nullptr;
}
T *get()
{
return data_;
}
const T *get() const
{
return data_;
}
T *release()
{
T *data = data_;
data_ = nullptr;
return data;
}
void reset()
{
this->user_remove(data_);
data_ = nullptr;
}
bool has_value() const
{
return data_ != nullptr;
}
uint64_t hash() const
{
return DefaultHash<T *>{}(data_);
}
friend bool operator==(const UserCounter &a, const UserCounter &b)
{
return a.data_ == b.data_;
}
friend std::ostream &operator<<(std::ostream &stream, const UserCounter &value)
{
stream << value.data_;
return stream;
}
private:
static void user_add(T *data)
{
if (data != nullptr) {
data->user_add();
}
}
static void user_remove(T *data)
{
if (data != nullptr) {
data->user_remove();
}
}
};
} // namespace blender

View File

@@ -279,6 +279,7 @@ set(SRC
BLI_timecode.h
BLI_timeit.hh
BLI_timer.h
BLI_user_counter.hh
BLI_utildefines.h
BLI_utildefines_iter.h
BLI_utildefines_stack.h

View File

@@ -141,6 +141,12 @@ void BLI_rng_get_tri_sample_float_v2(
copy_v2_v2(r_pt, rng->rng.get_triangle_sample(v1, v2, v3));
}
void BLI_rng_get_tri_sample_float_v3(
RNG *rng, const float v1[3], const float v2[3], const float v3[3], float r_pt[3])
{
copy_v3_v3(r_pt, rng->rng.get_triangle_sample_3d(v1, v2, v3));
}
void BLI_rng_shuffle_array(RNG *rng, void *data, unsigned int elem_size_i, unsigned int elem_tot)
{
const uint elem_size = elem_size_i;
@@ -425,6 +431,25 @@ float2 RandomNumberGenerator::get_triangle_sample(float2 v1, float2 v2, float2 v
return sample;
}
float3 RandomNumberGenerator::get_triangle_sample_3d(float3 v1, float3 v2, float3 v3)
{
float u = this->get_float();
float v = this->get_float();
if (u + v > 1.0f) {
u = 1.0f - u;
v = 1.0f - v;
}
float3 side_u = v2 - v1;
float3 side_v = v3 - v1;
float3 sample = v1;
sample += side_u * u;
sample += side_v * v;
return sample;
}
void RandomNumberGenerator::get_bytes(MutableSpan<char> r_bytes)
{
constexpr int64_t mask_bytes = 2;

View File

@@ -519,6 +519,20 @@ static void do_versions_point_attributes(CustomData *pdata)
}
}
static void do_versions_point_attribute_names(CustomData *pdata)
{
/* Change from capital initial letter to lower case (T82693). */
for (int i = 0; i < pdata->totlayer; i++) {
CustomDataLayer *layer = &pdata->layers[i];
if (layer->type == CD_PROP_FLOAT3 && STREQ(layer->name, "Position")) {
STRNCPY(layer->name, "position");
}
else if (layer->type == CD_PROP_FLOAT && STREQ(layer->name, "Radius")) {
STRNCPY(layer->name, "radius");
}
}
}
/* Move FCurve handles towards the control point in such a way that the curve itself doesn't
* change. Since 2.91 FCurves are computed slightly differently, which requires this update to keep
* the same animation result. Previous versions scaled down overlapping handles during evaluation.
@@ -1201,5 +1215,13 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
/* Hair and PointCloud attributes names. */
LISTBASE_FOREACH (Hair *, hair, &bmain->hairs) {
do_versions_point_attribute_names(&hair->pdata);
}
LISTBASE_FOREACH (PointCloud *, pointcloud, &bmain->pointclouds) {
do_versions_point_attribute_names(&pointcloud->pdata);
}
}
}

View File

@@ -24,6 +24,7 @@ set(INC
../blenlib
../bmesh
../draw
../functions
../makesdna
../makesrna
../modifiers

View File

@@ -141,6 +141,9 @@ void DEG_add_object_relation(struct DepsNodeHandle *node_handle,
void DEG_add_simulation_relation(struct DepsNodeHandle *node_handle,
struct Simulation *simulation,
const char *description);
void DEG_add_node_tree_relation(struct DepsNodeHandle *node_handle,
struct bNodeTree *node_tree,
const char *description);
void DEG_add_bone_relation(struct DepsNodeHandle *handle,
struct Object *object,
const char *bone_name,

View File

@@ -32,6 +32,7 @@
#include "PIL_time_utildefines.h"
#include "DNA_cachefile_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_simulation_types.h"
@@ -116,6 +117,17 @@ void DEG_add_simulation_relation(DepsNodeHandle *node_handle,
deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description);
}
void DEG_add_node_tree_relation(DepsNodeHandle *node_handle,
bNodeTree *node_tree,
const char *description)
{
/* Using shading key, because that's the one that exists right now. Should use something else in
* the future. */
deg::ComponentKey shading_key(&node_tree->id, deg::NodeType::SHADING);
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
deg_node_handle->builder->add_node_handle_relation(shading_key, deg_node_handle, description);
}
void DEG_add_object_cache_relation(DepsNodeHandle *node_handle,
CacheFile *cache_file,
eDepsObjectComponentType component,

View File

@@ -96,7 +96,7 @@ void ED_node_set_tree_type(struct SpaceNode *snode, struct bNodeTreeType *typein
bool ED_node_is_compositor(struct SpaceNode *snode);
bool ED_node_is_shader(struct SpaceNode *snode);
bool ED_node_is_texture(struct SpaceNode *snode);
bool ED_node_is_simulation(struct SpaceNode *snode);
bool ED_node_is_geometry(struct SpaceNode *snode);
void ED_node_shader_default(const struct bContext *C, struct ID *id);
void ED_node_composit_default(const struct bContext *C, struct Scene *scene);

View File

@@ -90,6 +90,8 @@
#include "ED_screen.h"
#include "ED_sculpt.h"
#include "MOD_nodes.h"
#include "UI_interface.h"
#include "WM_api.h"
@@ -232,6 +234,9 @@ ModifierData *ED_object_modifier_add(
/* ensure skin-node customdata exists */
BKE_mesh_ensure_skin_customdata(ob->data);
}
else if (type == eModifierType_Nodes) {
MOD_nodes_init(bmain, (NodesModifierData *)new_md);
}
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);

View File

@@ -73,5 +73,8 @@ if(WITH_OPENIMAGEDENOISE)
add_definitions(-DWITH_OPENIMAGEDENOISE)
endif()
if(WITH_OPENSUBDIV)
add_definitions(-DWITH_OPENSUBDIV)
endif()
blender_add_lib(bf_editor_space_node "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@@ -71,8 +71,8 @@
#include "IMB_imbuf_types.h"
#include "NOD_composite.h"
#include "NOD_geometry.h"
#include "NOD_shader.h"
#include "NOD_simulation.h"
#include "NOD_texture.h"
#include "node_intern.h" /* own include */
@@ -3142,10 +3142,65 @@ static void node_texture_set_butfunc(bNodeType *ntype)
}
}
/* ****************** BUTTON CALLBACKS FOR SIMULATION NODES ***************** */
/* ****************** BUTTON CALLBACKS FOR GEOMETRY NODES ***************** */
static void node_simulation_set_butfunc(bNodeType *UNUSED(ntype))
static void node_geometry_buts_boolean_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_geometry_buts_subdivision_surface(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *UNUSED(ptr))
{
#ifndef WITH_OPENSUBDIV
uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR);
#else
UNUSED_VARS(layout);
#endif
}
static void node_geometry_buts_triangulate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "quad_method", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "ngon_method", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_geometry_buts_random_attribute(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_geometry_buts_attribute_math(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "input_type_a", DEFAULT_FLAGS, IFACE_("Type A"), ICON_NONE);
uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
}
static void node_geometry_set_butfunc(bNodeType *ntype)
{
switch (ntype->type) {
case GEO_NODE_BOOLEAN:
ntype->draw_buttons = node_geometry_buts_boolean_math;
break;
case GEO_NODE_SUBDIVISION_SURFACE:
ntype->draw_buttons = node_geometry_buts_subdivision_surface;
break;
case GEO_NODE_TRIANGULATE:
ntype->draw_buttons = node_geometry_buts_triangulate;
break;
case GEO_NODE_RANDOM_ATTRIBUTE:
ntype->draw_buttons = node_geometry_buts_random_attribute;
break;
case GEO_NODE_ATTRIBUTE_MATH:
ntype->draw_buttons = node_geometry_buts_attribute_math;
break;
}
}
/* ****************** BUTTON CALLBACKS FOR FUNCTION NODES ***************** */
@@ -3290,7 +3345,7 @@ void ED_node_init_butfuncs(void)
node_composit_set_butfunc(ntype);
node_shader_set_butfunc(ntype);
node_texture_set_butfunc(ntype);
node_simulation_set_butfunc(ntype);
node_geometry_set_butfunc(ntype);
node_function_set_butfunc(ntype);
/* define update callbacks for socket properties */
@@ -3302,7 +3357,7 @@ void ED_node_init_butfuncs(void)
ntreeType_Composite->ui_icon = ICON_NODE_COMPOSITING;
ntreeType_Shader->ui_icon = ICON_NODE_MATERIAL;
ntreeType_Texture->ui_icon = ICON_NODE_TEXTURE;
ntreeType_Simulation->ui_icon = ICON_PHYSICS; /* TODO: Use correct icon. */
ntreeType_Geometry->ui_icon = ICON_PHYSICS; /* TODO: Use correct icon. */
}
void ED_init_custom_node_type(bNodeType *ntype)

View File

@@ -138,6 +138,9 @@ void ED_node_tag_update_id(ID *id)
DEG_id_tag_update(id, 0);
WM_main_add_notifier(NC_TEXTURE | ND_NODES, id);
}
else if (ntree->type == NTREE_GEOMETRY) {
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id);
}
else if (id == &ntree->id) {
/* node groups */
DEG_id_tag_update(id, 0);

View File

@@ -68,8 +68,8 @@
#include "IMB_imbuf_types.h"
#include "NOD_composite.h"
#include "NOD_geometry.h"
#include "NOD_shader.h"
#include "NOD_simulation.h"
#include "NOD_texture.h"
#include "node_intern.h" /* own include */
@@ -391,6 +391,7 @@ void snode_dag_update(bContext *C, SpaceNode *snode)
}
DEG_id_tag_update(snode->id, 0);
DEG_id_tag_update(&snode->nodetree->id, 0);
}
void snode_notify(bContext *C, SpaceNode *snode)
@@ -416,6 +417,9 @@ void snode_notify(bContext *C, SpaceNode *snode)
else if (ED_node_is_texture(snode)) {
WM_event_add_notifier(C, NC_TEXTURE | ND_NODES, id);
}
else if (ED_node_is_geometry(snode)) {
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id);
}
}
void ED_node_set_tree_type(SpaceNode *snode, bNodeTreeType *typeinfo)
@@ -443,9 +447,9 @@ bool ED_node_is_texture(struct SpaceNode *snode)
return STREQ(snode->tree_idname, ntreeType_Texture->idname);
}
bool ED_node_is_simulation(struct SpaceNode *snode)
bool ED_node_is_geometry(struct SpaceNode *snode)
{
return STREQ(snode->tree_idname, ntreeType_Simulation->idname);
return STREQ(snode->tree_idname, ntreeType_Geometry->idname);
}
/* assumes nothing being done in ntree yet, sets the default in/out node */
@@ -1696,7 +1700,7 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
}
}
do_tag_update |= ED_node_is_simulation(snode);
do_tag_update |= ED_node_is_geometry(snode);
snode_notify(C, snode);
if (do_tag_update) {
@@ -1740,7 +1744,7 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
}
}
do_tag_update |= ED_node_is_simulation(snode);
do_tag_update |= ED_node_is_geometry(snode);
ntreeUpdateTree(CTX_data_main(C), snode->edittree);

View File

@@ -77,7 +77,7 @@ static bool node_group_operator_active_poll(bContext *C)
"ShaderNodeTree",
"CompositorNodeTree",
"TextureNodeTree",
"SimulationNodeTree")) {
"GeometryNodeTree")) {
return true;
}
}
@@ -94,7 +94,7 @@ static bool node_group_operator_editable(bContext *C)
* with same keymap.
*/
if (ED_node_is_shader(snode) || ED_node_is_compositor(snode) || ED_node_is_texture(snode) ||
ED_node_is_simulation(snode)) {
ED_node_is_geometry(snode)) {
return true;
}
}
@@ -120,8 +120,8 @@ static const char *group_node_idname(bContext *C)
if (ED_node_is_texture(snode)) {
return "TextureNodeGroup";
}
if (ED_node_is_simulation(snode)) {
return "SimulationNodeGroup";
if (ED_node_is_geometry(snode)) {
return "GeometryNodeGroup";
}
return "";

View File

@@ -655,7 +655,7 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
}
ntree->is_updating = false;
do_tag_update |= ED_node_is_simulation(snode);
do_tag_update |= ED_node_is_geometry(snode);
ntreeUpdateTree(bmain, ntree);
snode_notify(C, snode);
@@ -1052,7 +1052,7 @@ static int cut_links_exec(bContext *C, wmOperator *op)
}
}
do_tag_update |= ED_node_is_simulation(snode);
do_tag_update |= ED_node_is_geometry(snode);
if (found) {
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
@@ -1932,6 +1932,7 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
ntreeUpdateTree(bmain, snode->edittree); /* needed for pointers */
snode_update(snode, select);
ED_node_tag_update_id((ID *)snode->edittree);
ED_node_tag_update_id(snode->id);
}
}

View File

@@ -932,7 +932,7 @@ static void node_space_subtype_item_extend(bContext *C, EnumPropertyItem **item,
const EnumPropertyItem *item_src = RNA_enum_node_tree_types_itemf_impl(C, &free);
for (const EnumPropertyItem *item_iter = item_src; item_iter->identifier; item_iter++) {
if (!U.experimental.use_new_geometry_nodes &&
STREQ(item_iter->identifier, "SimulationNodeTree")) {
STREQ(item_iter->identifier, "GeometryNodeTree")) {
continue;
}
RNA_enum_item_add(item, totitem, item_iter);

View File

@@ -39,6 +39,7 @@ set(SRC
FN_attributes_ref.hh
FN_cpp_type.hh
FN_generic_pointer.hh
FN_generic_value_map.hh
FN_generic_vector_array.hh
FN_multi_function.hh
FN_multi_function_builder.hh

View File

@@ -0,0 +1,123 @@
/*
* 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_linear_allocator.hh"
#include "BLI_map.hh"
#include "FN_generic_pointer.hh"
namespace blender::fn {
/**
* This is a map that stores key-value-pairs. What makes it special is that the type of values does
* not have to be known at compile time. There just has to be a corresponding CPPType.
*/
template<typename Key> class GValueMap {
private:
/* Used to allocate values owned by this container. */
LinearAllocator<> &allocator_;
Map<Key, GMutablePointer> values_;
public:
GValueMap(LinearAllocator<> &allocator) : allocator_(allocator)
{
}
~GValueMap()
{
/* Destruct all values that are still in the map. */
for (GMutablePointer value : values_.values()) {
value.destruct();
}
}
/* Add a value to the container. The container becomes responsible for destructing the value that
* is passed in. The caller remains responsible for freeing the value after it has been
* destructed. */
template<typename ForwardKey> void add_new_direct(ForwardKey &&key, GMutablePointer value)
{
values_.add_new_as(std::forward<ForwardKey>(key), value);
}
/* Add a value to the container that is move constructed from the given value. The caller remains
* responsible for destructing and freeing the given value. */
template<typename ForwardKey> void add_new_by_move(ForwardKey &&key, GMutablePointer value)
{
const CPPType &type = *value.type();
void *buffer = allocator_.allocate(type.size(), type.alignment());
type.move_to_uninitialized(value.get(), buffer);
values_.add_new_as(std::forward<ForwardKey>(key), GMutablePointer{type, buffer});
}
/* Add a value to the container that is copy constructed from the given value. The caller remains
* responsible for destructing and freeing the given value. */
template<typename ForwardKey> void add_new_by_copy(ForwardKey &&key, GMutablePointer value)
{
const CPPType &type = *value.type();
void *buffer = allocator_.allocate(type.size(), type.alignment());
type.copy_to_uninitialized(value.get(), buffer);
values_.add_new_as(std::forward<ForwardKey>(key), GMutablePointer{type, buffer});
}
/* Add a value to the container. */
template<typename ForwardKey, typename T> void add_new(ForwardKey &&key, T &&value)
{
if constexpr (std::is_rvalue_reference_v<T>) {
this->add_new_by_move(std::forward<ForwardKey>(key), &value);
}
else {
this->add_new_by_copy(std::forward<ForwardKey>(key), &value);
}
}
/* Remove the value for the given name from the container and remove it. The caller is
* responsible for freeing it. The lifetime of the referenced memory might be bound to lifetime
* of the container. */
template<typename ForwardKey> GMutablePointer extract(const ForwardKey &key)
{
return values_.pop_as(key);
}
/* Remove the value for the given name from the container and remove it. */
template<typename T, typename ForwardKey> T extract(const ForwardKey &key)
{
GMutablePointer value = values_.pop_as(key);
const CPPType &type = *value.type();
BLI_assert(type.is<T>());
T return_value;
type.relocate_to_initialized(value.get(), &return_value);
return return_value;
}
template<typename T, typename ForwardKey> T lookup(const ForwardKey &key) const
{
GMutablePointer value = values_.lookup_as(key);
const CPPType &type = *value.type();
BLI_assert(type.is<T>());
T return_value;
type.copy_to_initialized(value.get(), &return_value);
return return_value;
}
template<typename ForwardKey> bool contains(const ForwardKey &key) const
{
return values_.contains_as(key);
}
};
} // namespace blender::fn

View File

@@ -122,4 +122,17 @@ inline MFParamsBuilder::MFParamsBuilder(const class MultiFunction &fn, int64_t m
extern const MultiFunction &dummy_multi_function;
namespace multi_function_types {
using fn::CPPType;
using fn::GMutableSpan;
using fn::GSpan;
using fn::MFContext;
using fn::MFContextBuilder;
using fn::MFDataType;
using fn::MFParams;
using fn::MFParamsBuilder;
using fn::MFParamType;
using fn::MultiFunction;
} // namespace multi_function_types
} // namespace blender::fn

View File

@@ -573,7 +573,7 @@
.flag = 0, \
}
#define _DNA_DEFAULT_SimulationModifierData \
#define _DNA_DEFAULT_NodesModifierData \
{ 0 }
#define _DNA_DEFAULT_SkinModifierData \

View File

@@ -94,7 +94,7 @@ typedef enum ModifierType {
eModifierType_WeightedNormal = 54,
eModifierType_Weld = 55,
eModifierType_Fluid = 56,
eModifierType_Simulation = 57,
eModifierType_Nodes = 57,
eModifierType_MeshToVolume = 58,
eModifierType_VolumeDisplace = 59,
eModifierType_VolumeToMesh = 60,
@@ -2224,9 +2224,16 @@ enum {
#define MOD_MESHSEQ_READ_ALL \
(MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)
typedef struct SimulationModifierData {
typedef struct NodesModifierSettings {
/* This stores data that is passed into the node group. */
struct IDProperty *properties;
} NodesModifierSettings;
typedef struct NodesModifierData {
ModifierData modifier;
} SimulationModifierData;
struct bNodeTree *node_group;
struct NodesModifierSettings settings;
} NodesModifierData;
typedef struct MeshToVolumeModifierData {
ModifierData modifier;

View File

@@ -504,7 +504,7 @@ typedef struct bNodeTree {
#define NTREE_SHADER 0
#define NTREE_COMPOSIT 1
#define NTREE_TEXTURE 2
#define NTREE_SIMULATION 3
#define NTREE_GEOMETRY 3
/* ntree->init, flag */
#define NTREE_TYPE_INIT 1
@@ -1437,17 +1437,32 @@ typedef enum NodeShaderOutputTarget {
SHD_OUTPUT_CYCLES = 2,
} NodeShaderOutputTarget;
/* Particle Time Step Event node */
typedef enum NodeSimParticleTimeStepEventType {
NODE_PARTICLE_TIME_STEP_EVENT_BEGIN = 0,
NODE_PARTICLE_TIME_STEP_EVENT_END = 1,
} NodeSimParticleTimeStepEventType;
/* Geometry Nodes */
/* Simulation Time node */
typedef enum NodeSimInputTimeType {
NODE_SIM_INPUT_SIMULATION_TIME = 0,
NODE_SIM_INPUT_SCENE_TIME = 1,
} NodeSimInputTimeType;
/* Boolean Node */
typedef enum GeometryNodeBooleanOperation {
GEO_NODE_BOOLEAN_INTERSECT = 0,
GEO_NODE_BOOLEAN_UNION = 1,
GEO_NODE_BOOLEAN_DIFFERENCE = 2,
} GeometryNodeBooleanOperation;
/* Triangulate Node */
typedef enum GeometryNodeTriangulateNGons {
GEO_NODE_TRIANGULATE_NGON_BEAUTY = 0,
GEO_NODE_TRIANGULATE_NGON_EARCLIP = 1,
} GeometryNodeTriangulateNGons;
typedef enum GeometryNodeTriangulateQuads {
GEO_NODE_TRIANGULATE_QUAD_BEAUTY = 0,
GEO_NODE_TRIANGULATE_QUAD_FIXED = 1,
GEO_NODE_TRIANGULATE_QUAD_ALTERNATE = 2,
GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE = 3,
} GeometryNodeTriangulateQuads;
typedef enum GeometryNodeUseAttributeFlag {
GEO_NODE_USE_ATTRIBUTE_A = (1 << 0),
GEO_NODE_USE_ATTRIBUTE_B = (1 << 1),
} GeometryNodeUseAttributeFlag;
#ifdef __cplusplus
}

View File

@@ -51,6 +51,7 @@ struct RigidBodyOb;
struct SculptSession;
struct SoftBody;
struct bGPdata;
struct GeometrySet;
/* Vertex Groups - Name Info */
typedef struct bDeformGroup {
@@ -153,6 +154,13 @@ typedef struct Object_Runtime {
* It has all modifiers applied.
*/
struct ID *data_eval;
/**
* Some objects support evaluating to a geometry set instead of a single ID. In those cases the
* evaluated geometry will be stored here instead of in #data_eval.
*/
struct GeometrySet *geometry_set_eval;
/**
* Mesh structure created during object evaluation.
* It has deformation only modifiers applied on it.

View File

@@ -271,7 +271,7 @@ SDNA_DEFAULT_DECL_STRUCT(ScrewModifierData);
/* Shape key modifier has no items. */
SDNA_DEFAULT_DECL_STRUCT(ShrinkwrapModifierData);
SDNA_DEFAULT_DECL_STRUCT(SimpleDeformModifierData);
SDNA_DEFAULT_DECL_STRUCT(SimulationModifierData);
SDNA_DEFAULT_DECL_STRUCT(NodesModifierData);
SDNA_DEFAULT_DECL_STRUCT(SkinModifierData);
SDNA_DEFAULT_DECL_STRUCT(SmoothModifierData);
/* Softbody modifier skipped for now. */
@@ -491,7 +491,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
/* Shape key modifier has no items. */
SDNA_DEFAULT_DECL(ShrinkwrapModifierData),
SDNA_DEFAULT_DECL(SimpleDeformModifierData),
SDNA_DEFAULT_DECL(SimulationModifierData),
SDNA_DEFAULT_DECL(NodesModifierData),
SDNA_DEFAULT_DECL(SkinModifierData),
SDNA_DEFAULT_DECL(SmoothModifierData),
/* Softbody modifier skipped for now. */

View File

@@ -566,10 +566,11 @@ extern StructRNA RNA_SimpleDeformModifier;
extern StructRNA RNA_SimplifyGpencilModifier;
extern StructRNA RNA_Simulation;
#ifdef WITH_GEOMETRY_NODES
extern StructRNA RNA_SimulationModifier;
extern StructRNA RNA_NodesModifier;
extern StructRNA RNA_NodesModifierSettings;
#endif
extern StructRNA RNA_SimulationNode;
extern StructRNA RNA_SimulationNodeTree;
extern StructRNA RNA_GeometryNode;
extern StructRNA RNA_GeometryNodeTree;
extern StructRNA RNA_SkinModifier;
extern StructRNA RNA_SmoothGpencilModifier;
extern StructRNA RNA_SmoothModifier;

View File

@@ -373,6 +373,7 @@ blender_include_dirs(
../../ikplugin
../../imbuf
../../makesdna
../../modifiers
../../nodes/
../../sequencer
../../simulation

View File

@@ -1160,8 +1160,8 @@ PropertySubType RNA_property_subtype(PropertyRNA *prop)
if (prop->magic != RNA_MAGIC) {
IDProperty *idprop = (IDProperty *)prop;
/* Restrict to arrays only for now for performance reasons. */
if (idprop->type == IDP_ARRAY && ELEM(idprop->subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)) {
if (ELEM(idprop->type, IDP_INT, IDP_FLOAT, IDP_DOUBLE) ||
((idprop->type == IDP_ARRAY) && ELEM(idprop->subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE))) {
const IDProperty *idp_ui = rna_idproperty_ui(prop);
if (idp_ui) {

View File

@@ -50,13 +50,12 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
const EnumPropertyItem rna_enum_attribute_domain_items[] = {
/* Not implement yet */
// {ATTR_DOMAIN_GEOMETRY, "GEOMETRY", 0, "Geometry", "Attribute on (whole) geometry"},
{ATTR_DOMAIN_VERTEX, "VERTEX", 0, "Vertex", "Attribute on mesh vertex"},
{ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"},
{ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"},
{ATTR_DOMAIN_CORNER, "CORNER", 0, "Corner", "Attribute on mesh polygon corner"},
{ATTR_DOMAIN_POLYGON, "POLYGON", 0, "Polygon", "Attribute on mesh polygons"},
/* Not implement yet */
// {ATTR_DOMAIN_GRIDS, "GRIDS", 0, "Grids", "Attribute on mesh multires grids"},
{ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"},
{ATTR_DOMAIN_CURVE, "CURVE", 0, "Curve", "Attribute on hair curve"},
{0, NULL, 0, NULL, NULL},
};
@@ -117,7 +116,7 @@ const EnumPropertyItem *rna_enum_attribute_domain_itemf(ID *id, bool *r_free)
if (id_type == ID_HA && !ELEM(domain_item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
continue;
}
if (id_type == ID_ME && ELEM(domain_item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
if (id_type == ID_ME && ELEM(domain_item->value, ATTR_DOMAIN_CURVE)) {
continue;
}
@@ -619,7 +618,7 @@ static void rna_def_attribute_group(BlenderRNA *brna)
parm = RNA_def_enum(func,
"domain",
rna_enum_attribute_domain_items,
ATTR_DOMAIN_VERTEX,
ATTR_DOMAIN_POINT,
"Domain",
"Type of element that attribute is stored on");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);

View File

@@ -29,7 +29,6 @@
#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_simulation_types.h"
#include "MEM_guardedalloc.h"
@@ -43,6 +42,7 @@
#include "BKE_dynamicpaint.h"
#include "BKE_effect.h"
#include "BKE_fluid.h" /* For BKE_fluid_modifier_free & BKE_fluid_modifier_create_type_data */
#include "BKE_idprop.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_remap.h"
#include "BKE_multires.h"
@@ -57,6 +57,8 @@
#include "WM_api.h"
#include "WM_types.h"
#include "MOD_nodes.h"
const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
{0, "", 0, N_("Modify"), ""},
{eModifierType_DataTransfer,
@@ -141,6 +143,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_MOD_EDGESPLIT,
"Edge Split",
"Split away joined faces at the edges"},
{eModifierType_Nodes, "NODES", ICON_NODETREE, "Geometry Nodes", ""},
{eModifierType_Mask,
"MASK",
ICON_MOD_MASK,
@@ -305,11 +308,6 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
"Spawn particles from the shape"},
{eModifierType_Softbody, "SOFT_BODY", ICON_MOD_SOFT, "Soft Body", ""},
{eModifierType_Surface, "SURFACE", ICON_MODIFIER, "Surface", ""},
{eModifierType_Simulation,
"SIMULATION",
ICON_PHYSICS,
"Simulation",
""}, /* TODO: Use correct icon. */
{0, NULL, 0, NULL, NULL},
};
@@ -1588,6 +1586,37 @@ static int rna_MeshSequenceCacheModifier_read_velocity_get(PointerRNA *ptr)
# endif
}
static bool rna_NodesModifier_node_group_poll(PointerRNA *ptr, PointerRNA value)
{
NodesModifierData *nmd = ptr->data;
bNodeTree *ntree = value.data;
UNUSED_VARS(nmd, ntree);
return true;
}
static void rna_NodesModifier_node_group_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
Object *object = (Object *)ptr->owner_id;
NodesModifierData *nmd = ptr->data;
rna_Modifier_dependency_update(bmain, scene, ptr);
MOD_nodes_update_interface(object, nmd);
}
static IDProperty *rna_NodesModifierSettings_properties(PointerRNA *ptr, bool create)
{
NodesModifierSettings *settings = ptr->data;
if (create && settings->properties == NULL) {
IDPropertyTemplate val = {0};
settings->properties = IDP_New(IDP_GROUP, &val, "Nodes Modifier Settings");
}
return settings->properties;
}
static char *rna_NodesModifierSettings_path(PointerRNA *UNUSED(ptr))
{
return BLI_strdup("settings");
}
#else
static void rna_def_property_subdivision_common(StructRNA *srna)
@@ -6907,18 +6936,43 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna)
}
# ifdef WITH_GEOMETRY_NODES
static void rna_def_modifier_simulation(BlenderRNA *brna)
static void rna_def_modifier_nodes_settings(BlenderRNA *brna)
{
StructRNA *srna;
srna = RNA_def_struct(brna, "SimulationModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Simulation Modifier", "");
RNA_def_struct_sdna(srna, "SimulationModifierData");
RNA_def_struct_ui_icon(srna, ICON_PHYSICS); /* TODO: Use correct icon. */
srna = RNA_def_struct(brna, "NodesModifierSettings", NULL);
RNA_def_struct_nested(brna, srna, "NodesModifier");
RNA_def_struct_path_func(srna, "rna_NodesModifierSettings_path");
RNA_def_struct_ui_text(
srna, "Nodes Modifier Settings", "Settings that are passed into the node group");
RNA_def_struct_idprops_func(srna, "rna_NodesModifierSettings_properties");
}
static void rna_def_modifier_nodes(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "NodesModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Nodes Modifier", "");
RNA_def_struct_sdna(srna, "NodesModifierData");
RNA_def_struct_ui_icon(srna, ICON_MESH_DATA); /* TODO: Use correct icon. */
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "node_group", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Node Group", "Node group that controls what this modifier does");
RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_NodesModifier_node_group_poll");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_update(prop, 0, "rna_NodesModifier_node_group_update");
prop = RNA_def_property(srna, "settings", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_ui_text(prop, "Settings", "Settings that are passed into the node group");
RNA_define_lib_overridable(false);
rna_def_modifier_nodes_settings(brna);
}
# endif
@@ -7277,7 +7331,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_surfacedeform(brna);
rna_def_modifier_weightednormal(brna);
# ifdef WITH_GEOMETRY_NODES
rna_def_modifier_simulation(brna);
rna_def_modifier_nodes(brna);
# endif
rna_def_modifier_mesh_to_volume(brna);
rna_def_modifier_volume_displace(brna);

View File

@@ -36,9 +36,9 @@
#include "DNA_texture_types.h"
#include "BKE_animsys.h"
#include "BKE_attribute.h"
#include "BKE_image.h"
#include "BKE_node.h"
#include "BKE_simulation.h"
#include "BKE_texture.h"
#include "RNA_access.h"
@@ -369,6 +369,72 @@ static const EnumPropertyItem prop_shader_output_target_items[] = {
{SHD_OUTPUT_CYCLES, "CYCLES", 0, "Cycles", "Use shaders for Cycles renderer"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_boolean_method_items[] = {
{GEO_NODE_BOOLEAN_INTERSECT,
"INTERSECT",
0,
"Intersect",
"Keep the part of the mesh that is common between all operands"},
{GEO_NODE_BOOLEAN_UNION, "UNION", 0, "Union", "Combine meshes in an additive way"},
{GEO_NODE_BOOLEAN_DIFFERENCE,
"DIFFERENCE",
0,
"Difference",
"Combine meshes in a subtractive way"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_triangulate_quad_method_items[] = {
{GEO_NODE_TRIANGULATE_QUAD_BEAUTY,
"BEAUTY",
0,
"Beauty",
"Split the quads in nice triangles, slower method"},
{GEO_NODE_TRIANGULATE_QUAD_FIXED,
"FIXED",
0,
"Fixed",
"Split the quads on the first and third vertices"},
{GEO_NODE_TRIANGULATE_QUAD_ALTERNATE,
"FIXED_ALTERNATE",
0,
"Fixed Alternate",
"Split the quads on the 2nd and 4th vertices"},
{GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE,
"SHORTEST_DIAGONAL",
0,
"Shortest Diagonal",
"Split the quads based on the distance between the vertices"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_triangulate_ngon_method_items[] = {
{GEO_NODE_TRIANGULATE_NGON_BEAUTY,
"BEAUTY",
0,
"Beauty",
"Arrange the new triangles evenly (slow)"},
{GEO_NODE_TRIANGULATE_NGON_EARCLIP,
"CLIP",
0,
"Clip",
"Split the polygons with an ear clipping algorithm"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_attribute_input_a_items[] = {
{0, "FLOAT", 0, "Float", ""},
{GEO_NODE_USE_ATTRIBUTE_A, "ATTRIBUTE", 0, "Attribute", ""},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem rna_node_geometry_attribute_input_b_items[] = {
{0, "FLOAT", 0, "Float", ""},
{GEO_NODE_USE_ATTRIBUTE_B, "ATTRIBUTE", 0, "Attribute", ""},
{0, NULL, 0, NULL, NULL},
};
#endif
#ifdef RNA_RUNTIME
@@ -727,9 +793,9 @@ static const EnumPropertyItem *rna_node_static_type_itemf(bContext *UNUSED(C),
# undef DefNode
}
if (RNA_struct_is_a(ptr->type, &RNA_SimulationNode)) {
if (RNA_struct_is_a(ptr->type, &RNA_GeometryNode)) {
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
if (STREQ(#Category, "SimulationNode")) { \
if (STREQ(#Category, "GeometryNode")) { \
tmp.value = ID; \
tmp.identifier = EnumName; \
tmp.name = UIName; \
@@ -1774,6 +1840,61 @@ static StructRNA *rna_Node_register(Main *bmain,
return nt->rna_ext.srna;
}
static const EnumPropertyItem *itemf_function_check(
const EnumPropertyItem *original_item_array,
bool (*value_supported)(const EnumPropertyItem *item))
{
EnumPropertyItem *item_array = NULL;
int items_len = 0;
for (const EnumPropertyItem *item = original_item_array; item->identifier != NULL; item++) {
if (value_supported(item)) {
RNA_enum_item_add(&item_array, &items_len, item);
}
}
RNA_enum_item_end(&item_array, &items_len);
return item_array;
}
static bool attribute_random_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3);
}
static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_type_itemf(
bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
{
*r_free = true;
return itemf_function_check(rna_enum_attribute_type_items, attribute_random_type_supported);
}
static bool attribute_random_domain_supported(const EnumPropertyItem *item)
{
return item->value == ATTR_DOMAIN_POINT;
}
static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_domain_itemf(
bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
{
*r_free = true;
return itemf_function_check(rna_enum_attribute_domain_items, attribute_random_domain_supported);
}
static bool attribute_math_operation_supported(const EnumPropertyItem *item)
{
return ELEM(item->value,
NODE_MATH_ADD,
NODE_MATH_SUBTRACT,
NODE_MATH_MULTIPLY,
NODE_MATH_DIVIDE) &&
(item->identifier[0] != '\0');
}
static const EnumPropertyItem *rna_GeometryNodeAttributeMath_operation_itemf(
bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
{
*r_free = true;
return itemf_function_check(rna_enum_node_math_items, attribute_math_operation_supported);
}
static StructRNA *rna_ShaderNode_register(Main *bmain,
ReportList *reports,
void *data,
@@ -1840,7 +1961,7 @@ static StructRNA *rna_TextureNode_register(Main *bmain,
return nt->rna_ext.srna;
}
static StructRNA *rna_SimulationNode_register(Main *bmain,
static StructRNA *rna_GeometryNode_register(Main *bmain,
ReportList *reports,
void *data,
const char *identifier,
@@ -1849,7 +1970,7 @@ static StructRNA *rna_SimulationNode_register(Main *bmain,
StructFreeFunc free)
{
bNodeType *nt = rna_Node_register_base(
bmain, reports, &RNA_SimulationNode, data, identifier, validate, call, free);
bmain, reports, &RNA_GeometryNode, data, identifier, validate, call, free);
if (!nt) {
return NULL;
}
@@ -2812,6 +2933,7 @@ static void rna_NodeSocketStandard_value_and_relation_update(struct bContext *C,
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
Main *bmain = CTX_data_main(C);
ntreeUpdateTree(bmain, ntree);
DEG_relations_tag_update(bmain);
}
/* ******** Node Types ******** */
@@ -3697,7 +3819,16 @@ static void rna_ShaderNode_socket_update(Main *bmain, Scene *scene, PointerRNA *
rna_Node_update(bmain, scene, ptr);
}
static void rna_FunctionNode_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr)
static void rna_Node_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNode *node = (bNode *)ptr->data;
nodeUpdate(ntree, node);
rna_Node_update(bmain, scene, ptr);
}
static void rna_GeometryNode_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNode *node = (bNode *)ptr->data;
@@ -4149,7 +4280,7 @@ static void def_boolean_math(StructRNA *srna)
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_enum_node_boolean_math_items);
RNA_def_property_ui_text(prop, "Operation", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_FunctionNode_socket_update");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_float_compare(StructRNA *srna)
@@ -4160,7 +4291,7 @@ static void def_float_compare(StructRNA *srna)
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_enum_node_float_compare_items);
RNA_def_property_ui_text(prop, "Operation", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_FunctionNode_socket_update");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_fn_switch(StructRNA *srna)
@@ -4171,7 +4302,7 @@ static void def_fn_switch(StructRNA *srna)
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, node_socket_data_type_items);
RNA_def_property_ui_text(prop, "Data Type", "Data type for inputs and outputs");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_FunctionNode_socket_update");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_vector_math(StructRNA *srna)
@@ -8140,6 +8271,103 @@ static void def_tex_bricks(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
/* -- Geometry Nodes --------------------------------------------------------- */
static void def_geo_boolean(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_node_geometry_boolean_method_items);
RNA_def_property_enum_default(prop, GEO_NODE_BOOLEAN_INTERSECT);
RNA_def_property_ui_text(prop, "Operation", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_triangulate(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "quad_method", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_node_geometry_triangulate_quad_method_items);
RNA_def_property_enum_default(prop, GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE);
RNA_def_property_ui_text(prop, "Quad Method", "Method for splitting the quads into triangles");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "ngon_method", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom2");
RNA_def_property_enum_items(prop, rna_node_geometry_triangulate_ngon_method_items);
RNA_def_property_enum_default(prop, GEO_NODE_TRIANGULATE_NGON_BEAUTY);
RNA_def_property_ui_text(
prop, "Polygon Method", "Method for splitting the polygons into triangles");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
/**
* \note Passing the item functions as arguments here allows reusing the same
* original list of items from Attribute RNA.
*/
static void def_geo_attribute_create_common(StructRNA *srna,
const char *type_items_func,
const char *domain_items_func)
{
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);
if (type_items_func != NULL) {
RNA_def_property_enum_funcs(prop, NULL, NULL, type_items_func);
}
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_items);
if (domain_items_func != NULL) {
RNA_def_property_enum_funcs(prop, NULL, NULL, domain_items_func);
}
RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
RNA_def_property_ui_text(prop, "Domain", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_random_attribute(StructRNA *srna)
{
def_geo_attribute_create_common(srna,
"rna_GeometryNodeAttributeRandom_type_itemf",
"rna_GeometryNodeAttributeRandom_domain_itemf");
}
static void def_geo_attribute_math(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_enum_node_math_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeMath_operation_itemf");
RNA_def_property_enum_default(prop, NODE_MATH_ADD);
RNA_def_property_ui_text(prop, "Operation", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "custom2");
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_a_items);
RNA_def_property_ui_text(prop, "Input Type A", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "custom2");
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_b_items);
RNA_def_property_ui_text(prop, "Input Type B", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
@@ -8177,14 +8405,14 @@ static void rna_def_texture_node(BlenderRNA *brna)
RNA_def_struct_register_funcs(srna, "rna_TextureNode_register", "rna_Node_unregister", NULL);
}
static void rna_def_simulation_node(BlenderRNA *brna)
static void rna_def_geometry_node(BlenderRNA *brna)
{
StructRNA *srna;
srna = RNA_def_struct(brna, "SimulationNode", "NodeInternal");
RNA_def_struct_ui_text(srna, "Simulation Node", "");
srna = RNA_def_struct(brna, "GeometryNode", "NodeInternal");
RNA_def_struct_ui_text(srna, "Geometry Node", "");
RNA_def_struct_sdna(srna, "bNode");
RNA_def_struct_register_funcs(srna, "rna_SimulationNode_register", "rna_Node_unregister", NULL);
RNA_def_struct_register_funcs(srna, "rna_GeometryNode_register", "rna_Node_unregister", NULL);
}
static void rna_def_function_node(BlenderRNA *brna)
@@ -9623,7 +9851,7 @@ static void rna_def_nodetree(BlenderRNA *brna)
{NTREE_SHADER, "SHADER", ICON_MATERIAL, "Shader", "Shader nodes"},
{NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"},
{NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"},
{NTREE_SIMULATION, "SIMULATION", ICON_PHYSICS, "Simulation", "Simulation nodes"},
{NTREE_GEOMETRY, "GEOMETRY", ICON_MESH_DATA, "Geometry", "Geometry nodes"},
{0, NULL, 0, NULL, NULL},
};
@@ -9847,15 +10075,15 @@ static void rna_def_texture_nodetree(BlenderRNA *brna)
RNA_def_struct_ui_icon(srna, ICON_TEXTURE);
}
static void rna_def_simulation_nodetree(BlenderRNA *brna)
static void rna_def_geometry_nodetree(BlenderRNA *brna)
{
StructRNA *srna;
srna = RNA_def_struct(brna, "SimulationNodeTree", "NodeTree");
srna = RNA_def_struct(brna, "GeometryNodeTree", "NodeTree");
RNA_def_struct_ui_text(
srna, "Simulation Node Tree", "Node tree consisting of linked nodes used for simulations");
srna, "Geometry Node Tree", "Node tree consisting of linked nodes used for geometries");
RNA_def_struct_sdna(srna, "bNodeTree");
RNA_def_struct_ui_icon(srna, ICON_PHYSICS); /* TODO: Use correct icon. */
RNA_def_struct_ui_icon(srna, ICON_MESH_DATA); /* TODO: Use correct icon. */
}
static StructRNA *define_specific_node(BlenderRNA *brna,
@@ -9944,7 +10172,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
rna_def_shader_node(brna);
rna_def_compositor_node(brna);
rna_def_texture_node(brna);
rna_def_simulation_node(brna);
rna_def_geometry_node(brna);
rna_def_function_node(brna);
rna_def_nodetree(brna);
@@ -9954,7 +10182,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
rna_def_composite_nodetree(brna);
rna_def_shader_nodetree(brna);
rna_def_texture_nodetree(brna);
rna_def_simulation_nodetree(brna);
rna_def_geometry_nodetree(brna);
# define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
{ \
@@ -9971,13 +10199,13 @@ void RNA_def_nodetree(BlenderRNA *brna)
*/
# include "../../nodes/NOD_static_types.h"
/* Node group types need to be defined for shader, compositor, texture, simulation nodes
/* Node group types need to be defined for shader, compositor, texture, geometry nodes
* individually. Cannot use the static types header for this, since they share the same int id.
*/
define_specific_node(brna, "ShaderNodeGroup", "ShaderNode", "Group", "", def_group);
define_specific_node(brna, "CompositorNodeGroup", "CompositorNode", "Group", "", def_group);
define_specific_node(brna, "TextureNodeGroup", "TextureNode", "Group", "", def_group);
define_specific_node(brna, "SimulationNodeGroup", "SimulationNode", "Group", "", def_group);
define_specific_node(brna, "GeometryNodeGroup", "GeometryNode", "Group", "", def_group);
def_custom_group(brna,
"ShaderNodeCustomGroup",
"ShaderNode",

View File

@@ -44,7 +44,6 @@
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_sequence_types.h"
#include "DNA_simulation_types.h"
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "DNA_workspace_types.h"
@@ -2179,40 +2178,6 @@ static void rna_SpaceNodeEditor_node_tree_update(const bContext *C, PointerRNA *
ED_node_tree_update(C);
}
# ifdef WITH_GEOMETRY_NODES
static PointerRNA rna_SpaceNodeEditor_simulation_get(PointerRNA *ptr)
{
SpaceNode *snode = (SpaceNode *)ptr->data;
ID *id = snode->id;
if (id && GS(id->name) == ID_SIM) {
return rna_pointer_inherit_refine(ptr, &RNA_Simulation, snode->id);
}
else {
return PointerRNA_NULL;
}
}
static void rna_SpaceNodeEditor_simulation_set(PointerRNA *ptr,
const PointerRNA value,
struct ReportList *UNUSED(reports))
{
SpaceNode *snode = (SpaceNode *)ptr->data;
if (!STREQ(snode->tree_idname, "SimulationNodeTree")) {
return;
}
Simulation *sim = (Simulation *)value.data;
if (sim != NULL) {
bNodeTree *ntree = sim->nodetree;
ED_node_tree_start(snode, ntree, NULL, NULL);
}
else {
ED_node_tree_start(snode, NULL, NULL, NULL);
}
snode->id = &sim->id;
}
# endif
static int rna_SpaceNodeEditor_tree_type_get(PointerRNA *ptr)
{
SpaceNode *snode = (SpaceNode *)ptr->data;
@@ -6350,19 +6315,6 @@ static void rna_def_space_node(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "ID From", "Data-block from which the edited data-block is linked");
# ifdef WITH_GEOMETRY_NODES
prop = RNA_def_property(srna, "simulation", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_struct_type(prop, "Simulation");
RNA_def_property_ui_text(prop, "Simulation", "Simulation that is being edited");
RNA_def_property_pointer_funcs(prop,
"rna_SpaceNodeEditor_simulation_get",
"rna_SpaceNodeEditor_simulation_set",
NULL,
NULL);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL);
# endif
prop = RNA_def_property(srna, "path", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "treepath", NULL);
RNA_def_property_struct_type(prop, "NodeTreePath");

View File

@@ -29,8 +29,10 @@ set(INC
../bmesh
../depsgraph
../editors/include
../functions
../makesdna
../makesrna
../nodes
../render
../windowmanager
../../../intern/eigen
@@ -86,7 +88,7 @@ set(SRC
intern/MOD_shapekey.c
intern/MOD_shrinkwrap.c
intern/MOD_simpledeform.c
intern/MOD_simulation.cc
intern/MOD_nodes.cc
intern/MOD_skin.c
intern/MOD_smooth.c
intern/MOD_softbody.c
@@ -114,6 +116,7 @@ set(SRC
intern/MOD_wireframe.c
MOD_modifiertypes.h
MOD_nodes.h
intern/MOD_meshcache_util.h
intern/MOD_solidify_util.h
intern/MOD_ui_common.h

View File

@@ -85,7 +85,7 @@ extern ModifierTypeInfo modifierType_CorrectiveSmooth;
extern ModifierTypeInfo modifierType_MeshSequenceCache;
extern ModifierTypeInfo modifierType_SurfaceDeform;
extern ModifierTypeInfo modifierType_WeightedNormal;
extern ModifierTypeInfo modifierType_Simulation;
extern ModifierTypeInfo modifierType_Nodes;
extern ModifierTypeInfo modifierType_MeshToVolume;
extern ModifierTypeInfo modifierType_VolumeDisplace;
extern ModifierTypeInfo modifierType_VolumeToMesh;

View File

@@ -16,15 +16,17 @@
#pragma once
struct Main;
struct Object;
struct NodesModifierData;
#ifdef __cplusplus
extern "C" {
#endif
extern struct bNodeTreeType *ntreeType_Simulation;
void MOD_nodes_update_interface(struct Object *object, struct NodesModifierData *nmd);
void register_node_tree_type_sim(void);
void register_node_type_sim_group(void);
void MOD_nodes_init(struct Main *bmain, struct NodesModifierData *nmd);
#ifdef __cplusplus
}

View File

@@ -53,7 +53,10 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
static Mesh *doEdgeSplit(Mesh *mesh, EdgeSplitModifierData *emd)
/* For edge split modifier node. */
Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd);
Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd)
{
Mesh *result;
BMesh *bm;

File diff suppressed because it is too large Load Diff

View File

@@ -1,194 +0,0 @@
/*
* 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.
*
* The Original Code is Copyright (C) 2005 by the Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup modifiers
*/
#include <cstring>
#include <iostream>
#include <string>
#include "MEM_guardedalloc.h"
#include "BLI_float3.hh"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_defaults.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_simulation_types.h"
#include "BKE_customdata.h"
#include "BKE_lib_query.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
#include "BKE_screen.h"
#include "BKE_simulation.h"
#include "BLO_read_write.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "RNA_access.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
using blender::float3;
static void initData(ModifierData *md)
{
SimulationModifierData *smd = (SimulationModifierData *)md;
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(smd, modifier));
MEMCPY_STRUCT_AFTER(smd, DNA_struct_default_get(SimulationModifierData), modifier);
}
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *UNUSED(ctx))
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
UNUSED_VARS(smd);
}
static void foreachIDLink(ModifierData *md,
Object *UNUSED(ob),
IDWalkFunc UNUSED(walk),
void *UNUSED(userData))
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
UNUSED_VARS(smd);
}
static bool isDisabled(const struct Scene *UNUSED(scene),
ModifierData *md,
bool UNUSED(useRenderParams))
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
UNUSED_VARS(smd);
return false;
}
static PointCloud *modifyPointCloud(ModifierData *md,
const ModifierEvalContext *UNUSED(ctx),
PointCloud *pointcloud)
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
UNUSED_VARS(smd);
return pointcloud;
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemL(layout, "This modifier does nothing currently", ICON_INFO);
modifier_panel_end(layout, ptr);
}
static void panelRegister(ARegionType *region_type)
{
modifier_panel_register(region_type, eModifierType_Simulation, panel_draw);
}
static void blendWrite(BlendWriter *writer, const ModifierData *md)
{
const SimulationModifierData *smd = reinterpret_cast<const SimulationModifierData *>(md);
UNUSED_VARS(smd, writer);
}
static void blendRead(BlendDataReader *reader, ModifierData *md)
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
UNUSED_VARS(smd, reader);
}
static void copyData(const ModifierData *md, ModifierData *target, const int flag)
{
const SimulationModifierData *smd = reinterpret_cast<const SimulationModifierData *>(md);
SimulationModifierData *tsmd = reinterpret_cast<SimulationModifierData *>(target);
UNUSED_VARS(smd, tsmd);
BKE_modifier_copydata_generic(md, target, flag);
}
static void freeData(ModifierData *md)
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
UNUSED_VARS(smd);
}
ModifierTypeInfo modifierType_Simulation = {
/* name */ "Simulation",
/* structName */ "SimulationModifierData",
/* structSize */ sizeof(SimulationModifierData),
#ifdef WITH_GEOMETRY_NODES
/* srna */ &RNA_SimulationModifier,
#else
/* srna */ &RNA_Modifier,
#endif
/* type */ eModifierTypeType_None,
/* flags */ (ModifierTypeFlag)0,
/* icon */ ICON_PHYSICS, /* TODO: Use correct icon. */
/* copyData */ copyData,
/* deformVerts */ nullptr,
/* deformMatrices */ nullptr,
/* deformVertsEM */ nullptr,
/* deformMatricesEM */ nullptr,
/* modifyMesh */ nullptr,
/* modifyHair */ nullptr,
/* modifyPointCloud */ modifyPointCloud,
/* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ nullptr,
/* freeData */ freeData,
/* isDisabled */ isDisabled,
/* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ nullptr,
/* dependsOnNormals */ nullptr,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ nullptr,
/* freeRuntimeData */ nullptr,
/* panelRegister */ panelRegister,
/* blendWrite */ blendWrite,
/* blendRead */ blendRead,
};

View File

@@ -48,7 +48,13 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
static Mesh *triangulate_mesh(Mesh *mesh,
Mesh *triangulate_mesh(Mesh *mesh,
const int quad_method,
const int ngon_method,
const int min_vertices,
const int flag);
Mesh *triangulate_mesh(Mesh *mesh,
const int quad_method,
const int ngon_method,
const int min_vertices,

View File

@@ -342,9 +342,9 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(MeshSequenceCache);
INIT_TYPE(SurfaceDeform);
INIT_TYPE(WeightedNormal);
INIT_TYPE(Simulation);
INIT_TYPE(MeshToVolume);
INIT_TYPE(VolumeDisplace);
INIT_TYPE(VolumeToMesh);
INIT_TYPE(Nodes);
#undef INIT_TYPE
}

View File

@@ -24,11 +24,12 @@ set(INC
function
intern
shader
simulation
geometry
texture
../blenkernel
../blenlib
../blentranslation
../bmesh
../depsgraph
../functions
../gpu
@@ -137,6 +138,22 @@ set(SRC
function/nodes/node_fn_switch.cc
function/node_function_util.cc
geometry/nodes/node_geo_attribute_math.cc
geometry/nodes/node_geo_common.cc
geometry/nodes/node_geo_boolean.cc
geometry/nodes/node_geo_edge_split.cc
geometry/nodes/node_geo_join_geometry.cc
geometry/nodes/node_geo_object_info.cc
geometry/nodes/node_geo_subdivision_surface.cc
geometry/nodes/node_geo_point_distribute.cc
geometry/nodes/node_geo_point_instance.cc
geometry/nodes/node_geo_random_attribute.cc
geometry/nodes/node_geo_transform.cc
geometry/nodes/node_geo_triangulate.cc
geometry/node_geometry_exec.cc
geometry/node_geometry_tree.cc
geometry/node_geometry_util.cc
shader/nodes/node_shader_add_shader.c
shader/nodes/node_shader_ambient_occlusion.c
shader/nodes/node_shader_attribute.c
@@ -230,10 +247,6 @@ set(SRC
shader/node_shader_tree.c
shader/node_shader_util.c
simulation/nodes/node_sim_common.cc
simulation/node_simulation_tree.cc
simulation/node_simulation_util.cc
texture/nodes/node_texture_at.c
texture/nodes/node_texture_bricks.c
texture/nodes/node_texture_checker.c
@@ -261,18 +274,21 @@ set(SRC
texture/node_texture_util.c
intern/derived_node_tree.cc
intern/math_functions.cc
intern/node_common.c
intern/node_exec.c
intern/node_geometry_exec.cc
intern/node_socket.cc
intern/node_tree_dependencies.cc
intern/node_tree_multi_function.cc
intern/node_tree_ref.cc
intern/node_util.c
intern/type_callbacks.cc
composite/node_composite_util.h
function/node_function_util.hh
shader/node_shader_util.h
simulation/node_simulation_util.h
geometry/node_geometry_util.hh
texture/node_texture_util.h
NOD_common.h
@@ -283,10 +299,12 @@ set(SRC
NOD_node_tree_multi_function.hh
NOD_node_tree_ref.hh
NOD_shader.h
NOD_simulation.h
NOD_geometry.h
NOD_math_functions.hh
NOD_socket.h
NOD_static_types.h
NOD_texture.h
NOD_type_callbacks.hh
intern/node_common.h
intern/node_exec.h
intern/node_util.h
@@ -295,6 +313,7 @@ set(SRC
set(LIB
bf_functions
bf_intern_sky
bf_bmesh
)
if(WITH_PYTHON)
@@ -326,9 +345,12 @@ if(WITH_COMPOSITOR)
add_definitions(-DWITH_COMPOSITOR)
endif()
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
if(WITH_OPENSUBDIV)
add_definitions(-DWITH_OPENSUBDIV)
endif()
blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@@ -31,6 +31,8 @@
#include "NOD_node_tree_ref.hh"
#include "BLI_vector_set.hh"
namespace blender::nodes {
class DSocket;
@@ -65,6 +67,8 @@ class DSocket : NonCopyable, NonMovable {
PointerRNA *rna() const;
StringRefNull idname() const;
StringRefNull name() const;
StringRefNull identifier() const;
bNodeSocketType *typeinfo() const;
const SocketRef &socket_ref() const;
bNodeSocket *bsocket() const;
@@ -147,6 +151,8 @@ class DNode : NonCopyable, NonMovable {
PointerRNA *rna() const;
StringRefNull idname() const;
StringRefNull name() const;
bNode *bnode() const;
bNodeType *typeinfo() const;
private:
void destruct_with_sockets();
@@ -180,11 +186,15 @@ class DerivedNodeTree : NonCopyable, NonMovable {
Vector<DOutputSocket *> output_sockets_;
MultiValueMap<const bNodeType *, DNode *> nodes_by_type_;
VectorSet<const NodeTreeRef *> used_node_tree_refs_;
bNodeTree *btree_;
public:
DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs);
~DerivedNodeTree();
bNodeTree *btree() const;
Span<const DNode *> nodes() const;
Span<const DNode *> nodes_by_type(StringRefNull idname) const;
Span<const DNode *> nodes_by_type(const bNodeType *nodetype) const;
@@ -195,6 +205,10 @@ class DerivedNodeTree : NonCopyable, NonMovable {
Span<const DGroupInput *> group_inputs() const;
Span<const NodeTreeRef *> used_node_tree_refs() const;
bool has_link_cycles() const;
std::string to_dot() const;
private:
@@ -229,6 +243,15 @@ class DerivedNodeTree : NonCopyable, NonMovable {
Vector<DParentNode *> &&all_parent_nodes);
};
namespace derived_node_tree_types {
using nodes::DerivedNodeTree;
using nodes::DGroupInput;
using nodes::DInputSocket;
using nodes::DNode;
using nodes::DOutputSocket;
using nodes::DParentNode;
}; // namespace derived_node_tree_types
/* --------------------------------------------------------------------
* DSocket inline methods.
*/
@@ -288,6 +311,16 @@ inline StringRefNull DSocket::name() const
return socket_ref_->name();
}
inline StringRefNull DSocket::identifier() const
{
return socket_ref_->identifier();
}
inline bNodeSocketType *DSocket::typeinfo() const
{
return socket_ref_->bsocket()->typeinfo;
}
inline const SocketRef &DSocket::socket_ref() const
{
return *socket_ref_;
@@ -445,6 +478,16 @@ inline StringRefNull DNode::name() const
return node_ref_->name();
}
inline bNode *DNode::bnode() const
{
return node_ref_->bnode();
}
inline bNodeType *DNode::typeinfo() const
{
return node_ref_->bnode()->typeinfo;
}
/* --------------------------------------------------------------------
* DParentNode inline methods.
*/
@@ -468,6 +511,11 @@ inline int DParentNode::id() const
* DerivedNodeTree inline methods.
*/
inline bNodeTree *DerivedNodeTree::btree() const
{
return btree_;
}
inline Span<const DNode *> DerivedNodeTree::nodes() const
{
return nodes_by_id_;
@@ -504,4 +552,9 @@ inline Span<const DGroupInput *> DerivedNodeTree::group_inputs() const
return group_inputs_;
}
inline Span<const NodeTreeRef *> DerivedNodeTree::used_node_tree_refs() const
{
return used_node_tree_refs_;
}
} // namespace blender::nodes

View File

@@ -0,0 +1,43 @@
/*
* 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
#ifdef __cplusplus
extern "C" {
#endif
extern struct bNodeTreeType *ntreeType_Geometry;
void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
void register_node_type_geo_boolean(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_transform(void);
void register_node_type_geo_subdivision_surface(void);
void register_node_type_geo_triangulate(void);
void register_node_type_geo_point_distribute(void);
void register_node_type_geo_point_instance(void);
void register_node_type_geo_object_info(void);
void register_node_type_geo_random_attribute(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_join_geometry(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,155 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "FN_generic_value_map.hh"
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.hh"
#include "BKE_persistent_data_handle.hh"
#include "DNA_node_types.h"
namespace blender::nodes {
using bke::Float3ReadAttribute;
using bke::Float3WriteAttribute;
using bke::FloatReadAttribute;
using bke::FloatWriteAttribute;
using bke::PersistentDataHandleMap;
using bke::PersistentObjectHandle;
using bke::ReadAttribute;
using bke::ReadAttributePtr;
using bke::WriteAttribute;
using bke::WriteAttributePtr;
using fn::CPPType;
using fn::GMutablePointer;
using fn::GValueMap;
class GeoNodeExecParams {
private:
const bNode &node_;
GValueMap<StringRef> &input_values_;
GValueMap<StringRef> &output_values_;
const PersistentDataHandleMap &handle_map_;
const Object *self_object_;
public:
GeoNodeExecParams(const bNode &node,
GValueMap<StringRef> &input_values,
GValueMap<StringRef> &output_values,
const PersistentDataHandleMap &handle_map,
const Object *self_object)
: node_(node),
input_values_(input_values),
output_values_(output_values),
handle_map_(handle_map),
self_object_(self_object)
{
}
/**
* Get the input value for the input socket with the given identifier.
*
* The node calling becomes responsible for destructing the value before it is done
* executing. This method can only be called once for each identifier.
*/
GMutablePointer extract_input(StringRef identifier)
{
#ifdef DEBUG
this->check_extract_input(identifier);
#endif
return input_values_.extract(identifier);
}
/**
* Get the input value for the input socket with the given identifier.
*
* This method can only be called once for each identifier.
*/
template<typename T> T extract_input(StringRef identifier)
{
#ifdef DEBUG
this->check_extract_input(identifier, &CPPType::get<T>());
#endif
return input_values_.extract<T>(identifier);
}
/**
* Get the input value for the input socket with the given identifier.
*
* This makes a copy of the value, which is fine for most types but should be avoided for
* geometry sets.
*/
template<typename T> T get_input(StringRef identifier) const
{
#ifdef DEBUG
this->check_extract_input(identifier, &CPPType::get<T>());
#endif
return input_values_.lookup<T>(identifier);
}
/**
* Move-construct a new value based on the given value and store it for the given socket
* identifier.
*/
void set_output_by_move(StringRef identifier, GMutablePointer value)
{
#ifdef DEBUG
BLI_assert(value.type() != nullptr);
BLI_assert(value.get() != nullptr);
this->check_set_output(identifier, *value.type());
#endif
output_values_.add_new_by_move(identifier, value);
}
/**
* Store the output value for the given socket identifier.
*/
template<typename T> void set_output(StringRef identifier, T &&value)
{
#ifdef DEBUG
this->check_set_output(identifier, CPPType::get<std::decay_t<T>>());
#endif
output_values_.add_new(identifier, std::forward<T>(value));
}
/**
* Get the node that is currently being executed.
*/
const bNode &node() const
{
return node_;
}
const PersistentDataHandleMap &handle_map() const
{
return handle_map_;
}
const Object *self_object() const
{
return self_object_;
}
private:
/* Utilities for detecting common errors at when using this class. */
void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr) const;
void check_set_output(StringRef identifier, const CPPType &value_type) const;
};
} // namespace blender::nodes

View File

@@ -0,0 +1,200 @@
/*
* 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 "DNA_node_types.h"
#include "BLI_math_base_safe.h"
#include "BLI_math_rotation.h"
#include "BLI_string_ref.hh"
namespace blender::nodes {
struct FloatMathOperationInfo {
StringRefNull title_case_name;
StringRefNull shader_name;
FloatMathOperationInfo() = delete;
FloatMathOperationInfo(StringRefNull title_case_name, StringRefNull shader_name)
: title_case_name(title_case_name), shader_name(shader_name)
{
}
};
const FloatMathOperationInfo *get_float_math_operation_info(const int operation);
/**
* This calls the `callback` with two arguments:
* 1. The math function that takes a float as input and outputs a new float.
* 2. A #FloatMathOperationInfo struct reference.
* Returns true when the callback has been called, otherwise false.
*
* The math function that is passed to the callback is actually a lambda function that is different
* for every operation. Therefore, if the callback is templated on the math function, it will get
* instantiated for every operation separately. This has two benefits:
* - The compiler can optimize the callback for every operation separately.
* - A static variable declared in the callback will be generated for every operation separately.
*
* If separate instantiations are not desired, the callback can also take a function pointer with
* the following signature as input instead: float (*math_function)(float a).
*/
template<typename Callback>
inline bool try_dispatch_float_math_fl_to_fl(const int operation, Callback &&callback)
{
const FloatMathOperationInfo *info = get_float_math_operation_info(operation);
if (info == nullptr) {
return false;
}
/* This is just an utility function to keep the individual cases smaller. */
auto dispatch = [&](auto math_function) -> bool {
callback(math_function, *info);
return true;
};
switch (operation) {
case NODE_MATH_EXPONENT:
return dispatch([](float a) { return expf(a); });
case NODE_MATH_SQRT:
return dispatch([](float a) { return safe_sqrtf(a); });
case NODE_MATH_INV_SQRT:
return dispatch([](float a) { return safe_inverse_sqrtf(a); });
case NODE_MATH_ABSOLUTE:
return dispatch([](float a) { return fabs(a); });
case NODE_MATH_RADIANS:
return dispatch([](float a) { return (float)DEG2RAD(a); });
case NODE_MATH_DEGREES:
return dispatch([](float a) { return (float)RAD2DEG(a); });
case NODE_MATH_SIGN:
return dispatch([](float a) { return compatible_signf(a); });
case NODE_MATH_ROUND:
return dispatch([](float a) { return floorf(a + 0.5f); });
case NODE_MATH_FLOOR:
return dispatch([](float a) { return floorf(a); });
case NODE_MATH_CEIL:
return dispatch([](float a) { return ceilf(a); });
case NODE_MATH_FRACTION:
return dispatch([](float a) { return a - floorf(a); });
case NODE_MATH_TRUNC:
return dispatch([](float a) { return a >= 0.0f ? floorf(a) : ceilf(a); });
case NODE_MATH_SINE:
return dispatch([](float a) { return sinf(a); });
case NODE_MATH_COSINE:
return dispatch([](float a) { return cosf(a); });
case NODE_MATH_TANGENT:
return dispatch([](float a) { return tanf(a); });
case NODE_MATH_SINH:
return dispatch([](float a) { return sinhf(a); });
case NODE_MATH_COSH:
return dispatch([](float a) { return coshf(a); });
case NODE_MATH_TANH:
return dispatch([](float a) { return tanhf(a); });
case NODE_MATH_ARCSINE:
return dispatch([](float a) { return safe_asinf(a); });
case NODE_MATH_ARCCOSINE:
return dispatch([](float a) { return safe_acosf(a); });
case NODE_MATH_ARCTANGENT:
return dispatch([](float a) { return atanf(a); });
}
return false;
}
/**
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
*/
template<typename Callback>
inline bool try_dispatch_float_math_fl_fl_to_fl(const int operation, Callback &&callback)
{
const FloatMathOperationInfo *info = get_float_math_operation_info(operation);
if (info == nullptr) {
return false;
}
/* This is just an utility function to keep the individual cases smaller. */
auto dispatch = [&](auto math_function) -> bool {
callback(math_function, *info);
return true;
};
switch (operation) {
case NODE_MATH_ADD:
return dispatch([](float a, float b) { return a + b; });
case NODE_MATH_SUBTRACT:
return dispatch([](float a, float b) { return a - b; });
case NODE_MATH_MULTIPLY:
return dispatch([](float a, float b) { return a * b; });
case NODE_MATH_DIVIDE:
return dispatch([](float a, float b) { return safe_divide(a, b); });
case NODE_MATH_POWER:
return dispatch([](float a, float b) { return safe_powf(a, b); });
case NODE_MATH_LOGARITHM:
return dispatch([](float a, float b) { return safe_logf(a, b); });
case NODE_MATH_MINIMUM:
return dispatch([](float a, float b) { return std::min(a, b); });
case NODE_MATH_MAXIMUM:
return dispatch([](float a, float b) { return std::max(a, b); });
case NODE_MATH_LESS_THAN:
return dispatch([](float a, float b) { return (float)(a < b); });
case NODE_MATH_GREATER_THAN:
return dispatch([](float a, float b) { return (float)(a > b); });
case NODE_MATH_MODULO:
return dispatch([](float a, float b) { return safe_modf(a, b); });
case NODE_MATH_SNAP:
return dispatch([](float a, float b) { return floorf(safe_divide(a, b)) * b; });
case NODE_MATH_ARCTAN2:
return dispatch([](float a, float b) { return atan2f(a, b); });
case NODE_MATH_PINGPONG:
return dispatch([](float a, float b) { return pingpongf(a, b); });
}
return false;
}
/**
* This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature.
*/
template<typename Callback>
inline bool try_dispatch_float_math_fl_fl_fl_to_fl(const int operation, Callback &&callback)
{
const FloatMathOperationInfo *info = get_float_math_operation_info(operation);
if (info == nullptr) {
return false;
}
/* This is just an utility function to keep the individual cases smaller. */
auto dispatch = [&](auto math_function) -> bool {
callback(math_function, *info);
return true;
};
switch (operation) {
case NODE_MATH_MULTIPLY_ADD:
return dispatch([](float a, float b, float c) { return a * b + c; });
case NODE_MATH_COMPARE:
return dispatch([](float a, float b, float c) -> float {
return ((a == b) || (fabsf(a - b) <= fmaxf(c, FLT_EPSILON))) ? 1.0f : 0.0f;
});
case NODE_MATH_SMOOTH_MIN:
return dispatch([](float a, float b, float c) { return smoothminf(a, b, c); });
case NODE_MATH_SMOOTH_MAX:
return dispatch([](float a, float b, float c) { return -smoothminf(-a, -b, -c); });
case NODE_MATH_WRAP:
return dispatch([](float a, float b, float c) { return wrapf(a, b, c); });
}
return false;
}
} // namespace blender::nodes

View File

@@ -26,21 +26,12 @@
#include "FN_multi_function_network.hh"
#include "NOD_derived_node_tree.hh"
#include "NOD_type_callbacks.hh"
#include "BLI_resource_collector.hh"
namespace blender::nodes {
/* Maybe this should be moved to BKE_node.h. */
inline bool is_multi_function_data_socket(const bNodeSocket *bsocket)
{
if (bsocket->typeinfo->get_mf_data_type != nullptr) {
BLI_assert(bsocket->typeinfo->expand_in_mf_network != nullptr);
return true;
}
return false;
}
/**
* A MFNetworkTreeMap maps various components of a DerivedNodeTree to components of a
* fn::MFNetwork. This is necessary for further processing of a multi-function network that has
@@ -149,7 +140,7 @@ class MFNetworkTreeMap {
if (!dsocket->is_available()) {
continue;
}
if (!is_multi_function_data_socket(dsocket->bsocket())) {
if (!socket_is_mf_data_socket(*dsocket->bsocket()->typeinfo)) {
continue;
}
fn::MFSocket *socket = sockets[used_sockets];
@@ -299,6 +290,11 @@ class SocketMFNetworkBuilder : public MFNetworkBuilderBase {
{
this->construct_generator_fn<fn::CustomMF_Constant<T>>(std::move(value));
}
void set_constant_value(const CPPType &type, const void *value)
{
/* The value has live as long as the generated mf network. */
this->construct_generator_fn<fn::CustomMF_GenericConstant>(type, value);
}
template<typename T, typename... Args> void construct_generator_fn(Args &&... args)
{
@@ -397,4 +393,37 @@ MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
const DerivedNodeTree &tree,
ResourceCollector &resources);
using MultiFunctionByNode = Map<const DNode *, const fn::MultiFunction *>;
MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
ResourceCollector &resources);
class DataTypeConversions {
private:
Map<std::pair<fn::MFDataType, fn::MFDataType>, const fn::MultiFunction *> conversions_;
public:
void add(fn::MFDataType from_type, fn::MFDataType to_type, const fn::MultiFunction &fn)
{
conversions_.add_new({from_type, to_type}, &fn);
}
const fn::MultiFunction *get_conversion(fn::MFDataType from, fn::MFDataType to) const
{
return conversions_.lookup_default({from, to}, nullptr);
}
bool is_convertible(const CPPType &from_type, const CPPType &to_type) const
{
return conversions_.contains(
{fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)});
}
void convert(const CPPType &from_type,
const CPPType &to_type,
const void *from_value,
void *to_value) const;
};
const DataTypeConversions &get_implicit_type_conversions();
} // namespace blender::nodes

View File

@@ -101,6 +101,7 @@ class SocketRef : NonCopyable, NonMovable {
StringRefNull idname() const;
StringRefNull name() const;
StringRefNull identifier() const;
bNodeSocket *bsocket() const;
bNode *bnode() const;
@@ -176,6 +177,8 @@ class NodeTreeRef : NonCopyable, NonMovable {
Span<const InputSocketRef *> input_sockets() const;
Span<const OutputSocketRef *> output_sockets() const;
bool has_link_cycles() const;
bNodeTree *btree() const;
std::string to_dot() const;
@@ -272,6 +275,11 @@ inline StringRefNull SocketRef::name() const
return bsocket_->name;
}
inline StringRefNull SocketRef::identifier() const
{
return bsocket_->identifier;
}
inline bNodeSocket *SocketRef::bsocket() const
{
return bsocket_;

View File

@@ -266,6 +266,17 @@ DefNode(FunctionNode, FN_NODE_COMBINE_STRINGS, 0, "COMBINE_STRINGS
DefNode(FunctionNode, FN_NODE_OBJECT_TRANSFORMS, 0, "OBJECT_TRANSFORMS", ObjectTransforms, "Object Transforms", "")
DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
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_INSTANCE, 0, "POINT_INSTANCE", PointInstance, "Point Instance", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "")
DefNode(GeometryNode, GEO_NODE_RANDOM_ATTRIBUTE, def_geo_random_attribute, "RANDOM_ATTRIBUTE", RandomAttribute, "Random Attribute", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
/* undefine macros */
#undef DefNode

View File

@@ -0,0 +1,36 @@
/*
* 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 <optional>
#include "BKE_node.h"
#include "FN_multi_function_data_type.hh"
namespace blender::nodes {
using fn::CPPType;
using fn::MFDataType;
const CPPType *socket_cpp_type_get(const bNodeSocketType &stype);
std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype);
bool socket_is_mf_data_socket(const bNodeSocketType &stype);
bool socket_cpp_value_get(const bNodeSocket &socket, void *r_value);
void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder);
} // namespace blender::nodes

View File

@@ -20,7 +20,7 @@
bool fn_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
{
/* Function nodes are only supported in simulation node trees so far. */
return STREQ(ntree->idname, "SimulationNodeTree");
return STREQ(ntree->idname, "GeometryNodeTree");
}
void fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)

View File

@@ -100,7 +100,7 @@ void register_node_type_fn_float_compare()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Boolean Math", 0, 0);
fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Float Compare", 0, 0);
node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out);
node_type_label(&ntype, node_float_compare_label);
node_type_update(&ntype, node_float_compare_update);

View File

@@ -21,7 +21,7 @@
static bNodeSocketTemplate fn_node_random_float_in[] = {
{SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE},
{SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE},
{SOCK_INT, N_("Seed")},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};

View File

@@ -0,0 +1,23 @@
/*
* 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 "NOD_geometry_exec.hh"
MAKE_CPP_TYPE(GeometrySet, GeometrySet);
namespace blender::nodes {
}

View File

@@ -18,7 +18,7 @@
#include "MEM_guardedalloc.h"
#include "NOD_simulation.h"
#include "NOD_geometry.h"
#include "BKE_node.h"
@@ -28,18 +28,18 @@
#include "RNA_access.h"
bNodeTreeType *ntreeType_Simulation;
bNodeTreeType *ntreeType_Geometry;
void register_node_tree_type_sim(void)
void register_node_tree_type_geo(void)
{
bNodeTreeType *tt = ntreeType_Simulation = static_cast<bNodeTreeType *>(
MEM_callocN(sizeof(bNodeTreeType), "simulation node tree type"));
tt->type = NTREE_SIMULATION;
strcpy(tt->idname, "SimulationNodeTree");
strcpy(tt->ui_name, N_("Simulation Editor"));
bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>(
MEM_callocN(sizeof(bNodeTreeType), "geometry node tree type"));
tt->type = NTREE_GEOMETRY;
strcpy(tt->idname, "GeometryNodeTree");
strcpy(tt->ui_name, N_("Geometry Node Editor"));
tt->ui_icon = 0; /* defined in drawnode.c */
strcpy(tt->ui_description, N_("Simulation nodes"));
tt->rna_ext.srna = &RNA_SimulationNodeTree;
strcpy(tt->ui_description, N_("Geometry nodes"));
tt->rna_ext.srna = &RNA_GeometryNodeTree;
ntreeTypeAdd(tt);
}

View File

@@ -14,16 +14,16 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "node_simulation_util.h"
#include "node_geometry_util.hh"
#include "node_util.h"
bool sim_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
bool geo_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
{
return STREQ(ntree->idname, "SimulationNodeTree");
return STREQ(ntree->idname, "GeometryNodeTree");
}
void sim_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
{
node_type_base(ntype, type, name, nclass, flag);
ntype->poll = sim_node_poll_default;
ntype->poll = geo_node_poll_default;
}

View File

@@ -18,6 +18,7 @@
#include <string.h>
#include "BLI_float3.hh"
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
@@ -28,10 +29,11 @@
#include "BLT_translation.h"
#include "NOD_simulation.h"
#include "NOD_geometry.h"
#include "NOD_geometry_exec.hh"
#include "node_util.h"
void sim_node_type_base(
void geo_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
bool sim_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
bool geo_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);

View File

@@ -0,0 +1,167 @@
/*
* 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 "BKE_attribute.h"
#include "BKE_attribute_access.hh"
#include "BLI_array.hh"
#include "BLI_math_base_safe.h"
#include "BLI_rand.hh"
#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "NOD_math_functions.hh"
static bNodeSocketTemplate geo_node_attribute_math_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute A")},
{SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_STRING, N_("Attribute B")},
{SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_STRING, N_("Result")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_attribute_math_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node)
{
node->custom1 = NODE_MATH_ADD;
node->custom2 = GEO_NODE_USE_ATTRIBUTE_A | GEO_NODE_USE_ATTRIBUTE_B;
}
static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *sock_attribute_a = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
bNodeSocket *sock_float_a = sock_attribute_a->next;
bNodeSocket *sock_attribute_b = sock_float_a->next;
bNodeSocket *sock_float_b = sock_attribute_b->next;
GeometryNodeUseAttributeFlag flag = static_cast<GeometryNodeUseAttributeFlag>(node->custom2);
nodeSetSocketAvailability(sock_attribute_a, flag & GEO_NODE_USE_ATTRIBUTE_A);
nodeSetSocketAvailability(sock_attribute_b, flag & GEO_NODE_USE_ATTRIBUTE_B);
nodeSetSocketAvailability(sock_float_a, !(flag & GEO_NODE_USE_ATTRIBUTE_A));
nodeSetSocketAvailability(sock_float_b, !(flag & GEO_NODE_USE_ATTRIBUTE_B));
}
namespace blender::nodes {
static void do_math_operation(const FloatReadAttribute &input_a,
const FloatReadAttribute &input_b,
FloatWriteAttribute result,
const int operation)
{
const int size = input_a.size();
Span<float> span_a = input_a.get_span();
Span<float> span_b = input_b.get_span();
MutableSpan<float> span_result = result.get_span();
bool success = try_dispatch_float_math_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
const float in1 = span_a[i];
const float in2 = span_b[i];
const float out = math_function(in1, in2);
span_result[i] = out;
}
});
result.apply_span();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecParams &params)
{
const bNode &node = params.node();
const int operation = node.custom1;
/* The result type of this node is always float. */
const CustomDataType result_type = CD_PROP_FLOAT;
/* The result domain is always point for now. */
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
/* Get result attribute first, in case it has to overwrite one of the existing attributes. */
const std::string result_name = params.get_input<std::string>("Result");
WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
}
GeometryNodeUseAttributeFlag flag = static_cast<GeometryNodeUseAttributeFlag>(node.custom2);
auto get_input_attribute = [&](GeometryNodeUseAttributeFlag use_flag,
StringRef attribute_socket_identifier,
StringRef value_socket_identifier) {
if (flag & use_flag) {
const std::string attribute_name = params.get_input<std::string>(
attribute_socket_identifier);
return component.attribute_try_get_for_read(attribute_name, result_domain, result_type);
}
const float value = params.get_input<float>(value_socket_identifier);
return component.attribute_get_constant_for_read(result_domain, result_type, &value);
};
ReadAttributePtr attribute_a = get_input_attribute(GEO_NODE_USE_ATTRIBUTE_A, "Attribute A", "A");
ReadAttributePtr attribute_b = get_input_attribute(GEO_NODE_USE_ATTRIBUTE_B, "Attribute B", "B");
if (!attribute_a || !attribute_b) {
/* Attribute wasn't found. */
return;
}
do_math_operation(
std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
}
static void geo_node_attribute_math_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
if (geometry_set.has<MeshComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
}
if (geometry_set.has<PointCloudComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_attribute_math()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_MATH, "Attribute Math", 0, 0);
node_type_socket_templates(&ntype, geo_node_attribute_math_in, geo_node_attribute_math_out);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_math_exec;
node_type_update(&ntype, geo_node_attribute_math_update);
node_type_init(&ntype, geo_node_attribute_math_init);
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,152 @@
/*
* 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 "MEM_guardedalloc.h"
#include "BLI_alloca.h"
#include "BLI_math_matrix.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "RNA_enum_types.h"
#include "BKE_mesh.h"
#include "bmesh.h"
#include "tools/bmesh_boolean.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_boolean_in[] = {
{SOCK_GEOMETRY, N_("Geometry A")},
{SOCK_GEOMETRY, N_("Geometry B")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_boolean_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
{
return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 1 : 0;
}
static Mesh *mesh_boolean_calc(const Mesh *mesh_a, const Mesh *mesh_b, int boolean_mode)
{
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh_a, mesh_b);
BMesh *bm;
{
struct BMeshCreateParams bmesh_create_params = {0};
bmesh_create_params.use_toolflags = false;
bm = BM_mesh_create(&allocsize, &bmesh_create_params);
}
{
struct BMeshFromMeshParams bmesh_from_mesh_params = {0};
bmesh_from_mesh_params.calc_face_normal = true;
BM_mesh_bm_from_me(bm, mesh_b, &bmesh_from_mesh_params);
BM_mesh_bm_from_me(bm, mesh_a, &bmesh_from_mesh_params);
}
const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
int tottri;
BMLoop *(*looptris)[3] = (BMLoop *
(*)[3])(MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__));
BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
const int i_faces_end = mesh_b->totpoly;
/* We need face normals because of 'BM_face_split_edgenet'
* we could calculate on the fly too (before calling split). */
int i = 0;
BMIter iter;
BMFace *bm_face;
BM_ITER_MESH (bm_face, &iter, bm, BM_FACES_OF_MESH) {
normalize_v3(bm_face->no);
/* Temp tag to test which side split faces are from. */
BM_elem_flag_enable(bm_face, BM_ELEM_DRAW);
i++;
if (i == i_faces_end) {
break;
}
}
BM_mesh_boolean(
bm, looptris, tottri, bm_face_isect_pair, nullptr, 2, false, false, boolean_mode);
Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh_a);
BM_mesh_free(bm);
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
MEM_freeN(looptris);
return result;
}
namespace blender::nodes {
static void geo_node_boolean_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set_in_a = params.extract_input<GeometrySet>("Geometry A");
GeometrySet geometry_set_in_b = params.extract_input<GeometrySet>("Geometry B");
GeometrySet geometry_set_out;
GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)params.node().custom1;
if (operation < 0 || operation > 2) {
BLI_assert(false);
params.set_output("Geometry", std::move(geometry_set_out));
return;
}
const Mesh *mesh_in_a = geometry_set_in_a.get_mesh_for_read();
const Mesh *mesh_in_b = geometry_set_in_b.get_mesh_for_read();
if (mesh_in_a == nullptr || mesh_in_b == nullptr) {
if (operation == GEO_NODE_BOOLEAN_UNION) {
if (mesh_in_a != nullptr) {
params.set_output("Geometry", geometry_set_in_a);
}
else {
params.set_output("Geometry", geometry_set_in_b);
}
}
else {
params.set_output("Geometry", geometry_set_in_a);
}
return;
}
Mesh *mesh_out = mesh_boolean_calc(mesh_in_a, mesh_in_b, operation);
geometry_set_out = GeometrySet::create_with_mesh(mesh_out);
params.set_output("Geometry", std::move(geometry_set_out));
}
} // namespace blender::nodes
void register_node_type_geo_boolean()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", 0, 0);
node_type_socket_templates(&ntype, geo_node_boolean_in, geo_node_boolean_out);
ntype.geometry_node_execute = blender::nodes::geo_node_boolean_exec;
nodeRegisterType(&ntype);
}

View File

@@ -16,23 +16,23 @@
#include "BKE_node.h"
#include "NOD_simulation.h"
#include "NOD_geometry.h"
#include "NOD_common.h"
#include "node_common.h"
#include "node_simulation_util.h"
#include "node_geometry_util.hh"
void register_node_type_sim_group(void)
void register_node_type_geo_group(void)
{
static bNodeType ntype;
node_type_base_custom(&ntype, "SimulationNodeGroup", "Group", 0, 0);
node_type_base_custom(&ntype, "GeometryNodeGroup", "Group", 0, 0);
ntype.type = NODE_GROUP;
ntype.poll = sim_node_poll_default;
ntype.poll = geo_node_poll_default;
ntype.poll_instance = node_group_poll_instance;
ntype.insert_link = node_insert_link_default;
ntype.update_internal_links = node_update_internal_links_default;
ntype.rna_ext.srna = RNA_struct_find("SimulationNodeGroup");
ntype.rna_ext.srna = RNA_struct_find("GeometryNodeGroup");
BLI_assert(ntype.rna_ext.srna != nullptr);
RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype);

View File

@@ -0,0 +1,96 @@
/*
* 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 "BLI_math_base.h"
#include "BLI_math_rotation.h"
#include "DNA_modifier_types.h"
#include "node_geometry_util.hh"
extern "C" {
Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd);
}
static bNodeSocketTemplate geo_node_edge_split_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_BOOLEAN, N_("Edge Angle"), true},
{SOCK_FLOAT,
N_("Angle"),
DEG2RADF(30.0f),
0.0f,
0.0f,
0.0f,
0.0f,
DEG2RADF(180.0f),
PROP_ANGLE},
{SOCK_BOOLEAN, N_("Sharp Edges")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_edge_split_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void geo_node_edge_split_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
const bool use_sharp_flag = params.extract_input<bool>("Sharp Edges");
const bool use_edge_angle = params.extract_input<bool>("Edge Angle");
if (!use_edge_angle && !use_sharp_flag) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
const float split_angle = params.extract_input<float>("Angle");
const Mesh *mesh_in = geometry_set.get_mesh_for_read();
/* Use modifier struct to pass arguments to the modifier code. */
EdgeSplitModifierData emd;
memset(&emd, 0, sizeof(EdgeSplitModifierData));
emd.split_angle = split_angle;
if (use_edge_angle) {
emd.flags = MOD_EDGESPLIT_FROMANGLE;
}
if (use_sharp_flag) {
emd.flags |= MOD_EDGESPLIT_FROMFLAG;
}
Mesh *mesh_out = doEdgeSplit(mesh_in, &emd);
geometry_set.replace_mesh(mesh_out);
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_edge_split()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_EDGE_SPLIT, "Edge Split", 0, 0);
node_type_socket_templates(&ntype, geo_node_edge_split_in, geo_node_edge_split_out);
ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,276 @@
/*
* 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 "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_join_geometry_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_join_geometry_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent *> src_components)
{
int totverts = 0;
int totloops = 0;
int totedges = 0;
int totpolys = 0;
for (const MeshComponent *mesh_component : src_components) {
const Mesh *mesh = mesh_component->get_for_read();
totverts += mesh->totvert;
totloops += mesh->totloop;
totedges += mesh->totedge;
totpolys += mesh->totpoly;
}
const Mesh *first_input_mesh = src_components[0]->get_for_read();
Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
BKE_mesh_copy_settings(new_mesh, first_input_mesh);
int vert_offset = 0;
int loop_offset = 0;
int edge_offset = 0;
int poly_offset = 0;
for (const MeshComponent *mesh_component : src_components) {
const Mesh *mesh = mesh_component->get_for_read();
if (mesh == nullptr) {
continue;
}
for (const int i : IndexRange(mesh->totvert)) {
const MVert &old_vert = mesh->mvert[i];
MVert &new_vert = new_mesh->mvert[vert_offset + i];
new_vert = old_vert;
}
for (const int i : IndexRange(mesh->totedge)) {
const MEdge &old_edge = mesh->medge[i];
MEdge &new_edge = new_mesh->medge[edge_offset + i];
new_edge = old_edge;
new_edge.v1 += vert_offset;
new_edge.v2 += vert_offset;
}
for (const int i : IndexRange(mesh->totloop)) {
const MLoop &old_loop = mesh->mloop[i];
MLoop &new_loop = new_mesh->mloop[loop_offset + i];
new_loop = old_loop;
new_loop.v += vert_offset;
new_loop.e += edge_offset;
}
for (const int i : IndexRange(mesh->totpoly)) {
const MPoly &old_poly = mesh->mpoly[i];
MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
new_poly = old_poly;
new_poly.loopstart += loop_offset;
}
vert_offset += mesh->totvert;
loop_offset += mesh->totloop;
edge_offset += mesh->totedge;
poly_offset += mesh->totpoly;
}
return new_mesh;
}
template<typename Component>
static Array<const GeometryComponent *> to_base_components(Span<const Component *> components)
{
return components;
}
static Set<std::string> find_all_attribute_names(Span<const GeometryComponent *> components)
{
Set<std::string> attribute_names;
for (const GeometryComponent *component : components) {
Set<std::string> names = component->attribute_names();
for (const std::string &name : names) {
attribute_names.add(name);
}
}
return attribute_names;
}
static void determine_final_data_type_and_domain(Span<const GeometryComponent *> components,
StringRef attribute_name,
CustomDataType *r_type,
AttributeDomain *r_domain)
{
for (const GeometryComponent *component : components) {
ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name);
if (attribute) {
/* TODO: Use data type with most information. */
*r_type = bke::cpp_type_to_custom_data_type(attribute->cpp_type());
/* TODO: Use highest priority domain. */
*r_domain = attribute->domain();
return;
}
}
BLI_assert(false);
}
static void fill_new_attribute(Span<const GeometryComponent *> src_components,
StringRef attribute_name,
const CustomDataType data_type,
const AttributeDomain domain,
fn::GMutableSpan dst_span)
{
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
int offset = 0;
for (const GeometryComponent *component : src_components) {
const int domain_size = component->attribute_domain_size(domain);
ReadAttributePtr read_attribute = component->attribute_get_for_read(
attribute_name, domain, data_type, nullptr);
fn::GSpan src_span = read_attribute->get_span();
const void *src_buffer = src_span.data();
void *dst_buffer = dst_span[offset];
cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size);
offset += domain_size;
}
}
static void join_attributes(Span<const GeometryComponent *> src_components,
GeometryComponent &result,
Span<StringRef> ignored_attributes = {})
{
Set<std::string> attribute_names = find_all_attribute_names(src_components);
for (StringRef name : ignored_attributes) {
attribute_names.remove(name);
}
for (const std::string &attribute_name : attribute_names) {
CustomDataType data_type;
AttributeDomain domain;
determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain);
result.attribute_try_create(attribute_name, domain, data_type);
WriteAttributePtr write_attribute = result.attribute_try_get_for_write(attribute_name);
if (!write_attribute ||
&write_attribute->cpp_type() != bke::custom_data_type_to_cpp_type(data_type) ||
write_attribute->domain() != domain) {
continue;
}
fn::GMutableSpan dst_span = write_attribute->get_span();
fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span);
write_attribute->apply_span();
}
}
static void join_components(Span<const MeshComponent *> src_components, GeometrySet &result)
{
Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(src_components);
MeshComponent &dst_component = result.get_component_for_write<MeshComponent>();
dst_component.replace(new_mesh);
/* The position attribute is handled above already. */
join_attributes(to_base_components(src_components), dst_component, {"position"});
}
static void join_components(Span<const PointCloudComponent *> src_components, GeometrySet &result)
{
int totpoints = 0;
for (const PointCloudComponent *pointcloud_component : src_components) {
totpoints += pointcloud_component->attribute_domain_size(ATTR_DOMAIN_POINT);
}
PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>();
PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoints);
dst_component.replace(pointcloud);
join_attributes(to_base_components(src_components), dst_component);
}
static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result)
{
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
for (const InstancesComponent *component : src_components) {
const int size = component->instances_amount();
Span<const Object *> objects = component->objects();
Span<float3> positions = component->positions();
Span<float3> rotations = component->rotations();
Span<float3> scales = component->scales();
for (const int i : IndexRange(size)) {
dst_component.add_instance(objects[i], positions[i], rotations[i], scales[i]);
}
}
}
template<typename Component>
static void join_component_type(Span<const GeometrySet *> src_geometry_sets, GeometrySet &result)
{
Vector<const Component *> components;
for (const GeometrySet *geometry_set : src_geometry_sets) {
const Component *component = geometry_set->get_component_for_read<Component>();
if (component != nullptr && !component->is_empty()) {
components.append(component);
}
}
if (components.size() == 0) {
return;
}
if (components.size() == 1) {
result.add(*components[0]);
return;
}
join_components(components, result);
}
static void geo_node_join_geometry_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set_a = params.extract_input<GeometrySet>("Geometry");
GeometrySet geometry_set_b = params.extract_input<GeometrySet>("Geometry_001");
GeometrySet geometry_set_result;
std::array<const GeometrySet *, 2> src_geometry_sets = {&geometry_set_a, &geometry_set_b};
join_component_type<MeshComponent>(src_geometry_sets, geometry_set_result);
join_component_type<PointCloudComponent>(src_geometry_sets, geometry_set_result);
join_component_type<InstancesComponent>(src_geometry_sets, geometry_set_result);
params.set_output("Geometry", std::move(geometry_set_result));
}
} // namespace blender::nodes
void register_node_type_geo_join_geometry()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_JOIN_GEOMETRY, "Join Geometry", 0, 0);
node_type_socket_templates(&ntype, geo_node_join_geometry_in, geo_node_join_geometry_out);
ntype.geometry_node_execute = blender::nodes::geo_node_join_geometry_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,94 @@
/*
* 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 "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BLI_math_matrix.h"
static bNodeSocketTemplate geo_node_object_info_in[] = {
{SOCK_OBJECT, N_("Object")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_object_info_out[] = {
{SOCK_VECTOR, N_("Location")},
{SOCK_VECTOR, N_("Rotation")},
{SOCK_VECTOR, N_("Scale")},
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void geo_node_object_info_exec(GeoNodeExecParams params)
{
bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
"Object");
Object *object = params.handle_map().lookup(object_handle);
float3 location = {0, 0, 0};
float3 rotation = {0, 0, 0};
float3 scale = {0, 0, 0};
GeometrySet geometry_set;
const Object *self_object = params.self_object();
if (object != nullptr) {
float quaternion[4];
mat4_decompose(location, quaternion, scale, object->obmat);
quat_to_eul(rotation, quaternion);
if (object != self_object) {
if (object->type == OB_MESH) {
Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object, false);
if (mesh != nullptr) {
BKE_mesh_wrapper_ensure_mdata(mesh);
/* Make a copy because the life time of the other mesh might be shorter. */
Mesh *copied_mesh = BKE_mesh_copy_for_eval(mesh, false);
/* Transform into the local space of the object that is being modified. */
float transform[4][4];
mul_m4_m4m4(transform, self_object->imat, object->obmat);
BKE_mesh_transform(copied_mesh, transform, true);
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_component.replace(copied_mesh);
mesh_component.copy_vertex_group_names_from_object(*object);
}
}
}
}
params.set_output("Location", location);
params.set_output("Rotation", rotation);
params.set_output("Scale", scale);
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_object_info()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_OBJECT_INFO, "Object Info", 0, 0);
node_type_socket_templates(&ntype, geo_node_object_info_in, geo_node_object_info_out);
ntype.geometry_node_execute = blender::nodes::geo_node_object_info_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,139 @@
/*
* 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 "BLI_float3.hh"
#include "BLI_hash.h"
#include "BLI_math_vector.h"
#include "BLI_rand.hh"
#include "BLI_span.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
#include "node_geometry_util.hh"
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_STRING, N_("Density Attribute")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_point_distribute_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static Vector<float3> scatter_points_from_mesh(const Mesh *mesh,
const float density,
const FloatReadAttribute &density_factors)
{
/* This only updates a cache and can be considered to be logically const. */
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(mesh));
const int looptris_len = BKE_mesh_runtime_looptri_len(mesh);
Vector<float3> points;
for (const int looptri_index : IndexRange(looptris_len)) {
const MLoopTri &looptri = looptris[looptri_index];
const int v0_index = mesh->mloop[looptri.tri[0]].v;
const int v1_index = mesh->mloop[looptri.tri[1]].v;
const int v2_index = mesh->mloop[looptri.tri[2]].v;
const float3 v0_pos = mesh->mvert[v0_index].co;
const float3 v1_pos = mesh->mvert[v1_index].co;
const float3 v2_pos = mesh->mvert[v2_index].co;
const float v0_density_factor = 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]);
const float looptri_density_factor = (v0_density_factor + v1_density_factor +
v2_density_factor) /
3.0f;
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
const int looptri_seed = BLI_hash_int(looptri_index);
RandomNumberGenerator looptri_rng(looptri_seed);
const float points_amount_fl = area * density * looptri_density_factor;
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;
for (int i = 0; i < point_amount; i++) {
const float3 bary_coords = looptri_rng.get_barycentric_coordinates();
float3 point_pos;
interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coords);
points.append(point_pos);
}
}
return points;
}
static void geo_node_point_distribute_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet geometry_set_out;
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", std::move(geometry_set_out));
return;
}
const float density = params.extract_input<float>("Density");
const std::string density_attribute = params.extract_input<std::string>("Density Attribute");
if (density <= 0.0f) {
params.set_output("Geometry", std::move(geometry_set_out));
return;
}
const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
const Mesh *mesh_in = mesh_component.get_for_read();
const FloatReadAttribute density_factors = mesh_component.attribute_get_for_read<float>(
density_attribute, ATTR_DOMAIN_POINT, 1.0f);
Vector<float3> points = scatter_points_from_mesh(mesh_in, density, density_factors);
PointCloud *pointcloud = BKE_pointcloud_new_nomain(points.size());
memcpy(pointcloud->co, points.data(), sizeof(float3) * points.size());
for (const int i : points.index_range()) {
*(float3 *)(pointcloud->co + i) = points[i];
pointcloud->radius[i] = 0.05f;
}
geometry_set_out.replace_pointcloud(pointcloud);
params.set_output("Geometry", std::move(geometry_set_out));
}
} // namespace blender::nodes
void register_node_type_geo_point_distribute()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_POINT_DISTRIBUTE, "Point Distribute", 0, 0);
node_type_socket_templates(&ntype, geo_node_point_distribute_in, geo_node_point_distribute_out);
ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,87 @@
/*
* 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 "BKE_mesh.h"
#include "BKE_persistent_data_handle.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_point_instance_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_OBJECT, N_("Object")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_point_instance_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void add_instances_from_geometry_component(InstancesComponent &instances,
const GeometryComponent &src_geometry,
Object *object)
{
Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
Float3ReadAttribute rotations = src_geometry.attribute_get_for_read<float3>(
"rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
Float3ReadAttribute scales = src_geometry.attribute_get_for_read<float3>(
"scale", ATTR_DOMAIN_POINT, {1, 1, 1});
for (const int i : IndexRange(positions.size())) {
instances.add_instance(object, positions[i], rotations[i], scales[i]);
}
}
static void geo_node_point_instance_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet geometry_set_out;
bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
"Object");
Object *object = params.handle_map().lookup(object_handle);
if (object != nullptr && object != params.self_object()) {
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
if (geometry_set.has<MeshComponent>()) {
add_instances_from_geometry_component(
instances, *geometry_set.get_component_for_read<MeshComponent>(), object);
}
if (geometry_set.has<PointCloudComponent>()) {
add_instances_from_geometry_component(
instances, *geometry_set.get_component_for_read<PointCloudComponent>(), object);
}
}
params.set_output("Geometry", std::move(geometry_set_out));
}
} // namespace blender::nodes
void register_node_type_geo_point_instance()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_POINT_INSTANCE, "Point Instance", 0, 0);
node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out);
ntype.geometry_node_execute = blender::nodes::geo_node_point_instance_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,160 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "node_geometry_util.hh"
#include "BLI_rand.hh"
#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
static bNodeSocketTemplate geo_node_random_attribute_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
{SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_VECTOR, N_("Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};
static bNodeSocketTemplate geo_node_random_attribute_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_node_random_attribute_init(bNodeTree *UNUSED(tree), bNode *node)
{
node->custom1 = CD_PROP_FLOAT;
}
static void geo_node_random_attribute_update(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
bNodeSocket *sock_max_vector = sock_min_vector->next;
bNodeSocket *sock_min_float = sock_max_vector->next;
bNodeSocket *sock_max_float = sock_min_float->next;
const int data_type = node->custom1;
nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3);
nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3);
nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT);
nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT);
}
namespace blender::nodes {
static void randomize_attribute(FloatWriteAttribute &attribute,
float min,
float max,
RandomNumberGenerator &rng)
{
MutableSpan<float> attribute_span = attribute.get_span();
for (const int i : IndexRange(attribute.size())) {
const float value = rng.get_float() * (max - min) + min;
attribute_span[i] = value;
}
attribute.apply_span();
}
static void randomize_attribute(Float3WriteAttribute &attribute,
float3 min,
float3 max,
RandomNumberGenerator &rng)
{
MutableSpan<float3> attribute_span = attribute.get_span();
for (const int i : IndexRange(attribute.size())) {
const float x = rng.get_float();
const float y = rng.get_float();
const float z = rng.get_float();
const float3 value = float3(x, y, z) * (max - min) + min;
attribute_span[i] = value;
}
attribute.apply_span();
}
static void randomize_attribute(GeometryComponent &component,
const GeoNodeExecParams &params,
RandomNumberGenerator &rng)
{
const bNode &node = params.node();
const CustomDataType data_type = static_cast<CustomDataType>(node.custom1);
const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2);
const std::string attribute_name = params.get_input<std::string>("Attribute");
if (attribute_name.empty()) {
return;
}
WriteAttributePtr attribute = component.attribute_try_ensure_for_write(
attribute_name, domain, data_type);
if (!attribute) {
return;
}
switch (data_type) {
case CD_PROP_FLOAT: {
FloatWriteAttribute float_attribute = std::move(attribute);
const float min_value = params.get_input<float>("Min_001");
const float max_value = params.get_input<float>("Max_001");
randomize_attribute(float_attribute, min_value, max_value, rng);
break;
}
case CD_PROP_FLOAT3: {
Float3WriteAttribute float3_attribute = std::move(attribute);
const float3 min_value = params.get_input<float3>("Min");
const float3 max_value = params.get_input<float3>("Max");
randomize_attribute(float3_attribute, min_value, max_value, rng);
break;
}
default:
break;
}
}
static void geo_node_random_attribute_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const int seed = params.get_input<int>("Seed");
if (geometry_set.has<MeshComponent>()) {
RandomNumberGenerator rng;
rng.seed_random(seed);
randomize_attribute(geometry_set.get_component_for_write<MeshComponent>(), params, rng);
}
if (geometry_set.has<PointCloudComponent>()) {
RandomNumberGenerator rng;
rng.seed_random(seed + 3245231);
randomize_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params, rng);
}
params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
void register_node_type_geo_random_attribute()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_RANDOM_ATTRIBUTE, "Random Attribute", 0, 0);
node_type_socket_templates(&ntype, geo_node_random_attribute_in, geo_node_random_attribute_out);
node_type_init(&ntype, geo_node_random_attribute_init);
node_type_update(&ntype, geo_node_random_attribute_update);
ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,113 @@
/*
* 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 "MEM_guardedalloc.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_mesh.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_subdivision_surface_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6},
{SOCK_BOOLEAN, N_("Use Creases")},
{SOCK_BOOLEAN, N_("Boundary Smooth")},
{SOCK_BOOLEAN, N_("Smooth UVs")},
{-1, ""},
};
static bNodeSocketTemplate geo_node_subdivision_surface_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", geometry_set);
return;
}
#ifndef WITH_OPENSUBDIV
/* Return input geometry if Blender is built without OpenSubdiv. */
params.set_output("Geometry", std::move(geometry_set));
return;
#else
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30);
/* Only process subdivion if level is greater than 0. */
if (subdiv_level == 0) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
const bool use_crease = params.extract_input<bool>("Use Creases");
const bool boundary_smooth = params.extract_input<bool>("Boundary Smooth");
const bool smooth_uvs = params.extract_input<bool>("Smooth UVs");
const Mesh *mesh_in = geometry_set.get_mesh_for_read();
/* Initialize mesh settings. */
SubdivToMeshSettings mesh_settings;
mesh_settings.resolution = (1 << subdiv_level) + 1;
mesh_settings.use_optimal_display = false;
/* Initialize subdivision settings. */
SubdivSettings subdiv_settings;
subdiv_settings.is_simple = false;
subdiv_settings.is_adaptive = false;
subdiv_settings.use_creases = use_crease;
subdiv_settings.level = subdiv_level;
subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf(
boundary_smooth);
subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
smooth_uvs);
/* Apply subdivision to mesh. */
Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in);
/* In case of bad topology, skip to input mesh. */
if (subdiv == nullptr) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in);
geometry_set.replace_mesh(mesh_out);
// BKE_subdiv_stats_print(&subdiv->stats);
BKE_subdiv_free(subdiv);
params.set_output("Geometry", std::move(geometry_set));
#endif
}
} // namespace blender::nodes
void register_node_type_geo_subdivision_surface()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", 0, 0);
node_type_socket_templates(
&ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out);
ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,145 @@
/*
* 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 "BLI_math_matrix.h"
#include "DNA_pointcloud_types.h"
#include "BKE_mesh.h"
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_transform_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_VECTOR, N_("Translation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
{SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
{-1, ""},
};
static bNodeSocketTemplate geo_node_transform_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
static bool use_translate(const float3 rotation, const float3 scale)
{
if (compare_ff(rotation.length_squared(), 0.0f, 1e-9f) != 1) {
return false;
}
if (compare_ff(scale.x, 1.0f, 1e-9f) != 1 || compare_ff(scale.y, 1.0f, 1e-9f) != 1 ||
compare_ff(scale.z, 1.0f, 1e-9f) != 1) {
return false;
}
return true;
}
static void transform_mesh(Mesh *mesh,
const float3 translation,
const float3 rotation,
const float3 scale)
{
/* Use only translation if rotation and scale are zero. */
if (use_translate(rotation, scale)) {
BKE_mesh_translate(mesh, translation, true);
}
else {
float mat[4][4];
loc_eul_size_to_mat4(mat, translation, rotation, scale);
BKE_mesh_transform(mesh, mat, true);
BKE_mesh_calc_normals(mesh);
}
}
static void transform_pointcloud(PointCloud *pointcloud,
const float3 translation,
const float3 rotation,
const float3 scale)
{
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
for (int i = 0; i < pointcloud->totpoint; i++) {
add_v3_v3(pointcloud->co[i], translation);
}
}
else {
float mat[4][4];
loc_eul_size_to_mat4(mat, translation, rotation, scale);
for (int i = 0; i < pointcloud->totpoint; i++) {
mul_m4_v3(mat, pointcloud->co[i]);
}
}
}
static void transform_instances(InstancesComponent &instances,
const float3 translation,
const float3 rotation,
const float3 scale)
{
MutableSpan<float3> positions = instances.positions();
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
for (float3 &position : positions) {
add_v3_v3(position, translation);
}
}
else {
float mat[4][4];
loc_eul_size_to_mat4(mat, translation, rotation, scale);
for (float3 &position : positions) {
mul_m4_v3(mat, position);
}
}
}
static void geo_node_transform_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const float3 translation = params.extract_input<float3>("Translation");
const float3 rotation = params.extract_input<float3>("Rotation");
const float3 scale = params.extract_input<float3>("Scale");
if (geometry_set.has_mesh()) {
Mesh *mesh = geometry_set.get_mesh_for_write();
transform_mesh(mesh, translation, rotation, scale);
}
if (geometry_set.has_pointcloud()) {
PointCloud *pointcloud = geometry_set.get_pointcloud_for_write();
transform_pointcloud(pointcloud, translation, rotation, scale);
}
if (geometry_set.has_instances()) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
transform_instances(instances, translation, rotation, scale);
}
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_transform()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_TRANSFORM, "Transform", 0, 0);
node_type_socket_templates(&ntype, geo_node_transform_in, geo_node_transform_out);
ntype.geometry_node_execute = blender::nodes::geo_node_transform_exec;
nodeRegisterType(&ntype);
}

View File

@@ -0,0 +1,79 @@
/*
* 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_node_types.h"
#include "RNA_enum_types.h"
#include "node_geometry_util.hh"
extern "C" {
Mesh *triangulate_mesh(Mesh *mesh,
const int quad_method,
const int ngon_method,
const int min_vertices,
const int flag);
}
static bNodeSocketTemplate geo_node_triangulate_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_INT, N_("Minimum Vertices"), 4, 0, 0, 0, 4, 10000},
{-1, ""},
};
static bNodeSocketTemplate geo_node_triangulate_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node)
{
node->custom1 = GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE;
node->custom2 = GEO_NODE_TRIANGULATE_NGON_BEAUTY;
}
namespace blender::nodes {
static void geo_node_triangulate_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const int min_vertices = std::max(params.extract_input<int>("Minimum Vertices"), 4);
GeometryNodeTriangulateQuads quad_method = static_cast<GeometryNodeTriangulateQuads>(
params.node().custom1);
GeometryNodeTriangulateNGons ngon_method = static_cast<GeometryNodeTriangulateNGons>(
params.node().custom2);
/* #triangulate_mesh might modify the input mesh currently. */
Mesh *mesh_in = geometry_set.get_mesh_for_write();
if (mesh_in != nullptr) {
Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0);
geometry_set.replace_mesh(mesh_out);
}
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_triangulate()
{
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_TRIANGULATE, "Triangulate", 0, 0);
node_type_socket_templates(&ntype, geo_node_triangulate_in, geo_node_triangulate_out);
node_type_init(&ntype, geo_triangulate_init);
ntype.geometry_node_execute = blender::nodes::geo_node_triangulate_exec;
nodeRegisterType(&ntype);
}

View File

@@ -28,9 +28,12 @@ static const NodeTreeRef &get_tree_ref(NodeTreeRefMap &node_tree_refs, bNodeTree
[&]() { return std::make_unique<NodeTreeRef>(btree); });
}
DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs)
DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs) : btree_(btree)
{
BLI_assert(btree != nullptr);
const NodeTreeRef &main_tree_ref = get_tree_ref(node_tree_refs, btree);
used_node_tree_refs_.add_new(&main_tree_ref);
Vector<DNode *> all_nodes;
Vector<DGroupInput *> all_group_inputs;
@@ -137,6 +140,7 @@ BLI_NOINLINE void DerivedNodeTree::expand_group_node(DNode &group_node,
}
const NodeTreeRef &group_ref = get_tree_ref(node_tree_refs, btree);
used_node_tree_refs_.add(&group_ref);
DParentNode &parent = *allocator_.construct<DParentNode>();
parent.id_ = all_parent_nodes.append_and_get_index(&parent);
@@ -358,6 +362,16 @@ DerivedNodeTree::~DerivedNodeTree()
}
}
bool DerivedNodeTree::has_link_cycles() const
{
for (const NodeTreeRef *tree : used_node_tree_refs_) {
if (tree->has_link_cycles()) {
return true;
}
}
return false;
}
static dot::Cluster *get_cluster_for_parent(dot::DirectedGraph &graph,
Map<const DParentNode *, dot::Cluster *> &clusters,
const DParentNode *parent)

View File

@@ -0,0 +1,117 @@
/*
* 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 "NOD_math_functions.hh"
namespace blender::nodes {
const FloatMathOperationInfo *get_float_math_operation_info(const int operation)
{
#define RETURN_OPERATION_INFO(title_case_name, shader_name) \
{ \
static const FloatMathOperationInfo info{title_case_name, shader_name}; \
return &info; \
} \
((void)0)
switch (operation) {
case NODE_MATH_ADD:
RETURN_OPERATION_INFO("Add", "math_add");
case NODE_MATH_SUBTRACT:
RETURN_OPERATION_INFO("Subtract", "math_subtract");
case NODE_MATH_MULTIPLY:
RETURN_OPERATION_INFO("Multiply", "math_multiply");
case NODE_MATH_DIVIDE:
RETURN_OPERATION_INFO("Divide", "math_divide");
case NODE_MATH_SINE:
RETURN_OPERATION_INFO("Sine", "math_sine");
case NODE_MATH_COSINE:
RETURN_OPERATION_INFO("Cosine", "math_cosine");
case NODE_MATH_ARCSINE:
RETURN_OPERATION_INFO("Arc Sine", "math_arcsine");
case NODE_MATH_ARCCOSINE:
RETURN_OPERATION_INFO("Arc Cosine", "math_arccosine");
case NODE_MATH_ARCTANGENT:
RETURN_OPERATION_INFO("Arc Tangent", "math_arctangent");
case NODE_MATH_POWER:
RETURN_OPERATION_INFO("Power", "math_power");
case NODE_MATH_LOGARITHM:
RETURN_OPERATION_INFO("Logarithm", "math_logarithm");
case NODE_MATH_MINIMUM:
RETURN_OPERATION_INFO("Minimum", "math_minimum");
case NODE_MATH_MAXIMUM:
RETURN_OPERATION_INFO("Maximum", "math_maximum");
case NODE_MATH_ROUND:
RETURN_OPERATION_INFO("Round", "math_round");
case NODE_MATH_LESS_THAN:
RETURN_OPERATION_INFO("Less Than", "math_less_than");
case NODE_MATH_GREATER_THAN:
RETURN_OPERATION_INFO("Greater Than", "math_greater_than");
case NODE_MATH_MODULO:
RETURN_OPERATION_INFO("Modulo", "math_modulo");
case NODE_MATH_ABSOLUTE:
RETURN_OPERATION_INFO("Absolute", "math_absolute");
case NODE_MATH_ARCTAN2:
RETURN_OPERATION_INFO("Arc Tangent 2", "math_arctan2");
case NODE_MATH_FLOOR:
RETURN_OPERATION_INFO("Floor", "math_floor");
case NODE_MATH_CEIL:
RETURN_OPERATION_INFO("Ceil", "math_ceil");
case NODE_MATH_FRACTION:
RETURN_OPERATION_INFO("Fraction", "math_fraction");
case NODE_MATH_SQRT:
RETURN_OPERATION_INFO("Sqrt", "math_sqrt");
case NODE_MATH_INV_SQRT:
RETURN_OPERATION_INFO("Inverse Sqrt", "math_inversesqrt");
case NODE_MATH_SIGN:
RETURN_OPERATION_INFO("Sign", "math_sign");
case NODE_MATH_EXPONENT:
RETURN_OPERATION_INFO("Exponent", "math_exponent");
case NODE_MATH_RADIANS:
RETURN_OPERATION_INFO("Radians", "math_radians");
case NODE_MATH_DEGREES:
RETURN_OPERATION_INFO("Degrees", "math_degrees");
case NODE_MATH_SINH:
RETURN_OPERATION_INFO("Hyperbolic Sine", "math_sinh");
case NODE_MATH_COSH:
RETURN_OPERATION_INFO("Hyperbolic Cosine", "math_cosh");
case NODE_MATH_TANH:
RETURN_OPERATION_INFO("Hyperbolic Tangent", "math_tanh");
case NODE_MATH_TRUNC:
RETURN_OPERATION_INFO("Truncate", "math_trunc");
case NODE_MATH_SNAP:
RETURN_OPERATION_INFO("Snap", "math_snap");
case NODE_MATH_WRAP:
RETURN_OPERATION_INFO("Wrap", "math_wrap");
case NODE_MATH_COMPARE:
RETURN_OPERATION_INFO("Compare", "math_compare");
case NODE_MATH_MULTIPLY_ADD:
RETURN_OPERATION_INFO("Multiply Add", "math_multiply_add");
case NODE_MATH_PINGPONG:
RETURN_OPERATION_INFO("Ping Pong", "math_pingpong");
case NODE_MATH_SMOOTH_MIN:
RETURN_OPERATION_INFO("Smooth Min", "math_smoothmin");
case NODE_MATH_SMOOTH_MAX:
RETURN_OPERATION_INFO("Smooth Max", "math_smoothmax");
}
#undef RETURN_OPERATION_INFO
return nullptr;
}
} // namespace blender::nodes

View File

@@ -0,0 +1,103 @@
/*
* 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 "NOD_geometry_exec.hh"
#include "NOD_type_callbacks.hh"
namespace blender::nodes {
void GeoNodeExecParams::check_extract_input(StringRef identifier,
const CPPType *requested_type) const
{
bNodeSocket *found_socket = nullptr;
LISTBASE_FOREACH (bNodeSocket *, socket, &node_.inputs) {
if (identifier == socket->identifier) {
found_socket = socket;
break;
}
}
if (found_socket == nullptr) {
std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
LISTBASE_FOREACH (bNodeSocket *, socket, &node_.inputs) {
if ((socket->flag & SOCK_UNAVAIL) == 0) {
std::cout << "'" << socket->identifier << "', ";
}
}
std::cout << "\n";
BLI_assert(false);
}
else if (found_socket->flag & SOCK_UNAVAIL) {
std::cout << "The socket corresponding to the identifier '" << identifier
<< "' is disabled.\n";
BLI_assert(false);
}
else if (!input_values_.contains(identifier)) {
std::cout << "The identifier '" << identifier
<< "' is valid, but there is no value for it anymore.\n";
std::cout << "Most likely it has been extracted before.\n";
BLI_assert(false);
}
else if (requested_type != nullptr) {
const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
if (*requested_type != expected_type) {
std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
BLI_assert(false);
}
}
}
void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &value_type) const
{
bNodeSocket *found_socket = nullptr;
LISTBASE_FOREACH (bNodeSocket *, socket, &node_.outputs) {
if (identifier == socket->identifier) {
found_socket = socket;
break;
}
}
if (found_socket == nullptr) {
std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
LISTBASE_FOREACH (bNodeSocket *, socket, &node_.outputs) {
if ((socket->flag & SOCK_UNAVAIL) == 0) {
std::cout << "'" << socket->identifier << "', ";
}
}
std::cout << "\n";
BLI_assert(false);
}
else if (found_socket->flag & SOCK_UNAVAIL) {
std::cout << "The socket corresponding to the identifier '" << identifier
<< "' is disabled.\n";
BLI_assert(false);
}
else if (output_values_.contains(identifier)) {
std::cout << "The identifier '" << identifier << "' has been set already.\n";
BLI_assert(false);
}
else {
const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
if (value_type != expected_type) {
std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
BLI_assert(false);
}
}
}
} // namespace blender::nodes

View File

@@ -32,6 +32,7 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_node.h"
#include "BKE_persistent_data_handle.hh"
@@ -552,10 +553,9 @@ static bNodeSocketType *make_socket_type_virtual()
static bNodeSocketType *make_socket_type_bool()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE);
socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<bool>(); };
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
bool value = builder.socket_default_value<bNodeSocketValueBoolean>()->value;
builder.set_constant_value(value);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<bool>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value;
};
return socktype;
}
@@ -563,10 +563,9 @@ static bNodeSocketType *make_socket_type_bool()
static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype);
socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<float>(); };
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
float value = builder.socket_default_value<bNodeSocketValueFloat>()->value;
builder.set_constant_value(value);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<float>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value;
};
return socktype;
}
@@ -574,10 +573,9 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype);
socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<int>(); };
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
int value = builder.socket_default_value<bNodeSocketValueInt>()->value;
builder.set_constant_value(value);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<int>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value;
};
return socktype;
}
@@ -585,12 +583,9 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype);
socktype->get_mf_data_type = []() {
return blender::fn::MFDataType::ForSingle<blender::float3>();
};
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
blender::float3 value = builder.socket_default_value<bNodeSocketValueVector>()->value;
builder.set_constant_value(value);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::float3>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value;
};
return socktype;
}
@@ -598,12 +593,9 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
static bNodeSocketType *make_socket_type_rgba()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE);
socktype->get_mf_data_type = []() {
return blender::fn::MFDataType::ForSingle<blender::Color4f>();
};
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
blender::Color4f value = builder.socket_default_value<bNodeSocketValueRGBA>()->value;
builder.set_constant_value(value);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::Color4f>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(blender::Color4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
};
return socktype;
}
@@ -611,10 +603,9 @@ static bNodeSocketType *make_socket_type_rgba()
static bNodeSocketType *make_socket_type_string()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE);
socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<std::string>(); };
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
std::string value = builder.socket_default_value<bNodeSocketValueString>()->value;
builder.set_constant_value(value);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<std::string>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value);
};
return socktype;
}
@@ -661,9 +652,9 @@ MAKE_CPP_TYPE(PersistentObjectHandle, blender::bke::PersistentObjectHandle);
static bNodeSocketType *make_socket_type_object()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE);
socktype->get_mf_data_type = []() {
socktype->get_cpp_type = []() {
/* Objects are not passed along as raw pointers, but as handles. */
return blender::fn::MFDataType::ForSingle<blender::bke::PersistentObjectHandle>();
return &blender::fn::CPPType::get<blender::bke::PersistentObjectHandle>();
};
socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value;
@@ -675,6 +666,10 @@ static bNodeSocketType *make_socket_type_object()
static bNodeSocketType *make_socket_type_geometry()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_GEOMETRY, PROP_NONE);
socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<GeometrySet>(); };
socktype->get_cpp_value = [](const bNodeSocket &UNUSED(socket), void *r_value) {
new (r_value) GeometrySet();
};
return socktype;
}

View File

@@ -16,21 +16,13 @@
#include "NOD_node_tree_multi_function.hh"
#include "FN_multi_function_network_evaluation.hh"
#include "BLI_color.hh"
#include "BLI_float3.hh"
namespace blender::nodes {
/* Maybe this should be moved to BKE_node.h. */
static std::optional<fn::MFDataType> try_get_multi_function_data_type_of_socket(
const bNodeSocket *bsocket)
{
if (bsocket->typeinfo->get_mf_data_type == nullptr) {
return {};
}
return bsocket->typeinfo->get_mf_data_type();
}
const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
{
Vector<fn::MFDataType, 10> input_types;
@@ -38,8 +30,7 @@ const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
for (const DInputSocket *dsocket : dnode_.inputs()) {
if (dsocket->is_available()) {
std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
dsocket->bsocket());
std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
if (data_type.has_value()) {
input_types.append(*data_type);
}
@@ -47,8 +38,7 @@ const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
}
for (const DOutputSocket *dsocket : dnode_.outputs()) {
if (dsocket->is_available()) {
std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
dsocket->bsocket());
std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
if (data_type.has_value()) {
output_types.append(*data_type);
}
@@ -70,8 +60,7 @@ static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &d
for (const DInputSocket *dsocket : dnode.inputs()) {
if (dsocket->is_available()) {
std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
dsocket->bsocket());
std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
if (data_type.has_value()) {
input_types.append(*data_type);
input_names.append(dsocket->name());
@@ -86,8 +75,7 @@ static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &d
for (const DOutputSocket *dsocket : dnode.outputs()) {
if (dsocket->is_available()) {
std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
dsocket->bsocket());
std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
if (data_type.has_value()) {
output_types.append(*data_type);
output_names.append(dsocket->name());
@@ -106,12 +94,12 @@ static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &d
static bool has_data_sockets(const DNode &dnode)
{
for (const DInputSocket *socket : dnode.inputs()) {
if (is_multi_function_data_socket(socket->bsocket())) {
if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) {
return true;
}
}
for (const DOutputSocket *socket : dnode.outputs()) {
if (is_multi_function_data_socket(socket->bsocket())) {
if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) {
return true;
}
}
@@ -140,7 +128,7 @@ static void insert_group_inputs(CommonMFNetworkBuilderData &common)
{
for (const DGroupInput *group_input : common.tree.group_inputs()) {
bNodeSocket *bsocket = group_input->bsocket();
if (is_multi_function_data_socket(bsocket)) {
if (socket_is_mf_data_socket(*bsocket->typeinfo)) {
bNodeSocketType *socktype = bsocket->typeinfo;
BLI_assert(socktype->expand_in_mf_network != nullptr);
@@ -171,44 +159,43 @@ static fn::MFOutputSocket *try_find_origin(CommonMFNetworkBuilderData &common,
if (!from_dsocket.is_available()) {
return nullptr;
}
if (is_multi_function_data_socket(from_dsocket.bsocket())) {
if (socket_is_mf_data_socket(*from_dsocket.bsocket()->typeinfo)) {
return &common.network_map.lookup(from_dsocket);
}
return nullptr;
}
const DGroupInput &from_group_input = *from_group_inputs[0];
if (is_multi_function_data_socket(from_group_input.bsocket())) {
if (socket_is_mf_data_socket(*from_group_input.bsocket()->typeinfo)) {
return &common.network_map.lookup(from_group_input);
}
return nullptr;
}
using ImplicitConversionsMap =
Map<std::pair<fn::MFDataType, fn::MFDataType>, const fn::MultiFunction *>;
template<typename From, typename To>
static void add_implicit_conversion(ImplicitConversionsMap &map)
static void add_implicit_conversion(DataTypeConversions &conversions)
{
static fn::CustomMF_Convert<From, To> function;
map.add({fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>()}, &function);
conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
}
template<typename From, typename To, typename ConversionF>
static void add_implicit_conversion(ImplicitConversionsMap &map,
static void add_implicit_conversion(DataTypeConversions &conversions,
StringRef name,
ConversionF conversion)
{
static fn::CustomMF_SI_SO<From, To> function{name, conversion};
map.add({fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>()}, &function);
conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
}
static ImplicitConversionsMap get_implicit_conversions()
static DataTypeConversions create_implicit_conversions()
{
ImplicitConversionsMap conversions;
DataTypeConversions conversions;
add_implicit_conversion<float, int32_t>(conversions);
add_implicit_conversion<float, float3>(conversions);
add_implicit_conversion<int32_t, float>(conversions);
add_implicit_conversion<float, bool>(conversions);
add_implicit_conversion<bool, float>(conversions);
add_implicit_conversion<float3, float>(
conversions, "Vector Length", [](float3 a) { return a.length(); });
add_implicit_conversion<int32_t, float3>(
@@ -220,11 +207,26 @@ static ImplicitConversionsMap get_implicit_conversions()
return conversions;
}
static const fn::MultiFunction *try_get_conversion_function(fn::MFDataType from, fn::MFDataType to)
const DataTypeConversions &get_implicit_type_conversions()
{
static const ImplicitConversionsMap conversions = get_implicit_conversions();
const fn::MultiFunction *function = conversions.lookup_default({from, to}, nullptr);
return function;
static const DataTypeConversions conversions = create_implicit_conversions();
return conversions;
}
void DataTypeConversions::convert(const CPPType &from_type,
const CPPType &to_type,
const void *from_value,
void *to_value) const
{
const fn::MultiFunction *fn = this->get_conversion(MFDataType::ForSingle(from_type),
MFDataType::ForSingle(to_type));
BLI_assert(fn != nullptr);
fn::MFContextBuilder context;
fn::MFParamsBuilder params{*fn, 1};
params.add_readonly_single_input(fn::GSpan(from_type, from_value, 1));
params.add_uninitialized_single_output(fn::GMutableSpan(to_type, to_value, 1));
fn->call({0}, params, context);
}
static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common,
@@ -253,7 +255,7 @@ static void insert_links(CommonMFNetworkBuilderData &common)
if (!to_dsocket->is_linked()) {
continue;
}
if (!is_multi_function_data_socket(to_dsocket->bsocket())) {
if (!socket_is_mf_data_socket(*to_dsocket->bsocket()->typeinfo)) {
continue;
}
@@ -269,7 +271,8 @@ static void insert_links(CommonMFNetworkBuilderData &common)
fn::MFDataType from_type = from_socket->data_type();
if (from_type != to_type) {
const fn::MultiFunction *conversion_fn = try_get_conversion_function(from_type, to_type);
const fn::MultiFunction *conversion_fn = get_implicit_type_conversions().get_conversion(
from_type, to_type);
if (conversion_fn != nullptr) {
fn::MFNode &node = common.network.add_function(*conversion_fn);
common.network.add_link(*from_socket, node.input(0));
@@ -308,7 +311,7 @@ static void insert_unlinked_inputs(CommonMFNetworkBuilderData &common)
Vector<const DInputSocket *> unlinked_data_inputs;
for (const DInputSocket *dsocket : common.tree.input_sockets()) {
if (dsocket->is_available()) {
if (is_multi_function_data_socket(dsocket->bsocket())) {
if (socket_is_mf_data_socket(*dsocket->bsocket()->typeinfo)) {
if (!dsocket->is_linked()) {
insert_unlinked_input(common, *dsocket);
}
@@ -340,4 +343,150 @@ MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
return network_map;
}
/**
* A single node is allowed to expand into multiple nodes before evaluation. Depending on what
* nodes it expands to, it belongs a different type of the ones below.
*/
enum class NodeExpandType {
SingleFunctionNode,
MultipleFunctionNodes,
HasDummyNodes,
};
/**
* Checks how the given node expanded in the multi-function network. If it is only a single
* function node, the corresponding function is returned as well.
*/
static NodeExpandType get_node_expand_type(MFNetworkTreeMap &network_map,
const DNode &dnode,
const fn::MultiFunction **r_single_function)
{
const fn::MFFunctionNode *single_function_node = nullptr;
bool has_multiple_nodes = false;
bool has_dummy_nodes = false;
auto check_mf_node = [&](fn::MFNode &mf_node) {
if (mf_node.is_function()) {
if (single_function_node == nullptr) {
single_function_node = &mf_node.as_function();
}
if (&mf_node != single_function_node) {
has_multiple_nodes = true;
}
}
else {
BLI_assert(mf_node.is_dummy());
has_dummy_nodes = true;
}
};
for (const DInputSocket *dsocket : dnode.inputs()) {
if (dsocket->is_available()) {
for (fn::MFInputSocket *mf_input : network_map.lookup(*dsocket)) {
check_mf_node(mf_input->node());
}
}
}
for (const DOutputSocket *dsocket : dnode.outputs()) {
if (dsocket->is_available()) {
fn::MFOutputSocket &mf_output = network_map.lookup(*dsocket);
check_mf_node(mf_output.node());
}
}
if (has_dummy_nodes) {
return NodeExpandType::HasDummyNodes;
}
if (has_multiple_nodes) {
return NodeExpandType::MultipleFunctionNodes;
}
*r_single_function = &single_function_node->function();
return NodeExpandType::SingleFunctionNode;
}
static const fn::MultiFunction &create_function_for_node_that_expands_into_multiple(
const DNode &dnode,
fn::MFNetwork &network,
MFNetworkTreeMap &network_map,
ResourceCollector &resources)
{
Vector<const fn::MFOutputSocket *> dummy_fn_inputs;
for (const DInputSocket *dsocket : dnode.inputs()) {
if (dsocket->is_available()) {
MFDataType data_type = *socket_mf_type_get(*dsocket->typeinfo());
fn::MFOutputSocket &fn_input = network.add_input(data_type.to_string(), data_type);
for (fn::MFInputSocket *mf_input : network_map.lookup(*dsocket)) {
network.add_link(fn_input, *mf_input);
dummy_fn_inputs.append(&fn_input);
}
}
}
Vector<const fn::MFInputSocket *> dummy_fn_outputs;
for (const DOutputSocket *dsocket : dnode.outputs()) {
if (dsocket->is_available()) {
fn::MFOutputSocket &mf_output = network_map.lookup(*dsocket);
MFDataType data_type = mf_output.data_type();
fn::MFInputSocket &fn_output = network.add_output(data_type.to_string(), data_type);
network.add_link(mf_output, fn_output);
dummy_fn_outputs.append(&fn_output);
}
}
fn::MFNetworkEvaluator &fn_evaluator = resources.construct<fn::MFNetworkEvaluator>(
__func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs));
return fn_evaluator;
}
/**
* Returns a single multi-function for every node that supports it. This makes it easier to reuse
* the multi-function implementation of nodes in different contexts.
*/
MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
ResourceCollector &resources)
{
/* Build a network that nodes can insert themselves into. However, the individual nodes are not
* connected. */
fn::MFNetwork &network = resources.construct<fn::MFNetwork>(__func__);
MFNetworkTreeMap network_map{tree, network};
MultiFunctionByNode functions_by_node;
CommonMFNetworkBuilderData common{resources, network, network_map, tree};
for (const DNode *dnode : tree.nodes()) {
const bNodeType *node_type = dnode->typeinfo();
if (node_type->expand_in_mf_network == nullptr) {
/* This node does not have a multi-function implementation. */
continue;
}
NodeMFNetworkBuilder builder{common, *dnode};
node_type->expand_in_mf_network(builder);
const fn::MultiFunction *single_function = nullptr;
const NodeExpandType expand_type = get_node_expand_type(network_map, *dnode, &single_function);
switch (expand_type) {
case NodeExpandType::HasDummyNodes: {
/* Dummy nodes cannot be executed, so skip them. */
break;
}
case NodeExpandType::SingleFunctionNode: {
/* This is the common case. Most nodes just expand to a single function. */
functions_by_node.add_new(dnode, single_function);
break;
}
case NodeExpandType::MultipleFunctionNodes: {
/* If a node expanded into multiple functions, a new function has to be created that
* combines those. */
const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple(
*dnode, network, network_map, resources);
functions_by_node.add_new(dnode, &fn);
break;
}
}
}
return functions_by_node;
}
} // namespace blender::nodes

View File

@@ -137,6 +137,48 @@ void NodeTreeRef::find_targets_skipping_reroutes(OutputSocketRef &socket,
}
}
static bool has_link_cycles_recursive(const NodeRef &node,
MutableSpan<bool> visited,
MutableSpan<bool> is_in_stack)
{
const int node_id = node.id();
if (is_in_stack[node_id]) {
return true;
}
if (visited[node_id]) {
return false;
}
visited[node_id] = true;
is_in_stack[node_id] = true;
for (const OutputSocketRef *from_socket : node.outputs()) {
for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
const NodeRef &to_node = to_socket->node();
if (has_link_cycles_recursive(to_node, visited, is_in_stack)) {
return true;
}
}
}
is_in_stack[node_id] = false;
return false;
}
bool NodeTreeRef::has_link_cycles() const
{
const int node_amount = nodes_by_id_.size();
Array<bool> visited(node_amount, false);
Array<bool> is_in_stack(node_amount, false);
for (const NodeRef *node : nodes_by_id_) {
if (has_link_cycles_recursive(*node, visited, is_in_stack)) {
return true;
}
}
return false;
}
std::string NodeTreeRef::to_dot() const
{
dot::DirectedGraph digraph;

Some files were not shown because too many files have changed in this diff Show More