diff options
Diffstat (limited to 'source/blender/nodes')
41 files changed, 1630 insertions, 180 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index d27f45c4d98..929b1c3e6b2 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -134,6 +134,7 @@ set(SRC function/nodes/node_fn_boolean_math.cc function/nodes/node_fn_float_compare.cc + function/nodes/node_fn_float_to_int.cc function/nodes/node_fn_input_string.cc function/nodes/node_fn_input_vector.cc function/nodes/node_fn_random_float.cc @@ -165,17 +166,20 @@ set(SRC geometry/nodes/node_geo_collection_info.cc geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_convex_hull.cc - geometry/nodes/node_geo_curve_length.cc + geometry/nodes/node_geo_curve_endpoints.cc + geometry/nodes/node_geo_curve_length.cc geometry/nodes/node_geo_curve_primitive_bezier_segment.cc geometry/nodes/node_geo_curve_primitive_circle.cc + geometry/nodes/node_geo_curve_primitive_line.cc geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc + geometry/nodes/node_geo_curve_primitive_quadrilateral.cc geometry/nodes/node_geo_curve_primitive_spiral.cc geometry/nodes/node_geo_curve_primitive_star.cc - geometry/nodes/node_geo_curve_to_mesh.cc - geometry/nodes/node_geo_curve_to_points.cc geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_curve_reverse.cc geometry/nodes/node_geo_curve_subdivide.cc + geometry/nodes/node_geo_curve_to_mesh.cc + geometry/nodes/node_geo_curve_to_points.cc geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_input_material.cc @@ -191,6 +195,7 @@ set(SRC geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc geometry/nodes/node_geo_mesh_primitive_line.cc geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc + geometry/nodes/node_geo_mesh_subdivide.cc geometry/nodes/node_geo_mesh_to_curve.cc geometry/nodes/node_geo_object_info.cc geometry/nodes/node_geo_point_distribute.cc @@ -203,11 +208,11 @@ set(SRC geometry/nodes/node_geo_raycast.cc geometry/nodes/node_geo_select_by_material.cc geometry/nodes/node_geo_separate_components.cc - geometry/nodes/node_geo_subdivide.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_switch.cc geometry/nodes/node_geo_transform.cc geometry/nodes/node_geo_triangulate.cc + geometry/nodes/node_geo_viewer.cc geometry/nodes/node_geo_volume_to_mesh.cc geometry/node_geometry_exec.cc geometry/node_geometry_tree.cc @@ -333,6 +338,7 @@ set(SRC texture/node_texture_util.c intern/derived_node_tree.cc + intern/geometry_nodes_eval_log.cc intern/math_functions.cc intern/attribute_ref.cc intern/node_common.c @@ -358,6 +364,7 @@ set(SRC NOD_function.h NOD_geometry.h NOD_geometry_exec.hh + NOD_geometry_nodes_eval_log.hh NOD_math_functions.hh NOD_node_tree_multi_function.hh NOD_node_tree_ref.hh @@ -418,6 +425,11 @@ if(WITH_TBB) ${TBB_INCLUDE_DIRS} ) add_definitions(-DWITH_TBB) + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() endif() if(WITH_IMAGE_OPENEXR) diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index b31b5326d66..29f1a465491 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -22,6 +22,7 @@ extern "C" { void register_node_type_fn_boolean_math(void); void register_node_type_fn_float_compare(void); +void register_node_type_fn_float_to_int(void); void register_node_type_fn_input_string(void); void register_node_type_fn_input_vector(void); void register_node_type_fn_random_float(void); diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 788978c4246..3b546d208e4 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -43,27 +43,30 @@ void register_node_type_geo_attribute_math(void); void register_node_type_geo_attribute_mix(void); void register_node_type_geo_attribute_proximity(void); void register_node_type_geo_attribute_randomize(void); +void register_node_type_geo_attribute_remove(void); void register_node_type_geo_attribute_separate_xyz(void); void register_node_type_geo_attribute_transfer(void); void register_node_type_geo_attribute_vector_math(void); void register_node_type_geo_attribute_vector_rotate(void); -void register_node_type_geo_attribute_remove(void); void register_node_type_geo_attribute_set(void); void register_node_type_geo_boolean(void); void register_node_type_geo_bounding_box(void); void register_node_type_geo_collection_info(void); void register_node_type_geo_convex_hull(void); +void register_node_type_geo_curve_endpoints(void); void register_node_type_geo_curve_length(void); void register_node_type_geo_curve_primitive_bezier_segment(void); void register_node_type_geo_curve_primitive_circle(void); +void register_node_type_geo_curve_primitive_line(void); void register_node_type_geo_curve_primitive_quadratic_bezier(void); +void register_node_type_geo_curve_primitive_quadrilateral(void); void register_node_type_geo_curve_primitive_spiral(void); void register_node_type_geo_curve_primitive_star(void); -void register_node_type_geo_curve_to_mesh(void); -void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_resample(void); void register_node_type_geo_curve_reverse(void); void register_node_type_geo_curve_subdivide(void); +void register_node_type_geo_curve_to_mesh(void); +void register_node_type_geo_curve_to_points(void); void register_node_type_geo_delete_geometry(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_input_material(void); @@ -79,6 +82,7 @@ void register_node_type_geo_mesh_primitive_grid(void); void register_node_type_geo_mesh_primitive_ico_sphere(void); void register_node_type_geo_mesh_primitive_line(void); void register_node_type_geo_mesh_primitive_uv_sphere(void); +void register_node_type_geo_mesh_subdivide(void); void register_node_type_geo_mesh_to_curve(void); void register_node_type_geo_object_info(void); void register_node_type_geo_point_distribute(void); @@ -92,11 +96,11 @@ void register_node_type_geo_raycast(void); void register_node_type_geo_sample_texture(void); void register_node_type_geo_select_by_material(void); void register_node_type_geo_separate_components(void); -void register_node_type_geo_subdivide(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); void register_node_type_geo_transform(void); void register_node_type_geo_triangulate(void); +void register_node_type_geo_viewer(void); void register_node_type_geo_volume_to_mesh(void); #ifdef __cplusplus diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index caefccfdba0..cc01465f4e5 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -21,12 +21,12 @@ #include "BKE_attribute_access.hh" #include "BKE_geometry_set.hh" #include "BKE_geometry_set_instances.hh" -#include "BKE_node_ui_storage.hh" #include "DNA_node_types.h" #include "NOD_attribute_ref.hh" #include "NOD_derived_node_tree.hh" +#include "NOD_geometry_nodes_eval_log.hh" struct Depsgraph; struct ModifierData; @@ -53,10 +53,11 @@ using fn::GVMutableArray; using fn::GVMutableArray_GSpan; using fn::GVMutableArray_Typed; using fn::GVMutableArrayPtr; +using geometry_nodes_eval_log::NodeWarningType; /** - * This class exists to separate the memory management details of the geometry nodes evaluator from - * the node execution functions and related utilities. + * This class exists to separate the memory management details of the geometry nodes evaluator + * from the node execution functions and related utilities. */ class GeoNodeExecParamsProvider { public: @@ -64,6 +65,7 @@ class GeoNodeExecParamsProvider { const Object *self_object = nullptr; const ModifierData *modifier = nullptr; Depsgraph *depsgraph = nullptr; + geometry_nodes_eval_log::GeoLogger *logger = nullptr; /** * Returns true when the node is allowed to get/extract the input value. The identifier is diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh new file mode 100644 index 00000000000..b85862a0176 --- /dev/null +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -0,0 +1,309 @@ +/* + * 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 + +/** + * Many geometry nodes related UI features need access to data produced during evaluation. Not only + * is the final output required but also the intermediate results. Those features include + * attribute search, node warnings, socket inspection and the viewer node. + * + * This file provides the framework for logging data during evaluation and accessing the data after + * evaluation. + * + * During logging every thread gets its own local logger to avoid too much locking (logging + * generally happens for every socket). After geometry nodes evaluation is done, the thread-local + * logging information is combined and post-processed to make it easier for the UI to lookup. + * necessary information. + */ + +#include "BLI_enumerable_thread_specific.hh" +#include "BLI_linear_allocator.hh" +#include "BLI_map.hh" + +#include "BKE_geometry_set.hh" + +#include "FN_generic_pointer.hh" + +#include "NOD_derived_node_tree.hh" + +struct SpaceNode; +struct SpaceSpreadsheet; + +namespace blender::nodes::geometry_nodes_eval_log { + +using fn::GMutablePointer; +using fn::GPointer; + +/** Contains information about a value that has been computed during geometry nodes evaluation. */ +class ValueLog { + public: + virtual ~ValueLog() = default; +}; + +/** Contains an owned copy of a value of a generic type. */ +class GenericValueLog : public ValueLog { + private: + GMutablePointer data_; + + public: + GenericValueLog(GMutablePointer data) : data_(data) + { + } + + ~GenericValueLog() + { + data_.destruct(); + } + + GPointer value() const + { + return data_; + } +}; + +struct GeometryAttributeInfo { + std::string name; + AttributeDomain domain; + CustomDataType data_type; +}; + +/** Contains information about a geometry set. In most cases this does not store the entire + * geometry set as this would require too much memory. */ +class GeometryValueLog : public ValueLog { + private: + Vector<GeometryAttributeInfo> attributes_; + Vector<GeometryComponentType> component_types_; + std::unique_ptr<GeometrySet> full_geometry_; + + public: + struct MeshInfo { + int tot_verts, tot_edges, tot_faces; + }; + struct CurveInfo { + int tot_splines; + }; + struct PointCloudInfo { + int tot_points; + }; + struct InstancesInfo { + int tot_instances; + }; + + std::optional<MeshInfo> mesh_info; + std::optional<CurveInfo> curve_info; + std::optional<PointCloudInfo> pointcloud_info; + std::optional<InstancesInfo> instances_info; + + GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry); + + Span<GeometryAttributeInfo> attributes() const + { + return attributes_; + } + + Span<GeometryComponentType> component_types() const + { + return component_types_; + } + + const GeometrySet *full_geometry() const + { + return full_geometry_.get(); + } +}; + +enum class NodeWarningType { + Error, + Warning, + Info, +}; + +struct NodeWarning { + NodeWarningType type; + std::string message; +}; + +struct NodeWithWarning { + DNode node; + NodeWarning warning; +}; + +/** The same value can be referenced by multiple sockets when they are linked. */ +struct ValueOfSockets { + Span<DSocket> sockets; + destruct_ptr<ValueLog> value; +}; + +class GeoLogger; +class ModifierLog; + +/** Every thread has its own local logger to avoid having to communicate between threads during + * evaluation. After evaluation the individual logs are combined. */ +class LocalGeoLogger { + private: + /* Back pointer to the owner of this local logger. */ + GeoLogger *main_logger_; + /* Allocator for the many small allocations during logging. This is in a `unique_ptr` so that + * ownership can be transferred later on. */ + std::unique_ptr<LinearAllocator<>> allocator_; + Vector<ValueOfSockets> values_; + Vector<NodeWithWarning> node_warnings_; + + friend ModifierLog; + + public: + LocalGeoLogger(GeoLogger &main_logger) : main_logger_(&main_logger) + { + this->allocator_ = std::make_unique<LinearAllocator<>>(); + } + + void log_value_for_sockets(Span<DSocket> sockets, GPointer value); + void log_multi_value_socket(DSocket socket, Span<GPointer> values); + void log_node_warning(DNode node, NodeWarningType type, std::string message); +}; + +/** The root logger class. */ +class GeoLogger { + private: + /** The entire geometry of sockets in this set should be cached, because e.g. the spreadsheet + * displays the data. We don't log the entire geometry at all places, because that would require + * way too much memory. */ + Set<DSocket> log_full_geometry_sockets_; + threading::EnumerableThreadSpecific<LocalGeoLogger> threadlocals_; + + friend LocalGeoLogger; + + public: + GeoLogger(Set<DSocket> log_full_geometry_sockets) + : log_full_geometry_sockets_(std::move(log_full_geometry_sockets)), + threadlocals_([this]() { return LocalGeoLogger(*this); }) + { + } + + LocalGeoLogger &local() + { + return threadlocals_.local(); + } + + auto begin() + { + return threadlocals_.begin(); + } + + auto end() + { + return threadlocals_.end(); + } +}; + +/** Contains information that has been logged for one specific socket. */ +class SocketLog { + private: + ValueLog *value_ = nullptr; + + friend ModifierLog; + + public: + const ValueLog *value() const + { + return value_; + } +}; + +/** Contains information that has been logged for one specific node. */ +class NodeLog { + private: + Vector<SocketLog> input_logs_; + Vector<SocketLog> output_logs_; + Vector<NodeWarning, 0> warnings_; + + friend ModifierLog; + + public: + const SocketLog *lookup_socket_log(eNodeSocketInOut in_out, int index) const; + const SocketLog *lookup_socket_log(const bNode &node, const bNodeSocket &socket) const; + + Span<SocketLog> input_logs() const + { + return input_logs_; + } + + Span<SocketLog> output_logs() const + { + return output_logs_; + } + + Span<NodeWarning> warnings() const + { + return warnings_; + } + + Vector<const GeometryAttributeInfo *> lookup_available_attributes() const; +}; + +/** Contains information that has been logged for one specific tree. */ +class TreeLog { + private: + Map<std::string, destruct_ptr<NodeLog>> node_logs_; + Map<std::string, destruct_ptr<TreeLog>> child_logs_; + + friend ModifierLog; + + public: + const NodeLog *lookup_node_log(StringRef node_name) const; + const NodeLog *lookup_node_log(const bNode &node) const; + const TreeLog *lookup_child_log(StringRef node_name) const; +}; + +/** Contains information about an entire geometry nodes evaluation. */ +class ModifierLog { + private: + LinearAllocator<> allocator_; + /* Allocators of the individual loggers. */ + Vector<std::unique_ptr<LinearAllocator<>>> logger_allocators_; + destruct_ptr<TreeLog> root_tree_logs_; + Vector<destruct_ptr<ValueLog>> logged_values_; + + public: + ModifierLog(GeoLogger &logger); + + const TreeLog &root_tree() const + { + return *root_tree_logs_; + } + + /* Utilities to find logged information for a specific context. */ + static const ModifierLog *find_root_by_node_editor_context(const SpaceNode &snode); + static const TreeLog *find_tree_by_node_editor_context(const SpaceNode &snode); + static const NodeLog *find_node_by_node_editor_context(const SpaceNode &snode, + const bNode &node); + static const SocketLog *find_socket_by_node_editor_context(const SpaceNode &snode, + const bNode &node, + const bNodeSocket &socket); + static const NodeLog *find_node_by_spreadsheet_editor_context( + const SpaceSpreadsheet &sspreadsheet); + + private: + using LogByTreeContext = Map<const DTreeContext *, TreeLog *>; + + TreeLog &lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context, + const DTreeContext &tree_context); + NodeLog &lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node); + SocketLog &lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context, DSocket socket); +}; + +} // namespace blender::nodes::geometry_nodes_eval_log diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index f5f8f77f2a1..60a10d2c508 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -210,7 +210,7 @@ DefNode(CompositorNode, CMP_NODE_MASK_ELLIPSE, def_cmp_ellipsemask, "ELLIPS DefNode(CompositorNode, CMP_NODE_BOKEHIMAGE, def_cmp_bokehimage, "BOKEHIMAGE", BokehImage, "Bokeh Image", "" ) DefNode(CompositorNode, CMP_NODE_BOKEHBLUR, def_cmp_bokehblur, "BOKEHBLUR", BokehBlur, "Bokeh Blur", "" ) DefNode(CompositorNode, CMP_NODE_SWITCH, def_cmp_switch, "SWITCH", Switch, "Switch", "" ) -DefNode(CompositorNode, CMP_NODE_SWITCH_VIEW, def_cmp_switch_view, "VIEWSWITCH", SwitchView, "View Switch", "" ) +DefNode(CompositorNode, CMP_NODE_SWITCH_VIEW, def_cmp_switch_view, "VIEWSWITCH", SwitchView, "Switch View", "" ) DefNode(CompositorNode, CMP_NODE_COLORCORRECTION,def_cmp_colorcorrection,"COLORCORRECTION",ColorCorrection, "Color Correction", "" ) DefNode(CompositorNode, CMP_NODE_MASK, def_cmp_mask, "MASK", Mask, "Mask", "" ) DefNode(CompositorNode, CMP_NODE_KEYINGSCREEN, def_cmp_keyingscreen, "KEYINGSCREEN", KeyingScreen, "Keying Screen", "" ) @@ -263,6 +263,7 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "") +DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "") DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") @@ -294,7 +295,9 @@ DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLEC DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "") -DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Circle", "") +DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "") +DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "") +DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMITIVE_QUADRATIC_BEZIER", CurveQuadraticBezier, "Quadratic Bezier", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") @@ -303,6 +306,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_ DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") +DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") @@ -310,13 +314,13 @@ DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Vi DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "") -DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Circle", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", MeshGrid, "Grid", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") -DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "") +DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") @@ -330,11 +334,12 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POIN DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") -DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "") +DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") +DefNode(GeometryNode, GEO_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "") DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "") /* undefine macros */ diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c index 19815d01278..013d196e1c8 100644 --- a/source/blender/nodes/composite/node_composite_tree.c +++ b/source/blender/nodes/composite/node_composite_tree.c @@ -205,10 +205,11 @@ static void composite_node_add_init(bNodeTree *UNUSED(bnodetree), bNode *bnode) } } -static bool composite_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, - bNodeTreeType *UNUSED(ntreetype)) +static bool composite_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype), + bNodeSocketType *socket_type) { - return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA); + return nodeIsStaticSocketType(socket_type) && + ELEM(socket_type->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA); } bNodeTreeType *ntreeType_Composite; diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c index 7437496d878..fa276e9a794 100644 --- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c +++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c @@ -42,9 +42,9 @@ static void node_composit_init_antialiasing(bNodeTree *UNUSED(ntree), bNode *nod { NodeAntiAliasingData *data = MEM_callocN(sizeof(NodeAntiAliasingData), "node antialiasing data"); - data->threshold = 1.0f; - data->contrast_limit = 0.2f; - data->corner_rounding = 0.25f; + data->threshold = CMP_DEFAULT_SMAA_THRESHOLD; + data->contrast_limit = CMP_DEFAULT_SMAA_CONTRAST_LIMIT; + data->corner_rounding = CMP_DEFAULT_SMAA_CORNER_ROUNDING; node->storage = data; } diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.c b/source/blender/nodes/composite/nodes/node_composite_zcombine.c index be1359f795f..5041b16c303 100644 --- a/source/blender/nodes/composite/nodes/node_composite_zcombine.c +++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.c @@ -24,7 +24,7 @@ #include "node_composite_util.h" /* **************** Z COMBINE ******************** */ -/* lazy coder note: node->custom2 is abused to send signal */ +/* lazy coder NOTE: node->custom2 is abused to send signal. */ static bNodeSocketTemplate cmp_node_zcombine_in[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {SOCK_FLOAT, N_("Z"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_NONE}, diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc new file mode 100644 index 00000000000..26cde576400 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -0,0 +1,95 @@ +/* + * 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 <cmath> + +#include "BLI_string.h" + +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_function_util.hh" + +static bNodeSocketTemplate fn_node_float_to_int_in[] = { + {SOCK_FLOAT, N_("Float"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {-1, ""}, +}; + +static bNodeSocketTemplate fn_node_float_to_int_out[] = { + {SOCK_INT, N_("Integer")}, + {-1, ""}, +}; + +static void fn_node_float_to_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "rounding_mode", 0, "", ICON_NONE); +} + +static void node_float_to_int_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +{ + const char *name; + bool enum_label = RNA_enum_name(rna_enum_node_float_to_int_items, node->custom1, &name); + if (!enum_label) { + name = "Unknown"; + } + BLI_strncpy(label, IFACE_(name), maxlen); +} + +static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) +{ + static blender::fn::CustomMF_SI_SO<float, int> round_fn{"Round", + [](float a) { return (int)round(a); }}; + static blender::fn::CustomMF_SI_SO<float, int> floor_fn{"Floor", + [](float a) { return (int)floor(a); }}; + static blender::fn::CustomMF_SI_SO<float, int> ceil_fn{"Ceiling", + [](float a) { return (int)ceil(a); }}; + static blender::fn::CustomMF_SI_SO<float, int> trunc_fn{"Truncate", + [](float a) { return (int)trunc(a); }}; + + switch (static_cast<FloatToIntRoundingMode>(bnode.custom1)) { + case FN_NODE_FLOAT_TO_INT_ROUND: + return round_fn; + case FN_NODE_FLOAT_TO_INT_FLOOR: + return floor_fn; + case FN_NODE_FLOAT_TO_INT_CEIL: + return ceil_fn; + case FN_NODE_FLOAT_TO_INT_TRUNCATE: + return trunc_fn; + } + + BLI_assert_unreachable(); + return blender::fn::dummy_multi_function; +} + +static void node_float_to_int_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +{ + const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + builder.set_matching_fn(fn); +} + +void register_node_type_fn_float_to_int() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTOR, 0); + node_type_socket_templates(&ntype, fn_node_float_to_int_in, fn_node_float_to_int_out); + node_type_label(&ntype, node_float_to_int_label); + ntype.expand_in_mf_network = node_float_to_int_expand_in_mf_network; + ntype.draw_buttons = fn_node_float_to_int_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index f36d5b3c9ca..5f10cd87049 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -108,22 +108,22 @@ static bool geometry_node_tree_validate_link(bNodeTree *UNUSED(ntree), bNodeLink return (link->tosock->type == link->fromsock->type); } -static bool geometry_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, - bNodeTreeType *UNUSED(ntreetype)) +static bool geometry_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype), + bNodeSocketType *socket_type) { - return ELEM(socket_type, - SOCK_FLOAT, - SOCK_VECTOR, - SOCK_RGBA, - SOCK_BOOLEAN, - SOCK_INT, - SOCK_STRING, - SOCK_OBJECT, - SOCK_GEOMETRY, - SOCK_COLLECTION, - SOCK_TEXTURE, - SOCK_MATERIAL, - SOCK_ATTRIBUTE); + return nodeIsStaticSocketType(socket_type) && ELEM(socket_type->type, + SOCK_FLOAT, + SOCK_VECTOR, + SOCK_RGBA, + SOCK_BOOLEAN, + SOCK_INT, + SOCK_STRING, + SOCK_OBJECT, + SOCK_GEOMETRY, + SOCK_COLLECTION, + SOCK_TEXTURE, + SOCK_MATERIAL, + SOCK_ATTRIBUTE); } void register_node_tree_type_geo(void) diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 67d32d540a8..98c92eac98f 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -79,4 +79,26 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, Span<bool> masks, const bool invert); +struct CurveToPointsResults { + int result_size; + MutableSpan<float3> positions; + MutableSpan<float> radii; + MutableSpan<float> tilts; + + Map<std::string, GMutableSpan> point_attributes; + + MutableSpan<float3> tangents; + MutableSpan<float3> normals; + MutableSpan<float3> rotations; +}; +/** + * Create references for all result point cloud attributes to simplify accessing them later on. + */ +CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponent &points, + const CurveEval &curve); + +void curve_create_default_rotation_attribute(Span<float3> tangents, + Span<float3> normals, + MutableSpan<float3> rotations); + } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc index 06a4327a6c5..3b951bda95e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc @@ -108,15 +108,14 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef result_name) { /* Use the domain of the result attribute if it already exists. */ - ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name); - if (result_attribute) { - return result_attribute.domain; + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; } - /* Otherwise use the input attribute's domain if it exists. */ - ReadAttributeLookup input_attribute = component.attribute_try_get_for_read(input_name); - if (input_attribute) { - return input_attribute.domain; + std::optional<AttributeMetaData> input_info = component.attribute_get_meta_data(input_name); + if (input_info) { + return input_info->domain; } return ATTR_DOMAIN_POINT; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc index b7863d38fc2..d71cb09f1bd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc @@ -143,8 +143,7 @@ static bool bvh_from_mesh(const Mesh *target_mesh, break; } - /* This only updates a cache and can be considered to be logically const. */ - BKE_bvhtree_from_mesh_get(&r_tree_data_mesh, const_cast<Mesh *>(target_mesh), bvh_type, 2); + BKE_bvhtree_from_mesh_get(&r_tree_data_mesh, target_mesh, bvh_type, 2); if (r_tree_data_mesh.tree == nullptr) { return false; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc index d1114713672..756f93f154f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc @@ -162,7 +162,7 @@ static void get_closest_mesh_points(const Mesh &mesh, { BLI_assert(mesh.totvert > 0); BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_VERTS, 2); + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_VERTS, 2); get_closest_in_bvhtree(tree_data, positions, r_point_indices, r_distances_sq, r_positions); free_bvhtree_from_mesh(&tree_data); } @@ -175,7 +175,7 @@ static void get_closest_mesh_edges(const Mesh &mesh, { BLI_assert(mesh.totedge > 0); BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_EDGES, 2); + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_EDGES, 2); get_closest_in_bvhtree(tree_data, positions, r_edge_indices, r_distances_sq, r_positions); free_bvhtree_from_mesh(&tree_data); } @@ -188,7 +188,7 @@ static void get_closest_mesh_looptris(const Mesh &mesh, { BLI_assert(mesh.totpoly > 0); BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_LOOPTRI, 2); + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); get_closest_in_bvhtree(tree_data, positions, r_looptri_indices, r_distances_sq, r_positions); free_bvhtree_from_mesh(&tree_data); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index b1b17a321b8..4286db52115 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -82,7 +82,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) copy_v3_v3(result->mvert[i].co, co); } else { - BLI_assert(!"Unexpected new vertex in hull output"); + BLI_assert_msg(0, "Unexpected new vertex in hull output"); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc new file mode 100644 index 00000000000..1f878259f30 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc @@ -0,0 +1,223 @@ +/* + * 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_task.hh" +#include "BLI_timeit.hh" + +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_endpoints_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_endpoints_out[] = { + {SOCK_GEOMETRY, N_("Start Points")}, + {SOCK_GEOMETRY, N_("End Points")}, + {-1, ""}, +}; + +namespace blender::nodes { + +/** + * Evaluate splines in parallel to speed up the rest of the node's execution. + */ +static void evaluate_splines(Span<SplinePtr> splines) +{ + threading::parallel_for_each(splines, [](const SplinePtr &spline) { + /* These functions fill the corresponding caches on each spline. */ + spline->evaluated_positions(); + spline->evaluated_tangents(); + spline->evaluated_normals(); + spline->evaluated_lengths(); + }); +} + +/** + * \note Use attributes from the curve component rather than the attribute data directly on the + * attribute storage to allow reading the virtual spline attributes like "cyclic" and "resolution". + */ +static void copy_spline_domain_attributes(const CurveComponent &curve_component, + Span<int> offsets, + PointCloudComponent &points) +{ + curve_component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { + if (meta_data.domain != ATTR_DOMAIN_CURVE) { + return true; + } + GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( + name, ATTR_DOMAIN_CURVE, meta_data.data_type); + + OutputAttribute result_attribute = points.attribute_try_get_for_output_only( + name, ATTR_DOMAIN_POINT, meta_data.data_type); + GMutableSpan result = result_attribute.as_span(); + + /* Only copy the attributes of splines in the offsets. */ + for (const int i : offsets.index_range()) { + spline_attribute->get(offsets[i], result[i]); + } + + result_attribute.save(); + return true; + }); +} + +/** + * Get the offsets for the splines whose endpoints we want to output. + * Filter those which are cyclic, or that evaluate to empty. + * Could be easily adapted to include a selection argument to support attribute selection. + */ +static blender::Vector<int> get_endpoint_spline_offsets(Span<SplinePtr> splines) +{ + blender::Vector<int> spline_offsets; + spline_offsets.reserve(splines.size()); + + for (const int i : splines.index_range()) { + if (!(splines[i]->is_cyclic() || splines[i]->evaluated_points_size() == 0)) { + spline_offsets.append(i); + } + } + + return spline_offsets; +} + +/** + * Copy the endpoint attributes from the correct positions at the splines at the offsets to + * the start and end attributes. + */ +static void copy_endpoint_attributes(Span<SplinePtr> splines, + Span<int> offsets, + CurveToPointsResults &start_data, + CurveToPointsResults &end_data) +{ + threading::parallel_for(offsets.index_range(), 64, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[offsets[i]]; + + /* Copy the start and end point data over. */ + start_data.positions[i] = spline.evaluated_positions().first(); + start_data.tangents[i] = spline.evaluated_tangents().first(); + start_data.normals[i] = spline.evaluated_normals().first(); + start_data.radii[i] = spline.radii().first(); + start_data.tilts[i] = spline.tilts().first(); + + end_data.positions[i] = spline.evaluated_positions().last(); + end_data.tangents[i] = spline.evaluated_tangents().last(); + end_data.normals[i] = spline.evaluated_normals().last(); + end_data.radii[i] = spline.radii().last(); + end_data.tilts[i] = spline.tilts().last(); + + /* Copy the point attribute data over. */ + for (const auto &item : start_data.point_attributes.items()) { + const StringRef name = item.key; + GMutableSpan point_span = item.value; + + BLI_assert(spline.attributes.get_for_read(name)); + GSpan spline_span = *spline.attributes.get_for_read(name); + blender::fn::GVArray_For_GSpan(spline_span).get(0, point_span[i]); + } + + for (const auto &item : end_data.point_attributes.items()) { + const StringRef name = item.key; + GMutableSpan point_span = item.value; + + BLI_assert(spline.attributes.get_for_read(name)); + GSpan spline_span = *spline.attributes.get_for_read(name); + blender::fn::GVArray_For_GSpan(spline_span).get(spline.size() - 1, point_span[i]); + } + } + }); +} + +static void geo_node_curve_endpoints_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_curve()) { + params.set_output("Start Points", GeometrySet()); + params.set_output("End Points", GeometrySet()); + return; + } + + const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>(); + const CurveEval &curve = *curve_component.get_for_read(); + const Span<SplinePtr> splines = curve.splines(); + curve.assert_valid_point_attributes(); + + evaluate_splines(splines); + + const Vector<int> offsets = get_endpoint_spline_offsets(splines); + const int total_size = offsets.size(); + + if (total_size == 0) { + params.set_output("Start Points", GeometrySet()); + params.set_output("End Points", GeometrySet()); + return; + } + + GeometrySet start_result = GeometrySet::create_with_pointcloud( + BKE_pointcloud_new_nomain(total_size)); + GeometrySet end_result = GeometrySet::create_with_pointcloud( + BKE_pointcloud_new_nomain(total_size)); + PointCloudComponent &start_point_component = + start_result.get_component_for_write<PointCloudComponent>(); + PointCloudComponent &end_point_component = + end_result.get_component_for_write<PointCloudComponent>(); + + CurveToPointsResults start_attributes = curve_to_points_create_result_attributes( + start_point_component, curve); + CurveToPointsResults end_attributes = curve_to_points_create_result_attributes( + end_point_component, curve); + + copy_endpoint_attributes(splines, offsets.as_span(), start_attributes, end_attributes); + copy_spline_domain_attributes(curve_component, offsets.as_span(), start_point_component); + curve_create_default_rotation_attribute( + start_attributes.tangents, start_attributes.normals, start_attributes.rotations); + curve_create_default_rotation_attribute( + end_attributes.tangents, end_attributes.normals, end_attributes.rotations); + + /* The default radius is way too large for points, divide by 10. */ + for (float &radius : start_attributes.radii) { + radius *= 0.1f; + } + for (float &radius : end_attributes.radii) { + radius *= 0.1f; + } + + params.set_output("Start Points", std::move(start_result)); + params.set_output("End Points", std::move(end_result)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_endpoints() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_endpoints_in, geo_node_curve_endpoints_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_endpoints_exec; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index 963c48b7536..ae947b7aeed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -83,9 +83,10 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) } static std::unique_ptr<CurveEval> create_point_circle_curve( - const float3 p1, const float3 p2, const float3 p3, const int resolution, float center_out[3]) + const float3 p1, const float3 p2, const float3 p3, const int resolution, float3 &r_center) { if (colinear_f3_f3_f3(p1, p2, p3)) { + r_center = float3(0); return nullptr; } @@ -118,6 +119,7 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( /* If the 3 planes do not intersect at one point, just return empty geometry. */ if (!isect_plane_plane_plane_v3(plane_1, plane_2, plane_3, center)) { + r_center = float3(0); return nullptr; } @@ -126,7 +128,7 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( const float theta_step = ((2 * M_PI) / (float)resolution); for (const int i : IndexRange(resolution)) { - /* Formula for a circle around a point and 2 unit vectors perpendicular. + /* Formula for a circle around a point and 2 unit vectors perpendicular * to each other and the axis of the circle from: * https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space */ @@ -141,7 +143,7 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( curve->add_spline(std::move(spline)); curve->attributes.reallocate(curve->splines().size()); - copy_v3_v3(center_out, center); + r_center = center; return curve; } @@ -179,18 +181,13 @@ static void geo_node_curve_primitive_circle_exec(GeoNodeExecParams params) std::unique_ptr<CurveEval> curve; if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS) { - float center_point[3]; + float3 center_point; curve = create_point_circle_curve(params.extract_input<float3>("Point 1"), params.extract_input<float3>("Point 2"), params.extract_input<float3>("Point 3"), std::max(params.extract_input<int>("Resolution"), 3), center_point); - if (curve) { - params.set_output("Center", float3(center_point)); - } - else { - params.set_output("Center", float3(0, 0, 0)); - } + params.set_output("Center", center_point); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS) { curve = create_radius_circle_curve(std::max(params.extract_input<int>("Resolution"), 3), @@ -210,7 +207,8 @@ static void geo_node_curve_primitive_circle_exec(GeoNodeExecParams params) void register_node_type_geo_curve_primitive_circle() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, "Circle", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, "Curve Circle", NODE_CLASS_GEOMETRY, 0); node_type_socket_templates( &ntype, geo_node_curve_primitive_circle_in, geo_node_curve_primitive_circle_out); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc new file mode 100644 index 00000000000..eed3a998ef6 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -0,0 +1,146 @@ +/* + * 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_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_primitive_line_in[] = { + {SOCK_VECTOR, N_("Start"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("End"), 0.0f, 0.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_VECTOR, N_("Direction"), 0.0f, 0.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX}, + {SOCK_FLOAT, N_("Length"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_primitive_line_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +static void geo_node_curve_primitive_line_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +namespace blender::nodes { + +static void geo_node_curve_primitive_line_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurvePrimitiveLine *data = (NodeGeometryCurvePrimitiveLine *)MEM_callocN( + sizeof(NodeGeometryCurvePrimitiveLine), __func__); + + data->mode = GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS; + node->storage = data; +} + +static void geo_node_curve_primitive_line_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryCurvePrimitiveLine *node_storage = (NodeGeometryCurvePrimitiveLine *) + node->storage; + const GeometryNodeCurvePrimitiveLineMode mode = (const GeometryNodeCurvePrimitiveLineMode) + node_storage->mode; + + bNodeSocket *p2_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *direction_socket = p2_socket->next; + bNodeSocket *length_socket = direction_socket->next; + + nodeSetSocketAvailability(p2_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS); + nodeSetSocketAvailability(direction_socket, + mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION); + nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION); +} + +static std::unique_ptr<CurveEval> create_point_line_curve(const float3 start, const float3 end) +{ + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + + spline->resize(2); + MutableSpan<float3> positions = spline->positions(); + positions[0] = start; + positions[1] = end; + spline->radii().fill(1.0f); + spline->tilts().fill(0.0f); + curve->add_spline(std::move(spline)); + curve->attributes.reallocate(curve->splines().size()); + return curve; +} + +static std::unique_ptr<CurveEval> create_direction_line_curve(const float3 start, + const float3 direction, + const float length) +{ + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + + spline->resize(2); + MutableSpan<float3> positions = spline->positions(); + positions[0] = start; + positions[1] = direction.normalized() * length + start; + + spline->radii().fill(1.0f); + spline->tilts().fill(0.0f); + curve->add_spline(std::move(spline)); + curve->attributes.reallocate(curve->splines().size()); + return curve; +} + +static void geo_node_curve_primitive_line_exec(GeoNodeExecParams params) +{ + + const NodeGeometryCurvePrimitiveLine *node_storage = + (NodeGeometryCurvePrimitiveLine *)params.node().storage; + + GeometryNodeCurvePrimitiveLineMode mode = (GeometryNodeCurvePrimitiveLineMode)node_storage->mode; + + std::unique_ptr<CurveEval> curve; + if (mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS) { + curve = create_point_line_curve(params.extract_input<float3>("Start"), + params.extract_input<float3>("End")); + } + else if (mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION) { + curve = create_direction_line_curve(params.extract_input<float3>("Start"), + params.extract_input<float3>("Direction"), + params.extract_input<float>("Length")); + } + + params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_primitive_line() +{ + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_LINE, "Curve Line", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_curve_primitive_line_in, geo_node_curve_primitive_line_out); + node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_line_init); + node_type_update(&ntype, blender::nodes::geo_node_curve_primitive_line_update); + node_type_storage(&ntype, + "NodeGeometryCurvePrimitiveLine", + node_free_standard_storage, + node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_line_exec; + ntype.draw_buttons = geo_node_curve_primitive_line_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc new file mode 100644 index 00000000000..5f3f159c305 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -0,0 +1,241 @@ +/* + * 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_spline.hh" +#include "UI_interface.h" +#include "UI_resources.h" +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_primitive_quadrilateral_in[] = { + {SOCK_FLOAT, N_("Width"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Height"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Bottom Width"), 4.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Top Width"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Offset"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Bottom Height"), 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Top Height"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Point 1"), -1.0f, 1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Point 2"), 1.0f, 1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Point 3"), 1.0f, -1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Point 4"), -1.0f, -1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_primitive_quadrilateral_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +static void geo_node_curve_primitive_quadrilateral_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void geo_node_curve_primitive_quadrilateral_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurvePrimitiveQuad *data = (NodeGeometryCurvePrimitiveQuad *)MEM_callocN( + sizeof(NodeGeometryCurvePrimitiveQuad), __func__); + data->mode = GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE; + node->storage = data; +} + +namespace blender::nodes { + +static void geo_node_curve_primitive_quadrilateral_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryCurvePrimitiveQuad &node_storage = *(NodeGeometryCurvePrimitiveQuad *)node->storage; + GeometryNodeCurvePrimitiveQuadMode mode = static_cast<GeometryNodeCurvePrimitiveQuadMode>( + node_storage.mode); + + bNodeSocket *width = ((bNodeSocket *)node->inputs.first); + bNodeSocket *height = width->next; + bNodeSocket *bottom = height->next; + bNodeSocket *top = bottom->next; + bNodeSocket *offset = top->next; + bNodeSocket *bottom_height = offset->next; + bNodeSocket *top_height = bottom_height->next; + bNodeSocket *p1 = top_height->next; + bNodeSocket *p2 = p1->next; + bNodeSocket *p3 = p2->next; + bNodeSocket *p4 = p3->next; + + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + nodeSetSocketAvailability(sock, false); + } + + if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE) { + nodeSetSocketAvailability(width, true); + nodeSetSocketAvailability(height, true); + } + else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM) { + nodeSetSocketAvailability(width, true); + nodeSetSocketAvailability(height, true); + nodeSetSocketAvailability(offset, true); + } + else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID) { + nodeSetSocketAvailability(bottom, true); + nodeSetSocketAvailability(top, true); + nodeSetSocketAvailability(offset, true); + nodeSetSocketAvailability(height, true); + } + else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_KITE) { + nodeSetSocketAvailability(width, true); + nodeSetSocketAvailability(bottom_height, true); + nodeSetSocketAvailability(top_height, true); + } + else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS) { + nodeSetSocketAvailability(p1, true); + nodeSetSocketAvailability(p2, true); + nodeSetSocketAvailability(p3, true); + nodeSetSocketAvailability(p4, true); + } +} + +static void create_rectangle_curve(MutableSpan<float3> positions, + const float height, + const float width) +{ + positions[0] = float3(width / 2.0f, -height / 2.0f, 0.0f); + positions[1] = float3(-width / 2.0f, -height / 2.0f, 0.0f); + positions[2] = float3(-width / 2.0f, height / 2.0f, 0.0f); + positions[3] = float3(width / 2.0f, height / 2.0f, 0.0f); +} + +static void create_points_curve(MutableSpan<float3> positions, + const float3 &p1, + const float3 &p2, + const float3 &p3, + const float3 &p4) +{ + positions[0] = p1; + positions[1] = p2; + positions[2] = p3; + positions[3] = p4; +} + +static void create_parallelogram_curve(MutableSpan<float3> positions, + const float height, + const float width, + const float offset) +{ + positions[0] = float3(width / 2.0f - offset / 2.0f, -height / 2.0f, 0.0f); + positions[1] = float3(-width / 2.0f - offset / 2.0f, -height / 2.0f, 0.0f); + positions[2] = float3(-width / 2.0f + offset / 2.0f, height / 2.0f, 0.0f); + positions[3] = float3(width / 2.0f + offset / 2.0f, height / 2.0f, 0.0f); +} +static void create_trapezoid_curve(MutableSpan<float3> positions, + const float bottom, + const float top, + const float offset, + const float height) +{ + positions[0] = float3(bottom / 2.0f, -height / 2.0f, 0.0f); + positions[1] = float3(-bottom / 2.0f, -height / 2.0f, 0.0f); + positions[2] = float3(-top / 2.0f + offset, height / 2.0f, 0.0f); + positions[3] = float3(top / 2.0f + offset, height / 2.0f, 0.0f); +} + +static void create_kite_curve(MutableSpan<float3> positions, + const float width, + const float bottom_height, + const float top_height) +{ + positions[0] = float3(-width / 2.0f, 0, 0); + positions[1] = float3(0, top_height, 0); + positions[2] = float3(width / 2, 0, 0); + positions[3] = float3(0, -bottom_height, 0); +} + +static void geo_node_curve_primitive_quadrilateral_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurvePrimitiveQuad &node_storage = + *(NodeGeometryCurvePrimitiveQuad *)(params.node()).storage; + const GeometryNodeCurvePrimitiveQuadMode mode = static_cast<GeometryNodeCurvePrimitiveQuadMode>( + node_storage.mode); + + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->resize(4); + spline->radii().fill(1.0f); + spline->tilts().fill(0.0f); + spline->set_cyclic(true); + MutableSpan<float3> positions = spline->positions(); + + switch (mode) { + case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE: + create_rectangle_curve(positions, + std::max(params.extract_input<float>("Height"), 0.0f), + std::max(params.extract_input<float>("Width"), 0.0f)); + break; + + case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM: + create_parallelogram_curve(positions, + std::max(params.extract_input<float>("Height"), 0.0f), + std::max(params.extract_input<float>("Width"), 0.0f), + params.extract_input<float>("Offset")); + break; + case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID: + create_trapezoid_curve(positions, + std::max(params.extract_input<float>("Bottom Width"), 0.0f), + std::max(params.extract_input<float>("Top Width"), 0.0f), + params.extract_input<float>("Offset"), + std::max(params.extract_input<float>("Height"), 0.0f)); + break; + case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_KITE: + create_kite_curve(positions, + std::max(params.extract_input<float>("Width"), 0.0f), + std::max(params.extract_input<float>("Bottom Height"), 0.0f), + params.extract_input<float>("Top Height")); + break; + case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS: + create_points_curve(positions, + params.extract_input<float3>("Point 1"), + params.extract_input<float3>("Point 2"), + params.extract_input<float3>("Point 3"), + params.extract_input<float3>("Point 4")); + break; + default: + params.set_output("Curve", GeometrySet()); + return; + } + + curve->add_spline(std::move(spline)); + curve->attributes.reallocate(curve->splines().size()); + params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_primitive_quadrilateral() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, "Quadrilateral", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, + geo_node_curve_primitive_quadrilateral_in, + geo_node_curve_primitive_quadrilateral_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_quadrilateral_exec; + ntype.draw_buttons = geo_node_curve_primitive_quadrilateral_layout; + node_type_update(&ntype, blender::nodes::geo_node_curve_primitive_quadrilateral_update); + node_type_init(&ntype, geo_node_curve_primitive_quadrilateral_init); + node_type_storage(&ntype, + "NodeGeometryCurvePrimitiveQuad", + node_free_standard_storage, + node_copy_standard_storage); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc index 367da3bc3c2..2e2bb247e2b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc @@ -19,7 +19,7 @@ #include "node_geometry_util.hh" static bNodeSocketTemplate geo_node_curve_primitive_star_in[] = { - {SOCK_INT, N_("Points"), 8.0f, 0.0f, 0.0f, 0.0f, 4, 256, PROP_UNSIGNED}, + {SOCK_INT, N_("Points"), 8.0f, 0.0f, 0.0f, 0.0f, 3, 256, PROP_UNSIGNED}, {SOCK_FLOAT, N_("Inner Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, {SOCK_FLOAT, N_("Outer Radius"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, {SOCK_FLOAT, N_("Twist"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_ANGLE}, @@ -64,7 +64,7 @@ static void geo_node_curve_primitive_star_exec(GeoNodeExecParams params) std::max(params.extract_input<float>("Inner Radius"), 0.0f), std::max(params.extract_input<float>("Outer Radius"), 0.0f), params.extract_input<float>("Twist"), - std::max(params.extract_input<int>("Points"), 4)); + std::max(params.extract_input<int>("Points"), 3)); params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index fc65d1754e9..ad0c453c2af 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -87,6 +87,23 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count) input_spline.tilts().first(), input_spline.radii().first()); output_spline->attributes.reallocate(1); + input_spline.attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + std::optional<GSpan> src = input_spline.attributes.get_for_read(name); + BLI_assert(src); + if (!output_spline->attributes.create(name, meta_data.data_type)) { + BLI_assert_unreachable(); + return false; + } + std::optional<GMutableSpan> dst = output_spline->attributes.get_for_write(name); + if (!dst) { + BLI_assert_unreachable(); + return false; + } + src->type().copy_assign(src->data(), dst->data()); + return true; + }, + ATTR_DOMAIN_POINT); return output_spline; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 3de2604cd0a..7908c26e2dc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -116,38 +116,6 @@ static void subdivide_attribute(Span<T> src, } /** - * De Casteljau Bezier subdivision. - * - * <pre> - * handle_prev handle_next - * O----------------O - * / \ - * / x---O---x \ - * / new_* \ - * / \ - * O O - * point_prev point_next - * </pre> - */ -static void calculate_new_bezier_point(const float3 &point_prev, - float3 &handle_prev, - float3 &new_left_handle, - float3 &new_position, - float3 &new_right_handle, - float3 &handle_next, - const float3 &point_next, - const float parameter) -{ - const float3 center_point = float3::interpolate(handle_prev, handle_next, parameter); - - handle_prev = float3::interpolate(point_prev, handle_prev, parameter); - handle_next = float3::interpolate(handle_next, point_next, parameter); - new_left_handle = float3::interpolate(handle_prev, center_point, parameter); - new_right_handle = float3::interpolate(center_point, handle_next, parameter); - new_position = float3::interpolate(new_left_handle, new_right_handle, parameter); -} - -/** * In order to generate a Bezier spline with the same shape as the input spline, apply the * De Casteljau algorithm iteratively for the provided number of cuts, constantly updating the * previous result point's right handle and the left handle at the end of the segment. @@ -171,6 +139,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, { const bool is_last_cyclic_segment = index == (src.size() - 1); const int next_index = is_last_cyclic_segment ? 0 : index + 1; + + /* The first point in the segment is always copied. */ + dst_positions[offset] = src_positions[index]; + if (src.segment_is_vector(index)) { if (is_last_cyclic_segment) { dst_type_left.first() = BezierSpline::HandleType::Vector; @@ -178,7 +150,6 @@ static void subdivide_bezier_segment(const BezierSpline &src, dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector); dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector); - dst_positions[offset] = src_positions[index]; const float factor_delta = 1.0f / result_size; for (const int cut : IndexRange(result_size)) { const float factor = cut * factor_delta; @@ -194,21 +165,38 @@ static void subdivide_bezier_segment(const BezierSpline &src, dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free); const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; - dst_positions[offset] = src_positions[index]; - dst_handles_right[offset] = src_handles_right[index]; - dst_handles_left[i_segment_last] = src_handles_left[next_index]; + + /* Create a Bezier segment to update iteratively for every subdivision + * and references to the meaningful values for ease of use. */ + BezierSpline temp; + temp.resize(2); + float3 &segment_start = temp.positions().first(); + float3 &segment_end = temp.positions().last(); + float3 &handle_prev = temp.handle_positions_right().first(); + float3 &handle_next = temp.handle_positions_left().last(); + segment_start = src_positions[index]; + segment_end = src_positions[next_index]; + handle_prev = src_handles_right[index]; + handle_next = src_handles_left[next_index]; for (const int cut : IndexRange(result_size - 1)) { const float parameter = 1.0f / (result_size - cut); - calculate_new_bezier_point(dst_positions[offset + cut], - dst_handles_right[offset + cut], - dst_handles_left[offset + cut + 1], - dst_positions[offset + cut + 1], - dst_handles_right[offset + cut + 1], - dst_handles_left[i_segment_last], - src_positions[next_index], - parameter); + const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter); + + /* Copy relevant temporary data to the result. */ + dst_handles_right[offset + cut] = insert.handle_prev; + dst_handles_left[offset + cut + 1] = insert.left_handle; + dst_positions[offset + cut + 1] = insert.position; + + /* Update the segment to prepare it for the next subdivision. */ + segment_start = insert.position; + handle_prev = insert.right_handle; + handle_next = insert.handle_next; } + + /* Copy the handles for the last segment from the temporary spline. */ + dst_handles_right[offset + result_size - 1] = handle_prev; + dst_handles_left[i_segment_last] = handle_next; } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index c0d817385e2..f1bcb4ed47f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -337,7 +337,17 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) curve_set = bke::geometry_set_realize_instances(curve_set); profile_set = bke::geometry_set_realize_instances(profile_set); + /* NOTE: Theoretically an "is empty" check would be more correct for errors. */ + if (profile_set.has_mesh() && !profile_set.has_curve()) { + params.error_message_add(NodeWarningType::Warning, + TIP_("No curve data available in profile input")); + } + if (!curve_set.has_curve()) { + if (curve_set.has_mesh()) { + params.error_message_add(NodeWarningType::Warning, + TIP_("No curve data available in curve input")); + } params.set_output("Mesh", GeometrySet()); return; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index e37822bd262..bb8f560f92d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -118,22 +118,6 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, return {0}; } -/** - * \note This doesn't store a map for spline domain attributes. - */ -struct ResultAttributes { - int result_size; - MutableSpan<float3> positions; - MutableSpan<float> radii; - MutableSpan<float> tilts; - - Map<std::string, GMutableSpan> point_attributes; - - MutableSpan<float3> tangents; - MutableSpan<float3> normals; - MutableSpan<float3> rotations; -}; - static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points, const StringRef name, const CustomDataType data_type) @@ -153,13 +137,10 @@ static MutableSpan<T> create_attribute_and_retrieve_span(PointCloudComponent &po return attribute.typed<T>(); } -/** - * Create references for all result point cloud attributes to simplify accessing them later on. - */ -static ResultAttributes create_point_attributes(PointCloudComponent &points, - const CurveEval &curve) +CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponent &points, + const CurveEval &curve) { - ResultAttributes attributes; + CurveToPointsResults attributes; attributes.result_size = points.attribute_domain_size(ATTR_DOMAIN_POINT); @@ -190,7 +171,7 @@ static ResultAttributes create_point_attributes(PointCloudComponent &points, */ static void copy_evaluated_point_attributes(Span<SplinePtr> splines, Span<int> offsets, - ResultAttributes &data) + CurveToPointsResults &data) { threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { for (const int i : range) { @@ -221,7 +202,7 @@ static void copy_evaluated_point_attributes(Span<SplinePtr> splines, static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines, Span<int> offsets, - ResultAttributes &data) + CurveToPointsResults &data) { threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { for (const int i : range) { @@ -307,13 +288,14 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component, }); } -static void create_default_rotation_attribute(ResultAttributes &data) +void curve_create_default_rotation_attribute(Span<float3> tangents, + Span<float3> normals, + MutableSpan<float3> rotations) { - threading::parallel_for(IndexRange(data.result_size), 512, [&](IndexRange range) { + threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) { for (const int i : range) { - data.rotations[i] = float4x4::from_normalized_axis_data( - {0, 0, 0}, data.normals[i], data.tangents[i]) - .to_euler(); + rotations[i] = + float4x4::from_normalized_axis_data({0, 0, 0}, normals[i], tangents[i]).to_euler(); } }); } @@ -348,8 +330,8 @@ static void geo_node_curve_to_points_exec(GeoNodeExecParams params) GeometrySet result = GeometrySet::create_with_pointcloud(BKE_pointcloud_new_nomain(total_size)); PointCloudComponent &point_component = result.get_component_for_write<PointCloudComponent>(); - ResultAttributes new_attributes = create_point_attributes(point_component, curve); - + CurveToPointsResults new_attributes = curve_to_points_create_result_attributes(point_component, + curve); switch (mode) { case GEO_NODE_CURVE_SAMPLE_COUNT: case GEO_NODE_CURVE_SAMPLE_LENGTH: @@ -361,7 +343,8 @@ static void geo_node_curve_to_points_exec(GeoNodeExecParams params) } copy_spline_domain_attributes(curve_component, offsets, point_component); - create_default_rotation_attribute(new_attributes); + curve_create_default_rotation_attribute( + new_attributes.tangents, new_attributes.normals, new_attributes.rotations); /* The default radius is way too large for points, divide by 10. */ for (float &radius : new_attributes.radii) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index b1da2dcd3c4..8c697275f88 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -627,7 +627,7 @@ static void delete_mesh_selection(MeshComponent &component, mesh_out = nullptr; break; } - component.replace_mesh_but_keep_vertex_group_names(mesh_out); + component.replace(mesh_out); } static void geo_node_delete_geometry_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 2915a17d2c8..667e1c931bd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -228,7 +228,8 @@ void register_node_type_geo_mesh_primitive_circle() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Circle", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Mesh Circle", NODE_CLASS_GEOMETRY, 0); node_type_socket_templates( &ntype, geo_node_mesh_primitive_circle_in, geo_node_mesh_primitive_circle_out); node_type_init(&ntype, geo_node_mesh_primitive_circle_init); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index e841455e58c..a193c05daa1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -177,7 +177,7 @@ void register_node_type_geo_mesh_primitive_line() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_LINE, "Line", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_LINE, "Mesh Line", NODE_CLASS_GEOMETRY, 0); node_type_socket_templates( &ntype, geo_node_mesh_primitive_line_in, geo_node_mesh_primitive_line_out); node_type_init(&ntype, geo_node_mesh_primitive_line_init); diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index a4c6522c57a..245d7800621 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -14,8 +14,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// #include "MEM_guardedalloc.h" - #include "BKE_mesh.h" #include "BKE_subdiv.h" #include "BKE_subdiv_mesh.h" @@ -25,20 +23,20 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_subdivide_in[] = { +static bNodeSocketTemplate geo_node_mesh_subdivide_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, {SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6}, {-1, ""}, }; -static bNodeSocketTemplate geo_node_subdivide_out[] = { +static bNodeSocketTemplate geo_node_mesh_subdivide_out[] = { {SOCK_GEOMETRY, N_("Geometry")}, {-1, ""}, }; namespace blender::nodes { -static void geo_node_subdivide_exec(GeoNodeExecParams params) +static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); geometry_set = geometry_set_realize_instances(geometry_set); @@ -93,7 +91,7 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params) BKE_mesh_calc_normals(mesh_out); MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - mesh_component.replace_mesh_but_keep_vertex_group_names(mesh_out); + mesh_component.replace(mesh_out); BKE_subdiv_free(subdiv); @@ -102,12 +100,12 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_subdivide() +void register_node_type_geo_mesh_subdivide() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_SUBDIVIDE, "Subdivide", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_subdivide_in, geo_node_subdivide_out); - ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec; + geo_node_type_base(&ntype, GEO_NODE_MESH_SUBDIVIDE, "Mesh Subdivide", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_mesh_subdivide_in, geo_node_mesh_subdivide_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_subdivide_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc index 772bd8a1080..d456c72744f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc @@ -83,8 +83,7 @@ static float3 normal_to_euler_rotation(const float3 normal) static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) { - /* 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 MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(&mesh); const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh); return {looptris, looptris_len}; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index bfd6027e0fc..0c1d8645411 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -95,7 +95,7 @@ static void raycast_to_mesh(const Mesh *mesh, BLI_assert(ray_origins.size() == r_hit_distances.size() || r_hit_distances.is_empty()); BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(mesh), BVHTREE_FROM_LOOPTRI, 4); + BKE_bvhtree_from_mesh_get(&tree_data, mesh, BVHTREE_FROM_LOOPTRI, 4); if (tree_data.tree != nullptr) { for (const int i : ray_origins.index_range()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 20ffcdb516d..f0f032ed8f4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -96,7 +96,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) BKE_mesh_calc_normals(mesh_out); MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - mesh_component.replace_mesh_but_keep_vertex_group_names(mesh_out); + mesh_component.replace(mesh_out); // BKE_subdiv_stats_print(&subdiv->stats); BKE_subdiv_free(subdiv); diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc new file mode 100644 index 00000000000..f0b91f49f52 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc @@ -0,0 +1,31 @@ +/* + * 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" + +static bNodeSocketTemplate geo_node_viewer_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +void register_node_type_geo_viewer() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, 0); + node_type_socket_templates(&ntype, geo_node_viewer_in, nullptr); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc new file mode 100644 index 00000000000..3024cc51cad --- /dev/null +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -0,0 +1,361 @@ +/* + * 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_nodes_eval_log.hh" + +#include "BKE_geometry_set_instances.hh" + +#include "DNA_modifier_types.h" +#include "DNA_space_types.h" + +namespace blender::nodes::geometry_nodes_eval_log { + +using fn::CPPType; + +ModifierLog::ModifierLog(GeoLogger &logger) +{ + root_tree_logs_ = allocator_.construct<TreeLog>(); + + LogByTreeContext log_by_tree_context; + + /* Combine all the local loggers that have been used by separate threads. */ + for (LocalGeoLogger &local_logger : logger) { + /* Take ownership of the allocator. */ + logger_allocators_.append(std::move(local_logger.allocator_)); + + for (ValueOfSockets &value_of_sockets : local_logger.values_) { + ValueLog *value_log = value_of_sockets.value.get(); + + /* Take centralized ownership of the logged value. It might be referenced by multiple + * sockets. */ + logged_values_.append(std::move(value_of_sockets.value)); + + for (const DSocket &socket : value_of_sockets.sockets) { + SocketLog &socket_log = this->lookup_or_add_socket_log(log_by_tree_context, socket); + socket_log.value_ = value_log; + } + } + + for (NodeWithWarning &node_with_warning : local_logger.node_warnings_) { + NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, + node_with_warning.node); + node_log.warnings_.append(node_with_warning.warning); + } + } +} + +TreeLog &ModifierLog::lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context, + const DTreeContext &tree_context) +{ + TreeLog *tree_log = log_by_tree_context.lookup_default(&tree_context, nullptr); + if (tree_log != nullptr) { + return *tree_log; + } + + const DTreeContext *parent_context = tree_context.parent_context(); + if (parent_context == nullptr) { + return *root_tree_logs_.get(); + } + TreeLog &parent_log = this->lookup_or_add_tree_log(log_by_tree_context, *parent_context); + destruct_ptr<TreeLog> owned_tree_log = allocator_.construct<TreeLog>(); + tree_log = owned_tree_log.get(); + log_by_tree_context.add_new(&tree_context, tree_log); + parent_log.child_logs_.add_new(tree_context.parent_node()->name(), std::move(owned_tree_log)); + return *tree_log; +} + +NodeLog &ModifierLog::lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node) +{ + TreeLog &tree_log = this->lookup_or_add_tree_log(log_by_tree_context, *node.context()); + NodeLog &node_log = *tree_log.node_logs_.lookup_or_add_cb(node->name(), [&]() { + destruct_ptr<NodeLog> node_log = allocator_.construct<NodeLog>(); + node_log->input_logs_.resize(node->inputs().size()); + node_log->output_logs_.resize(node->outputs().size()); + return node_log; + }); + return node_log; +} + +SocketLog &ModifierLog::lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context, + DSocket socket) +{ + NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, socket.node()); + MutableSpan<SocketLog> socket_logs = socket->is_input() ? node_log.input_logs_ : + node_log.output_logs_; + SocketLog &socket_log = socket_logs[socket->index()]; + return socket_log; +} + +const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const +{ + const destruct_ptr<NodeLog> *node_log = node_logs_.lookup_ptr_as(node_name); + if (node_log == nullptr) { + return nullptr; + } + return node_log->get(); +} + +const NodeLog *TreeLog::lookup_node_log(const bNode &node) const +{ + return this->lookup_node_log(node.name); +} + +const TreeLog *TreeLog::lookup_child_log(StringRef node_name) const +{ + const destruct_ptr<TreeLog> *tree_log = child_logs_.lookup_ptr_as(node_name); + if (tree_log == nullptr) { + return nullptr; + } + return tree_log->get(); +} + +const SocketLog *NodeLog::lookup_socket_log(eNodeSocketInOut in_out, int index) const +{ + BLI_assert(index >= 0); + Span<SocketLog> socket_logs = (in_out == SOCK_IN) ? input_logs_ : output_logs_; + if (index >= socket_logs.size()) { + return nullptr; + } + return &socket_logs[index]; +} + +const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket &socket) const +{ + ListBase sockets = socket.in_out == SOCK_IN ? node.inputs : node.outputs; + int index = BLI_findindex(&sockets, &socket); + return this->lookup_socket_log((eNodeSocketInOut)socket.in_out, index); +} + +GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry) +{ + bke::geometry_set_instances_attribute_foreach( + geometry_set, + [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) { + this->attributes_.append({attribute_name, meta_data.domain, meta_data.data_type}); + return true; + }, + 8); + for (const GeometryComponent *component : geometry_set.get_components_for_read()) { + component_types_.append(component->type()); + switch (component->type()) { + case GEO_COMPONENT_TYPE_MESH: { + const MeshComponent &mesh_component = *(const MeshComponent *)component; + MeshInfo &info = this->mesh_info.emplace(); + info.tot_verts = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); + info.tot_edges = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); + info.tot_faces = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE); + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + const CurveComponent &curve_component = *(const CurveComponent *)component; + CurveInfo &info = this->curve_info.emplace(); + info.tot_splines = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE); + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component; + PointCloudInfo &info = this->pointcloud_info.emplace(); + info.tot_points = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT); + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + const InstancesComponent &instances_component = *(const InstancesComponent *)component; + InstancesInfo &info = this->instances_info.emplace(); + info.tot_instances = instances_component.instances_amount(); + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + break; + } + } + } + if (log_full_geometry) { + full_geometry_ = std::make_unique<GeometrySet>(geometry_set); + full_geometry_->ensure_owns_direct_data(); + } +} + +Vector<const GeometryAttributeInfo *> NodeLog::lookup_available_attributes() const +{ + Vector<const GeometryAttributeInfo *> attributes; + Set<StringRef> names; + for (const SocketLog &socket_log : input_logs_) { + const ValueLog *value_log = socket_log.value(); + if (const GeometryValueLog *geo_value_log = dynamic_cast<const GeometryValueLog *>( + value_log)) { + for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) { + if (names.add(attribute.name)) { + attributes.append(&attribute); + } + } + } + } + return attributes; +} + +const ModifierLog *ModifierLog::find_root_by_node_editor_context(const SpaceNode &snode) +{ + if (snode.id == nullptr) { + return nullptr; + } + if (GS(snode.id->name) != ID_OB) { + return nullptr; + } + Object *object = (Object *)snode.id; + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = (NodesModifierData *)md; + if (nmd->node_group == snode.nodetree) { + return (ModifierLog *)nmd->runtime_eval_log; + } + } + } + return nullptr; +} + +const TreeLog *ModifierLog::find_tree_by_node_editor_context(const SpaceNode &snode) +{ + const ModifierLog *eval_log = ModifierLog::find_root_by_node_editor_context(snode); + if (eval_log == nullptr) { + return nullptr; + } + Vector<bNodeTreePath *> tree_path_vec = snode.treepath; + if (tree_path_vec.is_empty()) { + return nullptr; + } + TreeLog *current = eval_log->root_tree_logs_.get(); + for (bNodeTreePath *path : tree_path_vec.as_span().drop_front(1)) { + destruct_ptr<TreeLog> *tree_log = current->child_logs_.lookup_ptr_as(path->node_name); + if (tree_log == nullptr) { + return nullptr; + } + current = tree_log->get(); + } + return current; +} + +const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode, + const bNode &node) +{ + const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode); + if (tree_log == nullptr) { + return nullptr; + } + return tree_log->lookup_node_log(node); +} + +const SocketLog *ModifierLog::find_socket_by_node_editor_context(const SpaceNode &snode, + const bNode &node, + const bNodeSocket &socket) +{ + const NodeLog *node_log = ModifierLog::find_node_by_node_editor_context(snode, node); + if (node_log == nullptr) { + return nullptr; + } + return node_log->lookup_socket_log(node, socket); +} + +const NodeLog *ModifierLog::find_node_by_spreadsheet_editor_context( + const SpaceSpreadsheet &sspreadsheet) +{ + Vector<SpreadsheetContext *> context_path = sspreadsheet.context_path; + if (context_path.size() <= 2) { + return nullptr; + } + if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { + return nullptr; + } + if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) { + return nullptr; + } + for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) { + if (context->type != SPREADSHEET_CONTEXT_NODE) { + return nullptr; + } + } + Span<SpreadsheetContextNode *> node_contexts = + context_path.as_span().drop_front(2).cast<SpreadsheetContextNode *>(); + + Object *object = ((SpreadsheetContextObject *)context_path[0])->object; + StringRefNull modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name; + if (object == nullptr) { + return nullptr; + } + + const ModifierLog *eval_log = nullptr; + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + if (md->name == modifier_name) { + NodesModifierData *nmd = (NodesModifierData *)md; + eval_log = (const ModifierLog *)nmd->runtime_eval_log; + break; + } + } + } + if (eval_log == nullptr) { + return nullptr; + } + + const TreeLog *tree_log = &eval_log->root_tree(); + for (SpreadsheetContextNode *context : node_contexts.drop_back(1)) { + tree_log = tree_log->lookup_child_log(context->node_name); + if (tree_log == nullptr) { + return nullptr; + } + } + const NodeLog *node_log = tree_log->lookup_node_log(node_contexts.last()->node_name); + return node_log; +} + +void LocalGeoLogger::log_value_for_sockets(Span<DSocket> sockets, GPointer value) +{ + const CPPType &type = *value.type(); + Span<DSocket> copied_sockets = allocator_->construct_array_copy(sockets); + if (type.is<GeometrySet>()) { + bool log_full_geometry = false; + for (const DSocket &socket : sockets) { + if (main_logger_->log_full_geometry_sockets_.contains(socket)) { + log_full_geometry = true; + break; + } + } + + const GeometrySet &geometry_set = *value.get<GeometrySet>(); + destruct_ptr<GeometryValueLog> value_log = allocator_->construct<GeometryValueLog>( + geometry_set, log_full_geometry); + values_.append({copied_sockets, std::move(value_log)}); + } + else { + void *buffer = allocator_->allocate(type.size(), type.alignment()); + type.copy_construct(value.get(), buffer); + destruct_ptr<GenericValueLog> value_log = allocator_->construct<GenericValueLog>( + GMutablePointer{type, buffer}); + values_.append({copied_sockets, std::move(value_log)}); + } +} + +void LocalGeoLogger::log_multi_value_socket(DSocket socket, Span<GPointer> values) +{ + /* Doesn't have to be logged currently. */ + UNUSED_VARS(socket, values); +} + +void LocalGeoLogger::log_node_warning(DNode node, NodeWarningType type, std::string message) +{ + node_warnings_.append({node, {type, std::move(message)}}); +} + +} // namespace blender::nodes::geometry_nodes_eval_log diff --git a/source/blender/nodes/intern/node_common.c b/source/blender/nodes/intern/node_common.c index 7fc9f664df0..b8c89d1db37 100644 --- a/source/blender/nodes/intern/node_common.c +++ b/source/blender/nodes/intern/node_common.c @@ -127,7 +127,7 @@ static bNodeSocket *group_verify_socket( bNodeSocket *sock; for (sock = verify_lb->first; sock; sock = sock->next) { - if (sock->typeinfo == iosock->typeinfo && STREQ(sock->identifier, iosock->identifier)) { + if (STREQ(sock->identifier, iosock->identifier)) { break; } } @@ -137,6 +137,13 @@ static bNodeSocket *group_verify_socket( const int mask = SOCK_HIDE_VALUE; sock->flag = (sock->flag & ~mask) | (iosock->flag & mask); + /* Update socket type if necessary */ + if (sock->typeinfo != iosock->typeinfo) { + nodeModifySocketType(ntree, gnode, sock, iosock->idname); + /* Flag the tree to make sure link validity is updated after type changes. */ + ntree->update |= NTREE_UPDATE_LINKS; + } + if (iosock->typeinfo->interface_verify_socket) { iosock->typeinfo->interface_verify_socket(ntree, iosock, gnode, sock, "interface"); } @@ -259,7 +266,7 @@ static void node_reroute_update_internal_links(bNodeTree *ntree, bNode *node) static void node_reroute_init(bNodeTree *ntree, bNode *node) { - /* Note: Cannot use socket templates for this, since it would reset the socket type + /* NOTE: Cannot use socket templates for this, since it would reset the socket type * on each file read via the template verification procedure. */ nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, "Input", "Input"); diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 8b61758a673..1e39aa5214d 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -16,8 +16,6 @@ #include "DNA_modifier_types.h" -#include "BKE_node_ui_storage.hh" - #include "DEG_depsgraph_query.h" #include "NOD_geometry_exec.hh" @@ -26,21 +24,17 @@ #include "node_geometry_util.hh" +using blender::nodes::geometry_nodes_eval_log::LocalGeoLogger; + namespace blender::nodes { void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const { - bNodeTree *btree_cow = provider_->dnode->btree(); - BLI_assert(btree_cow != nullptr); - if (btree_cow == nullptr) { + if (provider_->logger == nullptr) { return; } - bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow); - - const NodeTreeEvaluationContext context(*provider_->self_object, *provider_->modifier); - - BKE_nodetree_error_message_add( - *btree_original, context, *provider_->dnode->bnode(), type, std::move(message)); + LocalGeoLogger &local_logger = provider_->logger->local(); + local_logger.log_node_warning(provider_->dnode, type, std::move(message)); } const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index c0a8382a661..e2568fd8f85 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -131,7 +131,7 @@ static bNodeSocket *verify_socket_template(bNodeTree *ntree, } if (sock) { if (sock->type != stemp->type) { - nodeModifySocketType(ntree, node, sock, stemp->type, stemp->subtype); + nodeModifySocketTypeStatic(ntree, node, sock, stemp->type, stemp->subtype); } sock->flag |= stemp->flag; } @@ -563,12 +563,14 @@ static bNodeSocketType *make_standard_socket_type(int type, int subtype) { const char *socket_idname = nodeStaticSocketType(type, subtype); const char *interface_idname = nodeStaticSocketInterfaceType(type, subtype); + const char *socket_label = nodeStaticSocketLabel(type, subtype); bNodeSocketType *stype; StructRNA *srna; stype = (bNodeSocketType *)MEM_callocN(sizeof(bNodeSocketType), "node socket C type"); stype->free_self = (void (*)(bNodeSocketType * stype)) MEM_freeN; BLI_strncpy(stype->idname, socket_idname, sizeof(stype->idname)); + BLI_strncpy(stype->label, socket_label, sizeof(stype->label)); /* set the RNA type * uses the exact same identifier as the socket type idname */ diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index bed4d60382d..9ce9d6fc273 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -461,7 +461,7 @@ bool NodeTreeRef::has_undefined_nodes_or_sockets() const return true; } } - return true; + return false; } std::string NodeTreeRef::to_dot() const diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index e23e2ac3b9d..7367f73d171 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -184,10 +184,11 @@ static bool shader_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link) return true; } -static bool shader_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, - bNodeTreeType *UNUSED(ntreetype)) +static bool shader_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype), + bNodeSocketType *socket_type) { - return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER); + return nodeIsStaticSocketType(socket_type) && + ELEM(socket_type->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER); } bNodeTreeType *ntreeType_Shader; @@ -790,7 +791,7 @@ static void ntree_shader_relink_displacement(bNodeTree *ntree, bNode *output_nod */ nodeAddLink(ntree, displacement_node, displacement_socket, bump_node, bump_input_socket); - /* Tag as part of the new displacmeent tree. */ + /* Tag as part of the new displacement tree. */ dot_node->tmp_flag = -2; geo_node->tmp_flag = -2; bump_node->tmp_flag = -2; diff --git a/source/blender/nodes/shader/nodes/node_shader_hueSatVal.c b/source/blender/nodes/shader/nodes/node_shader_hueSatVal.c index 7a05fc95eec..50eb5bf32c9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_hueSatVal.c +++ b/source/blender/nodes/shader/nodes/node_shader_hueSatVal.c @@ -37,7 +37,7 @@ static bNodeSocketTemplate sh_node_hue_sat_out[] = { {-1, ""}, }; -/* note: it would be possible to use CMP version for both nodes */ +/* NOTE: it would be possible to use CMP version for both nodes. */ static void do_hue_sat_fac( bNode *UNUSED(node), float *out, float hue, float sat, float val, const float in[4], float fac) { diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index 2ae722e3cd8..f771b4934b2 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -152,10 +152,11 @@ static void update(bNodeTree *ntree) } } -static bool texture_node_tree_socket_type_valid(eNodeSocketDatatype socket_type, - bNodeTreeType *UNUSED(ntreetype)) +static bool texture_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype), + bNodeSocketType *socket_type) { - return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA); + return nodeIsStaticSocketType(socket_type) && + ELEM(socket_type->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA); } bNodeTreeType *ntreeType_Texture; |