Compare commits
142 Commits
geometry-n
...
attribute-
Author | SHA1 | Date | |
---|---|---|---|
04147e2864 | |||
9a157d6532 | |||
7234fd5b31 | |||
6b8a52e2b1 | |||
2eb00bd479 | |||
d755281413 | |||
0e9567c63c | |||
1faab5c82d | |||
d3e05090a8 | |||
a889254974 | |||
3c020f9416 | |||
8786624534 | |||
52e3608fe9 | |||
6ba6e97407 | |||
4913b624d8 | |||
96ce1e9a95 | |||
a3efa1d798 | |||
e50553c61a | |||
b458ea6b23 | |||
056d7bb175 | |||
![]() |
9769cf1ee6 | ||
4a2734a835 | |||
1ddd717803 | |||
0066e59f2d | |||
ec7fffb033 | |||
956cf9a48d | |||
c27095b5cb | |||
![]() |
8ef8cb7e34 | ||
0f6bee8e54 | |||
3093f89498 | |||
f211030344 | |||
b277025d8e | |||
94572a4e30 | |||
bc2230df71 | |||
caa942b033 | |||
5dff952b67 | |||
5877e34eb4 | |||
770bcfac9b | |||
b081108819 | |||
2be7b2aaf9 | |||
912b38001f | |||
c4352f44bc | |||
0feca5f07d | |||
2984fb2b49 | |||
9e6553c0d4 | |||
107a0894cc | |||
8ecc1bea4c | |||
91ad33ef8f | |||
fa5190e742 | |||
74ed591f62 | |||
3d5efb4335 | |||
![]() |
140b7cfe0d | ||
af7cc3f8bb | |||
d2c4af9865 | |||
e5c637f5fe | |||
11d12d543d | |||
2c6114b238 | |||
152bd43ae1 | |||
45012c3309 | |||
5177c35498 | |||
49c8042f29 | |||
![]() |
f8c52590c0 | ||
0b897e0308 | |||
c34438dd78 | |||
e0a4dc9396 | |||
51fa44522f | |||
343e13f464 | |||
1103809fab | |||
5b89d49b0d | |||
e805bfc768 | |||
67cb4fdbdc | |||
8ed74c9daf | |||
d7cb25e028 | |||
e3395093ff | |||
f73a420e5a | |||
b8b240b329 | |||
ae2827bb52 | |||
bb97284f8a | |||
209e82da45 | |||
e63d43e6b9 | |||
802ba35654 | |||
79c9c11b35 | |||
e1d0ab2dbc | |||
687f994251 | |||
5c7767e0e0 | |||
af9ee8e2bc | |||
562f2e604a | |||
84e81b8cc3 | |||
5ae9527770 | |||
082c17a2d2 | |||
09677d737c | |||
bec7248178 | |||
0103cb2051 | |||
9f38ebefcb | |||
8f934852f0 | |||
cb16db7803 | |||
f8965f12a1 | |||
7f2a20f89b | |||
5566818603 | |||
9a06c6a040 | |||
e097116072 | |||
b844caca6d | |||
046ac5fccd | |||
ef4fbba596 | |||
aa8360d0fa | |||
76c356c12e | |||
e04491073d | |||
a5dda5996e | |||
c48e4b7a44 | |||
4ae2d6206a | |||
47f5a635da | |||
eb8574bc8b | |||
bc4e31afb6 | |||
9d7672be71 | |||
994e7178bb | |||
1719743066 | |||
8910033f57 | |||
2a4c6c612a | |||
b062b922f9 | |||
895f4620a0 | |||
fafed6234b | |||
7ff8094a8b | |||
5aabf67a9c | |||
ab8c7fe946 | |||
a05012d500 | |||
97a93566e9 | |||
da4d697772 | |||
87218899be | |||
ffa0a6df9d | |||
706fa5ad76 | |||
b7f6de490d | |||
7e485b4620 | |||
a7dba81aab | |||
4606e83a75 | |||
1d28de57a4 | |||
3cfcfb938d | |||
bcdc6910a0 | |||
7793e8c884 | |||
05d9bd7c4a | |||
9255ce9247 | |||
a0ce0154e7 | |||
0cd7f7ddd1 |
@@ -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",
|
||||
|
@@ -19,23 +19,34 @@
|
||||
import bpy
|
||||
|
||||
|
||||
class NewSimulation(bpy.types.Operator):
|
||||
"""Create a new simulation data block and edit it in the opened simulation editor"""
|
||||
class NewGeometryNodeTree(bpy.types.Operator):
|
||||
"""Create a new geometry node tree"""
|
||||
|
||||
bl_idname = "simulation.new"
|
||||
bl_label = "New Simulation"
|
||||
bl_idname = "node.new_geometry_node_tree"
|
||||
bl_label = "New Geometry Node Tree"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.area.type == 'NODE_EDITOR' and context.space_data.tree_type == 'SimulationNodeTree'
|
||||
return context.area.type == 'NODE_EDITOR' and context.space_data.tree_type == 'GeometryNodeTree'
|
||||
|
||||
def execute(self, context):
|
||||
simulation = bpy.data.simulations.new("Simulation")
|
||||
context.space_data.simulation = simulation
|
||||
group = bpy.data.node_groups.new("Geometry Node Group", '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])
|
||||
|
||||
context.space_data.node_tree = group
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
classes = (
|
||||
NewSimulation,
|
||||
NewGeometryNodeTree,
|
||||
)
|
@@ -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
|
||||
|
@@ -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,10 +483,44 @@ 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_INPUT", "Input", items=[
|
||||
NodeItem("GeometryNodeObjectInfo"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_MESH", "Mesh", items=[
|
||||
NodeItem("GeometryNodeTriangulate"),
|
||||
NodeItem("GeometryNodeEdgeSplit"),
|
||||
NodeItem("GeometryNodeTransform"),
|
||||
NodeItem("GeometryNodeBoolean"),
|
||||
NodeItem("GeometryNodeSubdivisionSurface"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_SCATTERING", "Scattering", items=[
|
||||
NodeItem("GeometryNodePointDistribute"),
|
||||
NodeItem("GeometryNodePointInstance"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_ATTRIBUTES", "Attributes", items=[
|
||||
NodeItem("GeometryNodeRandomAttribute"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_MATH", "Misc", items=[
|
||||
NodeItem("ShaderNodeMapRange"),
|
||||
NodeItem("ShaderNodeClamp"),
|
||||
NodeItem("ShaderNodeMath"),
|
||||
NodeItem("ShaderNodeValToRGB"),
|
||||
NodeItem("ShaderNodeVectorMath"),
|
||||
NodeItem("ShaderNodeSeparateRGB"),
|
||||
NodeItem("ShaderNodeCombineRGB"),
|
||||
NodeItem("ShaderNodeSeparateXYZ"),
|
||||
NodeItem("ShaderNodeCombineXYZ"),
|
||||
NodeItem("FunctionNodeBooleanMath"),
|
||||
NodeItem("FunctionNodeFloatCompare"),
|
||||
# NodeItem("FunctionNodeCombineStrings"),
|
||||
NodeItem("FunctionNodeRandomFloat"),
|
||||
NodeItem("ShaderNodeValue"),
|
||||
# NodeItem("FunctionNodeGroupInstanceID"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items),
|
||||
GeometryNodeCategory("GEO_LAYOUT", "Layout", items=[
|
||||
NodeItem("NodeFrame"),
|
||||
NodeItem("NodeReroute"),
|
||||
]),
|
||||
@@ -497,14 +531,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__":
|
||||
|
212
source/blender/blenkernel/BKE_attribute_accessor.hh
Normal file
212
source/blender/blenkernel/BKE_attribute_accessor.hh
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FN_cpp_type.hh"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_geometry_set.hh"
|
||||
|
||||
struct Mesh;
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
using fn::CPPType;
|
||||
|
||||
class ReadAttribute {
|
||||
protected:
|
||||
const AttributeDomain domain_;
|
||||
const CPPType &cpp_type_;
|
||||
const int64_t size_;
|
||||
|
||||
public:
|
||||
ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
|
||||
: domain_(domain), cpp_type_(cpp_type), size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ReadAttribute();
|
||||
|
||||
AttributeDomain domain() const
|
||||
{
|
||||
return domain_;
|
||||
}
|
||||
|
||||
const CPPType &cpp_type() const
|
||||
{
|
||||
return cpp_type_;
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
void get(const int64_t index, void *r_value) const
|
||||
{
|
||||
BLI_assert(index < size_);
|
||||
this->get_internal(index, r_value);
|
||||
}
|
||||
|
||||
protected:
|
||||
/* r_value is expected to be uninitialized. */
|
||||
virtual void get_internal(const int64_t index, void *r_value) const = 0;
|
||||
};
|
||||
|
||||
class WriteAttribute {
|
||||
protected:
|
||||
const AttributeDomain domain_;
|
||||
const CPPType &cpp_type_;
|
||||
const int64_t size_;
|
||||
|
||||
public:
|
||||
WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
|
||||
: domain_(domain), cpp_type_(cpp_type), size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~WriteAttribute();
|
||||
|
||||
AttributeDomain domain() const
|
||||
{
|
||||
return domain_;
|
||||
}
|
||||
|
||||
const CPPType &cpp_type() const
|
||||
{
|
||||
return cpp_type_;
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
void get(const int64_t index, void *r_value) const
|
||||
{
|
||||
BLI_assert(index < size_);
|
||||
this->get_internal(index, r_value);
|
||||
}
|
||||
|
||||
void set(const int64_t index, const void *value)
|
||||
{
|
||||
BLI_assert(index < size_);
|
||||
this->set_internal(index, value);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void get_internal(const int64_t index, void *r_value) const = 0;
|
||||
virtual void set_internal(const int64_t index, const void *value) = 0;
|
||||
};
|
||||
|
||||
using ReadAttributePtr = std::unique_ptr<ReadAttribute>;
|
||||
using WriteAttributePtr = std::unique_ptr<WriteAttribute>;
|
||||
|
||||
template<typename T> class TypedReadAttribute {
|
||||
private:
|
||||
ReadAttributePtr attribute_;
|
||||
|
||||
public:
|
||||
TypedReadAttribute(ReadAttributePtr attribute) : attribute_(std::move(attribute))
|
||||
{
|
||||
BLI_assert(attribute_);
|
||||
BLI_assert(attribute_->cpp_type().is<T>());
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return attribute_->size();
|
||||
}
|
||||
|
||||
T operator[](const int64_t index) const
|
||||
{
|
||||
BLI_assert(index < attribute_->size());
|
||||
T value;
|
||||
value.~T();
|
||||
attribute_->get(index, &value);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class TypedWriteAttribute {
|
||||
private:
|
||||
WriteAttributePtr attribute_;
|
||||
|
||||
public:
|
||||
TypedWriteAttribute(WriteAttributePtr attribute) : attribute_(std::move(attribute))
|
||||
{
|
||||
BLI_assert(attribute_);
|
||||
BLI_assert(attribute_->cpp_type().is<T>());
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return attribute_->size();
|
||||
}
|
||||
|
||||
T operator[](const int64_t index) const
|
||||
{
|
||||
BLI_assert(index < attribute_->size());
|
||||
T value;
|
||||
value.~T();
|
||||
attribute_->get(index, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void set(const int64_t index, const T &value)
|
||||
{
|
||||
attribute_->set(index, &value);
|
||||
}
|
||||
};
|
||||
|
||||
using FloatReadAttribute = TypedReadAttribute<float>;
|
||||
using Float3ReadAttribute = TypedReadAttribute<float3>;
|
||||
using FloatWriteAttribute = TypedWriteAttribute<float>;
|
||||
using Float3WriteAttribute = TypedWriteAttribute<float3>;
|
||||
|
||||
ReadAttributePtr mesh_attribute_get_for_read(const MeshComponent &mesh_component,
|
||||
const StringRef attribute_name);
|
||||
std::optional<WriteAttributePtr> mesh_attribute_get_for_write(MeshComponent &mesh_component,
|
||||
const StringRef attribute_name);
|
||||
|
||||
ReadAttributePtr mesh_attribute_adapt_domain(const MeshComponent &mesh_component,
|
||||
ReadAttributePtr attribute,
|
||||
const AttributeDomain to_domain);
|
||||
|
||||
ReadAttributePtr mesh_attribute_get_for_read(const MeshComponent &mesh_component,
|
||||
const StringRef attribute_name,
|
||||
const CPPType &cpp_type,
|
||||
const AttributeDomain domain,
|
||||
const void *default_value = nullptr);
|
||||
|
||||
template<typename T>
|
||||
TypedReadAttribute<T> mesh_attribute_get_for_read(const MeshComponent &mesh_component,
|
||||
const StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
const T &default_value)
|
||||
{
|
||||
ReadAttributePtr attribute = mesh_attribute_get_for_read(
|
||||
mesh_component,
|
||||
attribute_name,
|
||||
CPPType::get<T>(),
|
||||
domain,
|
||||
static_cast<const void *>(&default_value));
|
||||
BLI_assert(attribute);
|
||||
return attribute;
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
40
source/blender/blenkernel/BKE_geometry_set.h
Normal file
40
source/blender/blenkernel/BKE_geometry_set.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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],
|
||||
struct Object ***r_objects);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
212
source/blender/blenkernel/BKE_geometry_set.hh
Normal file
212
source/blender/blenkernel/BKE_geometry_set.hh
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
|
||||
#include "BLI_float3.hh"
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_user_counter.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. */
|
||||
std::atomic<int> users_ = 1;
|
||||
|
||||
public:
|
||||
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();
|
||||
void user_remove();
|
||||
bool is_mutable() const;
|
||||
};
|
||||
|
||||
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.
|
||||
*/
|
||||
class 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::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::type));
|
||||
}
|
||||
|
||||
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();
|
||||
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);
|
||||
int vertex_group_index(blender::StringRef vertex_group_name) const;
|
||||
|
||||
const Mesh *get_for_read() const;
|
||||
Mesh *get_for_write();
|
||||
|
||||
static constexpr inline GeometryComponentType 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();
|
||||
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();
|
||||
|
||||
static constexpr inline GeometryComponentType type = GeometryComponentType::PointCloud;
|
||||
};
|
||||
|
||||
/** A geometry component that stores instances. */
|
||||
class InstancesComponent : public GeometryComponent {
|
||||
private:
|
||||
blender::Vector<blender::float3> positions_;
|
||||
blender::Vector<const Object *> objects_;
|
||||
|
||||
public:
|
||||
~InstancesComponent() = default;
|
||||
GeometryComponent *copy() const override;
|
||||
|
||||
void replace(blender::Vector<blender::float3> positions,
|
||||
blender::Vector<const Object *> objects);
|
||||
void replace(blender::Vector<blender::float3> positions, const Object *object);
|
||||
|
||||
blender::Span<const Object *> objects() const;
|
||||
blender::Span<blender::float3> positions() const;
|
||||
blender::MutableSpan<blender::float3> positions();
|
||||
int instances_amount() const;
|
||||
|
||||
static constexpr inline GeometryComponentType type = GeometryComponentType::Instances;
|
||||
};
|
@@ -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,
|
||||
const struct ModifierEvalContext *ctx,
|
||||
struct PointCloud *pointcloud);
|
||||
void (*modifyPointCloud)(struct ModifierData *md,
|
||||
const struct ModifierEvalContext *ctx,
|
||||
struct GeometrySet *geometry_set);
|
||||
struct Volume *(*modifyVolume)(struct ModifierData *md,
|
||||
const struct ModifierEvalContext *ctx,
|
||||
struct Volume *volume);
|
||||
|
@@ -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,22 @@ 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
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Function Nodes
|
||||
* \{ */
|
||||
|
@@ -38,6 +38,7 @@ 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);
|
||||
|
||||
|
@@ -78,6 +78,7 @@ set(SRC
|
||||
intern/armature_deform.c
|
||||
intern/armature_update.c
|
||||
intern/attribute.c
|
||||
intern/attribute_accessor.cc
|
||||
intern/autoexec.c
|
||||
intern/blender.c
|
||||
intern/blender_copybuffer.c
|
||||
@@ -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_accessor.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
|
||||
|
478
source/blender/blenkernel/intern/attribute_accessor.cc
Normal file
478
source/blender/blenkernel/intern/attribute_accessor.cc
Normal file
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "BKE_attribute_accessor.hh"
|
||||
#include "BKE_deform.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_float2.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
ReadAttribute::~ReadAttribute() = default;
|
||||
WriteAttribute::~WriteAttribute() = default;
|
||||
|
||||
class VertexWeightWriteAttribute final : public WriteAttribute {
|
||||
private:
|
||||
MutableSpan<MDeformVert> dverts_;
|
||||
const int dvert_index_;
|
||||
|
||||
public:
|
||||
VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
|
||||
: WriteAttribute(ATTR_DOMAIN_VERTEX, CPPType::get<float>(), totvert),
|
||||
dverts_(dverts, totvert),
|
||||
dvert_index_(dvert_index)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
this->get_internal(dverts_, dvert_index_, index, r_value);
|
||||
}
|
||||
|
||||
void set_internal(const int64_t index, const void *value) override
|
||||
{
|
||||
MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
|
||||
weight->weight = *reinterpret_cast<const float *>(value);
|
||||
}
|
||||
|
||||
static void get_internal(const Span<MDeformVert> dverts,
|
||||
const int dvert_index,
|
||||
const int64_t index,
|
||||
void *r_value)
|
||||
{
|
||||
const MDeformVert &dvert = dverts[index];
|
||||
for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
|
||||
if (weight.def_nr == dvert_index) {
|
||||
*(float *)r_value = weight.weight;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*(float *)r_value = 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class VertexWeightReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
const Span<MDeformVert> dverts_;
|
||||
const int dvert_index_;
|
||||
|
||||
public:
|
||||
VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
|
||||
: ReadAttribute(ATTR_DOMAIN_VERTEX, CPPType::get<float>(), totvert),
|
||||
dverts_(dverts, totvert),
|
||||
dvert_index_(dvert_index)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
|
||||
private:
|
||||
MutableSpan<T> data_;
|
||||
|
||||
public:
|
||||
ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data)
|
||||
: WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
new (r_value) T(data_[index]);
|
||||
}
|
||||
|
||||
void set_internal(const int64_t index, const void *value) override
|
||||
{
|
||||
data_[index] = *reinterpret_cast<const T *>(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class ArrayReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
Span<T> data_;
|
||||
|
||||
public:
|
||||
ArrayReadAttribute(AttributeDomain domain, Span<T> data)
|
||||
: ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
new (r_value) T(data_[index]);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StructT, typename ElemT, typename GetFuncT, typename SetFuncT>
|
||||
class DerivedArrayWriteAttribute final : public WriteAttribute {
|
||||
private:
|
||||
MutableSpan<StructT> data_;
|
||||
GetFuncT get_function_;
|
||||
SetFuncT set_function_;
|
||||
|
||||
public:
|
||||
DerivedArrayWriteAttribute(AttributeDomain domain,
|
||||
MutableSpan<StructT> data,
|
||||
GetFuncT get_function,
|
||||
SetFuncT set_function)
|
||||
: WriteAttribute(domain, CPPType::get<ElemT>(), data.size()),
|
||||
data_(data),
|
||||
get_function_(std::move(get_function)),
|
||||
set_function_(std::move(set_function))
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
const StructT &struct_value = data_[index];
|
||||
const ElemT value = get_function_(struct_value);
|
||||
new (r_value) ElemT(value);
|
||||
}
|
||||
|
||||
void set_internal(const int64_t index, const void *value) override
|
||||
{
|
||||
StructT &struct_value = data_[index];
|
||||
const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
|
||||
set_function_(struct_value, typed_value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StructT, typename ElemT, typename GetFuncT>
|
||||
class DerivedArrayReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
Span<StructT> data_;
|
||||
GetFuncT get_function_;
|
||||
|
||||
public:
|
||||
DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data, GetFuncT get_function)
|
||||
: ReadAttribute(domain, CPPType::get<ElemT>(), data.size()),
|
||||
data_(data),
|
||||
get_function_(std::move(get_function))
|
||||
{
|
||||
}
|
||||
|
||||
void get_internal(const int64_t index, void *r_value) const override
|
||||
{
|
||||
const StructT &struct_value = data_[index];
|
||||
const ElemT value = get_function_(struct_value);
|
||||
new (r_value) ElemT(value);
|
||||
}
|
||||
};
|
||||
|
||||
class ConstantReadAttribute final : public ReadAttribute {
|
||||
private:
|
||||
void *value_;
|
||||
|
||||
public:
|
||||
ConstantReadAttribute(AttributeDomain domain,
|
||||
const int64_t size,
|
||||
const CPPType &type,
|
||||
const void *value)
|
||||
: ReadAttribute(domain, type, size)
|
||||
{
|
||||
value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
|
||||
type.copy_to_uninitialized(value, value_);
|
||||
}
|
||||
|
||||
void get_internal(const int64_t UNUSED(index), void *r_value) const override
|
||||
{
|
||||
this->cpp_type_.copy_to_uninitialized(value_, r_value);
|
||||
}
|
||||
};
|
||||
|
||||
static ReadAttributePtr mesh_attribute_custom_data_read(const CustomData &custom_data,
|
||||
const int size,
|
||||
const StringRef attribute_name,
|
||||
const AttributeDomain domain)
|
||||
{
|
||||
for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) {
|
||||
if (layer.name != nullptr && layer.name == attribute_name) {
|
||||
switch (layer.type) {
|
||||
case CD_PROP_FLOAT:
|
||||
return std::make_unique<ArrayReadAttribute<float>>(
|
||||
domain, Span(static_cast<float *>(layer.data), size));
|
||||
case CD_PROP_FLOAT2:
|
||||
return std::make_unique<ArrayReadAttribute<float2>>(
|
||||
domain, Span(static_cast<float2 *>(layer.data), size));
|
||||
case CD_PROP_FLOAT3:
|
||||
return std::make_unique<ArrayReadAttribute<float3>>(
|
||||
domain, Span(static_cast<float3 *>(layer.data), size));
|
||||
case CD_PROP_INT32:
|
||||
return std::make_unique<ArrayReadAttribute<int>>(
|
||||
domain, Span(static_cast<int *>(layer.data), size));
|
||||
case CD_PROP_COLOR:
|
||||
return std::make_unique<ArrayReadAttribute<Color4f>>(
|
||||
domain, Span(static_cast<Color4f *>(layer.data), size));
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static WriteAttributePtr mesh_attribute_custom_data_write(CustomData custom_data,
|
||||
const int size,
|
||||
const StringRef attribute_name,
|
||||
const AttributeDomain domain)
|
||||
{
|
||||
for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) {
|
||||
if (layer.name != nullptr && layer.name == attribute_name) {
|
||||
switch (layer.type) {
|
||||
case CD_PROP_FLOAT:
|
||||
return std::make_unique<ArrayWriteAttribute<float>>(
|
||||
domain, MutableSpan(static_cast<float *>(layer.data), size));
|
||||
case CD_PROP_FLOAT2:
|
||||
return std::make_unique<ArrayWriteAttribute<float2>>(
|
||||
domain, MutableSpan(static_cast<float2 *>(layer.data), size));
|
||||
case CD_PROP_FLOAT3:
|
||||
return std::make_unique<ArrayWriteAttribute<float3>>(
|
||||
domain, MutableSpan(static_cast<float3 *>(layer.data), size));
|
||||
case CD_PROP_INT32:
|
||||
return std::make_unique<ArrayWriteAttribute<int>>(
|
||||
domain, MutableSpan(static_cast<int *>(layer.data), size));
|
||||
case CD_PROP_COLOR:
|
||||
return std::make_unique<ArrayWriteAttribute<Color4f>>(
|
||||
domain, MutableSpan(static_cast<Color4f *>(layer.data), size));
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ReadAttributePtr mesh_attribute_get_for_read(const MeshComponent &mesh_component,
|
||||
const StringRef attribute_name)
|
||||
{
|
||||
const Mesh *mesh = mesh_component.get_for_read();
|
||||
if (mesh == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
ReadAttributePtr corner_attribute = mesh_attribute_custom_data_read(
|
||||
mesh->ldata, mesh->totloop, attribute_name, ATTR_DOMAIN_CORNER);
|
||||
if (corner_attribute) {
|
||||
return corner_attribute;
|
||||
}
|
||||
|
||||
if (attribute_name == "Position") {
|
||||
auto get_vertex_position = [](const MVert &vert) { return float3(vert.co); };
|
||||
return std::make_unique<
|
||||
DerivedArrayReadAttribute<MVert, float3, decltype(get_vertex_position)>>(
|
||||
ATTR_DOMAIN_VERTEX, Span(mesh->mvert, mesh->totvert), get_vertex_position);
|
||||
}
|
||||
|
||||
const int vertex_group_index = mesh_component.vertex_group_index(attribute_name);
|
||||
if (vertex_group_index >= 0) {
|
||||
return std::make_unique<VertexWeightReadAttribute>(
|
||||
mesh->dvert, mesh->totvert, vertex_group_index);
|
||||
}
|
||||
|
||||
ReadAttributePtr vertex_attribute = mesh_attribute_custom_data_read(
|
||||
mesh->vdata, mesh->totvert, attribute_name, ATTR_DOMAIN_VERTEX);
|
||||
if (vertex_attribute) {
|
||||
return vertex_attribute;
|
||||
}
|
||||
|
||||
ReadAttributePtr edge_attribute = mesh_attribute_custom_data_read(
|
||||
mesh->edata, mesh->totedge, attribute_name, ATTR_DOMAIN_EDGE);
|
||||
if (edge_attribute) {
|
||||
return edge_attribute;
|
||||
}
|
||||
|
||||
ReadAttributePtr polygon_attribute = mesh_attribute_custom_data_read(
|
||||
mesh->pdata, mesh->totpoly, attribute_name, ATTR_DOMAIN_POLYGON);
|
||||
if (polygon_attribute) {
|
||||
return polygon_attribute;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<WriteAttributePtr> mesh_attribute_get_for_write(MeshComponent &mesh_component,
|
||||
const StringRef attribute_name)
|
||||
{
|
||||
Mesh *mesh = mesh_component.get_for_write();
|
||||
if (mesh == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
WriteAttributePtr corner_attribute = mesh_attribute_custom_data_write(
|
||||
mesh->ldata, mesh->totloop, attribute_name, ATTR_DOMAIN_CORNER);
|
||||
if (corner_attribute) {
|
||||
return corner_attribute;
|
||||
}
|
||||
|
||||
if (attribute_name == "Position") {
|
||||
auto get_vertex_position = [](const MVert &vert) { return float3(vert.co); };
|
||||
auto set_vertex_position = [](MVert &vert, const float3 &co) { copy_v3_v3(vert.co, co); };
|
||||
return std::make_unique<DerivedArrayWriteAttribute<MVert,
|
||||
float3,
|
||||
decltype(get_vertex_position),
|
||||
decltype(set_vertex_position)>>(
|
||||
ATTR_DOMAIN_VERTEX,
|
||||
MutableSpan(mesh->mvert, mesh->totvert),
|
||||
get_vertex_position,
|
||||
set_vertex_position);
|
||||
}
|
||||
|
||||
const int vertex_group_index = mesh_component.vertex_group_index(attribute_name);
|
||||
if (vertex_group_index >= 0) {
|
||||
return std::make_unique<VertexWeightWriteAttribute>(
|
||||
mesh->dvert, mesh->totvert, vertex_group_index);
|
||||
}
|
||||
|
||||
WriteAttributePtr vertex_attribute = mesh_attribute_custom_data_write(
|
||||
mesh->vdata, mesh->totvert, attribute_name, ATTR_DOMAIN_VERTEX);
|
||||
if (vertex_attribute) {
|
||||
return vertex_attribute;
|
||||
}
|
||||
|
||||
WriteAttributePtr edge_attribute = mesh_attribute_custom_data_write(
|
||||
mesh->edata, mesh->totedge, attribute_name, ATTR_DOMAIN_EDGE);
|
||||
if (edge_attribute) {
|
||||
return edge_attribute;
|
||||
}
|
||||
|
||||
WriteAttributePtr polygon_attribute = mesh_attribute_custom_data_write(
|
||||
mesh->pdata, mesh->totpoly, attribute_name, ATTR_DOMAIN_POLYGON);
|
||||
if (polygon_attribute) {
|
||||
return polygon_attribute;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_attribute_to_corner(const MeshComponent &UNUSED(mesh_component),
|
||||
ReadAttributePtr UNUSED(attribute))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_attribute_to_vertex(const MeshComponent &UNUSED(mesh_component),
|
||||
ReadAttributePtr UNUSED(attribute))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_attribute_to_edge(const MeshComponent &UNUSED(mesh_component),
|
||||
ReadAttributePtr UNUSED(attribute))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
static ReadAttributePtr adapt_mesh_attribute_to_polygon(
|
||||
const MeshComponent &UNUSED(mesh_component), ReadAttributePtr UNUSED(attribute))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
ReadAttributePtr mesh_attribute_adapt_domain(const MeshComponent &mesh_component,
|
||||
ReadAttributePtr attribute,
|
||||
const AttributeDomain to_domain)
|
||||
{
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
const AttributeDomain from_domain = attribute->domain();
|
||||
if (from_domain == to_domain) {
|
||||
return attribute;
|
||||
}
|
||||
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return adapt_mesh_attribute_to_corner(mesh_component, std::move(attribute));
|
||||
case ATTR_DOMAIN_VERTEX:
|
||||
return adapt_mesh_attribute_to_vertex(mesh_component, std::move(attribute));
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return adapt_mesh_attribute_to_edge(mesh_component, std::move(attribute));
|
||||
case ATTR_DOMAIN_POLYGON:
|
||||
return adapt_mesh_attribute_to_polygon(mesh_component, std::move(attribute));
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
static int get_domain_length(const MeshComponent &mesh_component, const AttributeDomain domain)
|
||||
{
|
||||
const Mesh *mesh = mesh_component.get_for_read();
|
||||
if (mesh == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return mesh->totloop;
|
||||
case ATTR_DOMAIN_VERTEX:
|
||||
return mesh->totvert;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return mesh->totedge;
|
||||
case ATTR_DOMAIN_POLYGON:
|
||||
return mesh->totpoly;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ReadAttributePtr make_default_attribute(const MeshComponent &mesh_component,
|
||||
const AttributeDomain domain,
|
||||
const CPPType &cpp_type,
|
||||
const void *default_value)
|
||||
{
|
||||
|
||||
const int length = get_domain_length(mesh_component, domain);
|
||||
return std::make_unique<ConstantReadAttribute>(domain, length, cpp_type, default_value);
|
||||
}
|
||||
|
||||
ReadAttributePtr mesh_attribute_get_for_read(const MeshComponent &mesh_component,
|
||||
const StringRef attribute_name,
|
||||
const CPPType &cpp_type,
|
||||
const AttributeDomain domain,
|
||||
const void *default_value)
|
||||
{
|
||||
ReadAttributePtr attribute = mesh_attribute_get_for_read(mesh_component, attribute_name);
|
||||
auto get_default_or_empty = [&]() -> ReadAttributePtr {
|
||||
if (default_value != nullptr) {
|
||||
return make_default_attribute(mesh_component, domain, cpp_type, default_value);
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
if (!attribute) {
|
||||
return get_default_or_empty();
|
||||
}
|
||||
if (attribute->domain() != domain) {
|
||||
attribute = mesh_attribute_adapt_domain(mesh_component, std::move(attribute), domain);
|
||||
}
|
||||
if (!attribute) {
|
||||
return get_default_or_empty();
|
||||
}
|
||||
if (attribute->cpp_type() != cpp_type) {
|
||||
/* TODO: Support some type conversions. */
|
||||
return get_default_or_empty();
|
||||
}
|
||||
return attribute;
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
467
source/blender/blenkernel/intern/geometry_set.cc
Normal file
467
source/blender/blenkernel/intern/geometry_set.cc
Normal file
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* 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_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()
|
||||
{
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
users_.fetch_add(1);
|
||||
}
|
||||
|
||||
void GeometryComponent::user_remove()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \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;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
int MeshComponent::vertex_group_index(StringRef vertex_group_name) const
|
||||
{
|
||||
return vertex_group_names_.lookup_default_as(vertex_group_name, -1);
|
||||
}
|
||||
|
||||
/* 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_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Pointcloud Component
|
||||
* \{ */
|
||||
|
||||
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_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Instances Component
|
||||
* \{ */
|
||||
|
||||
GeometryComponent *InstancesComponent::copy() const
|
||||
{
|
||||
InstancesComponent *new_component = new InstancesComponent();
|
||||
new_component->positions_ = positions_;
|
||||
new_component->objects_ = objects_;
|
||||
return new_component;
|
||||
}
|
||||
|
||||
void InstancesComponent::replace(Vector<float3> positions, Vector<const Object *> objects)
|
||||
{
|
||||
BLI_assert(positions.size() == objects.size());
|
||||
positions_ = std::move(positions);
|
||||
objects_ = std::move(objects);
|
||||
}
|
||||
|
||||
void InstancesComponent::replace(Vector<float3> positions, const Object *object)
|
||||
{
|
||||
positions_ = std::move(positions);
|
||||
objects_.clear();
|
||||
objects_.append_n_times(object, positions_.size());
|
||||
}
|
||||
|
||||
Span<const Object *> InstancesComponent::objects() const
|
||||
{
|
||||
return objects_;
|
||||
}
|
||||
|
||||
Span<float3> InstancesComponent::positions() const
|
||||
{
|
||||
return positions_;
|
||||
}
|
||||
|
||||
MutableSpan<float3> InstancesComponent::positions()
|
||||
{
|
||||
return positions_;
|
||||
}
|
||||
|
||||
int InstancesComponent::instances_amount() const
|
||||
{
|
||||
BLI_assert(positions_.size() == objects_.size());
|
||||
return objects_.size();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \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],
|
||||
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_objects = (Object **)component->objects().data();
|
||||
return component->instances_amount();
|
||||
}
|
||||
|
||||
/** \} */
|
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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 */
|
||||
@@ -281,6 +283,7 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
|
||||
case __SOCK_MESH:
|
||||
case SOCK_CUSTOM:
|
||||
case SOCK_SHADER:
|
||||
case SOCK_GEOMETRY:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -373,6 +376,7 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
|
||||
case __SOCK_MESH:
|
||||
case SOCK_CUSTOM:
|
||||
case SOCK_SHADER:
|
||||
case SOCK_GEOMETRY:
|
||||
BLI_assert(false);
|
||||
break;
|
||||
}
|
||||
@@ -714,6 +718,7 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
|
||||
case __SOCK_MESH:
|
||||
case SOCK_CUSTOM:
|
||||
case SOCK_SHADER:
|
||||
case SOCK_GEOMETRY:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -792,6 +797,7 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
|
||||
case __SOCK_MESH:
|
||||
case SOCK_CUSTOM:
|
||||
case SOCK_SHADER:
|
||||
case SOCK_GEOMETRY:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1346,6 +1352,7 @@ static void socket_id_user_increment(bNodeSocket *sock)
|
||||
case __SOCK_MESH:
|
||||
case SOCK_CUSTOM:
|
||||
case SOCK_SHADER:
|
||||
case SOCK_GEOMETRY:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1372,6 +1379,7 @@ static void socket_id_user_decrement(bNodeSocket *sock)
|
||||
case __SOCK_MESH:
|
||||
case SOCK_CUSTOM:
|
||||
case SOCK_SHADER:
|
||||
case SOCK_GEOMETRY:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1499,6 +1507,8 @@ const char *nodeStaticSocketType(int type, int subtype)
|
||||
return "NodeSocketObject";
|
||||
case SOCK_IMAGE:
|
||||
return "NodeSocketImage";
|
||||
case SOCK_GEOMETRY:
|
||||
return "NodeSocketGeometry";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -1564,6 +1574,8 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
|
||||
return "NodeSocketInterfaceObject";
|
||||
case SOCK_IMAGE:
|
||||
return "NodeSocketInterfaceImage";
|
||||
case SOCK_GEOMETRY:
|
||||
return "NodeSocketInterfaceGeometry";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -3946,14 +3958,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);
|
||||
}
|
||||
@@ -3967,6 +3983,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_Empty) {
|
||||
NodesModifierData *nmd = (NodesModifierData *)md;
|
||||
if (nmd->node_group == ngroup) {
|
||||
MOD_nodes_update_interface(object, nmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
|
||||
@@ -4010,7 +4039,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)) {
|
||||
@@ -4647,9 +4676,19 @@ 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();
|
||||
}
|
||||
|
||||
static void registerFunctionNodes(void)
|
||||
@@ -4676,7 +4715,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();
|
||||
@@ -4686,7 +4725,7 @@ void BKE_node_system_init(void)
|
||||
registerCompositNodes();
|
||||
registerShaderNodes();
|
||||
registerTextureNodes();
|
||||
registerSimulationNodes();
|
||||
registerGeometryNodes();
|
||||
registerFunctionNodes();
|
||||
}
|
||||
|
||||
|
@@ -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) {
|
||||
@@ -4872,6 +4884,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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,38 @@ static const DupliGenerator gen_dupli_verts_pointcloud = {
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Instances Geometry Component Implementation
|
||||
* \{ */
|
||||
|
||||
static void make_duplis_instances_component(const DupliContext *ctx)
|
||||
{
|
||||
float(*positions)[3];
|
||||
Object **objects;
|
||||
const int amount = BKE_geometry_set_instances(
|
||||
ctx->object->runtime.geometry_set_eval, &positions, &objects);
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Object *object = objects[i];
|
||||
if (object == NULL) {
|
||||
continue;
|
||||
}
|
||||
float mat[4][4];
|
||||
unit_m4(mat);
|
||||
copy_v3_v3(mat[3], positions[i]);
|
||||
mul_m4_m4_pre(mat, ctx->object->obmat);
|
||||
make_dupli(ctx, object, mat, i);
|
||||
make_recursive_duplis(ctx, object, mat, i);
|
||||
}
|
||||
}
|
||||
|
||||
static const DupliGenerator gen_dupli_instances_component = {
|
||||
0,
|
||||
make_duplis_instances_component,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Dupli-Faces Implementation (#OB_DUPLIFACES)
|
||||
* \{ */
|
||||
@@ -1473,7 +1506,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 +1516,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;
|
||||
}
|
||||
|
@@ -1198,9 +1198,6 @@ static bool foreach_object_modifier_ptcache(Object *object,
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (md->type == eModifierType_Simulation) {
|
||||
/* TODO(jacques): */
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@
|
||||
|
||||
#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"
|
||||
@@ -86,6 +87,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,6 +233,29 @@ void *BKE_pointcloud_add_default(Main *bmain, const char *name)
|
||||
return pointcloud;
|
||||
}
|
||||
|
||||
PointCloud *BKE_pointcloud_new_nomain(const int totpoint)
|
||||
{
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(BKE_libblock_alloc(
|
||||
nullptr, ID_PT, BKE_idtype_idcode_to_name(ID_PT), LIB_ID_CREATE_LOCALIZE));
|
||||
|
||||
pointcloud_init_data(&pointcloud->id);
|
||||
|
||||
pointcloud->totpoint = totpoint;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BoundBox *BKE_pointcloud_boundbox_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_POINTCLOUD);
|
||||
@@ -306,13 +332,11 @@ PointCloud *BKE_pointcloud_copy_for_eval(struct PointCloud *pointcloud_src, bool
|
||||
return result;
|
||||
}
|
||||
|
||||
static PointCloud *pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
|
||||
struct Scene *scene,
|
||||
Object *object,
|
||||
PointCloud *pointcloud_input)
|
||||
static void pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
|
||||
struct Scene *scene,
|
||||
Object *object,
|
||||
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 +356,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 +369,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 */
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -80,6 +80,13 @@ struct float2 {
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
uint64_t x1 = *reinterpret_cast<const uint32_t *>(&x);
|
||||
uint64_t x2 = *reinterpret_cast<const uint32_t *>(&y);
|
||||
return (x1 * 812519) ^ (x2 * 707951);
|
||||
}
|
||||
|
||||
friend float2 operator+(const float2 &a, const float2 &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y};
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
|
||||
/**
|
||||
|
158
source/blender/blenlib/BLI_user_counter.hh
Normal file
158
source/blender/blenlib/BLI_user_counter.hh
Normal 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. This should probably be moved to another file eventually.
|
||||
* 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
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -89,6 +89,7 @@
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "BKE_workspace.h"
|
||||
|
||||
#include "DRW_engine.h"
|
||||
|
@@ -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,
|
||||
|
@@ -145,6 +145,16 @@ typedef struct DEGObjectIterData {
|
||||
|
||||
eEvaluationMode eval_mode;
|
||||
|
||||
/* **** Iteration over geometry components **** */
|
||||
|
||||
/* The object whose components we currently iterate over.
|
||||
* This might point to #temp_dupli_object. */
|
||||
struct Object *geometry_component_owner;
|
||||
/* Some identifier that is used to determine which geometry component should be returned next. */
|
||||
int geometry_component_id;
|
||||
/* Temporary storage for an object that is created from a component. */
|
||||
struct Object temp_geometry_component_object;
|
||||
|
||||
/* **** Iteration over dupli-list. *** */
|
||||
|
||||
/* Object which created the dupli-list. */
|
||||
|
@@ -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,
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BKE_duplilist.h"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_idprop.h"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_node.h"
|
||||
@@ -121,9 +122,82 @@ bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, DupliObject
|
||||
return false;
|
||||
}
|
||||
|
||||
bool deg_objects_dupli_iterator_next(BLI_Iterator *iter)
|
||||
void deg_iterator_components_init(DEGObjectIterData *data, Object *object)
|
||||
{
|
||||
data->geometry_component_owner = object;
|
||||
data->geometry_component_id = 0;
|
||||
}
|
||||
|
||||
/* Returns false when iterator is exhausted. */
|
||||
bool deg_iterator_components_step(BLI_Iterator *iter)
|
||||
{
|
||||
DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
|
||||
if (data->geometry_component_owner == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data->geometry_component_owner->type != OB_POINTCLOUD) {
|
||||
/* Only point clouds support multiple geometry components currently. */
|
||||
iter->current = data->geometry_component_owner;
|
||||
data->geometry_component_owner = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
GeometrySet *geometry_set = data->geometry_component_owner->runtime.geometry_set_eval;
|
||||
if (geometry_set == nullptr) {
|
||||
data->geometry_component_owner = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data->geometry_component_id == 0) {
|
||||
data->geometry_component_id++;
|
||||
|
||||
/* The mesh component. */
|
||||
const Mesh *mesh = geometry_set->get_mesh_for_read();
|
||||
if (mesh != nullptr) {
|
||||
Object *temp_object = &data->temp_geometry_component_object;
|
||||
*temp_object = *data->geometry_component_owner;
|
||||
temp_object->type = OB_MESH;
|
||||
temp_object->data = (void *)mesh;
|
||||
iter->current = temp_object;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (data->geometry_component_id == 1) {
|
||||
data->geometry_component_id++;
|
||||
|
||||
/* The pointcloud component. */
|
||||
const PointCloud *pointcloud = geometry_set->get_pointcloud_for_read();
|
||||
if (pointcloud != nullptr) {
|
||||
Object *temp_object = &data->temp_geometry_component_object;
|
||||
*temp_object = *data->geometry_component_owner;
|
||||
temp_object->type = OB_POINTCLOUD;
|
||||
temp_object->data = (void *)pointcloud;
|
||||
iter->current = temp_object;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
data->geometry_component_owner = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
void deg_iterator_duplis_init(DEGObjectIterData *data, Object *object)
|
||||
{
|
||||
if ((data->flag & DEG_ITER_OBJECT_FLAG_DUPLI) &&
|
||||
((object->transflag & OB_DUPLI) || object->runtime.geometry_set_eval != nullptr)) {
|
||||
data->dupli_parent = object;
|
||||
data->dupli_list = object_duplilist(data->graph, data->scene, object);
|
||||
data->dupli_object_next = (DupliObject *)data->dupli_list->first;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns false when iterator is exhausted. */
|
||||
bool deg_iterator_duplis_step(DEGObjectIterData *data)
|
||||
{
|
||||
if (data->dupli_list == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (data->dupli_object_next != nullptr) {
|
||||
DupliObject *dob = data->dupli_object_next;
|
||||
Object *obd = dob->ob;
|
||||
@@ -170,72 +244,80 @@ bool deg_objects_dupli_iterator_next(BLI_Iterator *iter)
|
||||
|
||||
copy_m4_m4(data->temp_dupli_object.obmat, dob->mat);
|
||||
invert_m4_m4(data->temp_dupli_object.imat, data->temp_dupli_object.obmat);
|
||||
iter->current = &data->temp_dupli_object;
|
||||
deg_iterator_components_init(data, &data->temp_dupli_object);
|
||||
BLI_assert(deg::deg_validate_copy_on_write_datablock(&data->temp_dupli_object.id));
|
||||
return true;
|
||||
}
|
||||
|
||||
verify_id_properties_freed(data);
|
||||
free_object_duplilist(data->dupli_list);
|
||||
data->dupli_parent = nullptr;
|
||||
data->dupli_list = nullptr;
|
||||
data->dupli_object_next = nullptr;
|
||||
data->dupli_object_current = nullptr;
|
||||
deg_invalidate_iterator_work_data(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
void deg_iterator_objects_step(BLI_Iterator *iter, deg::IDNode *id_node)
|
||||
/* Returns false when iterator is exhausted. */
|
||||
bool deg_iterator_objects_step(DEGObjectIterData *data)
|
||||
{
|
||||
/* Set it early in case we need to exit and we are running from within a loop. */
|
||||
iter->skip = true;
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(data->graph);
|
||||
|
||||
if (!id_node->is_directly_visible) {
|
||||
return;
|
||||
}
|
||||
for (; data->id_node_index < data->num_id_nodes; data->id_node_index++) {
|
||||
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
|
||||
|
||||
DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
|
||||
const ID_Type id_type = GS(id_node->id_orig->name);
|
||||
|
||||
if (id_type != ID_OB) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (id_node->linked_state) {
|
||||
case deg::DEG_ID_LINKED_DIRECTLY:
|
||||
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY) == 0) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case deg::DEG_ID_LINKED_VIA_SET:
|
||||
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) == 0) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case deg::DEG_ID_LINKED_INDIRECTLY:
|
||||
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_INDIRECTLY) == 0) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Object *object = (Object *)id_node->id_cow;
|
||||
BLI_assert(deg::deg_validate_copy_on_write_datablock(&object->id));
|
||||
|
||||
int ob_visibility = OB_VISIBLE_ALL;
|
||||
if (data->flag & DEG_ITER_OBJECT_FLAG_VISIBLE) {
|
||||
ob_visibility = BKE_object_visibility(object, data->eval_mode);
|
||||
|
||||
if (object->type != OB_MBALL && deg_object_hide_original(data->eval_mode, object, nullptr)) {
|
||||
return;
|
||||
if (!id_node->is_directly_visible) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (ob_visibility & OB_VISIBLE_INSTANCES) {
|
||||
if ((data->flag & DEG_ITER_OBJECT_FLAG_DUPLI) && (object->transflag & OB_DUPLI)) {
|
||||
data->dupli_parent = object;
|
||||
data->dupli_list = object_duplilist(data->graph, data->scene, object);
|
||||
data->dupli_object_next = (DupliObject *)data->dupli_list->first;
|
||||
const ID_Type id_type = GS(id_node->id_orig->name);
|
||||
|
||||
if (id_type != ID_OB) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) {
|
||||
iter->current = object;
|
||||
iter->skip = false;
|
||||
switch (id_node->linked_state) {
|
||||
case deg::DEG_ID_LINKED_DIRECTLY:
|
||||
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY) == 0) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case deg::DEG_ID_LINKED_VIA_SET:
|
||||
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) == 0) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case deg::DEG_ID_LINKED_INDIRECTLY:
|
||||
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_INDIRECTLY) == 0) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Object *object = (Object *)id_node->id_cow;
|
||||
BLI_assert(deg::deg_validate_copy_on_write_datablock(&object->id));
|
||||
|
||||
int ob_visibility = OB_VISIBLE_ALL;
|
||||
if (data->flag & DEG_ITER_OBJECT_FLAG_VISIBLE) {
|
||||
ob_visibility = BKE_object_visibility(object, data->eval_mode);
|
||||
|
||||
if (object->type != OB_MBALL && deg_object_hide_original(data->eval_mode, object, nullptr)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (ob_visibility & OB_VISIBLE_INSTANCES) {
|
||||
deg_iterator_duplis_init(data, object);
|
||||
}
|
||||
|
||||
if (ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) {
|
||||
deg_iterator_components_init(data, object);
|
||||
}
|
||||
data->id_node_index++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -261,46 +343,29 @@ void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data)
|
||||
data->id_node_index = 0;
|
||||
data->num_id_nodes = num_id_nodes;
|
||||
data->eval_mode = DEG_get_mode(depsgraph);
|
||||
data->geometry_component_id = 0;
|
||||
data->geometry_component_owner = nullptr;
|
||||
deg_invalidate_iterator_work_data(data);
|
||||
|
||||
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
|
||||
deg_iterator_objects_step(iter, id_node);
|
||||
|
||||
if (iter->skip) {
|
||||
DEG_iterator_objects_next(iter);
|
||||
}
|
||||
DEG_iterator_objects_next(iter);
|
||||
}
|
||||
|
||||
void DEG_iterator_objects_next(BLI_Iterator *iter)
|
||||
{
|
||||
DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
|
||||
Depsgraph *depsgraph = data->graph;
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
||||
do {
|
||||
iter->skip = false;
|
||||
if (data->dupli_list) {
|
||||
if (deg_objects_dupli_iterator_next(iter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
verify_id_properties_freed(data);
|
||||
free_object_duplilist(data->dupli_list);
|
||||
data->dupli_parent = nullptr;
|
||||
data->dupli_list = nullptr;
|
||||
data->dupli_object_next = nullptr;
|
||||
data->dupli_object_current = nullptr;
|
||||
deg_invalidate_iterator_work_data(data);
|
||||
}
|
||||
|
||||
++data->id_node_index;
|
||||
if (data->id_node_index == data->num_id_nodes) {
|
||||
iter->valid = false;
|
||||
while (true) {
|
||||
if (deg_iterator_components_step(iter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
|
||||
deg_iterator_objects_step(iter, id_node);
|
||||
} while (iter->skip);
|
||||
if (deg_iterator_duplis_step(data)) {
|
||||
continue;
|
||||
}
|
||||
if (deg_iterator_objects_step(data)) {
|
||||
continue;
|
||||
}
|
||||
iter->valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DEG_iterator_objects_end(BLI_Iterator *iter)
|
||||
|
@@ -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);
|
||||
|
@@ -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_Empty) {
|
||||
MOD_nodes_init(bmain, (NodesModifierData *)new_md);
|
||||
}
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
|
@@ -72,5 +72,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}")
|
||||
|
@@ -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 */
|
||||
|
||||
@@ -3139,10 +3139,54 @@ 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);
|
||||
uiItemR(layout, ptr, "domain", DEFAULT_FLAGS, "", 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* ****************** BUTTON CALLBACKS FOR FUNCTION NODES ***************** */
|
||||
@@ -3287,7 +3331,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 */
|
||||
@@ -3299,7 +3343,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)
|
||||
@@ -3328,8 +3372,9 @@ static const float std_node_socket_colors[][4] = {
|
||||
{0.0, 0.0, 0.0, 1.0}, /*__SOCK_MESH (deprecated) */
|
||||
{0.06, 0.52, 0.15, 1.0}, /* SOCK_INT */
|
||||
{0.39, 0.39, 0.39, 1.0}, /* SOCK_STRING */
|
||||
{0.40, 0.10, 0.10, 1.0}, /* SOCK_OBJECT */
|
||||
{0.85, 0.34, 0.11, 1.0}, /* SOCK_OBJECT */
|
||||
{0.10, 0.40, 0.10, 1.0}, /* SOCK_IMAGE */
|
||||
{0.00, 0.83, 0.64, 1.0}, /* SOCK_GEOMETRY */
|
||||
};
|
||||
|
||||
/* common color callbacks for standard types */
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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 "";
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -936,7 +936,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);
|
||||
|
@@ -38,6 +38,8 @@ set(SRC
|
||||
FN_array_spans.hh
|
||||
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
|
||||
|
@@ -91,6 +91,14 @@ class CPPType : NonCopyable, NonMovable {
|
||||
using CopyToUninitializedNF = void (*)(const void *src, void *dst, int64_t n);
|
||||
using CopyToUninitializedIndicesF = void (*)(const void *src, void *dst, IndexMask mask);
|
||||
|
||||
using MoveToInitializedF = void (*)(void *src, void *dst);
|
||||
using MoveToInitializedNF = void (*)(void *src, void *dst, int64_t n);
|
||||
using MoveToInitializedIndicesF = void (*)(void *src, void *dst, IndexMask mask);
|
||||
|
||||
using MoveToUninitializedF = void (*)(void *src, void *dst);
|
||||
using MoveToUninitializedNF = void (*)(void *src, void *dst, int64_t n);
|
||||
using MoveToUninitializedIndicesF = void (*)(void *src, void *dst, IndexMask mask);
|
||||
|
||||
using RelocateToInitializedF = void (*)(void *src, void *dst);
|
||||
using RelocateToInitializedNF = void (*)(void *src, void *dst, int64_t n);
|
||||
using RelocateToInitializedIndicesF = void (*)(void *src, void *dst, IndexMask mask);
|
||||
@@ -131,6 +139,14 @@ class CPPType : NonCopyable, NonMovable {
|
||||
CopyToUninitializedNF copy_to_uninitialized_n_;
|
||||
CopyToUninitializedIndicesF copy_to_uninitialized_indices_;
|
||||
|
||||
MoveToInitializedF move_to_initialized_;
|
||||
MoveToInitializedNF move_to_initialized_n_;
|
||||
MoveToInitializedIndicesF move_to_initialized_indices_;
|
||||
|
||||
MoveToUninitializedF move_to_uninitialized_;
|
||||
MoveToUninitializedNF move_to_uninitialized_n_;
|
||||
MoveToUninitializedIndicesF move_to_uninitialized_indices_;
|
||||
|
||||
RelocateToInitializedF relocate_to_initialized_;
|
||||
RelocateToInitializedNF relocate_to_initialized_n_;
|
||||
RelocateToInitializedIndicesF relocate_to_initialized_indices_;
|
||||
@@ -169,6 +185,12 @@ class CPPType : NonCopyable, NonMovable {
|
||||
CopyToUninitializedF copy_to_uninitialized,
|
||||
CopyToUninitializedNF copy_to_uninitialized_n,
|
||||
CopyToUninitializedIndicesF copy_to_uninitialized_indices,
|
||||
MoveToInitializedF move_to_initialized,
|
||||
MoveToInitializedNF move_to_initialized_n,
|
||||
MoveToInitializedIndicesF move_to_initialized_indices,
|
||||
MoveToUninitializedF move_to_uninitialized,
|
||||
MoveToUninitializedNF move_to_uninitialized_n,
|
||||
MoveToUninitializedIndicesF move_to_uninitialized_indices,
|
||||
RelocateToInitializedF relocate_to_initialized,
|
||||
RelocateToInitializedNF relocate_to_initialized_n,
|
||||
RelocateToInitializedIndicesF relocate_to_initialized_indices,
|
||||
@@ -198,6 +220,12 @@ class CPPType : NonCopyable, NonMovable {
|
||||
copy_to_uninitialized_(copy_to_uninitialized),
|
||||
copy_to_uninitialized_n_(copy_to_uninitialized_n),
|
||||
copy_to_uninitialized_indices_(copy_to_uninitialized_indices),
|
||||
move_to_initialized_(move_to_initialized),
|
||||
move_to_initialized_n_(move_to_initialized_n),
|
||||
move_to_initialized_indices_(move_to_initialized_indices),
|
||||
move_to_uninitialized_(move_to_uninitialized),
|
||||
move_to_uninitialized_n_(move_to_uninitialized_n),
|
||||
move_to_uninitialized_indices_(move_to_uninitialized_indices),
|
||||
relocate_to_initialized_(relocate_to_initialized),
|
||||
relocate_to_initialized_n_(relocate_to_initialized_n),
|
||||
relocate_to_initialized_indices_(relocate_to_initialized_indices),
|
||||
@@ -421,6 +449,76 @@ class CPPType : NonCopyable, NonMovable {
|
||||
copy_to_uninitialized_indices_(src, dst, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move an instance of this type from src to dst.
|
||||
*
|
||||
* The memory pointed to by dst should be initialized.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* dst = std::move(src);
|
||||
*/
|
||||
void move_to_initialized(void *src, void *dst) const
|
||||
{
|
||||
BLI_assert(src != dst);
|
||||
BLI_assert(this->pointer_can_point_to_instance(src));
|
||||
BLI_assert(this->pointer_can_point_to_instance(dst));
|
||||
|
||||
move_to_initialized_(src, dst);
|
||||
}
|
||||
|
||||
void move_to_initialized_n(void *src, void *dst, int64_t n) const
|
||||
{
|
||||
BLI_assert(n == 0 || src != dst);
|
||||
BLI_assert(n == 0 || this->pointer_can_point_to_instance(src));
|
||||
BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
|
||||
|
||||
move_to_initialized_n_(src, dst, n);
|
||||
}
|
||||
|
||||
void move_to_initialized_indices(void *src, void *dst, IndexMask mask) const
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || src != dst);
|
||||
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
|
||||
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
|
||||
|
||||
move_to_initialized_indices_(src, dst, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move an instance of this type from src to dst.
|
||||
*
|
||||
* The memory pointed to by dst should be uninitialized.
|
||||
*
|
||||
* C++ equivalent:
|
||||
* new (dst) T(std::move(src));
|
||||
*/
|
||||
void move_to_uninitialized(void *src, void *dst) const
|
||||
{
|
||||
BLI_assert(src != dst);
|
||||
BLI_assert(this->pointer_can_point_to_instance(src));
|
||||
BLI_assert(this->pointer_can_point_to_instance(dst));
|
||||
|
||||
move_to_uninitialized_(src, dst);
|
||||
}
|
||||
|
||||
void move_to_uninitialized_n(void *src, void *dst, int64_t n) const
|
||||
{
|
||||
BLI_assert(n == 0 || src != dst);
|
||||
BLI_assert(n == 0 || this->pointer_can_point_to_instance(src));
|
||||
BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
|
||||
|
||||
move_to_uninitialized_n_(src, dst, n);
|
||||
}
|
||||
|
||||
void move_to_uninitialized_indices(void *src, void *dst, IndexMask mask) const
|
||||
{
|
||||
BLI_assert(mask.size() == 0 || src != dst);
|
||||
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
|
||||
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
|
||||
|
||||
move_to_uninitialized_indices_(src, dst, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Relocates an instance of this type from src to dst. src will point to uninitialized memory
|
||||
* afterwards.
|
||||
@@ -644,6 +742,38 @@ void copy_to_uninitialized_indices_cb(const void *src, void *dst, IndexMask mask
|
||||
mask.foreach_index([&](int64_t i) { new (dst_ + i) T(src_[i]); });
|
||||
}
|
||||
|
||||
template<typename T> void move_to_initialized_cb(void *src, void *dst)
|
||||
{
|
||||
blender::initialized_move_n(static_cast<T *>(src), 1, static_cast<T *>(dst));
|
||||
}
|
||||
template<typename T> void move_to_initialized_n_cb(void *src, void *dst, int64_t n)
|
||||
{
|
||||
blender::initialized_move_n(static_cast<T *>(src), n, static_cast<T *>(dst));
|
||||
}
|
||||
template<typename T> void move_to_initialized_indices_cb(void *src, void *dst, IndexMask mask)
|
||||
{
|
||||
T *src_ = static_cast<T *>(src);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
|
||||
mask.foreach_index([&](int64_t i) { dst_[i] = std::move(src_[i]); });
|
||||
}
|
||||
|
||||
template<typename T> void move_to_uninitialized_cb(void *src, void *dst)
|
||||
{
|
||||
blender::uninitialized_move_n(static_cast<T *>(src), 1, static_cast<T *>(dst));
|
||||
}
|
||||
template<typename T> void move_to_uninitialized_n_cb(void *src, void *dst, int64_t n)
|
||||
{
|
||||
blender::uninitialized_move_n(static_cast<T *>(src), n, static_cast<T *>(dst));
|
||||
}
|
||||
template<typename T> void move_to_uninitialized_indices_cb(void *src, void *dst, IndexMask mask)
|
||||
{
|
||||
T *src_ = static_cast<T *>(src);
|
||||
T *dst_ = static_cast<T *>(dst);
|
||||
|
||||
mask.foreach_index([&](int64_t i) { new (dst_ + i) T(std::move(src_[i])); });
|
||||
}
|
||||
|
||||
template<typename T> void relocate_to_initialized_cb(void *src, void *dst)
|
||||
{
|
||||
T *src_ = static_cast<T *>(src);
|
||||
@@ -767,6 +897,12 @@ inline std::unique_ptr<const CPPType> create_cpp_type(StringRef name, const T &d
|
||||
copy_to_uninitialized_cb<T>,
|
||||
copy_to_uninitialized_n_cb<T>,
|
||||
copy_to_uninitialized_indices_cb<T>,
|
||||
move_to_initialized_cb<T>,
|
||||
move_to_initialized_n_cb<T>,
|
||||
move_to_initialized_indices_cb<T>,
|
||||
move_to_uninitialized_cb<T>,
|
||||
move_to_uninitialized_n_cb<T>,
|
||||
move_to_uninitialized_indices_cb<T>,
|
||||
relocate_to_initialized_cb<T>,
|
||||
relocate_to_initialized_n_cb<T>,
|
||||
relocate_to_initialized_indices_cb<T>,
|
||||
|
76
source/blender/functions/FN_generic_pointer.hh
Normal file
76
source/blender/functions/FN_generic_pointer.hh
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FN_cpp_type.hh"
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
/**
|
||||
* A generic pointer whose type is only known at runtime.
|
||||
*/
|
||||
class GMutablePointer {
|
||||
private:
|
||||
const CPPType *type_ = nullptr;
|
||||
void *data_ = nullptr;
|
||||
|
||||
public:
|
||||
GMutablePointer() = default;
|
||||
|
||||
GMutablePointer(const CPPType *type, void *data = nullptr) : type_(type), data_(data)
|
||||
{
|
||||
/* If there is data, there has to be a type. */
|
||||
BLI_assert(data_ == nullptr || type_ != nullptr);
|
||||
}
|
||||
|
||||
GMutablePointer(const CPPType &type, void *data = nullptr) : GMutablePointer(&type, data)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T> GMutablePointer(T *data) : GMutablePointer(&CPPType::get<T>(), data)
|
||||
{
|
||||
}
|
||||
|
||||
void *get() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const CPPType *type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
template<typename T> T *get() const
|
||||
{
|
||||
BLI_assert(this->is_type<T>());
|
||||
return reinterpret_cast<T *>(data_);
|
||||
}
|
||||
|
||||
template<typename T> bool is_type() const
|
||||
{
|
||||
return type_ != nullptr && type_->is<T>();
|
||||
}
|
||||
|
||||
void destruct()
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
type_->destruct(data_);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::fn
|
113
source/blender/functions/FN_generic_value_map.hh
Normal file
113
source/blender/functions/FN_generic_value_map.hh
Normal 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.
|
||||
*/
|
||||
|
||||
#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 ForwardKey> bool contains(const ForwardKey &key) const
|
||||
{
|
||||
return values_.contains_as(key);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::fn
|
@@ -26,6 +26,7 @@ namespace blender::fn {
|
||||
MAKE_CPP_TYPE(bool, bool)
|
||||
|
||||
MAKE_CPP_TYPE(float, float)
|
||||
MAKE_CPP_TYPE(float2, blender::float2)
|
||||
MAKE_CPP_TYPE(float3, blender::float3)
|
||||
MAKE_CPP_TYPE(float4x4, blender::float4x4)
|
||||
|
||||
|
@@ -573,7 +573,7 @@
|
||||
.flag = 0, \
|
||||
}
|
||||
|
||||
#define _DNA_DEFAULT_SimulationModifierData \
|
||||
#define _DNA_DEFAULT_NodesModifierData \
|
||||
{ 0 }
|
||||
|
||||
#define _DNA_DEFAULT_SkinModifierData \
|
||||
|
@@ -94,7 +94,7 @@ typedef enum ModifierType {
|
||||
eModifierType_WeightedNormal = 54,
|
||||
eModifierType_Weld = 55,
|
||||
eModifierType_Fluid = 56,
|
||||
eModifierType_Simulation = 57,
|
||||
eModifierType_Empty = 57,
|
||||
eModifierType_MeshToVolume = 58,
|
||||
eModifierType_VolumeDisplace = 59,
|
||||
eModifierType_VolumeToMesh = 60,
|
||||
@@ -2220,9 +2220,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;
|
||||
|
@@ -155,6 +155,7 @@ typedef enum eNodeSocketDatatype {
|
||||
SOCK_STRING = 7,
|
||||
SOCK_OBJECT = 8,
|
||||
SOCK_IMAGE = 9,
|
||||
SOCK_GEOMETRY = 10,
|
||||
} eNodeSocketDatatype;
|
||||
|
||||
/* socket shape */
|
||||
@@ -499,7 +500,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
|
||||
@@ -1432,14 +1433,24 @@ 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;
|
||||
|
@@ -51,6 +51,7 @@ struct RigidBodyOb;
|
||||
struct SculptSession;
|
||||
struct SoftBody;
|
||||
struct bGPdata;
|
||||
struct GeometrySet;
|
||||
|
||||
/* Vertex Groups - Name Info */
|
||||
typedef struct bDeformGroup {
|
||||
@@ -150,6 +151,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.
|
||||
|
@@ -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. */
|
||||
|
@@ -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;
|
||||
|
@@ -373,6 +373,7 @@ blender_include_dirs(
|
||||
../../ikplugin
|
||||
../../imbuf
|
||||
../../makesdna
|
||||
../../modifiers
|
||||
../../nodes/
|
||||
../../sequencer
|
||||
../../simulation
|
||||
|
@@ -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) {
|
||||
|
@@ -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_Empty, "EMPTY", ICON_MESH_DATA, "Empty", ""}, /* TODO: Use correct icon. */
|
||||
{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)
|
||||
@@ -6902,18 +6931,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
|
||||
|
||||
@@ -7272,7 +7326,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);
|
||||
|
@@ -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"
|
||||
@@ -84,6 +84,7 @@ static const EnumPropertyItem node_socket_type_items[] = {
|
||||
{SOCK_SHADER, "SHADER", 0, "Shader", ""},
|
||||
{SOCK_OBJECT, "OBJECT", 0, "Object", ""},
|
||||
{SOCK_IMAGE, "IMAGE", 0, "Image", ""},
|
||||
{SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
@@ -96,6 +97,7 @@ static const EnumPropertyItem node_socket_data_type_items[] = {
|
||||
{SOCK_RGBA, "RGBA", 0, "Color", ""},
|
||||
{SOCK_OBJECT, "OBJECT", 0, "Object", ""},
|
||||
{SOCK_IMAGE, "IMAGE", 0, "Image", ""},
|
||||
{SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
@@ -367,6 +369,60 @@ 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},
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef RNA_RUNTIME
|
||||
@@ -725,9 +781,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; \
|
||||
@@ -1868,16 +1924,16 @@ static StructRNA *rna_TextureNode_register(Main *bmain,
|
||||
return nt->rna_ext.srna;
|
||||
}
|
||||
|
||||
static StructRNA *rna_SimulationNode_register(Main *bmain,
|
||||
ReportList *reports,
|
||||
void *data,
|
||||
const char *identifier,
|
||||
StructValidateFunc validate,
|
||||
StructCallbackFunc call,
|
||||
StructFreeFunc free)
|
||||
static StructRNA *rna_GeometryNode_register(Main *bmain,
|
||||
ReportList *reports,
|
||||
void *data,
|
||||
const char *identifier,
|
||||
StructValidateFunc validate,
|
||||
StructCallbackFunc call,
|
||||
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;
|
||||
}
|
||||
@@ -2840,6 +2896,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 ******** */
|
||||
@@ -8168,6 +8225,64 @@ 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");
|
||||
}
|
||||
|
||||
static void def_geo_attribute_create_common(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "custom1");
|
||||
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
|
||||
RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
|
||||
RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
|
||||
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "custom2");
|
||||
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
|
||||
RNA_def_property_enum_default(prop, ATTR_DOMAIN_VERTEX);
|
||||
RNA_def_property_ui_text(prop, "Domain", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
|
||||
}
|
||||
|
||||
static void def_geo_random_attribute(StructRNA *srna)
|
||||
{
|
||||
def_geo_attribute_create_common(srna);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static void rna_def_shader_node(BlenderRNA *brna)
|
||||
@@ -8205,14 +8320,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)
|
||||
@@ -8859,6 +8974,21 @@ static void rna_def_node_socket_image(BlenderRNA *brna,
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
|
||||
}
|
||||
|
||||
static void rna_def_node_socket_geometry(BlenderRNA *brna,
|
||||
const char *identifier,
|
||||
const char *interface_idname)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
||||
srna = RNA_def_struct(brna, identifier, "NodeSocketStandard");
|
||||
RNA_def_struct_ui_text(srna, "Geometry Node Socket", "Geometry socket of a node");
|
||||
RNA_def_struct_sdna(srna, "bNodeSocket");
|
||||
|
||||
srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
|
||||
RNA_def_struct_ui_text(srna, "Geometry Node Socket Interface", "Geometry socket of a node");
|
||||
RNA_def_struct_sdna(srna, "bNodeSocket");
|
||||
}
|
||||
|
||||
static void rna_def_node_socket_standard_types(BlenderRNA *brna)
|
||||
{
|
||||
/* XXX Workaround: Registered functions are not exposed in python by bpy,
|
||||
@@ -8997,6 +9127,8 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
|
||||
rna_def_node_socket_object(brna, "NodeSocketObject", "NodeSocketInterfaceObject");
|
||||
|
||||
rna_def_node_socket_image(brna, "NodeSocketImage", "NodeSocketInterfaceImage");
|
||||
|
||||
rna_def_node_socket_geometry(brna, "NodeSocketGeometry", "NodeSocketInterfaceGeometry");
|
||||
}
|
||||
|
||||
static void rna_def_internal_node(BlenderRNA *brna)
|
||||
@@ -9634,7 +9766,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},
|
||||
};
|
||||
|
||||
@@ -9858,15 +9990,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,
|
||||
@@ -9955,7 +10087,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);
|
||||
@@ -9965,7 +10097,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) \
|
||||
{ \
|
||||
@@ -9982,13 +10114,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",
|
||||
|
@@ -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;
|
||||
@@ -6346,19 +6311,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");
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
}
|
@@ -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;
|
||||
|
1029
source/blender/modifiers/intern/MOD_nodes.cc
Normal file
1029
source/blender/modifiers/intern/MOD_nodes.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
};
|
@@ -48,11 +48,17 @@
|
||||
#include "MOD_modifiertypes.h"
|
||||
#include "MOD_ui_common.h"
|
||||
|
||||
static 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,
|
||||
const int flag);
|
||||
|
||||
Mesh *triangulate_mesh(Mesh *mesh,
|
||||
const int quad_method,
|
||||
const int ngon_method,
|
||||
const int min_vertices,
|
||||
const int flag)
|
||||
{
|
||||
Mesh *result;
|
||||
BMesh *bm;
|
||||
|
@@ -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);
|
||||
#undef INIT_TYPE
|
||||
types[eModifierType_Empty] = &modifierType_Nodes;
|
||||
}
|
||||
|
@@ -24,11 +24,12 @@ set(INC
|
||||
function
|
||||
intern
|
||||
shader
|
||||
simulation
|
||||
geometry
|
||||
texture
|
||||
../blenkernel
|
||||
../blenlib
|
||||
../blentranslation
|
||||
../bmesh
|
||||
../depsgraph
|
||||
../functions
|
||||
../gpu
|
||||
@@ -137,6 +138,20 @@ set(SRC
|
||||
function/nodes/node_fn_switch.cc
|
||||
function/node_function_util.cc
|
||||
|
||||
geometry/nodes/node_geo_common.cc
|
||||
geometry/nodes/node_geo_boolean.cc
|
||||
geometry/nodes/node_geo_edge_split.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 +245,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
|
||||
@@ -263,16 +274,18 @@ set(SRC
|
||||
intern/derived_node_tree.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 +296,11 @@ set(SRC
|
||||
NOD_node_tree_multi_function.hh
|
||||
NOD_node_tree_ref.hh
|
||||
NOD_shader.h
|
||||
NOD_simulation.h
|
||||
NOD_geometry.h
|
||||
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 +309,7 @@ set(SRC
|
||||
set(LIB
|
||||
bf_functions
|
||||
bf_intern_sky
|
||||
bf_bmesh
|
||||
)
|
||||
|
||||
if(WITH_PYTHON)
|
||||
@@ -326,9 +341,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}")
|
||||
|
@@ -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:
|
||||
@@ -288,6 +302,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 +469,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 +502,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 +543,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
|
||||
|
41
source/blender/nodes/NOD_geometry.h
Normal file
41
source/blender/nodes/NOD_geometry.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
132
source/blender/nodes/NOD_geometry_exec.hh
Normal file
132
source/blender/nodes/NOD_geometry_exec.hh
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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_accessor.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_;
|
||||
|
||||
public:
|
||||
GeoNodeExecParams(const bNode &node,
|
||||
GValueMap<StringRef> &input_values,
|
||||
GValueMap<StringRef> &output_values,
|
||||
const PersistentDataHandleMap &handle_map)
|
||||
: node_(node),
|
||||
input_values_(input_values),
|
||||
output_values_(output_values),
|
||||
handle_map_(handle_map)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_;
|
||||
}
|
||||
|
||||
private:
|
||||
void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr);
|
||||
void check_set_output(StringRef identifier, const CPPType &value_type);
|
||||
};
|
||||
|
||||
} // namespace blender::nodes
|
@@ -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
|
||||
|
@@ -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_;
|
||||
|
@@ -266,6 +266,15 @@ 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", "")
|
||||
|
||||
/* undefine macros */
|
||||
#undef DefNode
|
||||
|
36
source/blender/nodes/NOD_type_callbacks.hh
Normal file
36
source/blender/nodes/NOD_type_callbacks.hh
Normal 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
|
@@ -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)
|
||||
|
@@ -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, ""},
|
||||
};
|
||||
|
||||
|
23
source/blender/nodes/geometry/node_geometry_exec.cc
Normal file
23
source/blender/nodes/geometry/node_geometry_exec.cc
Normal 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 {
|
||||
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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);
|
141
source/blender/nodes/geometry/nodes/node_geo_boolean.cc
Normal file
141
source/blender/nodes/geometry/nodes/node_geo_boolean.cc
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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_a, &bmesh_from_mesh_params);
|
||||
BM_mesh_bm_from_me(bm, mesh_b, &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_a->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_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;
|
||||
|
||||
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) {
|
||||
params.set_output("Geometry", std::move(geometry_set_out));
|
||||
return;
|
||||
}
|
||||
|
||||
GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)params.node().custom1;
|
||||
if (operation < 0 || operation > 2) {
|
||||
BLI_assert(false);
|
||||
params.set_output("Geometry", std::move(geometry_set_out));
|
||||
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_boolean_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -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);
|
||||
|
87
source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
Normal file
87
source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
Normal 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 "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_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_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 float split_angle = params.extract_input<float>("Angle");
|
||||
const bool use_sharp_flag = params.extract_input<bool>("Sharp Edges");
|
||||
|
||||
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;
|
||||
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_edge_split_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
81
source/blender/nodes/geometry/nodes/node_geo_object_info.cc
Normal file
81
source/blender/nodes/geometry/nodes/node_geo_object_info.cc
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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_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;
|
||||
|
||||
if (object != nullptr) {
|
||||
float quaternion[4];
|
||||
mat4_decompose(location, quaternion, scale, object->obmat);
|
||||
quat_to_eul(rotation, quaternion);
|
||||
|
||||
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);
|
||||
geometry_set = GeometrySet::create_with_mesh(copied_mesh);
|
||||
geometry_set.get_component_for_write<MeshComponent>().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_object_info_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
142
source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
Normal file
142
source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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 = density_factors[v0_index];
|
||||
const float v1_density_factor = density_factors[v1_index];
|
||||
const float v2_density_factor = density_factors[v2_index];
|
||||
const float looptri_density_factor = (v0_density_factor + v1_density_factor +
|
||||
v2_density_factor) /
|
||||
3.0f;
|
||||
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_point_distribute_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 float density = params.extract_input<float>("Density");
|
||||
const std::string density_attribute = params.extract_input<std::string>("Density Attribute");
|
||||
|
||||
if (density <= 0.0f) {
|
||||
geometry_set.replace_mesh(nullptr);
|
||||
geometry_set.replace_pointcloud(nullptr);
|
||||
params.set_output("Geometry", std::move(geometry_set));
|
||||
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 = bke::mesh_attribute_get_for_read<float>(
|
||||
mesh_component, density_attribute, ATTR_DOMAIN_VERTEX, 1.0f);
|
||||
|
||||
Vector<float3> points = scatter_points_from_mesh(mesh_in, density, density_factors);
|
||||
|
||||
PointCloud *pointcloud = BKE_pointcloud_new_nomain(points.size());
|
||||
memcpy(pointcloud->co, points.data(), sizeof(float3) * points.size());
|
||||
for (const int i : points.index_range()) {
|
||||
*(float3 *)(pointcloud->co + i) = points[i];
|
||||
pointcloud->radius[i] = 0.05f;
|
||||
}
|
||||
|
||||
geometry_set.replace_mesh(nullptr);
|
||||
geometry_set.replace_pointcloud(pointcloud);
|
||||
|
||||
params.set_output("Geometry", std::move(geometry_set));
|
||||
}
|
||||
} // 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_point_distribute_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 geo_point_instance_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
Vector<float3> instance_positions;
|
||||
if (geometry_set.has_pointcloud()) {
|
||||
const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read();
|
||||
instance_positions.extend((const float3 *)pointcloud->co, pointcloud->totpoint);
|
||||
}
|
||||
if (geometry_set.has_mesh()) {
|
||||
const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
|
||||
Float3ReadAttribute positions = bke::mesh_attribute_get_for_read<float3>(
|
||||
mesh_component, "Position", ATTR_DOMAIN_VERTEX, {0, 0, 0});
|
||||
for (const int i : IndexRange(positions.size())) {
|
||||
instance_positions.append(positions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
|
||||
"Object");
|
||||
Object *object = params.handle_map().lookup(object_handle);
|
||||
|
||||
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
|
||||
instances.replace(std::move(instance_positions), object);
|
||||
|
||||
params.set_output("Geometry", std::move(geometry_set));
|
||||
}
|
||||
} // 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_point_instance_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
110
source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc
Normal file
110
source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
#include "BLI_rand.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
|
||||
static bNodeSocketTemplate geo_node_random_attribute_in[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_STRING, N_("Attribute")},
|
||||
{SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||
{SOCK_VECTOR, N_("Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
|
||||
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate geo_node_random_attribute_out[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static void geo_attribute_random_init(bNodeTree *UNUSED(tree), bNode *node)
|
||||
{
|
||||
node->custom1 = CD_PROP_FLOAT;
|
||||
}
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static void geo_random_attribute_exec(GeoNodeExecParams params)
|
||||
{
|
||||
const bNode &node = params.node();
|
||||
const int data_type = node.custom1;
|
||||
const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2);
|
||||
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
const std::string attribute_name = params.extract_input<std::string>("Attribute");
|
||||
const float3 min_value = params.extract_input<float3>("Min");
|
||||
const float3 max_value = params.extract_input<float3>("Max");
|
||||
const int seed = params.extract_input<int>("Seed");
|
||||
|
||||
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
|
||||
Mesh *mesh = mesh_component.get_for_write();
|
||||
if (mesh == nullptr) {
|
||||
params.set_output("Geometry", geometry_set);
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<WriteAttributePtr> attribute_opt = bke::mesh_attribute_get_for_write(
|
||||
mesh_component, attribute_name);
|
||||
|
||||
if (!attribute_opt.has_value()) {
|
||||
BKE_id_attribute_new(&mesh->id, attribute_name.c_str(), data_type, domain, nullptr);
|
||||
attribute_opt = bke::mesh_attribute_get_for_write(mesh_component, attribute_name);
|
||||
}
|
||||
|
||||
RandomNumberGenerator rng;
|
||||
rng.seed_random(seed);
|
||||
|
||||
if (attribute_opt.has_value()) {
|
||||
WriteAttributePtr attribute = std::move(*attribute_opt);
|
||||
const int size = attribute->size();
|
||||
if (attribute->cpp_type().is<float>()) {
|
||||
FloatWriteAttribute float_attribute = std::move(attribute);
|
||||
for (const int i : IndexRange(size)) {
|
||||
const float value = rng.get_float() * (max_value.x - min_value.x) + min_value.x;
|
||||
float_attribute.set(i, value);
|
||||
}
|
||||
}
|
||||
else if (attribute->cpp_type().is<float3>()) {
|
||||
Float3WriteAttribute float3_attribute = std::move(attribute);
|
||||
for (const int i : IndexRange(size)) {
|
||||
const float x = rng.get_float();
|
||||
const float y = rng.get_float();
|
||||
const float z = rng.get_float();
|
||||
const float3 value = float3(x, y, z) * (max_value - min_value) + min_value;
|
||||
float3_attribute.set(i, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params.set_output("Geometry", geometry_set);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
void register_node_type_geo_random_attribute()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
geo_node_type_base(&ntype, GEO_NODE_RANDOM_ATTRIBUTE, "Random Attribute", 0, 0);
|
||||
node_type_socket_templates(&ntype, geo_node_random_attribute_in, geo_node_random_attribute_out);
|
||||
node_type_init(&ntype, geo_attribute_random_init);
|
||||
ntype.geometry_node_execute = blender::nodes::geo_random_attribute_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -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_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_subdivision_surface_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
144
source/blender/nodes/geometry/nodes/node_geo_transform.cc
Normal file
144
source/blender/nodes/geometry/nodes/node_geo_transform.cc
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
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_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_transform_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
79
source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
Normal file
79
source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
Normal 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_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_triangulate_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
@@ -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)
|
||||
|
102
source/blender/nodes/intern/node_geometry_exec.cc
Normal file
102
source/blender/nodes/intern/node_geometry_exec.cc
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
@@ -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;
|
||||
@@ -672,6 +663,16 @@ static bNodeSocketType *make_socket_type_object()
|
||||
return socktype;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void register_standard_node_socket_types(void)
|
||||
{
|
||||
/* draw callbacks are set in drawnode.c to avoid bad-level calls */
|
||||
@@ -708,5 +709,7 @@ void register_standard_node_socket_types(void)
|
||||
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_IMAGE, PROP_NONE));
|
||||
|
||||
nodeRegisterSocketType(make_socket_type_geometry());
|
||||
|
||||
nodeRegisterSocketType(make_socket_type_virtual());
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
76
source/blender/nodes/intern/type_callbacks.cc
Normal file
76
source/blender/nodes/intern/type_callbacks.cc
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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_node_tree_multi_function.hh"
|
||||
#include "NOD_type_callbacks.hh"
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
const CPPType *socket_cpp_type_get(const bNodeSocketType &stype)
|
||||
{
|
||||
if (stype.get_cpp_type != nullptr) {
|
||||
return stype.get_cpp_type();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype)
|
||||
{
|
||||
const CPPType *cpp_type = socket_cpp_type_get(stype);
|
||||
if (cpp_type != nullptr) {
|
||||
return MFDataType::ForSingle(*cpp_type);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool socket_is_mf_data_socket(const bNodeSocketType &stype)
|
||||
{
|
||||
if (!socket_mf_type_get(stype).has_value()) {
|
||||
return false;
|
||||
}
|
||||
if (stype.expand_in_mf_network == nullptr && stype.get_cpp_value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool socket_cpp_value_get(const bNodeSocket &socket, void *r_value)
|
||||
{
|
||||
if (socket.typeinfo->get_cpp_value != nullptr) {
|
||||
socket.typeinfo->get_cpp_value(socket, r_value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder)
|
||||
{
|
||||
bNodeSocket &socket = builder.bsocket();
|
||||
if (socket.typeinfo->expand_in_mf_network != nullptr) {
|
||||
socket.typeinfo->expand_in_mf_network(builder);
|
||||
}
|
||||
else if (socket.typeinfo->get_cpp_value != nullptr) {
|
||||
const CPPType &type = *socket_cpp_type_get(*socket.typeinfo);
|
||||
void *buffer = builder.resources().linear_allocator().allocate(type.size(), type.alignment());
|
||||
socket.typeinfo->get_cpp_value(socket, buffer);
|
||||
builder.set_constant_value(type, buffer);
|
||||
}
|
||||
else {
|
||||
BLI_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
@@ -34,7 +34,7 @@ bool sh_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
|
||||
|
||||
static bool sh_fn_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
|
||||
{
|
||||
return STREQ(ntree->idname, "ShaderNodeTree") || STREQ(ntree->idname, "SimulationNodeTree");
|
||||
return STREQ(ntree->idname, "ShaderNodeTree") || STREQ(ntree->idname, "GeometryNodeTree");
|
||||
}
|
||||
|
||||
void sh_node_type_base(
|
||||
|
Reference in New Issue
Block a user