From 263fa406cd2bc1aefe410fe735c22967ee052e22 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 5 Aug 2021 18:27:07 -0500 Subject: Fix T90087: Assigning object data doesn't copy vertex groups Assigning a mesh seems to do its own parameter copying, which means we need to manual copy its vertex groups here, which was just overlooked in rB3b6ee8cee708. Differential Revision: https://developer.blender.org/D12110 --- source/blender/blenkernel/intern/mesh_convert.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index 0e4fe91e577..fbcd916a0df 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -38,6 +38,7 @@ #include "BLI_utildefines.h" #include "BKE_DerivedMesh.h" +#include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_editmesh.h" #include "BKE_key.h" @@ -1665,6 +1666,9 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, /* skip the listbase */ MEMCPY_STRUCT_AFTER(mesh_dst, &tmp, id.prev); + BKE_defgroup_copy_list(&mesh_dst->vertex_group_names, &mesh_src->vertex_group_names); + mesh_dst->vertex_group_active_index = mesh_src->vertex_group_active_index; + if (take_ownership) { if (alloctype == CD_ASSIGN) { CustomData_free_typemask(&mesh_src->vdata, mesh_src->totvert, ~mask->vmask); -- cgit v1.2.3 From bc0d55e724a27fba61a93cc95f2cc48e205e1cd8 Mon Sep 17 00:00:00 2001 From: Mattias Fredriksson Date: Thu, 5 Aug 2021 18:34:32 -0500 Subject: Fix: Avoid floating point error in some mesh primitive nodes Some mesh primitives created using geometry nodes use loops to create vertices and accumulates positions/angles in FP variables. This allows rounding errors to accumulate and can introduce significant errors. To minimize changes from original implementation, variables allowing errors to accumulate are replaced by: delta * index. Affected Mesh Primitives nodes are Line, Grid, Cylinder, Circle, Cone, and UV-Sphere. Differential Revision: https://developer.blender.org/D12136 --- .../nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc | 10 +++++----- .../nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc | 5 ++--- .../nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc | 14 ++++++-------- .../nodes/geometry/nodes/node_geo_mesh_primitive_line.cc | 4 +--- .../geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc | 14 ++++++-------- 5 files changed, 20 insertions(+), 27 deletions(-) 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 96c6f073ab3..131f9548b40 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 @@ -126,11 +126,11 @@ static Mesh *create_circle_mesh(const float radius, MutableSpan edges{mesh->medge, mesh->totedge}; MutableSpan polys{mesh->mpoly, mesh->totpoly}; - float angle = 0.0f; - const float angle_delta = 2.0f * M_PI / static_cast(verts_num); - for (MVert &vert : verts) { - copy_v3_v3(vert.co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f)); - angle += angle_delta; + /* Assign vertex coordinates. */ + const float angle_delta = 2.0f * (M_PI / static_cast(verts_num)); + for (const int i : IndexRange(verts_num)) { + const float angle = i * angle_delta; + copy_v3_v3(verts[i].co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f)); } if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { copy_v3_v3(verts.last().co, float3(0)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 790a518e584..b834f5e2fa0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -318,9 +318,9 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, /* Calculate vertex positions. */ const int top_verts_start = 0; const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1); - float angle = 0.0f; - const float angle_delta = 2.0f * M_PI / static_cast(verts_num); + const float angle_delta = 2.0f * (M_PI / static_cast(verts_num)); for (const int i : IndexRange(verts_num)) { + const float angle = i * angle_delta; const float x = std::cos(angle); const float y = std::sin(angle); if (!top_is_point) { @@ -330,7 +330,6 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, copy_v3_v3(verts[bottom_verts_start + i].co, float3(x * radius_bottom, y * radius_bottom, -height)); } - angle += angle_delta; } if (top_is_point) { copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 7a97ae8e318..410290c79ee 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -79,19 +79,17 @@ static Mesh *create_grid_mesh(const int verts_x, MutableSpan polys{mesh->mpoly, mesh->totpoly}; { - const float dx = size_x / edges_x; - const float dy = size_y / edges_y; - float x = -size_x * 0.5; + const float dx = edges_x == 0 ? 0.0f : size_x / edges_x; + const float dy = edges_y == 0 ? 0.0f : size_y / edges_y; + const float x_shift = edges_x / 2.0f; + const float y_shift = edges_y / 2.0f; for (const int x_index : IndexRange(verts_x)) { - float y = -size_y * 0.5; for (const int y_index : IndexRange(verts_y)) { const int vert_index = x_index * verts_y + y_index; - verts[vert_index].co[0] = x; - verts[vert_index].co[1] = y; + verts[vert_index].co[0] = (x_index - x_shift) * dx; + verts[vert_index].co[1] = (y_index - y_shift) * dy; verts[vert_index].co[2] = 0.0f; - y += dy; } - x += dx; } } 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 a193c05daa1..2e6d8ca34c8 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 @@ -118,11 +118,9 @@ static Mesh *create_line_mesh(const float3 start, const float3 delta, const int short normal[3]; normal_float_to_short_v3(normal, delta.normalized()); - float3 co = start; for (const int i : verts.index_range()) { - copy_v3_v3(verts[i].co, co); + copy_v3_v3(verts[i].co, start + delta * i); copy_v3_v3_short(verts[i].no, normal); - co += delta; } fill_edge_data(edges); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index fe456dc4564..affba602234 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -69,26 +69,24 @@ static void calculate_sphere_vertex_data(MutableSpan verts, const int rings) { const float delta_theta = M_PI / rings; - const float delta_phi = (2 * M_PI) / segments; + const float delta_phi = (2.0f * M_PI) / segments; copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius)); normal_float_to_short_v3(verts[0].no, float3(0.0f, 0.0f, 1.0f)); int vert_index = 1; - float theta = delta_theta; - for (const int UNUSED(ring) : IndexRange(rings - 1)) { - float phi = 0.0f; - const float z = cosf(theta); - for (const int UNUSED(segment) : IndexRange(segments)) { + for (const int ring : IndexRange(1, rings - 1)) { + const float theta = ring * delta_theta; + const float z = std::cos(theta); + for (const int segment : IndexRange(1, segments)) { + const float phi = segment * delta_phi; const float sin_theta = std::sin(theta); const float x = sin_theta * std::cos(phi); const float y = sin_theta * std::sin(phi); copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius); normal_float_to_short_v3(verts[vert_index].no, float3(x, y, z)); - phi += delta_phi; vert_index++; } - theta += delta_theta; } copy_v3_v3(verts.last().co, float3(0.0f, 0.0f, -radius)); -- cgit v1.2.3 From 1f8485ae8222d4bcf6c71b8156bc61525cc5f391 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 5 Aug 2021 18:42:20 -0500 Subject: Geometry Nodes: Select by Handle Type Node Just like the "Select by Material" node, this node outputs a boolean attribute for control points that have a matching handle type. By default left and right handles are considered, but it's possible to only check one side with the toggle in the node. Differential Revision: https://developer.blender.org/D12135 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesdna/DNA_node_types.h | 7 + source/blender/makesrna/intern/rna_nodetree.c | 78 ++++++----- source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_geo_curve_select_by_handle_type.cc | 147 +++++++++++++++++++++ 9 files changed, 208 insertions(+), 30 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 03859b04c50..9c67ab1e57f 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -512,6 +512,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveReverse"), NodeItem("GeometryNodeCurveSplineType"), NodeItem("GeometryNodeCurveSetHandles"), + NodeItem("GeometryNodeCurveSelectHandles"), ]), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ NodeItem("GeometryNodeCurvePrimitiveLine"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 084ec20c172..caa7ab6de0a 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1476,6 +1476,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_TRIM 1071 #define GEO_NODE_CURVE_SET_HANDLES 1072 #define GEO_NODE_CURVE_SPLINE_TYPE 1073 +#define GEO_NODE_CURVE_SELECT_HANDLES 1074 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 54bcdf4c92a..4aec5a9e667 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5181,6 +5181,7 @@ static void registerGeometryNodes() register_node_type_geo_points_to_volume(); register_node_type_geo_raycast(); register_node_type_geo_sample_texture(); + register_node_type_geo_select_by_handle_type(); register_node_type_geo_select_by_material(); register_node_type_geo_separate_components(); register_node_type_geo_subdivision_surface(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 4b08aeb2008..ad7722d3ed0 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1367,6 +1367,13 @@ typedef struct NodeGeometryCurveSetHandles { uint8_t mode; } NodeGeometryCurveSetHandles; +typedef struct NodeGeometryCurveSelectHandles { + /* GeometryNodeCurveHandleType. */ + uint8_t handle_type; + /* GeometryNodeCurveHandleMode. */ + uint8_t mode; +} NodeGeometryCurveSelectHandles; + typedef struct NodeGeometryCurvePrimitiveLine { /* GeometryNodeCurvePrimitiveLineMode. */ uint8_t mode; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index cd7cbbf76d3..5de7aa9a18b 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -439,6 +439,34 @@ static const EnumPropertyItem rna_node_geometry_attribute_randomize_operation_it {0, NULL, 0, NULL, NULL}, }; +static const EnumPropertyItem rna_node_geometry_curve_handle_type_items[] = { + {GEO_NODE_CURVE_HANDLE_FREE, + "FREE", + ICON_HANDLE_FREE, + "Free", + "The handle can be moved anywhere, and doesn't influence the point's other handle"}, + {GEO_NODE_CURVE_HANDLE_AUTO, + "AUTO", + ICON_HANDLE_AUTO, + "Auto", + "The location is automatically calculated to be smooth"}, + {GEO_NODE_CURVE_HANDLE_VECTOR, + "VECTOR", + ICON_HANDLE_VECTOR, + "Vector", + "The location is calculated to point to the next/previous control point"}, + {GEO_NODE_CURVE_HANDLE_ALIGN, + "ALIGN", + ICON_HANDLE_ALIGNED, + "Align", + "The location is constrained to point in the opposite direction as the other handle"}, + {0, NULL, 0, NULL, NULL}}; + +static const EnumPropertyItem rna_node_geometry_curve_handle_side_items[] = { + {GEO_NODE_CURVE_HANDLE_LEFT, "LEFT", ICON_NONE, "Left", "Use the left handles"}, + {GEO_NODE_CURVE_HANDLE_RIGHT, "RIGHT", ICON_NONE, "Right", "Use the right handles"}, + {0, NULL, 0, NULL, NULL}}; + #ifndef RNA_RUNTIME static const EnumPropertyItem node_sampler_type_items[] = { {0, "NEAREST", 0, "Nearest", ""}, @@ -9460,50 +9488,40 @@ static void def_geo_curve_spline_type(StructRNA *srna) static void def_geo_curve_set_handles(StructRNA *srna) { - static const EnumPropertyItem type_items[] = { - {GEO_NODE_CURVE_HANDLE_FREE, - "FREE", - ICON_HANDLE_FREE, - "Free", - "The handle can be moved anywhere, and doesn't influence the point's other handle"}, - {GEO_NODE_CURVE_HANDLE_AUTO, - "AUTO", - ICON_HANDLE_AUTO, - "Auto", - "The location is automatically calculated to be smooth"}, - {GEO_NODE_CURVE_HANDLE_VECTOR, - "VECTOR", - ICON_HANDLE_VECTOR, - "Vector", - "The location is calculated to point to the next/previous control point"}, - {GEO_NODE_CURVE_HANDLE_ALIGN, - "ALIGN", - ICON_HANDLE_ALIGNED, - "Align", - "The location is constrained to point in the opposite direction as the other handle"}, - {0, NULL, 0, NULL, NULL}}; - - static const EnumPropertyItem mode_items[] = { - {GEO_NODE_CURVE_HANDLE_LEFT, "LEFT", ICON_NONE, "Left", "Update the left handles"}, - {GEO_NODE_CURVE_HANDLE_RIGHT, "RIGHT", ICON_NONE, "Right", "Update the right handles"}, - {0, NULL, 0, NULL, NULL}}; - PropertyRNA *prop; RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSetHandles", "storage"); prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "handle_type"); - RNA_def_property_enum_items(prop, type_items); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles"); RNA_def_property_flag(prop, PROP_ENUM_FLAG); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_select_handles(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSelectHandles", "storage"); + + prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "handle_type"); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); + RNA_def_property_ui_text(prop, "Mode", "Whether to check the type of left and right handles"); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_primitive_circle(StructRNA *srna) { static const EnumPropertyItem mode_items[] = { diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 7defb36bb83..46fb9f54bfe 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -175,6 +175,7 @@ set(SRC geometry/nodes/node_geo_curve_primitive_star.cc geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_curve_reverse.cc + geometry/nodes/node_geo_curve_select_by_handle_type.cc geometry/nodes/node_geo_curve_set_handles.cc geometry/nodes/node_geo_curve_spline_type.cc geometry/nodes/node_geo_curve_subdivide.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index c2297796b97..856d787c8d0 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -95,6 +95,7 @@ void register_node_type_geo_point_translate(void); void register_node_type_geo_points_to_volume(void); void register_node_type_geo_raycast(void); void register_node_type_geo_sample_texture(void); +void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_select_by_material(void); void register_node_type_geo_separate_components(void); void register_node_type_geo_subdivision_surface(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 6c3f91df434..3852819746e 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -303,6 +303,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "CURVE_SELECT_HANDLES", CurveSelectHandles, "Select by Handle Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc new file mode 100644 index 00000000000..fb21c05b0c0 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc @@ -0,0 +1,147 @@ +/* + * 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 "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_select_by_handle_type_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_select_by_handle_type_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_curve_select_by_handle_type_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); +} + +static void geo_node_curve_select_by_handle_type_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSelectHandles *data = (NodeGeometryCurveSelectHandles *)MEM_callocN( + sizeof(NodeGeometryCurveSelectHandles), __func__); + + data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; + data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; + node->storage = data; +} + +static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) +{ + switch (type) { + case GEO_NODE_CURVE_HANDLE_AUTO: + return BezierSpline::HandleType::Auto; + case GEO_NODE_CURVE_HANDLE_ALIGN: + return BezierSpline::HandleType::Align; + case GEO_NODE_CURVE_HANDLE_FREE: + return BezierSpline::HandleType::Free; + case GEO_NODE_CURVE_HANDLE_VECTOR: + return BezierSpline::HandleType::Vector; + } + BLI_assert_unreachable(); + return BezierSpline::HandleType::Auto; +} + +namespace blender::nodes { + +static void select_curve_by_handle_type(const CurveEval &curve, + const BezierSpline::HandleType type, + const GeometryNodeCurveHandleMode mode, + const MutableSpan r_selection) +{ + const Array offsets = curve.control_point_offsets(); + Span splines = curve.splines(); + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i_spline : range) { + const Spline &spline = *splines[i_spline]; + if (spline.type() == Spline::Type::Bezier) { + const BezierSpline &bezier_spline = static_cast(spline); + Span types_left = bezier_spline.handle_types_left(); + Span types_right = bezier_spline.handle_types_right(); + for (const int i_point : IndexRange(bezier_spline.size())) { + r_selection[offsets[i_spline] + i_point] = (mode & GEO_NODE_CURVE_HANDLE_LEFT && + types_left[i_point] == type) || + (mode & GEO_NODE_CURVE_HANDLE_RIGHT && + types_right[i_point] == type); + } + } + else { + r_selection.slice(offsets[i_spline], offsets[i_spline + 1]).fill(false); + } + } + }); +} + +static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSelectHandles *storage = + (const NodeGeometryCurveSelectHandles *)params.node().storage; + const BezierSpline::HandleType handle_type = handle_type_from_input_type( + (GeometryNodeCurveHandleType)storage->handle_type); + const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode; + + GeometrySet geometry_set = params.extract_input("Geometry"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + CurveComponent &curve_component = geometry_set.get_component_for_write(); + const CurveEval *curve = curve_component.get_for_read(); + + if (curve != nullptr) { + const std::string selection_name = params.extract_input("Selection"); + OutputAttribute_Typed selection = + curve_component.attribute_try_get_for_output_only(selection_name, ATTR_DOMAIN_POINT); + if (selection) { + select_curve_by_handle_type(*curve, handle_type, mode, selection.as_span()); + selection.save(); + } + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_select_by_handle_type() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_CURVE_SELECT_HANDLES, "Select by Handle Type", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_select_by_handle_type_in, geo_node_select_by_handle_type_out); + ntype.geometry_node_execute = blender::nodes::geo_node_select_by_handle_type_exec; + node_type_init(&ntype, geo_node_curve_select_by_handle_type_init); + node_type_storage(&ntype, + "NodeGeometryCurveSelectHandles", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = geo_node_curve_select_by_handle_type_layout; + + nodeRegisterType(&ntype); +} -- cgit v1.2.3 From 99738fbfdc8b4639d95c796bcc8711bd89c1aaa4 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 5 Aug 2021 21:10:54 -0500 Subject: Fix memory leak from rB263fa406cd2b --- source/blender/blenkernel/intern/mesh_convert.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index fbcd916a0df..ca594470cba 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -1666,6 +1666,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, /* skip the listbase */ MEMCPY_STRUCT_AFTER(mesh_dst, &tmp, id.prev); + BLI_freelistN(&mesh_dst->vertex_group_names); BKE_defgroup_copy_list(&mesh_dst->vertex_group_names, &mesh_src->vertex_group_names); mesh_dst->vertex_group_active_index = mesh_src->vertex_group_active_index; -- cgit v1.2.3 From bc97d7832906318a1185b3fb460b1d8d89020ba0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 6 Aug 2021 13:59:38 +1000 Subject: Cleanup: use MEM_SAFE_FREE macro --- source/blender/blenkernel/intern/boids.c | 30 ++++------------ source/blender/blenkernel/intern/brush.c | 5 +-- source/blender/blenkernel/intern/cloth.c | 12 ++----- source/blender/blenkernel/intern/colortools.c | 20 +++-------- source/blender/blenkernel/intern/curve.c | 5 +-- source/blender/blenkernel/intern/customdata.c | 5 +-- source/blender/blenkernel/intern/deform.c | 5 +-- source/blender/blenkernel/intern/dynamicpaint.c | 10 ++---- source/blender/blenkernel/intern/fluid.c | 30 ++++------------ source/blender/blenkernel/intern/mesh.c | 5 +-- source/blender/blenkernel/intern/multires.c | 10 ++---- source/blender/blenkernel/intern/object.c | 5 +-- source/blender/blenkernel/intern/object_deform.c | 10 ++---- source/blender/blenkernel/intern/particle.c | 25 +++----------- source/blender/blenkernel/intern/particle_system.c | 15 ++------ source/blender/blenkernel/intern/softbody.c | 6 +--- .../blender/blenkernel/intern/text_suggestions.c | 10 ++---- source/blender/blenkernel/intern/texture.c | 20 ++++------- source/blender/blentranslation/intern/blt_lang.c | 5 +-- source/blender/editors/animation/fmodifier_ui.c | 5 +-- .../blender/editors/animation/keyframes_general.c | 10 ++---- source/blender/editors/armature/meshlaplacian.c | 5 +-- source/blender/editors/curve/editcurve.c | 10 ++---- source/blender/editors/gpencil/gpencil_convert.c | 10 ++---- .../blender/editors/gpencil/gpencil_sculpt_paint.c | 5 +-- .../interface/interface_eyedropper_driver.c | 5 +-- .../blender/editors/interface/interface_handlers.c | 10 ++---- source/blender/editors/mesh/editmesh_utils.c | 5 +-- source/blender/editors/mesh/mesh_mirror.c | 5 +-- source/blender/editors/physics/particle_edit.c | 15 ++------ .../blender/editors/physics/particle_edit_undo.c | 5 +-- source/blender/editors/screen/area.c | 10 +++--- source/blender/editors/screen/screen_edit.c | 10 ++---- source/blender/editors/screen/screen_ops.c | 15 ++------ .../blender/editors/sculpt_paint/paint_image_2d.c | 10 ++---- source/blender/editors/sculpt_paint/sculpt.c | 5 +-- .../blender/editors/sculpt_paint/sculpt_dyntopo.c | 10 ++---- .../editors/space_clip/tracking_ops_solve.c | 5 +-- source/blender/editors/space_file/space_file.c | 5 +-- source/blender/editors/space_image/image_ops.c | 5 +-- source/blender/editors/space_info/info_stats.c | 5 +-- .../editors/space_sequencer/sequencer_add.c | 5 +-- source/blender/editors/space_text/text_draw.c | 5 +-- source/blender/editors/space_text/text_ops.c | 5 +-- source/blender/editors/space_view3d/view3d_edit.c | 5 +-- .../blender/editors/uvedit/uvedit_parametrizer.c | 5 +-- .../blender/editors/uvedit/uvedit_smart_stitch.c | 25 +++----------- .../gpencil_modifiers/intern/MOD_gpenciltint.c | 5 +-- source/blender/imbuf/intern/colormanagement.c | 6 +--- source/blender/imbuf/intern/moviecache.c | 16 ++------- source/blender/makesrna/intern/rna_access.c | 5 +-- .../blender/render/intern/texture_pointdensity.c | 5 +-- source/blender/render/intern/zbuf.c | 9 ++--- source/blender/sequencer/intern/effects.c | 40 ++++------------------ source/blender/windowmanager/intern/wm_operators.c | 10 ++---- 55 files changed, 112 insertions(+), 437 deletions(-) diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c index 9caf416cd0c..a7257133821 100644 --- a/source/blender/blenkernel/intern/boids.c +++ b/source/blender/blenkernel/intern/boids.c @@ -342,10 +342,7 @@ static bool rule_avoid_collision(BoidRule *rule, } } } - if (ptn) { - MEM_freeN(ptn); - ptn = NULL; - } + MEM_SAFE_FREE(ptn); /* check boids in other systems */ for (pt = bbd->sim->psys->targets.first; pt; pt = pt->next) { @@ -401,10 +398,7 @@ static bool rule_avoid_collision(BoidRule *rule, } } - if (ptn) { - MEM_freeN(ptn); - ptn = NULL; - } + MEM_SAFE_FREE(ptn); } } @@ -435,10 +429,7 @@ static bool rule_separate(BoidRule *UNUSED(rule), len = ptn[1].dist; ret = 1; } - if (ptn) { - MEM_freeN(ptn); - ptn = NULL; - } + MEM_SAFE_FREE(ptn); /* check other boid systems */ for (pt = bbd->sim->psys->targets.first; pt; pt = pt->next) { @@ -457,10 +448,7 @@ static bool rule_separate(BoidRule *UNUSED(rule), ret = true; } - if (ptn) { - MEM_freeN(ptn); - ptn = NULL; - } + MEM_SAFE_FREE(ptn); } } return ret; @@ -723,10 +711,7 @@ static bool rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, Part f_strength += bbd->part->boids->strength * health; - if (ptn) { - MEM_freeN(ptn); - ptn = NULL; - } + MEM_SAFE_FREE(ptn); /* add other friendlies and calculate enemy strength and find closest enemy */ for (pt = bbd->sim->psys->targets.first; pt; pt = pt->next) { @@ -755,10 +740,7 @@ static bool rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, Part f_strength += epsys->part->boids->strength * health; } - if (ptn) { - MEM_freeN(ptn); - ptn = NULL; - } + MEM_SAFE_FREE(ptn); } } /* decide action if enemy presence found */ diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index abf7bab7612..3418e37642c 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -660,10 +660,7 @@ static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset) break; } - if (cuma->table) { - MEM_freeN(cuma->table); - cuma->table = NULL; - } + MEM_SAFE_FREE(cuma->table); } void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 0fa58a74f2b..f5ff936e18b 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -447,11 +447,7 @@ void cloth_free_modifier(ClothModifierData *clmd) SIM_cloth_solver_free(clmd); /* Free the verts. */ - if (cloth->verts != NULL) { - MEM_freeN(cloth->verts); - } - - cloth->verts = NULL; + MEM_SAFE_FREE(cloth->verts); cloth->mvert_num = 0; /* Free the springs. */ @@ -529,11 +525,7 @@ void cloth_free_modifier_extern(ClothModifierData *clmd) SIM_cloth_solver_free(clmd); /* Free the verts. */ - if (cloth->verts != NULL) { - MEM_freeN(cloth->verts); - } - - cloth->verts = NULL; + MEM_SAFE_FREE(cloth->verts); cloth->mvert_num = 0; /* Free the springs. */ diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index a9f0f69b855..f2c2e552a9f 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -1716,22 +1716,10 @@ void BKE_scopes_update(Scopes *scopes, void BKE_scopes_free(Scopes *scopes) { - if (scopes->waveform_1) { - MEM_freeN(scopes->waveform_1); - scopes->waveform_1 = NULL; - } - if (scopes->waveform_2) { - MEM_freeN(scopes->waveform_2); - scopes->waveform_2 = NULL; - } - if (scopes->waveform_3) { - MEM_freeN(scopes->waveform_3); - scopes->waveform_3 = NULL; - } - if (scopes->vecscope) { - MEM_freeN(scopes->vecscope); - scopes->vecscope = NULL; - } + MEM_SAFE_FREE(scopes->waveform_1); + MEM_SAFE_FREE(scopes->waveform_2); + MEM_SAFE_FREE(scopes->waveform_3); + MEM_SAFE_FREE(scopes->vecscope); } void BKE_scopes_new(Scopes *scopes) diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index f1369254347..49c81d793c3 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -5010,10 +5010,7 @@ bool BKE_nurb_type_convert(Nurb *nu, MEM_freeN(nu->knotsu); /* python created nurbs have a knotsu of zero */ } nu->knotsu = NULL; - if (nu->knotsv) { - MEM_freeN(nu->knotsv); - } - nu->knotsv = NULL; + MEM_SAFE_FREE(nu->knotsv); } else if (type == CU_BEZIER) { /* to Bezier */ nr = nu->pntsu / 3; diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 7aa9d1958eb..a9a8fd7410a 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -724,10 +724,7 @@ static void layerFree_grid_paint_mask(void *data, int count, int UNUSED(size)) GridPaintMask *gpm = data; for (int i = 0; i < count; i++) { - if (gpm[i].data) { - MEM_freeN(gpm[i].data); - } - gpm[i].data = NULL; + MEM_SAFE_FREE(gpm[i].data); gpm[i].level = 0; } } diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index f7ef84728b6..13222747a52 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -894,10 +894,7 @@ void BKE_defvert_remove_group(MDeformVert *dvert, MDeformWeight *dw) void BKE_defvert_clear(MDeformVert *dvert) { - if (dvert->dw) { - MEM_freeN(dvert->dw); - dvert->dw = NULL; - } + MEM_SAFE_FREE(dvert->dw); dvert->totweight = 0; } diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 52996e3bcc7..8f94c407cae 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -844,10 +844,7 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) if (temp_s_num) { MEM_freeN(temp_s_num); } - if (temp_t_index) { - MEM_freeN(temp_t_index); - } - grid->temp_t_index = NULL; + MEM_SAFE_FREE(temp_t_index); if (error || !grid->s_num) { setError(surface->canvas, N_("Not enough free memory")); @@ -988,10 +985,7 @@ void dynamicPaint_freeSurface(const DynamicPaintModifierData *pmd, DynamicPaintS } surface->pointcache = NULL; - if (surface->effector_weights) { - MEM_freeN(surface->effector_weights); - } - surface->effector_weights = NULL; + MEM_SAFE_FREE(surface->effector_weights); BLI_remlink(&(surface->canvas->surfaces), surface); dynamicPaint_freeSurfaceData(surface); diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index d33697d551e..799d6553682 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -4766,20 +4766,14 @@ static void BKE_fluid_modifier_freeDomain(FluidModifierData *fmd) BLI_rw_mutex_free(fmd->domain->fluid_mutex); } - if (fmd->domain->effector_weights) { - MEM_freeN(fmd->domain->effector_weights); - } - fmd->domain->effector_weights = NULL; + MEM_SAFE_FREE(fmd->domain->effector_weights); if (!(fmd->modifier.flag & eModifierFlag_SharedCaches)) { BKE_ptcache_free_list(&(fmd->domain->ptcaches[0])); fmd->domain->point_cache[0] = NULL; } - if (fmd->domain->mesh_velocities) { - MEM_freeN(fmd->domain->mesh_velocities); - } - fmd->domain->mesh_velocities = NULL; + MEM_SAFE_FREE(fmd->domain->mesh_velocities); if (fmd->domain->coba) { MEM_freeN(fmd->domain->coba); @@ -4798,10 +4792,7 @@ static void BKE_fluid_modifier_freeFlow(FluidModifierData *fmd) } fmd->flow->mesh = NULL; - if (fmd->flow->verts_old) { - MEM_freeN(fmd->flow->verts_old); - } - fmd->flow->verts_old = NULL; + MEM_SAFE_FREE(fmd->flow->verts_old); fmd->flow->numverts = 0; fmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE; @@ -4818,10 +4809,7 @@ static void BKE_fluid_modifier_freeEffector(FluidModifierData *fmd) } fmd->effector->mesh = NULL; - if (fmd->effector->verts_old) { - MEM_freeN(fmd->effector->verts_old); - } - fmd->effector->verts_old = NULL; + MEM_SAFE_FREE(fmd->effector->verts_old); fmd->effector->numverts = 0; fmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE; @@ -4857,18 +4845,12 @@ static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need fmd->domain->active_fields = 0; } else if (fmd->flow) { - if (fmd->flow->verts_old) { - MEM_freeN(fmd->flow->verts_old); - } - fmd->flow->verts_old = NULL; + MEM_SAFE_FREE(fmd->flow->verts_old); fmd->flow->numverts = 0; fmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE; } else if (fmd->effector) { - if (fmd->effector->verts_old) { - MEM_freeN(fmd->effector->verts_old); - } - fmd->effector->verts_old = NULL; + MEM_SAFE_FREE(fmd->effector->verts_old); fmd->effector->numverts = 0; fmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE; } diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index b00670aad4c..088026ef945 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -1698,10 +1698,7 @@ void BKE_mesh_do_versions_cd_flag_init(Mesh *mesh) void BKE_mesh_mselect_clear(Mesh *me) { - if (me->mselect) { - MEM_freeN(me->mselect); - me->mselect = NULL; - } + MEM_SAFE_FREE(me->mselect); me->totselect = 0; } diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 54f0da30a2b..eaa11a6683a 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -468,15 +468,9 @@ void multires_force_sculpt_rebuild(Object *object) object->sculpt->pbvh = NULL; } - if (ss->pmap != NULL) { - MEM_freeN(ss->pmap); - ss->pmap = NULL; - } + MEM_SAFE_FREE(ss->pmap); - if (ss->pmap_mem != NULL) { - MEM_freeN(ss->pmap_mem); - ss->pmap_mem = NULL; - } + MEM_SAFE_FREE(ss->pmap_mem); } void multires_force_external_reload(Object *object) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index f969ca0ff1c..2e69782f6c0 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -4047,10 +4047,7 @@ void BKE_object_empty_draw_type_set(Object *ob, const int value) } } else { - if (ob->iuser) { - MEM_freeN(ob->iuser); - ob->iuser = NULL; - } + MEM_SAFE_FREE(ob->iuser); } } diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c index c69326a23c6..511f5d4ae66 100644 --- a/source/blender/blenkernel/intern/object_deform.c +++ b/source/blender/blenkernel/intern/object_deform.c @@ -307,10 +307,7 @@ static void object_defgroup_remove_common(Object *ob, bDeformGroup *dg, const in } else if (ob->type == OB_LATTICE) { Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data)); - if (lt->dvert) { - MEM_freeN(lt->dvert); - lt->dvert = NULL; - } + MEM_SAFE_FREE(lt->dvert); } } else if (BKE_object_defgroup_active_index_get(ob) < 1) { @@ -465,10 +462,7 @@ void BKE_object_defgroup_remove_all_ex(struct Object *ob, bool only_unlocked) } else if (ob->type == OB_LATTICE) { Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data)); - if (lt->dvert) { - MEM_freeN(lt->dvert); - lt->dvert = NULL; - } + MEM_SAFE_FREE(lt->dvert); } /* Fix counters/indices */ BKE_object_defgroup_active_index_set(ob, 0); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index f2f3c5d4ca6..cc8a051eceb 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -928,10 +928,7 @@ void free_hair(Object *object, ParticleSystem *psys, int dynamics) LOOP_PARTICLES { - if (pa->hair) { - MEM_freeN(pa->hair); - } - pa->hair = NULL; + MEM_SAFE_FREE(pa->hair); pa->totkey = 0; } @@ -1044,25 +1041,13 @@ void psys_free_particles(ParticleSystem *psys) void psys_free_pdd(ParticleSystem *psys) { if (psys->pdd) { - if (psys->pdd->cdata) { - MEM_freeN(psys->pdd->cdata); - } - psys->pdd->cdata = NULL; + MEM_SAFE_FREE(psys->pdd->cdata); - if (psys->pdd->vdata) { - MEM_freeN(psys->pdd->vdata); - } - psys->pdd->vdata = NULL; + MEM_SAFE_FREE(psys->pdd->vdata); - if (psys->pdd->ndata) { - MEM_freeN(psys->pdd->ndata); - } - psys->pdd->ndata = NULL; + MEM_SAFE_FREE(psys->pdd->ndata); - if (psys->pdd->vedata) { - MEM_freeN(psys->pdd->vedata); - } - psys->pdd->vedata = NULL; + MEM_SAFE_FREE(psys->pdd->vedata); psys->pdd->totpoint = 0; psys->pdd->totpart = 0; diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 9c0c5639777..f592b0f97ea 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -169,10 +169,7 @@ void psys_reset(ParticleSystem *psys, int mode) } /* reset children */ - if (psys->child) { - MEM_freeN(psys->child); - psys->child = NULL; - } + MEM_SAFE_FREE(psys->child); psys->totchild = 0; @@ -182,10 +179,7 @@ void psys_reset(ParticleSystem *psys, int mode) /* reset point cache */ BKE_ptcache_invalidate(psys->pointcache); - if (psys->fluid_springs) { - MEM_freeN(psys->fluid_springs); - psys->fluid_springs = NULL; - } + MEM_SAFE_FREE(psys->fluid_springs); psys->tot_fluidsprings = psys->alloc_fluidsprings = 0; } @@ -4516,10 +4510,7 @@ static void system_step(ParticleSimulationData *sim, float cfra, const bool use_ reset_all_particles(sim, 0.0, cfra, oldtotpart); free_unexisting_particles(sim); - if (psys->fluid_springs) { - MEM_freeN(psys->fluid_springs); - psys->fluid_springs = NULL; - } + MEM_SAFE_FREE(psys->fluid_springs); psys->tot_fluidsprings = psys->alloc_fluidsprings = 0; diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index f41251f6ea5..5e92be76197 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -891,11 +891,7 @@ static void free_softbody_baked(SoftBody *sb) MEM_freeN(key); } } - if (sb->keys) { - MEM_freeN(sb->keys); - } - - sb->keys = NULL; + MEM_SAFE_FREE(sb->keys); sb->totkey = 0; } static void free_scratch(SoftBody *sb) diff --git a/source/blender/blenkernel/intern/text_suggestions.c b/source/blender/blenkernel/intern/text_suggestions.c index d717b88e8ca..e93e969cb33 100644 --- a/source/blender/blenkernel/intern/text_suggestions.c +++ b/source/blender/blenkernel/intern/text_suggestions.c @@ -57,10 +57,7 @@ static void txttl_free_suggest(void) static void txttl_free_docs(void) { - if (documentation) { - MEM_freeN(documentation); - documentation = NULL; - } + MEM_SAFE_FREE(documentation); } /**************************/ @@ -240,10 +237,7 @@ void texttool_docs_show(const char *docs) len = strlen(docs); - if (documentation) { - MEM_freeN(documentation); - documentation = NULL; - } + MEM_SAFE_FREE(documentation); /* Ensure documentation ends with a '\n' */ if (docs[len - 1] != '\n') { diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 29c41b88135..beb92495d16 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -490,9 +490,8 @@ void set_current_linestyle_texture(FreestyleLineStyle *linestyle, Tex *newtex) linestyle->mtex[act]->tex = newtex; id_us_plus(&newtex->id); } - else if (linestyle->mtex[act]) { - MEM_freeN(linestyle->mtex[act]); - linestyle->mtex[act] = NULL; + else { + MEM_SAFE_FREE(linestyle->mtex[act]); } } @@ -595,9 +594,8 @@ void set_current_particle_texture(ParticleSettings *part, Tex *newtex) part->mtex[act]->tex = newtex; id_us_plus(&newtex->id); } - else if (part->mtex[act]) { - MEM_freeN(part->mtex[act]); - part->mtex[act] = NULL; + else { + MEM_SAFE_FREE(part->mtex[act]); } } @@ -660,14 +658,8 @@ void BKE_texture_pointdensity_free_data(PointDensity *pd) BLI_bvhtree_free(pd->point_tree); pd->point_tree = NULL; } - if (pd->point_data) { - MEM_freeN(pd->point_data); - pd->point_data = NULL; - } - if (pd->coba) { - MEM_freeN(pd->coba); - pd->coba = NULL; - } + MEM_SAFE_FREE(pd->point_data); + MEM_SAFE_FREE(pd->coba); BKE_curvemapping_free(pd->falloff_curve); /* can be NULL */ } diff --git a/source/blender/blentranslation/intern/blt_lang.c b/source/blender/blentranslation/intern/blt_lang.c index 97a1a83792a..91e8a81aec0 100644 --- a/source/blender/blentranslation/intern/blt_lang.c +++ b/source/blender/blentranslation/intern/blt_lang.c @@ -74,10 +74,7 @@ static void free_locales(void) MEM_freeN((void *)locales); locales = NULL; } - if (locales_menu) { - MEM_freeN(locales_menu); - locales_menu = NULL; - } + MEM_SAFE_FREE(locales_menu); num_locales = num_locales_menu = 0; } diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index 31552330071..40871fba2be 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -672,10 +672,7 @@ static void fmod_envelope_deletepoint_cb(bContext *UNUSED(C), void *fcm_dv, void } else { /* just free array, since the only vert was deleted */ - if (env->data) { - MEM_freeN(env->data); - env->data = NULL; - } + MEM_SAFE_FREE(env->data); env->totvert = 0; } } diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index eb91afa5c84..75ce62d5e27 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -92,10 +92,7 @@ void delete_fcurve_key(FCurve *fcu, int index, bool do_recalc) fcu->totvert--; if (fcu->totvert == 0) { - if (fcu->bezt) { - MEM_freeN(fcu->bezt); - } - fcu->bezt = NULL; + MEM_SAFE_FREE(fcu->bezt); } /* recalc handles - only if it won't cause problems */ @@ -136,10 +133,7 @@ bool delete_fcurve_keys(FCurve *fcu) void clear_fcurve_keys(FCurve *fcu) { - if (fcu->bezt) { - MEM_freeN(fcu->bezt); - } - fcu->bezt = NULL; + MEM_SAFE_FREE(fcu->bezt); fcu->totvert = 0; } diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index e362a2e2f40..990e7589d9d 100644 --- a/source/blender/editors/armature/meshlaplacian.c +++ b/source/blender/editors/armature/meshlaplacian.c @@ -307,10 +307,7 @@ static void laplacian_system_construct_end(LaplacianSystem *sys) MEM_freeN(sys->faces); sys->faces = NULL; - if (sys->varea) { - MEM_freeN(sys->varea); - sys->varea = NULL; - } + MEM_SAFE_FREE(sys->varea); BLI_edgehash_free(sys->edgehash, NULL); sys->edgehash = NULL; diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index e7d97ce343c..c399abfa52d 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -1848,10 +1848,7 @@ static void ed_surf_delete_selected(Object *obedit) nu->pntsv = 1; SWAP(short, nu->orderu, nu->orderv); BKE_nurb_order_clamp_u(nu); - if (nu->knotsv) { - MEM_freeN(nu->knotsv); - } - nu->knotsv = NULL; + MEM_SAFE_FREE(nu->knotsv); } else { nu->pntsu = newu; @@ -4650,10 +4647,7 @@ static int make_segment_exec(bContext *C, wmOperator *op) /* now join the knots */ if (nu1->type == CU_NURBS) { - if (nu1->knotsu != NULL) { - MEM_freeN(nu1->knotsu); - nu1->knotsu = NULL; - } + MEM_SAFE_FREE(nu1->knotsu); BKE_nurb_knot_calc_u(nu1); } diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index ee3536c2f3f..a0a58abc02f 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -1588,14 +1588,8 @@ static int gpencil_convert_layer_exec(bContext *C, wmOperator *op) C, op->reports, gpd, gpl, mode, norm_weights, rad_fac, link_strokes, >d); /* free temp memory */ - if (gtd.dists) { - MEM_freeN(gtd.dists); - gtd.dists = NULL; - } - if (gtd.times) { - MEM_freeN(gtd.times); - gtd.times = NULL; - } + MEM_SAFE_FREE(gtd.dists); + MEM_SAFE_FREE(gtd.times); /* notifiers */ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index 14caf0c08a7..869254cef3b 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -968,10 +968,7 @@ static void gpencil_brush_clone_free(tGP_BrushEditData *gso) tGPSB_CloneBrushData *data = gso->customdata; /* free strokes array */ - if (data->new_strokes) { - MEM_freeN(data->new_strokes); - data->new_strokes = NULL; - } + MEM_SAFE_FREE(data->new_strokes); /* free copybuf colormap */ if (data->new_colors) { diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/interface_eyedropper_driver.c index 8762a4819d4..ccf0e727da8 100644 --- a/source/blender/editors/interface/interface_eyedropper_driver.c +++ b/source/blender/editors/interface/interface_eyedropper_driver.c @@ -84,10 +84,7 @@ static void driverdropper_exit(bContext *C, wmOperator *op) { WM_cursor_modal_restore(CTX_wm_window(C)); - if (op->customdata) { - MEM_freeN(op->customdata); - op->customdata = NULL; - } + MEM_SAFE_FREE(op->customdata); } static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *event) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index d920ebbe11a..6f2232fabe5 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3438,10 +3438,7 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) const bool is_num_but = ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER); bool no_zero_strip = false; - if (data->str) { - MEM_freeN(data->str); - data->str = NULL; - } + MEM_SAFE_FREE(data->str); #ifdef USE_DRAG_MULTINUM /* this can happen from multi-drag */ @@ -8635,10 +8632,7 @@ static void button_activate_exit( } /* clean up button */ - if (but->active) { - MEM_freeN(but->active); - but->active = NULL; - } + MEM_SAFE_FREE(but->active); but->flag &= ~(UI_ACTIVE | UI_SELECT); but->flag |= UI_BUT_LAST_ACTIVE; diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 85c646d689c..c6a8e771362 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -355,10 +355,7 @@ void EDBM_mesh_clear(BMEditMesh *em) /* free tessellation data */ em->tottri = 0; - if (em->looptris) { - MEM_freeN(em->looptris); - em->looptris = NULL; - } + MEM_SAFE_FREE(em->looptris); } void EDBM_mesh_load(Main *bmain, Object *ob) diff --git a/source/blender/editors/mesh/mesh_mirror.c b/source/blender/editors/mesh/mesh_mirror.c index 5eb69aab48b..1a3e6a59588 100644 --- a/source/blender/editors/mesh/mesh_mirror.c +++ b/source/blender/editors/mesh/mesh_mirror.c @@ -365,10 +365,7 @@ void ED_mesh_mirrtopo_init(BMEditMesh *em, void ED_mesh_mirrtopo_free(MirrTopoStore_t *mesh_topo_store) { - if (mesh_topo_store->index_lookup) { - MEM_freeN(mesh_topo_store->index_lookup); - } - mesh_topo_store->index_lookup = NULL; + MEM_SAFE_FREE(mesh_topo_store->index_lookup); mesh_topo_store->prev_vert_tot = -1; mesh_topo_store->prev_edge_tot = -1; } diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 5a629058c81..85883a2d29a 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -2957,10 +2957,7 @@ static int remove_tagged_particles(Object *ob, ParticleSystem *psys, int mirror) } edit->points = new_points; - if (edit->mirror_cache) { - MEM_freeN(edit->mirror_cache); - edit->mirror_cache = NULL; - } + MEM_SAFE_FREE(edit->mirror_cache); if (psys->child) { MEM_freeN(psys->child); @@ -3576,10 +3573,7 @@ static void PE_mirror_x(Depsgraph *depsgraph, Scene *scene, Object *ob, int tagg } edit->points = new_points; - if (edit->mirror_cache) { - MEM_freeN(edit->mirror_cache); - edit->mirror_cache = NULL; - } + MEM_SAFE_FREE(edit->mirror_cache); edit->totpoint = psys->totpart = newtotpart; @@ -4497,10 +4491,7 @@ static int brush_add(const bContext *C, PEData *data, short number) } edit->points = new_points; - if (edit->mirror_cache) { - MEM_freeN(edit->mirror_cache); - edit->mirror_cache = NULL; - } + MEM_SAFE_FREE(edit->mirror_cache); /* create tree for interpolation */ if (pset->flag & PE_INTERPOLATE_ADDED && psys->totpart) { diff --git a/source/blender/editors/physics/particle_edit_undo.c b/source/blender/editors/physics/particle_edit_undo.c index 2c7b5c0de6a..601a8385a24 100644 --- a/source/blender/editors/physics/particle_edit_undo.c +++ b/source/blender/editors/physics/particle_edit_undo.c @@ -128,10 +128,7 @@ static void undoptcache_to_editcache(PTCacheUndo *undo, PTCacheEdit *edit) if (edit->points) { MEM_freeN(edit->points); } - if (edit->mirror_cache) { - MEM_freeN(edit->mirror_cache); - edit->mirror_cache = NULL; - } + MEM_SAFE_FREE(edit->mirror_cache); edit->points = MEM_dupallocN(undo->points); edit->totpoint = undo->totpoint; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index c351ade9954..e08a4e946f6 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -834,9 +834,8 @@ void ED_area_status_text(ScrArea *area, const char *str) BLI_strncpy(region->headerstr, str, UI_MAX_DRAW_STR); BLI_str_rstrip(region->headerstr); } - else if (region->headerstr) { - MEM_freeN(region->headerstr); - region->headerstr = NULL; + else { + MEM_SAFE_FREE(region->headerstr); } ED_region_tag_redraw(region); } @@ -859,9 +858,8 @@ void ED_workspace_status_text(bContext *C, const char *str) } BLI_strncpy(workspace->status_text, str, UI_MAX_DRAW_STR); } - else if (workspace->status_text) { - MEM_freeN(workspace->status_text); - workspace->status_text = NULL; + else { + MEM_SAFE_FREE(workspace->status_text); } /* Redraw status bar. */ diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 2a81fcfde8f..506b5a9859d 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -722,10 +722,7 @@ void ED_region_exit(bContext *C, ARegion *region) WM_event_modal_handler_region_replace(win, region, NULL); WM_draw_region_free(region, true); - if (region->headerstr) { - MEM_freeN(region->headerstr); - region->headerstr = NULL; - } + MEM_SAFE_FREE(region->headerstr); if (region->regiontimer) { WM_event_remove_timer(wm, win, region->regiontimer); @@ -1653,10 +1650,7 @@ void ED_refresh_viewport_fps(bContext *C) } else { /* playback stopped or shouldn't be running */ - if (scene->fps_info) { - MEM_freeN(scene->fps_info); - } - scene->fps_info = NULL; + MEM_SAFE_FREE(scene->fps_info); } } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index d8cef889a40..e150d7774f9 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -1023,10 +1023,7 @@ AZone *ED_area_azones_update(ScrArea *area, const int xy[2]) static void actionzone_exit(wmOperator *op) { - if (op->customdata) { - MEM_freeN(op->customdata); - } - op->customdata = NULL; + MEM_SAFE_FREE(op->customdata); G.moving &= ~G_TRANSFORM_WM; } @@ -1308,10 +1305,7 @@ static bool area_swap_init(wmOperator *op, const wmEvent *event) static void area_swap_exit(bContext *C, wmOperator *op) { WM_cursor_modal_restore(CTX_wm_window(C)); - if (op->customdata) { - MEM_freeN(op->customdata); - } - op->customdata = NULL; + MEM_SAFE_FREE(op->customdata); } static void area_swap_cancel(bContext *C, wmOperator *op) @@ -1892,10 +1886,7 @@ static void area_move_apply(bContext *C, wmOperator *op) static void area_move_exit(bContext *C, wmOperator *op) { - if (op->customdata) { - MEM_freeN(op->customdata); - } - op->customdata = NULL; + MEM_SAFE_FREE(op->customdata); /* this makes sure aligned edges will result in aligned grabbing */ BKE_screen_remove_double_scrverts(CTX_wm_screen(C)); diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 23b90171a1d..a35e248a78c 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -837,10 +837,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, if (diameter != cache->lastdiameter || (mask_rotation != cache->last_mask_rotation) || renew_maxmask) { - if (cache->tex_mask) { - MEM_freeN(cache->tex_mask); - cache->tex_mask = NULL; - } + MEM_SAFE_FREE(cache->tex_mask); brush_painter_2d_tex_mapping(s, tile->canvas, @@ -862,10 +859,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, } /* curve mask can only change if the size changes */ - if (cache->curve_mask) { - MEM_freeN(cache->curve_mask); - cache->curve_mask = NULL; - } + MEM_SAFE_FREE(cache->curve_mask); cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size, pos); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 8264affc465..7bde864e73f 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -6560,10 +6560,7 @@ static void sculpt_update_tex(const Scene *scene, Sculpt *sd, SculptSession *ss) Brush *brush = BKE_paint_brush(&sd->paint); const int radius = BKE_brush_size_get(scene, brush); - if (ss->texcache) { - MEM_freeN(ss->texcache); - ss->texcache = NULL; - } + MEM_SAFE_FREE(ss->texcache); if (ss->tex_pool) { BKE_image_pool_free(ss->tex_pool); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 87e0ea7f6a9..ae6dcbdbff4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -94,15 +94,9 @@ void SCULPT_pbvh_clear(Object *ob) ss->pbvh = NULL; } - if (ss->pmap) { - MEM_freeN(ss->pmap); - ss->pmap = NULL; - } + MEM_SAFE_FREE(ss->pmap); - if (ss->pmap_mem) { - MEM_freeN(ss->pmap_mem); - ss->pmap_mem = NULL; - } + MEM_SAFE_FREE(ss->pmap_mem); BKE_object_free_derived_caches(ob); diff --git a/source/blender/editors/space_clip/tracking_ops_solve.c b/source/blender/editors/space_clip/tracking_ops_solve.c index 96504651e44..58f9b307ef9 100644 --- a/source/blender/editors/space_clip/tracking_ops_solve.c +++ b/source/blender/editors/space_clip/tracking_ops_solve.c @@ -299,10 +299,7 @@ static int clear_solution_exec(bContext *C, wmOperator *UNUSED(op)) track->flag &= ~TRACK_HAS_BUNDLE; } - if (reconstruction->cameras != NULL) { - MEM_freeN(reconstruction->cameras); - reconstruction->cameras = NULL; - } + MEM_SAFE_FREE(reconstruction->cameras); reconstruction->camnr = 0; reconstruction->flag &= ~TRACKING_RECONSTRUCTED; diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index d7a6523de26..756fc179e31 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -176,10 +176,7 @@ static void file_free(SpaceLink *sl) MEM_SAFE_FREE(sfile->asset_params); MEM_SAFE_FREE(sfile->runtime); - if (sfile->layout) { - MEM_freeN(sfile->layout); - sfile->layout = NULL; - } + MEM_SAFE_FREE(sfile->layout); } /* spacetype; init callback, area size changes, screen set, etc */ diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index dad354ba8ee..613042a2ab9 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -2508,10 +2508,7 @@ static ImageNewData *image_new_init(bContext *C, wmOperator *op) static void image_new_free(wmOperator *op) { - if (op->customdata) { - MEM_freeN(op->customdata); - op->customdata = NULL; - } + MEM_SAFE_FREE(op->customdata); } static int image_new_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index 983e04127e0..e749e1a7947 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -466,10 +466,7 @@ static void stats_update(Depsgraph *depsgraph, void ED_info_stats_clear(wmWindowManager *wm, ViewLayer *view_layer) { - if (view_layer->stats) { - MEM_freeN(view_layer->stats); - view_layer->stats = NULL; - } + MEM_SAFE_FREE(view_layer->stats); LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { ViewLayer *view_layer_test = WM_window_get_active_view_layer(win); diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index b6b3d1841d2..c4575b3403e 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -565,10 +565,7 @@ static void sequencer_add_init(bContext *UNUSED(C), wmOperator *op) static void sequencer_add_cancel(bContext *UNUSED(C), wmOperator *op) { - if (op->customdata) { - MEM_freeN(op->customdata); - } - op->customdata = NULL; + MEM_SAFE_FREE(op->customdata); } static bool sequencer_add_draw_check_fn(PointerRNA *UNUSED(ptr), diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 1d8bc427212..b6ba95885e4 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -686,10 +686,7 @@ static void text_update_drawcache(SpaceText *st, ARegion *region) } } else { - if (drawcache->line_height) { - MEM_freeN(drawcache->line_height); - drawcache->line_height = NULL; - } + MEM_SAFE_FREE(drawcache->line_height); if (full_update || drawcache->update_flag) { nlines = BLI_listbase_count(&txt->lines); diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index b98dae0cd57..2b78ecb245d 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -236,10 +236,7 @@ void text_update_line_edited(TextLine *line) } /* we just free format here, and let it rebuild during draw */ - if (line->format) { - MEM_freeN(line->format); - line->format = NULL; - } + MEM_SAFE_FREE(line->format); } void text_update_edited(Text *text) diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 651ae8a3000..b055a0fe947 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -4917,10 +4917,7 @@ static int view3d_clipping_invoke(bContext *C, wmOperator *op, const wmEvent *ev if (rv3d->rflag & RV3D_CLIPPING) { rv3d->rflag &= ~RV3D_CLIPPING; ED_region_tag_redraw(region); - if (rv3d->clipbb) { - MEM_freeN(rv3d->clipbb); - } - rv3d->clipbb = NULL; + MEM_SAFE_FREE(rv3d->clipbb); return OPERATOR_FINISHED; } return WM_gesture_box_invoke(C, op, event); diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c index f97403a0919..b4bdeace716 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.c +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -3399,10 +3399,7 @@ static void p_chart_lscm_end(PChart *chart) EIG_linear_solver_delete(chart->u.lscm.context); } - if (chart->u.lscm.abf_alpha) { - MEM_freeN(chart->u.lscm.abf_alpha); - chart->u.lscm.abf_alpha = NULL; - } + MEM_SAFE_FREE(chart->u.lscm.abf_alpha); chart->u.lscm.context = NULL; chart->u.lscm.pin1 = NULL; diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 1bcd1fad9d6..c5485cc1495 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -265,26 +265,11 @@ static StitchPreviewer *stitch_preview_init(void) static void stitch_preview_delete(StitchPreviewer *stitch_preview) { if (stitch_preview) { - if (stitch_preview->preview_polys) { - MEM_freeN(stitch_preview->preview_polys); - stitch_preview->preview_polys = NULL; - } - if (stitch_preview->uvs_per_polygon) { - MEM_freeN(stitch_preview->uvs_per_polygon); - stitch_preview->uvs_per_polygon = NULL; - } - if (stitch_preview->preview_stitchable) { - MEM_freeN(stitch_preview->preview_stitchable); - stitch_preview->preview_stitchable = NULL; - } - if (stitch_preview->preview_unstitchable) { - MEM_freeN(stitch_preview->preview_unstitchable); - stitch_preview->preview_unstitchable = NULL; - } - if (stitch_preview->static_tris) { - MEM_freeN(stitch_preview->static_tris); - stitch_preview->static_tris = NULL; - } + MEM_SAFE_FREE(stitch_preview->preview_polys); + MEM_SAFE_FREE(stitch_preview->uvs_per_polygon); + MEM_SAFE_FREE(stitch_preview->preview_stitchable); + MEM_SAFE_FREE(stitch_preview->preview_unstitchable); + MEM_SAFE_FREE(stitch_preview->static_tris); MEM_freeN(stitch_preview); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c index 680f5ab05ec..7ce731c0dd6 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c @@ -308,10 +308,7 @@ static void bakeModifier(Main *UNUSED(bmain), static void freeData(GpencilModifierData *md) { TintGpencilModifierData *mmd = (TintGpencilModifierData *)md; - if (mmd->colorband) { - MEM_freeN(mmd->colorband); - mmd->colorband = NULL; - } + MEM_SAFE_FREE(mmd->colorband); if (mmd->curve_intensity) { BKE_curvemapping_free(mmd->curve_intensity); } diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 2cc44ebc67b..8b2ac2ed22d 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -759,11 +759,7 @@ static bool colormanage_use_look(const char *look, const char *view_name) void colormanage_cache_free(ImBuf *ibuf) { - if (ibuf->display_buffer_flags) { - MEM_freeN(ibuf->display_buffer_flags); - - ibuf->display_buffer_flags = NULL; - } + MEM_SAFE_FREE(ibuf->display_buffer_flags); if (ibuf->colormanage_cache) { ColormanageCacheData *cache_data = colormanage_cachedata_get(ibuf); diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c index e395222a214..6cc1932eff6 100644 --- a/source/blender/imbuf/intern/moviecache.c +++ b/source/blender/imbuf/intern/moviecache.c @@ -179,10 +179,7 @@ static void IMB_moviecache_destructor(void *p) item->c_handle = NULL; /* force cached segments to be updated */ - if (cache->points) { - MEM_freeN(cache->points); - cache->points = NULL; - } + MEM_SAFE_FREE(cache->points); } } @@ -355,10 +352,7 @@ static void do_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf, boo /* cache limiter can't remove unused keys which points to destroyed values */ check_unused_keys(cache); - if (cache->points) { - MEM_freeN(cache->points); - cache->points = NULL; - } + MEM_SAFE_FREE(cache->points); } void IMB_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf) @@ -488,11 +482,7 @@ void IMB_moviecache_get_cache_segments( } if (cache->proxy != proxy || cache->render_flags != render_flags) { - if (cache->points) { - MEM_freeN(cache->points); - } - - cache->points = NULL; + MEM_SAFE_FREE(cache->points); } if (cache->points) { diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index c991216da11..7bc09e3cd67 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -4980,10 +4980,7 @@ void rna_iterator_array_end(CollectionPropertyIterator *iter) { ArrayIterator *internal = &iter->internal.array; - if (internal->free_ptr) { - MEM_freeN(internal->free_ptr); - internal->free_ptr = NULL; - } + MEM_SAFE_FREE(internal->free_ptr); } PointerRNA rna_array_lookup_int( diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c index 31d5bf67f28..06dd570ce2c 100644 --- a/source/blender/render/intern/texture_pointdensity.c +++ b/source/blender/render/intern/texture_pointdensity.c @@ -494,10 +494,7 @@ static void free_pointdensity(PointDensity *pd) pd->point_tree = NULL; } - if (pd->point_data) { - MEM_freeN(pd->point_data); - pd->point_data = NULL; - } + MEM_SAFE_FREE(pd->point_data); pd->totpoints = 0; } diff --git a/source/blender/render/intern/zbuf.c b/source/blender/render/intern/zbuf.c index 242c8a199fb..726124871ee 100644 --- a/source/blender/render/intern/zbuf.c +++ b/source/blender/render/intern/zbuf.c @@ -56,13 +56,8 @@ void zbuf_alloc_span(ZSpan *zspan, int rectx, int recty) void zbuf_free_span(ZSpan *zspan) { if (zspan) { - if (zspan->span1) { - MEM_freeN(zspan->span1); - } - if (zspan->span2) { - MEM_freeN(zspan->span2); - } - zspan->span1 = zspan->span2 = NULL; + MEM_SAFE_FREE(zspan->span1); + MEM_SAFE_FREE(zspan->span2); } } diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 7757271a2e5..3ca0555d9a5 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -2164,11 +2164,7 @@ static int num_inputs_wipe(void) static void free_wipe_effect(Sequence *seq, const bool UNUSED(do_id_user)) { - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static void copy_wipe_effect(Sequence *dst, Sequence *src, const int UNUSED(flag)) @@ -2382,10 +2378,7 @@ static int num_inputs_transform(void) static void free_transform_effect(Sequence *seq, const bool UNUSED(do_id_user)) { - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static void copy_transform_effect(Sequence *dst, Sequence *src, const int UNUSED(flag)) @@ -2723,11 +2716,7 @@ static int num_inputs_glow(void) static void free_glow_effect(Sequence *seq, const bool UNUSED(do_id_user)) { - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static void copy_glow_effect(Sequence *dst, Sequence *src, const int UNUSED(flag)) @@ -2853,11 +2842,7 @@ static int num_inputs_color(void) static void free_solid_color(Sequence *seq, const bool UNUSED(do_id_user)) { - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static void copy_solid_color(Sequence *dst, Sequence *src, const int UNUSED(flag)) @@ -3111,10 +3096,7 @@ static void free_speed_effect(Sequence *seq, const bool UNUSED(do_id_user)) if (v->frameMap) { MEM_freeN(v->frameMap); } - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static void copy_speed_effect(Sequence *dst, Sequence *src, const int UNUSED(flag)) @@ -3394,11 +3376,7 @@ static int num_inputs_gaussian_blur(void) static void free_gaussian_blur_effect(Sequence *seq, const bool UNUSED(do_id_user)) { - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static void copy_gaussian_blur_effect(Sequence *dst, Sequence *src, const int UNUSED(flag)) @@ -4052,11 +4030,7 @@ static void copy_effect_default(Sequence *dst, Sequence *src, const int UNUSED(f static void free_effect_default(Sequence *seq, const bool UNUSED(do_id_user)) { - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static int early_out_noop(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1)) diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 8a8ea18cc43..50db8e73844 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2766,10 +2766,7 @@ static void radial_control_cancel(bContext *C, wmOperator *op) wmWindowManager *wm = CTX_wm_manager(C); ScrArea *area = CTX_wm_area(C); - if (rc->dial) { - MEM_freeN(rc->dial); - rc->dial = NULL; - } + MEM_SAFE_FREE(rc->dial); ED_area_status_text(area, NULL); @@ -2959,10 +2956,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even if (event->val == KM_RELEASE) { rc->slow_mode = false; handled = true; - if (rc->dial) { - MEM_freeN(rc->dial); - rc->dial = NULL; - } + MEM_SAFE_FREE(rc->dial); } break; } -- cgit v1.2.3 From bb8ce95b5ee79787d0a54cb59f522726d693dc7d Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 6 Aug 2021 09:10:28 +0200 Subject: Cleanup: const pass `keyframes_keylist`. --- .../blender/editors/animation/keyframes_keylist.c | 102 +++++++++++---------- .../blender/editors/include/ED_keyframes_keylist.h | 20 ++-- 2 files changed, 63 insertions(+), 59 deletions(-) diff --git a/source/blender/editors/animation/keyframes_keylist.c b/source/blender/editors/animation/keyframes_keylist.c index 8f0f6d753be..722edba0473 100644 --- a/source/blender/editors/animation/keyframes_keylist.c +++ b/source/blender/editors/animation/keyframes_keylist.c @@ -89,7 +89,10 @@ ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, float cfra) /* TODO(jbakker): Should we change this to use `ED_keylist_find_next(keys, min_fra)` and only check * boundary of `max_fra`. */ -ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, float min_fra, float max_fra) +/* TODO(jbakker): Use const Range2f. */ +ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, + const float min_fra, + const float max_fra) { for (ActKeyColumn *ak = keylist->keys.root; ak; ak = (ak->cfra < min_fra) ? ak->right : ak->left) { @@ -128,12 +131,12 @@ bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_ } /* ActKeyColumns (Keyframe Columns) ------------------------------------------ */ -BLI_INLINE bool is_cfra_eq(float a, float b) +BLI_INLINE bool is_cfra_eq(const float a, const float b) { return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH); } -BLI_INLINE bool is_cfra_lt(float a, float b) +BLI_INLINE bool is_cfra_lt(const float a, const float b) { return (b - a) > BEZT_BINARYSEARCH_THRESH; } @@ -144,7 +147,7 @@ short compare_ak_cfraPtr(void *node, void *data) { ActKeyColumn *ak = (ActKeyColumn *)node; const float *cframe = data; - float val = *cframe; + const float val = *cframe; if (is_cfra_eq(val, ak->cfra)) { return 0; @@ -168,7 +171,7 @@ typedef struct BezTripleChain { } BezTripleChain; /* Categorize the interpolation & handle type of the keyframe. */ -static eKeyframeHandleDrawOpts bezt_handle_type(BezTriple *bezt) +static eKeyframeHandleDrawOpts bezt_handle_type(const BezTriple *bezt) { if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) { return KEYFRAME_HANDLE_AUTO_CLAMP; @@ -188,14 +191,14 @@ static eKeyframeHandleDrawOpts bezt_handle_type(BezTriple *bezt) /* Determine if the keyframe is an extreme by comparing with neighbors. * Ends of fixed-value sections and of the whole curve are also marked. */ -static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain) +static eKeyframeExtremeDrawOpts bezt_extreme_type(const BezTripleChain *chain) { if (chain->prev == NULL && chain->next == NULL) { return KEYFRAME_EXTREME_NONE; } /* Keyframe values for the current one and neighbors. */ - float cur_y = chain->cur->vec[1][1]; + const float cur_y = chain->cur->vec[1][1]; float prev_y = cur_y, next_y = cur_y; if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) { @@ -216,20 +219,20 @@ static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain) } /* Bezier handle values for the overshoot check. */ - bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ; - bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ; - float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y; - float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y; + const bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ; + const bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ; + const float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y; + const float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y; /* Detect extremes. One of the neighbors is allowed to be equal to current. */ if (prev_y < cur_y || next_y < cur_y) { - bool is_overshoot = (handle_l > cur_y || handle_r > cur_y); + const bool is_overshoot = (handle_l > cur_y || handle_r > cur_y); return KEYFRAME_EXTREME_MAX | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0); } if (prev_y > cur_y || next_y > cur_y) { - bool is_overshoot = (handle_l < cur_y || handle_r < cur_y); + const bool is_overshoot = (handle_l < cur_y || handle_r < cur_y); return KEYFRAME_EXTREME_MIN | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0); } @@ -249,8 +252,8 @@ static short compare_ak_bezt(void *node, void *data) static DLRBT_Node *nalloc_ak_bezt(void *data) { ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); - BezTripleChain *chain = data; - BezTriple *bezt = chain->cur; + const BezTripleChain *chain = data; + const BezTriple *bezt = chain->cur; /* store settings based on state of BezTriple */ ak->cfra = bezt->vec[1][0]; @@ -269,8 +272,8 @@ static DLRBT_Node *nalloc_ak_bezt(void *data) static void nupdate_ak_bezt(void *node, void *data) { ActKeyColumn *ak = node; - BezTripleChain *chain = data; - BezTriple *bezt = chain->cur; + const BezTripleChain *chain = data; + const BezTriple *bezt = chain->cur; /* set selection status and 'touched' status */ if (BEZT_ISSEL_ANY(bezt)) { @@ -290,7 +293,7 @@ static void nupdate_ak_bezt(void *node, void *data) ak->handle_type = MAX2(ak->handle_type, bezt_handle_type(bezt)); /* For extremes, detect when combining different states. */ - char new_extreme = bezt_extreme_type(chain); + const char new_extreme = bezt_extreme_type(chain); if (new_extreme != ak->extreme_type) { /* Replace the flat status without adding mixed. */ @@ -308,7 +311,7 @@ static void nupdate_ak_bezt(void *node, void *data) /* Comparator callback used for ActKeyColumns and GPencil frame */ static short compare_ak_gpframe(void *node, void *data) { - bGPDframe *gpf = (bGPDframe *)data; + const bGPDframe *gpf = (bGPDframe *)data; float frame = gpf->framenum; return compare_ak_cfraPtr(node, &frame); @@ -318,7 +321,7 @@ static short compare_ak_gpframe(void *node, void *data) static DLRBT_Node *nalloc_ak_gpframe(void *data) { ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"); - bGPDframe *gpf = (bGPDframe *)data; + const bGPDframe *gpf = (bGPDframe *)data; /* store settings based on state of BezTriple */ ak->cfra = gpf->framenum; @@ -339,7 +342,7 @@ static DLRBT_Node *nalloc_ak_gpframe(void *data) static void nupdate_ak_gpframe(void *node, void *data) { ActKeyColumn *ak = (ActKeyColumn *)node; - bGPDframe *gpf = (bGPDframe *)data; + const bGPDframe *gpf = (bGPDframe *)data; /* set selection status and 'touched' status */ if (gpf->flag & GP_FRAME_SELECT) { @@ -361,7 +364,7 @@ static void nupdate_ak_gpframe(void *node, void *data) /* Comparator callback used for ActKeyColumns and GPencil frame */ static short compare_ak_masklayshape(void *node, void *data) { - MaskLayerShape *masklay_shape = (MaskLayerShape *)data; + const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; float frame = masklay_shape->frame; return compare_ak_cfraPtr(node, &frame); @@ -371,7 +374,7 @@ static short compare_ak_masklayshape(void *node, void *data) static DLRBT_Node *nalloc_ak_masklayshape(void *data) { ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"); - MaskLayerShape *masklay_shape = (MaskLayerShape *)data; + const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; /* store settings based on state of BezTriple */ ak->cfra = masklay_shape->frame; @@ -387,7 +390,7 @@ static DLRBT_Node *nalloc_ak_masklayshape(void *data) static void nupdate_ak_masklayshape(void *node, void *data) { ActKeyColumn *ak = (ActKeyColumn *)node; - MaskLayerShape *masklay_shape = (MaskLayerShape *)data; + const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; /* set selection status and 'touched' status */ if (masklay_shape->flag & MASK_SHAPE_SELECT) { @@ -438,7 +441,9 @@ static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape static const ActKeyBlockInfo dummy_keyblock = {0}; -static void compute_keyblock_data(ActKeyBlockInfo *info, BezTriple *prev, BezTriple *beztn) +static void compute_keyblock_data(ActKeyBlockInfo *info, + const BezTriple *prev, + const BezTriple *beztn) { memset(info, 0, sizeof(ActKeyBlockInfo)); @@ -501,7 +506,7 @@ static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block) } } -static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, int bezt_len) +static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len) { ActKeyColumn *col = keylist->keys.first; @@ -564,7 +569,7 @@ static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, in * This must be called even by animation sources that don't generate * keyblocks to keep the data structure consistent after adding columns. */ -static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, int bezt_len) +static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len) { /* Recompute the prev/next linked list. */ BLI_dlrbTree_linkedlist_sync(&keylist->keys); @@ -599,13 +604,13 @@ static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, int bezt_len /* --------- */ -bool actkeyblock_is_valid(ActKeyColumn *ac) +bool actkeyblock_is_valid(const ActKeyColumn *ac) { return ac != NULL && ac->next != NULL && ac->totblock > 0; } /* Checks if ActKeyBlock should exist... */ -int actkeyblock_get_valid_hold(ActKeyColumn *ac) +int actkeyblock_get_valid_hold(const ActKeyColumn *ac) { /* check that block is valid */ if (!actkeyblock_is_valid(ac)) { @@ -618,19 +623,17 @@ int actkeyblock_get_valid_hold(ActKeyColumn *ac) /* *************************** Keyframe List Conversions *************************** */ -void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, int saction_flag) +void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, const int saction_flag) { if (ac) { ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; /* get F-Curves to take keyframes from */ - filter = ANIMFILTER_DATA_VISIBLE; + const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* loop through each F-Curve, grabbing the keyframes */ - for (ale = anim_data.first; ale; ale = ale->next) { + for (const bAnimListElem *ale = anim_data.first; ale; ale = ale->next) { /* Why not use all #eAnim_KeyType here? * All of the other key types are actually "summaries" themselves, * and will just end up duplicating stuff that comes up through @@ -657,12 +660,10 @@ void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, int saction_flag } } -void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, int saction_flag) +void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, const int saction_flag) { bAnimContext ac = {NULL}; ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; bAnimListElem dummychan = {NULL}; @@ -681,23 +682,21 @@ void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, int sac ac.datatype = ANIMCONT_CHANNEL; /* get F-Curves to take keyframes from */ - filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ + const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* loop through each F-Curve, grabbing the keyframes */ - for (ale = anim_data.first; ale; ale = ale->next) { + for (const bAnimListElem *ale = anim_data.first; ale; ale = ale->next) { fcurve_to_keylist(ale->adt, ale->data, keylist, saction_flag); } ANIM_animdata_freelist(&anim_data); } -void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, int saction_flag) +void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, const int saction_flag) { bAnimContext ac = {NULL}; ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; bAnimListElem dummychan = {NULL}; Base dummybase = {NULL}; @@ -719,11 +718,11 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, int sactio ac.datatype = ANIMCONT_CHANNEL; /* get F-Curves to take keyframes from */ - filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ + const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* loop through each F-Curve, grabbing the keyframes */ - for (ale = anim_data.first; ale; ale = ale->next) { + for (const bAnimListElem *ale = anim_data.first; ale; ale = ale->next) { fcurve_to_keylist(ale->adt, ale->data, keylist, saction_flag); } @@ -733,7 +732,7 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, int sactio void cachefile_to_keylist(bDopeSheet *ads, CacheFile *cache_file, AnimKeylist *keylist, - int saction_flag) + const int saction_flag) { if (cache_file == NULL) { return; @@ -753,18 +752,18 @@ void cachefile_to_keylist(bDopeSheet *ads, /* get F-Curves to take keyframes from */ ListBase anim_data = {NULL, NULL}; - int filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ + const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* loop through each F-Curve, grabbing the keyframes */ - LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) { fcurve_to_keylist(ale->adt, ale->data, keylist, saction_flag); } ANIM_animdata_freelist(&anim_data); } -void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, int saction_flag) +void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag) { if (fcu && fcu->totvert && fcu->bezt) { /* apply NLA-mapping (if applicable) */ @@ -801,7 +800,10 @@ void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, int sac } } -void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, AnimKeylist *keylist, int saction_flag) +void agroup_to_keylist(AnimData *adt, + bActionGroup *agrp, + AnimKeylist *keylist, + const int saction_flag) { FCurve *fcu; @@ -813,7 +815,7 @@ void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, AnimKeylist *keylist, } } -void action_to_keylist(AnimData *adt, bAction *act, AnimKeylist *keylist, int saction_flag) +void action_to_keylist(AnimData *adt, bAction *act, AnimKeylist *keylist, const int saction_flag) { FCurve *fcu; diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h index d3690fa3aa0..8ab2dff36e5 100644 --- a/source/blender/editors/include/ED_keyframes_keylist.h +++ b/source/blender/editors/include/ED_keyframes_keylist.h @@ -154,34 +154,36 @@ bool ED_keylist_frame_range(const struct AnimKeylist *keylist, struct Range2f *r void fcurve_to_keylist(struct AnimData *adt, struct FCurve *fcu, struct AnimKeylist *keylist, - int saction_flag); + const int saction_flag); /* Action Group */ void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, struct AnimKeylist *keylist, - int saction_flag); + const int saction_flag); /* Action */ void action_to_keylist(struct AnimData *adt, struct bAction *act, struct AnimKeylist *keylist, - int saction_flag); + const int saction_flag); /* Object */ void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, struct AnimKeylist *keylist, - int saction_flag); + const int saction_flag); /* Cache File */ void cachefile_to_keylist(struct bDopeSheet *ads, struct CacheFile *cache_file, struct AnimKeylist *keylist, - int saction_flag); + const int saction_flag); /* Scene */ void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, struct AnimKeylist *keylist, - int saction_flag); + const int saction_flag); /* DopeSheet Summary */ -void summary_to_keylist(struct bAnimContext *ac, struct AnimKeylist *keylist, int saction_flag); +void summary_to_keylist(struct bAnimContext *ac, + struct AnimKeylist *keylist, + const int saction_flag); /* Grease Pencil datablock summary */ void gpencil_to_keylist(struct bDopeSheet *ads, struct bGPdata *gpd, @@ -199,10 +201,10 @@ void mask_to_keylist(struct bDopeSheet *ads, short compare_ak_cfraPtr(void *node, void *data); /* Checks if ActKeyColumn has any block data */ -bool actkeyblock_is_valid(ActKeyColumn *ac); +bool actkeyblock_is_valid(const ActKeyColumn *ac); /* Checks if ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds") */ -int actkeyblock_get_valid_hold(ActKeyColumn *ac); +int actkeyblock_get_valid_hold(const ActKeyColumn *ac); #ifdef __cplusplus } -- cgit v1.2.3 From 1ab75c1d494422270fa88bbf23cc42f7b203ed4b Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 6 Aug 2021 09:46:36 +0200 Subject: Cleanup: use range2f in `ED_keylist_find_any_between`. --- source/blender/blenlib/BLI_range.h | 5 +++++ source/blender/editors/animation/keyframes_keylist.c | 9 +++------ source/blender/editors/include/ED_keyframes_keylist.h | 8 ++++---- source/blender/editors/space_action/action_select.c | 7 +++---- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/source/blender/blenlib/BLI_range.h b/source/blender/blenlib/BLI_range.h index ad4cd6c162e..e55f443769d 100644 --- a/source/blender/blenlib/BLI_range.h +++ b/source/blender/blenlib/BLI_range.h @@ -29,6 +29,11 @@ typedef struct Range2f { float max; } Range2f; +BLI_INLINE bool range2f_in_range(const Range2f *range, const float value) +{ + return IN_RANGE(value, range->min, range->max); +} + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/animation/keyframes_keylist.c b/source/blender/editors/animation/keyframes_keylist.c index 722edba0473..a869d8d6388 100644 --- a/source/blender/editors/animation/keyframes_keylist.c +++ b/source/blender/editors/animation/keyframes_keylist.c @@ -89,14 +89,11 @@ ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, float cfra) /* TODO(jbakker): Should we change this to use `ED_keylist_find_next(keys, min_fra)` and only check * boundary of `max_fra`. */ -/* TODO(jbakker): Use const Range2f. */ -ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, - const float min_fra, - const float max_fra) +ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, const Range2f frame_range) { for (ActKeyColumn *ak = keylist->keys.root; ak; - ak = (ak->cfra < min_fra) ? ak->right : ak->left) { - if (IN_RANGE(ak->cfra, min_fra, max_fra)) { + ak = (ak->cfra < frame_range.min) ? ak->right : ak->left) { + if (range2f_in_range(&frame_range, ak->cfra)) { return ak; } } diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h index 8ab2dff36e5..86741b67698 100644 --- a/source/blender/editors/include/ED_keyframes_keylist.h +++ b/source/blender/editors/include/ED_keyframes_keylist.h @@ -23,6 +23,8 @@ #pragma once +#include "BLI_range.h" + #ifdef __cplusplus extern "C" { #endif @@ -37,7 +39,6 @@ struct Scene; struct bAnimContext; struct bDopeSheet; struct bGPDlayer; -struct Range2f; /* ****************************** Base Structs ****************************** */ @@ -142,11 +143,10 @@ struct ActKeyColumn *ED_keylist_find_exact(const struct AnimKeylist *keylist, fl struct ActKeyColumn *ED_keylist_find_next(const struct AnimKeylist *keylist, float cfra); struct ActKeyColumn *ED_keylist_find_prev(const struct AnimKeylist *keylist, float cfra); struct ActKeyColumn *ED_keylist_find_any_between(const struct AnimKeylist *keylist, - float min_fra, - float max_fra); + const Range2f frame_range); bool ED_keylist_is_empty(const struct AnimKeylist *keylist); const struct ListBase /* ActKeyColumn */ *ED_keylist_listbase(const struct AnimKeylist *keylist); -bool ED_keylist_frame_range(const struct AnimKeylist *keylist, struct Range2f *r_frame_range); +bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range); /* Key-data Generation --------------- */ diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 64ccca2c907..9dcfc626a50 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -170,10 +170,9 @@ static void actkeys_find_key_in_list_element(bAnimContext *ac, /* half-size (for either side), but rounded up to nearest int (for easier targeting) */ key_hsize = roundf(key_hsize / 2.0f); - float xmin = UI_view2d_region_to_view_x(v2d, region_x - (int)key_hsize); - float xmax = UI_view2d_region_to_view_x(v2d, region_x + (int)key_hsize); - - const ActKeyColumn *ak = ED_keylist_find_any_between(keylist, xmin, xmax); + const Range2f range = {UI_view2d_region_to_view_x(v2d, region_x - (int)key_hsize), + UI_view2d_region_to_view_x(v2d, region_x + (int)key_hsize)}; + const ActKeyColumn *ak = ED_keylist_find_any_between(keylist, range); if (ak) { /* set the frame to use, and apply inverse-correction for NLA-mapping -- cgit v1.2.3 From 6188c29603da211b8305d8fe7c082dcae61688ab Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 6 Aug 2021 09:54:56 +0200 Subject: Cleanup: use const result in `ED_keyframes_find_*` functions. --- source/blender/editors/animation/anim_draw.c | 2 +- source/blender/editors/animation/keyframes_keylist.c | 18 ++++++++++-------- source/blender/editors/armature/pose_slide.c | 4 ++-- source/blender/editors/include/ED_keyframes_keylist.h | 10 +++++----- source/blender/editors/screen/screen_ops.c | 2 +- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 735f3b86924..6d272bfc180 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -495,7 +495,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ Mask *mask = CTX_data_edit_mask(C); bDopeSheet ads = {NULL}; struct AnimKeylist *keylist = ED_keylist_create(); - ActKeyColumn *aknext, *akprev; + const ActKeyColumn *aknext, *akprev; float cfranext, cfraprev; bool donenext = false, doneprev = false; int nextcount = 0, prevcount = 0; diff --git a/source/blender/editors/animation/keyframes_keylist.c b/source/blender/editors/animation/keyframes_keylist.c index a869d8d6388..98aedb9cd0c 100644 --- a/source/blender/editors/animation/keyframes_keylist.c +++ b/source/blender/editors/animation/keyframes_keylist.c @@ -72,26 +72,28 @@ void ED_keylist_free(AnimKeylist *keylist) MEM_freeN(keylist); } -ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, float cfra) +const ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, float cfra) { - return (ActKeyColumn *)BLI_dlrbTree_search_exact(&keylist->keys, compare_ak_cfraPtr, &cfra); + return (const ActKeyColumn *)BLI_dlrbTree_search_exact( + &keylist->keys, compare_ak_cfraPtr, &cfra); } -ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, float cfra) +const ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, float cfra) { - return (ActKeyColumn *)BLI_dlrbTree_search_next(&keylist->keys, compare_ak_cfraPtr, &cfra); + return (const ActKeyColumn *)BLI_dlrbTree_search_next(&keylist->keys, compare_ak_cfraPtr, &cfra); } -ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, float cfra) +const ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, float cfra) { - return (ActKeyColumn *)BLI_dlrbTree_search_prev(&keylist->keys, compare_ak_cfraPtr, &cfra); + return (const ActKeyColumn *)BLI_dlrbTree_search_prev(&keylist->keys, compare_ak_cfraPtr, &cfra); } /* TODO(jbakker): Should we change this to use `ED_keylist_find_next(keys, min_fra)` and only check * boundary of `max_fra`. */ -ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, const Range2f frame_range) +const ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, + const Range2f frame_range) { - for (ActKeyColumn *ak = keylist->keys.root; ak; + for (const ActKeyColumn *ak = keylist->keys.root; ak; ak = (ak->cfra < frame_range.min) ? ak->right : ak->left) { if (range2f_in_range(&frame_range, ak->cfra)) { return ak; diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 238799650a0..bc5cbd92deb 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -1716,7 +1716,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st /* Find the long keyframe (i.e. hold), and hence obtain the endFrame value * - the best case would be one that starts on the frame itself */ - ActKeyColumn *ab = ED_keylist_find_exact(keylist, startFrame); + const ActKeyColumn *ab = ED_keylist_find_exact(keylist, startFrame); /* There are only two cases for no-exact match: * 1) the current frame is just before another key but not on a key itself @@ -1746,7 +1746,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st if (ab) { /* Go to next if it is also valid and meets "extension" criteria. */ while (ab->next) { - ActKeyColumn *abn = ab->next; + const ActKeyColumn *abn = ab->next; /* Must be valid. */ if ((actkeyblock_get_valid_hold(abn) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h index 86741b67698..3a9750c1206 100644 --- a/source/blender/editors/include/ED_keyframes_keylist.h +++ b/source/blender/editors/include/ED_keyframes_keylist.h @@ -139,11 +139,11 @@ typedef enum eKeyframeExtremeDrawOpts { struct AnimKeylist *ED_keylist_create(void); void ED_keylist_free(struct AnimKeylist *keylist); -struct ActKeyColumn *ED_keylist_find_exact(const struct AnimKeylist *keylist, float cfra); -struct ActKeyColumn *ED_keylist_find_next(const struct AnimKeylist *keylist, float cfra); -struct ActKeyColumn *ED_keylist_find_prev(const struct AnimKeylist *keylist, float cfra); -struct ActKeyColumn *ED_keylist_find_any_between(const struct AnimKeylist *keylist, - const Range2f frame_range); +const struct ActKeyColumn *ED_keylist_find_exact(const struct AnimKeylist *keylist, float cfra); +const struct ActKeyColumn *ED_keylist_find_next(const struct AnimKeylist *keylist, float cfra); +const struct ActKeyColumn *ED_keylist_find_prev(const struct AnimKeylist *keylist, float cfra); +const struct ActKeyColumn *ED_keylist_find_any_between(const struct AnimKeylist *keylist, + const Range2f frame_range); bool ED_keylist_is_empty(const struct AnimKeylist *keylist); const struct ListBase /* ActKeyColumn */ *ED_keylist_listbase(const struct AnimKeylist *keylist); bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index e150d7774f9..6af18104336 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -3077,7 +3077,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) } /* find matching keyframe in the right direction */ - ActKeyColumn *ak; + const ActKeyColumn *ak; if (next) { ak = ED_keylist_find_next(keylist, cfra); } -- cgit v1.2.3 From d98791a106ffd1546ec476f3f38caeedd6f99047 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 6 Aug 2021 10:20:24 +0200 Subject: Cleanup: clang tidy `bugprone-signed-char-misuse` --- source/blender/draw/intern/draw_cache_impl_gpencil.c | 2 +- source/blender/editors/gpencil/gpencil_primitive.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index 2a476ab41bb..6ed4148a7fe 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -553,7 +553,7 @@ bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob) gps->runtime.stroke_start = 1; /* Add one for the adjacency index. */ copy_v4_v4(gps->vert_color_fill, gpd->runtime.vert_color_fill); /* Caps. */ - gps->caps[0] = gps->caps[1] = brush->gpencil_settings->caps_type; + gps->caps[0] = gps->caps[1] = (short)brush->gpencil_settings->caps_type; gpd->runtime.sbuffer_gps = gps; } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 27374f21b66..7e6ff53de14 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -336,7 +336,7 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) gps->inittime = 0.0f; /* Set stroke caps. */ - gps->caps[0] = gps->caps[1] = brush->gpencil_settings->caps_type; + gps->caps[0] = gps->caps[1] = (short)brush->gpencil_settings->caps_type; /* Apply the vertex color to fill. */ ED_gpencil_fill_vertex_color_set(ts, brush, gps); -- cgit v1.2.3 From 151eed752b01970fdd3cbf69405211b310b80f74 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Fri, 6 Aug 2021 17:55:00 +0900 Subject: Fix invalid XR action map indices after alloc Although the relevant structs (wmXrRuntime/XrActionMap/ XrActionMapItem) are zero-allocated, the selected and active action map indices need to be initialized to -1 to prevent potential out-of-bounds list access. --- source/blender/windowmanager/xr/intern/wm_xr.c | 1 + source/blender/windowmanager/xr/intern/wm_xr_actionmap.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c index 716a0936a24..3091a3a19f1 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr.c +++ b/source/blender/windowmanager/xr/intern/wm_xr.c @@ -149,6 +149,7 @@ bool wm_xr_events_handle(wmWindowManager *wm) wmXrRuntimeData *wm_xr_runtime_data_create(void) { wmXrRuntimeData *runtime = MEM_callocN(sizeof(*runtime), __func__); + runtime->actactionmap = runtime->selactionmap = -1; return runtime; } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c index 7673f2aa212..f9ad34b5a9b 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c @@ -256,6 +256,7 @@ XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap, if (ami_prev) { WM_xr_actionmap_item_ensure_unique(actionmap, ami); } + ami->selbinding = -1; BLI_addtail(&actionmap->items, ami); @@ -398,6 +399,7 @@ XrActionMap *WM_xr_actionmap_new(wmXrRuntimeData *runtime, const char *name, boo if (am_prev) { WM_xr_actionmap_ensure_unique(runtime, am); } + am->selitem = -1; BLI_addtail(&runtime->actionmaps, am); -- cgit v1.2.3 From 4c675bc356182971b23248e79298fef3375835df Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Fri, 6 Aug 2021 17:55:32 +0900 Subject: Fix missing function param definition in rna_xr.c Was accidentally left out in rBe844e9e8f3bb. --- source/blender/makesrna/intern/rna_xr.c | 48 ++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 9a512e351cb..e55f29a7777 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -828,12 +828,12 @@ void rna_XrSessionState_haptic_action_stop(bContext *C, } static void rna_XrSessionState_controller_grip_location_get(bContext *C, - int *index, + int index, float r_values[3]) { # ifdef WITH_XR_OPENXR const wmWindowManager *wm = CTX_wm_manager(C); - WM_xr_session_state_controller_grip_location_get(&wm->xr, *index, r_values); + WM_xr_session_state_controller_grip_location_get(&wm->xr, index, r_values); # else UNUSED_VARS(C, index); zero_v3(r_values); @@ -854,12 +854,12 @@ static void rna_XrSessionState_controller_grip_rotation_get(bContext *C, } static void rna_XrSessionState_controller_aim_location_get(bContext *C, - int *index, + int index, float r_values[3]) { # ifdef WITH_XR_OPENXR const wmWindowManager *wm = CTX_wm_manager(C); - WM_xr_session_state_controller_aim_location_get(&wm->xr, *index, r_values); + WM_xr_session_state_controller_aim_location_get(&wm->xr, index, r_values); # else UNUSED_VARS(C, index); zero_v3(r_values); @@ -1695,16 +1695,16 @@ static void rna_def_xr_session_state(BlenderRNA *brna) RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - RNA_def_float_translation(func, - "location", - 3, - NULL, - -FLT_MAX, - FLT_MAX, - "Location", - "Controller grip location", - -FLT_MAX, - FLT_MAX); + parm = RNA_def_float_translation(func, + "location", + 3, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Controller grip location", + -FLT_MAX, + FLT_MAX); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); func = RNA_def_function( @@ -1739,16 +1739,16 @@ static void rna_def_xr_session_state(BlenderRNA *brna) RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - RNA_def_float_translation(func, - "location", - 3, - NULL, - -FLT_MAX, - FLT_MAX, - "Location", - "Controller aim location", - -FLT_MAX, - FLT_MAX); + parm = RNA_def_float_translation(func, + "location", + 3, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Controller aim location", + -FLT_MAX, + FLT_MAX); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); func = RNA_def_function( -- cgit v1.2.3 From b0df8f53ba62e769944dff66db29de374008ba8a Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Fri, 6 Aug 2021 17:56:06 +0900 Subject: Cleanup: rna_xr.c - Rename functions to use RNA identifiers - Use SET_FLAG_FROM_TEST macro - Specify max string length for relevant function params --- source/blender/makesrna/intern/rna_xr.c | 40 +++++++++++++-------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index e55f29a7777..88e285d7cc2 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -96,7 +96,7 @@ static XrActionMapBinding *rna_XrActionMapBinding_find(XrActionMapItem *ami, con # endif } -static int rna_XrActionMapBinding_axis0_flag_get(PointerRNA *ptr) +static int rna_XrActionMapBinding_axis0_region_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR XrActionMapBinding *amb = ptr->data; @@ -112,7 +112,7 @@ static int rna_XrActionMapBinding_axis0_flag_get(PointerRNA *ptr) return 0; } -static void rna_XrActionMapBinding_axis0_flag_set(PointerRNA *ptr, int value) +static void rna_XrActionMapBinding_axis0_region_set(PointerRNA *ptr, int value) { # ifdef WITH_XR_OPENXR XrActionMapBinding *amb = ptr->data; @@ -123,7 +123,7 @@ static void rna_XrActionMapBinding_axis0_flag_set(PointerRNA *ptr, int value) # endif } -static int rna_XrActionMapBinding_axis1_flag_get(PointerRNA *ptr) +static int rna_XrActionMapBinding_axis1_region_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR XrActionMapBinding *amb = ptr->data; @@ -139,7 +139,7 @@ static int rna_XrActionMapBinding_axis1_flag_get(PointerRNA *ptr) return 0; } -static void rna_XrActionMapBinding_axis1_flag_set(PointerRNA *ptr, int value) +static void rna_XrActionMapBinding_axis1_region_set(PointerRNA *ptr, int value) { # ifdef WITH_XR_OPENXR XrActionMapBinding *amb = ptr->data; @@ -289,12 +289,7 @@ static void rna_XrActionMapItem_bimanual_set(PointerRNA *ptr, bool value) { # ifdef WITH_XR_OPENXR XrActionMapItem *ami = ptr->data; - if (value) { - ami->action_flag |= XR_ACTION_BIMANUAL; - } - else { - ami->action_flag &= ~XR_ACTION_BIMANUAL; - } + SET_FLAG_FROM_TEST(ami->action_flag, value, XR_ACTION_BIMANUAL); # else UNUSED_VARS(ptr, value); # endif @@ -317,18 +312,13 @@ static void rna_XrActionMapItem_haptic_match_user_paths_set(PointerRNA *ptr, boo { # ifdef WITH_XR_OPENXR XrActionMapItem *ami = ptr->data; - if (value) { - ami->haptic_flag |= XR_HAPTIC_MATCHUSERPATHS; - } - else { - ami->haptic_flag &= ~XR_HAPTIC_MATCHUSERPATHS; - } + SET_FLAG_FROM_TEST(ami->haptic_flag, value, XR_HAPTIC_MATCHUSERPATHS); # else UNUSED_VARS(ptr, value); # endif } -static int rna_XrActionMapItem_haptic_flag_get(PointerRNA *ptr) +static int rna_XrActionMapItem_haptic_mode_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR XrActionMapItem *ami = ptr->data; @@ -345,7 +335,7 @@ static int rna_XrActionMapItem_haptic_flag_get(PointerRNA *ptr) return XR_HAPTIC_PRESS; } -static void rna_XrActionMapItem_haptic_flag_set(PointerRNA *ptr, int value) +static void rna_XrActionMapItem_haptic_mode_set(PointerRNA *ptr, int value) { # ifdef WITH_XR_OPENXR XrActionMapItem *ami = ptr->data; @@ -1061,7 +1051,7 @@ static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_struct_ui_text(srna, "XR Action Map Bindings", "Collection of XR action map bindings"); func = RNA_def_function(srna, "new", "rna_XrActionMapBinding_new"); - parm = RNA_def_string(func, "name", NULL, 0, "Name of the action map binding", ""); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name of the action map binding", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_boolean(func, "replace_existing", @@ -1110,7 +1100,7 @@ static void rna_def_xr_actionmap_items(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_struct_ui_text(srna, "XR Action Map Items", "Collection of XR action map items"); func = RNA_def_function(srna, "new", "rna_XrActionMapItem_new"); - parm = RNA_def_string(func, "name", NULL, 0, "Name of the action map item", ""); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name of the action map item", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_boolean(func, "replace_existing", @@ -1314,7 +1304,7 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) prop = RNA_def_property(srna, "haptic_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_xr_haptic_flags); RNA_def_property_enum_funcs( - prop, "rna_XrActionMapItem_haptic_flag_get", "rna_XrActionMapItem_haptic_flag_set", NULL); + prop, "rna_XrActionMapItem_haptic_mode_get", "rna_XrActionMapItem_haptic_mode_set", NULL); RNA_def_property_ui_text(prop, "Haptic mode", "Haptic application mode"); prop = RNA_def_property(srna, "bindings", PROP_COLLECTION, PROP_NONE); @@ -1357,8 +1347,8 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) prop = RNA_def_property(srna, "axis0_region", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_xr_axis0_flags); RNA_def_property_enum_funcs(prop, - "rna_XrActionMapBinding_axis0_flag_get", - "rna_XrActionMapBinding_axis0_flag_set", + "rna_XrActionMapBinding_axis0_region_get", + "rna_XrActionMapBinding_axis0_region_set", NULL); RNA_def_property_ui_text( prop, "Axis 0 Region", "Action execution region for the first input axis"); @@ -1366,8 +1356,8 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) prop = RNA_def_property(srna, "axis1_region", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_xr_axis1_flags); RNA_def_property_enum_funcs(prop, - "rna_XrActionMapBinding_axis1_flag_get", - "rna_XrActionMapBinding_axis1_flag_set", + "rna_XrActionMapBinding_axis1_region_get", + "rna_XrActionMapBinding_axis1_region_set", NULL); RNA_def_property_ui_text( prop, "Axis 1 Region", "Action execution region for the second input axis"); -- cgit v1.2.3 From f4adb35f28ece4096f19489273601e5327a08e99 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 6 Aug 2021 13:32:19 +0200 Subject: Fix T90476: intermittent wrong generated texture coordinates with modifiers This caused Cycles texture_space_mesh_modifier and panorama_dicing tests to randomly fail. The issue was introduced with D11377, due to a missing dependency. Now ensure we first copy the texture space parameters, and only then use or recompute then. In general it seems like this dependency should have already been there, since parameter evaluation includes animation and drivers, and geometry evaluation may depend on that (even if you would not typically animate e.g. an autosmooth angle). Thanks Campbell for tracking this one down. --- source/blender/depsgraph/intern/builder/deg_builder_relations.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index c7c6fafa512..7da3d2e25f0 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -2224,6 +2224,11 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) OperationKey obdata_geom_eval_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); OperationKey obdata_geom_done_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE); add_relation(obdata_geom_eval_key, obdata_geom_done_key, "ObData Geom Eval Done"); + + /* Link object data evaluation to parameter evaluation. */ + ComponentKey parameters_key(obdata, NodeType::PARAMETERS); + add_relation(parameters_key, obdata_geom_eval_key, "ObData Geom Params"); + /* Type-specific links. */ const ID_Type id_type = GS(obdata->name); switch (id_type) { -- cgit v1.2.3 From 01c1b1e82e5f59ea84686f7756086f0421e93c5f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 6 Aug 2021 14:38:01 +0200 Subject: Fix shortcut for Asset Details not showing in Asset Browser pulldown The shortcut wouldn't show up in the Asset Browser's "View" pulldown for the "Asset Details" item. It's the "N" key to toggle the right sidebar. --- release/scripts/presets/keyconfig/keymap_data/blender_default.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index a67c5244611..eda9c6e9792 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1968,8 +1968,8 @@ def km_file_browser(params): *_template_space_region_type_toggle( toolbar_key={"type": 'T', "value": 'PRESS'}, ), - ("screen.region_toggle", {"type": 'N', "value": 'PRESS'}, - {"properties": [("region_type", 'TOOL_PROPS')]}), + ("wm.context_toggle", {"type": 'N', "value": 'PRESS'}, + {"properties": [("data_path", 'space_data.show_region_tool_props')]}), ("file.parent", {"type": 'UP_ARROW', "value": 'PRESS', "alt": True}, None), ("file.previous", {"type": 'LEFT_ARROW', "value": 'PRESS', "alt": True}, None), ("file.next", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True}, None), -- cgit v1.2.3 From 2796ee7da049bbea3d83efdd42c3eaf1af1ebb2c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 6 Aug 2021 23:01:57 +1000 Subject: Fix error setting the ID name in disabled alembic nurbs importe This corrects code that's currently disabled, see `USE_NURBS` define. The name passed to `BKE_curve_add` was overwritten, bypassing uniqueness and utf8 encoding checks. Longer names would cause a buffer overrun as the length of the source data was passed to `BLI_strncpy` instead of the destination. Reviewed By: sybren Ref D12125 --- source/blender/io/alembic/intern/abc_reader_nurbs.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc index 2a5f4ecb787..25567aa8c24 100644 --- a/source/blender/io/alembic/intern/abc_reader_nurbs.cc +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc @@ -90,7 +90,7 @@ static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots) void AbcNurbsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) { - Curve *cu = static_cast(BKE_curve_add(bmain, "abc_curve", OB_SURF)); + Curve *cu = static_cast(BKE_curve_add(bmain, m_data_name.c_str(), OB_SURF)); cu->actvert = CU_ACT_NONE; std::vector>::iterator it; @@ -180,8 +180,6 @@ void AbcNurbsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSele BLI_addtail(BKE_curve_nurbs_get(cu), nu); } - BLI_strncpy(cu->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1); - m_object = BKE_object_add_only_object(bmain, OB_SURF, m_object_name.c_str()); m_object->data = cu; } -- cgit v1.2.3 From 9cff9f9f5df034ca27848875c25471dd952c34c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 6 Aug 2021 15:18:18 +0200 Subject: =?UTF-8?q?Cleanup:=20rename=20`FileList::asset=5Flibrary`=20?= =?UTF-8?q?=E2=86=92=20`asset=5Flibrary=5Fref`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the `FileList` struct, rename the `AssetLibraryReference *asset_library` field to `asset_library_ref` -- it's a description of which asset library is used, and not the asset library itself. This is to make space for a future `AssetLibrary *asset_library` field, which will point to an actual asset library struct/class. No functional changes. Reviewed by: Severin Differential Revision: https://developer.blender.org/D12151 --- release/scripts/startup/bl_ui/space_filebrowser.py | 8 +++--- source/blender/blenkernel/BKE_context.h | 2 +- source/blender/blenkernel/intern/context.c | 4 +-- source/blender/blenkernel/intern/workspace.c | 2 +- source/blender/blenloader/intern/versioning_300.c | 32 ++++++++++++++++++++-- source/blender/blenloader/intern/writefile.c | 4 +-- source/blender/editors/armature/pose_lib_2.c | 10 +++---- source/blender/editors/asset/ED_asset_handle.h | 2 +- .../editors/asset/ED_asset_temp_id_consumer.h | 2 +- .../blender/editors/asset/intern/asset_handle.cc | 4 +-- source/blender/editors/asset/intern/asset_ops.cc | 4 +-- .../editors/asset/intern/asset_temp_id_consumer.cc | 10 +++---- .../interface/interface_template_asset_view.cc | 24 ++++++++-------- source/blender/editors/screen/screen_context.c | 6 ++-- source/blender/editors/space_file/file_draw.c | 2 +- source/blender/editors/space_file/filelist.c | 30 ++++++++++---------- source/blender/editors/space_file/filelist.h | 2 +- source/blender/editors/space_file/filesel.c | 6 ++-- source/blender/editors/space_file/space_file.c | 8 +++--- source/blender/makesdna/DNA_asset_types.h | 2 +- source/blender/makesdna/DNA_space_types.h | 2 +- source/blender/makesdna/DNA_workspace_types.h | 2 +- source/blender/makesrna/intern/rna_asset.c | 4 +-- source/blender/makesrna/intern/rna_space.c | 4 +-- source/blender/makesrna/intern/rna_workspace.c | 4 +-- 25 files changed, 103 insertions(+), 77 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 9f70d26c71b..00ac69595c7 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -37,9 +37,9 @@ class FILEBROWSER_HT_header(Header): params = space_data.params row = layout.row(align=True) - row.prop(params, "asset_library", text="") + row.prop(params, "asset_library_ref", text="") # External libraries don't auto-refresh, add refresh button. - if params.asset_library != 'LOCAL': + if params.asset_library_ref != 'LOCAL': row.operator("file.refresh", text="", icon='FILE_REFRESH') layout.separator_spacer() @@ -678,8 +678,8 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): layout.label(text="No asset selected", icon='INFO') return - asset_library = context.asset_library - asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library) + asset_library_ref = context.asset_library_ref + asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library_ref) if asset_file_handle.local_id: # If the active file is an ID, use its name directly so renaming is possible from right here. diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 8917580689d..eda6a03fa1a 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -360,7 +360,7 @@ int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list); -const struct AssetLibraryReference *CTX_wm_asset_library(const bContext *C); +const struct AssetLibraryReference *CTX_wm_asset_library_ref(const bContext *C); struct AssetHandle CTX_wm_asset_handle(const bContext *C, bool *r_is_valid); bool CTX_wm_interface_locked(const bContext *C); diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index dced945bea0..7763bb9ca08 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1448,9 +1448,9 @@ int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list) return ctx_data_collection_get(C, "editable_gpencil_strokes", list); } -const AssetLibraryReference *CTX_wm_asset_library(const bContext *C) +const AssetLibraryReference *CTX_wm_asset_library_ref(const bContext *C) { - return ctx_data_pointer_get(C, "asset_library"); + return ctx_data_pointer_get(C, "asset_library_ref"); } AssetHandle CTX_wm_asset_handle(const bContext *C, bool *r_is_valid) diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 059dc68b1dc..329633c6759 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -58,7 +58,7 @@ static void workspace_init_data(ID *id) { WorkSpace *workspace = (WorkSpace *)id; - BKE_asset_library_reference_init_default(&workspace->asset_library); + BKE_asset_library_reference_init_default(&workspace->asset_library_ref); } static void workspace_free_data(ID *id) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 42af8f4bda2..862ef99fca5 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -657,12 +657,12 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!DNA_struct_elem_find( fd->filesdna, "WorkSpace", "AssetLibraryReference", "asset_library")) { LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { - BKE_asset_library_reference_init_default(&workspace->asset_library); + BKE_asset_library_reference_init_default(&workspace->asset_library_ref); } } if (!DNA_struct_elem_find( - fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library")) { + fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library_ref")) { LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) { @@ -671,7 +671,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) if (sfile->browse_mode != FILE_BROWSE_MODE_ASSETS) { continue; } - BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library); + BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library_ref); } } } @@ -749,5 +749,31 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + if (!DNA_struct_elem_find( + fd->filesdna, "WorkSpace", "AssetLibraryReference", "asset_library_ref")) { + LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { + BKE_asset_library_reference_init_default(&workspace->asset_library_ref); + } + } + + if (!DNA_struct_elem_find( + fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library_ref")) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) { + if (space->spacetype != SPACE_FILE) { + continue; + } + + SpaceFile *sfile = (SpaceFile *)space; + if (sfile->browse_mode != FILE_BROWSE_MODE_ASSETS) { + continue; + } + BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library_ref); + } + } + } + } } } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 12839a155e4..225548f3832 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -757,8 +757,8 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) BLO_write_struct(writer, bPathCompare, path_cmp); } - LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library, &userdef->asset_libraries) { - BLO_write_struct(writer, bUserAssetLibrary, asset_library); + LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library_ref, &userdef->asset_libraries) { + BLO_write_struct(writer, bUserAssetLibrary, asset_library_ref); } LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) { diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c index 32440f941ba..5b6100c9785 100644 --- a/source/blender/editors/armature/pose_lib_2.c +++ b/source/blender/editors/armature/pose_lib_2.c @@ -299,16 +299,16 @@ static void poselib_tempload_exit(PoseBlendData *pbd) static bAction *poselib_blend_init_get_action(bContext *C, wmOperator *op) { bool asset_handle_valid; - const AssetLibraryReference *asset_library = CTX_wm_asset_library(C); + const AssetLibraryReference *asset_library_ref = CTX_wm_asset_library_ref(C); const AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid); /* Poll callback should check. */ - BLI_assert((asset_library != NULL) && asset_handle_valid); + BLI_assert((asset_library_ref != NULL) && asset_handle_valid); PoseBlendData *pbd = op->customdata; pbd->temp_id_consumer = ED_asset_temp_id_consumer_create(&asset_handle); return (bAction *)ED_asset_temp_id_consumer_ensure_local_id( - pbd->temp_id_consumer, C, asset_library, ID_AC, CTX_data_main(C), op->reports); + pbd->temp_id_consumer, C, asset_library_ref, ID_AC, CTX_data_main(C), op->reports); } static bAction *flip_pose(bContext *C, Object *ob, bAction *action) @@ -540,10 +540,10 @@ static bool poselib_asset_in_context(bContext *C) { bool asset_handle_valid; /* Check whether the context provides the asset data needed to add a pose. */ - const AssetLibraryReference *asset_library = CTX_wm_asset_library(C); + const AssetLibraryReference *asset_library_ref = CTX_wm_asset_library_ref(C); AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid); - return (asset_library != NULL) && asset_handle_valid && + return (asset_library_ref != NULL) && asset_handle_valid && (ED_asset_handle_get_id_type(&asset_handle) == ID_AC); } diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h index c51ce422c25..efb99410d3d 100644 --- a/source/blender/editors/asset/ED_asset_handle.h +++ b/source/blender/editors/asset/ED_asset_handle.h @@ -36,7 +36,7 @@ struct ID *ED_asset_handle_get_local_id(const struct AssetHandle *asset); ID_Type ED_asset_handle_get_id_type(const struct AssetHandle *asset); int ED_asset_handle_get_preview_icon_id(const struct AssetHandle *asset); void ED_asset_handle_get_full_library_path(const struct bContext *C, - const struct AssetLibraryReference *asset_library, + const struct AssetLibraryReference *asset_library_ref, const struct AssetHandle *asset, char r_full_lib_path[]); diff --git a/source/blender/editors/asset/ED_asset_temp_id_consumer.h b/source/blender/editors/asset/ED_asset_temp_id_consumer.h index 9af08c5c52b..7c10d88262e 100644 --- a/source/blender/editors/asset/ED_asset_temp_id_consumer.h +++ b/source/blender/editors/asset/ED_asset_temp_id_consumer.h @@ -39,7 +39,7 @@ void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer); struct ID *ED_asset_temp_id_consumer_ensure_local_id( AssetTempIDConsumer *consumer, const struct bContext *C, - const struct AssetLibraryReference *asset_library, + const struct AssetLibraryReference *asset_library_ref, ID_Type id_type, struct Main *bmain, struct ReportList *reports); diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index aae85e61372..5c8d0b1349c 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -60,13 +60,13 @@ int ED_asset_handle_get_preview_icon_id(const AssetHandle *asset) } void ED_asset_handle_get_full_library_path(const bContext *C, - const AssetLibraryReference *asset_library, + const AssetLibraryReference *asset_library_ref, const AssetHandle *asset, char r_full_lib_path[FILE_MAX_LIBEXTRA]) { *r_full_lib_path = '\0'; - std::string asset_path = ED_assetlist_asset_filepath_get(C, *asset_library, *asset); + std::string asset_path = ED_assetlist_asset_filepath_get(C, *asset_library_ref, *asset); if (asset_path.empty()) { return; } diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 579803f7ff7..dc418a1b3d5 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -266,7 +266,7 @@ static void ASSET_OT_clear(wmOperatorType *ot) static bool asset_list_refresh_poll(bContext *C) { - const AssetLibraryReference *library = CTX_wm_asset_library(C); + const AssetLibraryReference *library = CTX_wm_asset_library_ref(C); if (!library) { return false; } @@ -276,7 +276,7 @@ static bool asset_list_refresh_poll(bContext *C) static int asset_list_refresh_exec(bContext *C, wmOperator *UNUSED(unused)) { - const AssetLibraryReference *library = CTX_wm_asset_library(C); + const AssetLibraryReference *library = CTX_wm_asset_library_ref(C); ED_assetlist_clear(library, C); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc index bed35fdeeb5..f664eab5cbb 100644 --- a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc +++ b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc @@ -60,14 +60,14 @@ class AssetTemporaryIDConsumer : NonCopyable, NonMovable { } ID *import_id(const bContext *C, - const AssetLibraryReference &asset_library, + const AssetLibraryReference &asset_library_ref, ID_Type id_type, Main &bmain, ReportList &reports) { const char *asset_name = ED_asset_handle_get_name(&handle_); char blend_file_path[FILE_MAX_LIBEXTRA]; - ED_asset_handle_get_full_library_path(C, &asset_library, &handle_, blend_file_path); + ED_asset_handle_get_full_library_path(C, &asset_library_ref, &handle_, blend_file_path); temp_lib_context_ = BLO_library_temp_load_id( &bmain, blend_file_path, id_type, asset_name, &reports); @@ -99,12 +99,12 @@ void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer) ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_, const bContext *C, - const AssetLibraryReference *asset_library, + const AssetLibraryReference *asset_library_ref, ID_Type id_type, Main *bmain, ReportList *reports) { - if (!(consumer_ && asset_library && bmain && reports)) { + if (!(consumer_ && asset_library_ref && bmain && reports)) { return nullptr; } AssetTemporaryIDConsumer *consumer = reinterpret_cast(consumer_); @@ -112,5 +112,5 @@ ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_, if (ID *local_id = consumer->get_local_id()) { return local_id; } - return consumer->import_id(C, *asset_library, id_type, *bmain, *reports); + return consumer->import_id(C, *asset_library_ref, id_type, *bmain, *reports); } diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc index b4ba6a7feab..9b601727e29 100644 --- a/source/blender/editors/interface/interface_template_asset_view.cc +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -44,7 +44,7 @@ #include "interface_intern.h" struct AssetViewListData { - AssetLibraryReference asset_library; + AssetLibraryReference asset_library_ref; bScreen *screen; }; @@ -62,7 +62,7 @@ static void asset_view_item_but_drag_set(uiBut *but, /* Context can be null here, it's only needed for a File Browser specific hack that should go * away before too long. */ ED_asset_handle_get_full_library_path( - nullptr, &list_data->asset_library, asset_handle, blend_path); + nullptr, &list_data->asset_library_ref, asset_handle, blend_path); if (blend_path[0]) { ImBuf *imbuf = ED_assetlist_asset_image_get(asset_handle); @@ -136,7 +136,7 @@ static void asset_view_listener(uiList *ui_list, wmRegionListenerParams *params) } } - if (ED_assetlist_listen(&list_data->asset_library, params->notifier)) { + if (ED_assetlist_listen(&list_data->asset_library_ref, params->notifier)) { ED_region_tag_redraw(params->region); } } @@ -153,7 +153,7 @@ uiListType *UI_UL_asset_view() } static void asset_view_template_refresh_asset_collection( - const AssetLibraryReference &asset_library, + const AssetLibraryReference &asset_library_ref, const AssetFilterSettings &filter_settings, PointerRNA &assets_dataptr, const char *assets_propname) @@ -175,7 +175,7 @@ static void asset_view_template_refresh_asset_collection( RNA_property_collection_clear(&assets_dataptr, assets_prop); - ED_assetlist_iterate(&asset_library, [&](AssetHandle asset) { + ED_assetlist_iterate(&asset_library_ref, [&](AssetHandle asset) { if (!ED_asset_filter_matches_asset(&filter_settings, &asset)) { /* Don't do anything else, but return true to continue iterating. */ return true; @@ -216,25 +216,25 @@ void uiTemplateAssetView(uiLayout *layout, PropertyRNA *asset_library_prop = RNA_struct_find_property(asset_library_dataptr, asset_library_propname); - AssetLibraryReference asset_library = ED_asset_library_reference_from_enum_value( + AssetLibraryReference asset_library_ref = ED_asset_library_reference_from_enum_value( RNA_property_enum_get(asset_library_dataptr, asset_library_prop)); uiLayout *row = uiLayoutRow(col, true); uiItemFullR(row, asset_library_dataptr, asset_library_prop, RNA_NO_INDEX, 0, 0, "", 0); - if (asset_library.type != ASSET_LIBRARY_LOCAL) { + if (asset_library_ref.type != ASSET_LIBRARY_LOCAL) { uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_list_refresh"); } - ED_assetlist_storage_fetch(&asset_library, C); - ED_assetlist_ensure_previews_job(&asset_library, C); - const int tot_items = ED_assetlist_size(&asset_library); + ED_assetlist_storage_fetch(&asset_library_ref, C); + ED_assetlist_ensure_previews_job(&asset_library_ref, C); + const int tot_items = ED_assetlist_size(&asset_library_ref); asset_view_template_refresh_asset_collection( - asset_library, *filter_settings, *assets_dataptr, assets_propname); + asset_library_ref, *filter_settings, *assets_dataptr, assets_propname); AssetViewListData *list_data = (AssetViewListData *)MEM_mallocN(sizeof(*list_data), "AssetViewListData"); - list_data->asset_library = asset_library; + list_data->asset_library_ref = asset_library_ref; list_data->screen = CTX_wm_screen(C); /* TODO can we have some kind of model-view API to handle referencing, filtering and lazy loading diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 8123d8bb104..b0181de96a0 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -113,7 +113,7 @@ const char *screen_context_dir[] = { "active_editable_fcurve", "selected_editable_keyframes", "ui_list", - "asset_library", + "asset_library_ref", NULL, }; @@ -1031,7 +1031,7 @@ static eContextResult screen_ctx_asset_library(const bContext *C, bContextDataRe { WorkSpace *workspace = CTX_wm_workspace(C); CTX_data_pointer_set( - result, &workspace->id, &RNA_AssetLibraryReference, &workspace->asset_library); + result, &workspace->id, &RNA_AssetLibraryReference, &workspace->asset_library_ref); return CTX_RESULT_OK; } @@ -1118,7 +1118,7 @@ static void ensure_ed_screen_context_functions(void) register_context_function("selected_visible_fcurves", screen_ctx_selected_visible_fcurves); register_context_function("active_editable_fcurve", screen_ctx_active_editable_fcurve); register_context_function("selected_editable_keyframes", screen_ctx_selected_editable_keyframes); - register_context_function("asset_library", screen_ctx_asset_library); + register_context_function("asset_library_ref", screen_ctx_asset_library); register_context_function("ui_list", screen_ctx_ui_list); } diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 7d9b8583838..9a46579780e 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -1106,7 +1106,7 @@ bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region) return false; } /* Check if the library exists. */ - if ((asset_params->asset_library.type == ASSET_LIBRARY_LOCAL) || + if ((asset_params->asset_library_ref.type == ASSET_LIBRARY_LOCAL) || filelist_is_dir(sfile->files, asset_params->base_params.dir)) { return false; } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 630c9aed157..4eb58642bba 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -384,7 +384,7 @@ typedef struct FileList { eFileSelectType type; /* The library this list was created for. Stored here so we know when to re-read. */ - AssetLibraryReference *asset_library; + AssetLibraryReference *asset_library_ref; short flags; @@ -1065,28 +1065,28 @@ static bool filelist_compare_asset_libraries(const AssetLibraryReference *librar } /** - * \param asset_library: May be NULL to unset the library. + * \param asset_library_ref: May be NULL to unset the library. */ -void filelist_setlibrary(FileList *filelist, const AssetLibraryReference *asset_library) +void filelist_setlibrary(FileList *filelist, const AssetLibraryReference *asset_library_ref) { /* Unset if needed. */ - if (!asset_library) { - if (filelist->asset_library) { - MEM_SAFE_FREE(filelist->asset_library); + if (!asset_library_ref) { + if (filelist->asset_library_ref) { + MEM_SAFE_FREE(filelist->asset_library_ref); filelist->flags |= FL_FORCE_RESET; } return; } - if (!filelist->asset_library) { - filelist->asset_library = MEM_mallocN(sizeof(*filelist->asset_library), - "filelist asset library"); - *filelist->asset_library = *asset_library; + if (!filelist->asset_library_ref) { + filelist->asset_library_ref = MEM_mallocN(sizeof(*filelist->asset_library_ref), + "filelist asset library"); + *filelist->asset_library_ref = *asset_library_ref; filelist->flags |= FL_FORCE_RESET; } - else if (!filelist_compare_asset_libraries(filelist->asset_library, asset_library)) { - *filelist->asset_library = *asset_library; + else if (!filelist_compare_asset_libraries(filelist->asset_library_ref, asset_library_ref)) { + *filelist->asset_library_ref = *asset_library_ref; filelist->flags |= FL_FORCE_RESET; } } @@ -1791,7 +1791,7 @@ void filelist_free(struct FileList *filelist) filelist->selection_state = NULL; } - MEM_SAFE_FREE(filelist->asset_library); + MEM_SAFE_FREE(filelist->asset_library_ref); memset(&filelist->filter_data, 0, sizeof(filelist->filter_data)); @@ -1867,7 +1867,7 @@ bool filelist_is_dir(struct FileList *filelist, const char *path) */ void filelist_setdir(struct FileList *filelist, char *r_dir) { - const bool allow_invalid = filelist->asset_library != NULL; + const bool allow_invalid = filelist->asset_library_ref != NULL; BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA); BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir); @@ -3381,7 +3381,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update flrj->tmp_filelist->libfiledata = NULL; memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache)); flrj->tmp_filelist->selection_state = NULL; - flrj->tmp_filelist->asset_library = NULL; + flrj->tmp_filelist->asset_library_ref = NULL; BLI_mutex_unlock(&flrj->lock); diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 6915e853681..d67cd89200b 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -73,7 +73,7 @@ void filelist_setfilter_options(struct FileList *filelist, const char *filter_search); void filelist_filter(struct FileList *filelist); void filelist_setlibrary(struct FileList *filelist, - const struct AssetLibraryReference *asset_library); + const struct AssetLibraryReference *asset_library_ref); void filelist_init_icons(void); void filelist_free_icons(void); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 89142b6669b..72e7e0716db 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -118,8 +118,8 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile) asset_params = sfile->asset_params = MEM_callocN(sizeof(*asset_params), "FileAssetSelectParams"); asset_params->base_params.details_flags = U_default.file_space_data.details_flags; - asset_params->asset_library.type = ASSET_LIBRARY_LOCAL; - asset_params->asset_library.custom_library_index = -1; + asset_params->asset_library_ref.type = ASSET_LIBRARY_LOCAL; + asset_params->asset_library_ref.custom_library_index = -1; asset_params->import_type = FILE_ASSET_IMPORT_APPEND; } @@ -415,7 +415,7 @@ FileAssetSelectParams *ED_fileselect_get_asset_params(const SpaceFile *sfile) static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params) { - AssetLibraryReference *library = &asset_params->asset_library; + AssetLibraryReference *library = &asset_params->asset_library_ref; FileSelectParams *base_params = &asset_params->base_params; bUserAssetLibrary *user_library = NULL; diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 756fc179e31..0b3349f5751 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -338,7 +338,7 @@ static void file_refresh(const bContext *C, ScrArea *area) filelist_setdir(sfile->files, params->dir); filelist_setrecursion(sfile->files, params->recursion_level); filelist_setsorting(sfile->files, params->sort, params->flag & FILE_SORT_INVERT); - filelist_setlibrary(sfile->files, asset_params ? &asset_params->asset_library : NULL); + filelist_setlibrary(sfile->files, asset_params ? &asset_params->asset_library_ref : NULL); filelist_setfilter_options( sfile->files, (params->flag & FILE_FILTER) != 0, @@ -863,7 +863,7 @@ static void file_space_subtype_item_extend(bContext *UNUSED(C), static const char *file_context_dir[] = { "active_file", - "asset_library", + "asset_library_ref", "id", NULL, }; @@ -897,14 +897,14 @@ static int /*eContextResult*/ file_context(const bContext *C, CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file); return CTX_RESULT_OK; } - if (CTX_data_equals(member, "asset_library")) { + if (CTX_data_equals(member, "asset_library_ref")) { FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); if (!asset_params) { return CTX_RESULT_NO_DATA; } CTX_data_pointer_set( - result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library); + result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library_ref); return CTX_RESULT_OK; } if (CTX_data_equals(member, "id")) { diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index ca16e6728dd..2975915eccd 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -81,7 +81,7 @@ typedef enum eAssetLibraryType { // ASSET_LIBRARY_PROJECT = 2, /** Display assets from custom asset libraries, as defined in the preferences - * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library.idname + * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library_ref.idname * then. * In RNA, we add the index of the custom library to this to identify it by index. So keep * this last! */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 27d5d83c7cb..863c53615c1 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -763,7 +763,7 @@ typedef struct FileSelectParams { typedef struct FileAssetSelectParams { FileSelectParams base_params; - AssetLibraryReference asset_library; + AssetLibraryReference asset_library_ref; short import_type; /* eFileAssetImportType */ char _pad[6]; diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h index 2bac040ea90..e0294d3534c 100644 --- a/source/blender/makesdna/DNA_workspace_types.h +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -139,7 +139,7 @@ typedef struct WorkSpace { /** Workspace-wide active asset library, for asset UIs to use (e.g. asset view UI template). The * Asset Browser has its own and doesn't use this. */ - AssetLibraryReference asset_library; + AssetLibraryReference asset_library_ref; } WorkSpace; /** diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index 80b2594d0c9..3b3a04e1128 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -273,7 +273,7 @@ static void rna_def_asset_handle_api(StructRNA *srna) parm = RNA_def_pointer(func, "asset_file_handle", "FileSelectEntry", "", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_pointer(func, - "asset_library", + "asset_library_ref", "AssetLibraryReference", "", "The asset library containing the given asset, only valid if the asset " @@ -329,7 +329,7 @@ PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna, const char *get, const char *set) { - PropertyRNA *prop = RNA_def_property(srna, "asset_library", PROP_ENUM, PROP_NONE); + PropertyRNA *prop = RNA_def_property(srna, "asset_library_ref", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, DummyRNA_NULL_items); RNA_def_property_enum_funcs(prop, get, set, "rna_asset_library_reference_itemf"); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index c7a5c43255a..8c62484f229 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2582,13 +2582,13 @@ static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr) /* Just an extra sanity check to ensure this isn't somehow called for RNA_FileSelectParams. */ BLI_assert(ptr->type == &RNA_FileAssetSelectParams); - return ED_asset_library_reference_to_enum_value(¶ms->asset_library); + return ED_asset_library_reference_to_enum_value(¶ms->asset_library_ref); } static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int value) { FileAssetSelectParams *params = ptr->data; - params->asset_library = ED_asset_library_reference_from_enum_value(value); + params->asset_library_ref = ED_asset_library_reference_from_enum_value(value); } static void rna_FileAssetSelectParams_asset_category_set(PointerRNA *ptr, uint64_t value) diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c index 95f62d7de16..d76ad254140 100644 --- a/source/blender/makesrna/intern/rna_workspace.c +++ b/source/blender/makesrna/intern/rna_workspace.c @@ -112,13 +112,13 @@ static void rna_WorkSpace_owner_ids_clear(WorkSpace *workspace) static int rna_WorkSpace_asset_library_get(PointerRNA *ptr) { const WorkSpace *workspace = ptr->data; - return ED_asset_library_reference_to_enum_value(&workspace->asset_library); + return ED_asset_library_reference_to_enum_value(&workspace->asset_library_ref); } static void rna_WorkSpace_asset_library_set(PointerRNA *ptr, int value) { WorkSpace *workspace = ptr->data; - workspace->asset_library = ED_asset_library_reference_from_enum_value(value); + workspace->asset_library_ref = ED_asset_library_reference_from_enum_value(value); } static bToolRef *rna_WorkSpace_tools_from_tkey(WorkSpace *workspace, -- cgit v1.2.3 From 3fbe6f513d11563339e16d501708fc361d265f3b Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 6 Aug 2021 15:18:32 +0200 Subject: Move NanoSVG lib to extern The library has some modifications and it has been included in a diff. Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D12142 (Some minor changes done in the patch) --- extern/nanosvg/README.blender | 7 + extern/nanosvg/nanosvg.h | 3327 ++++++++++++++++++++ extern/nanosvg/patches/NanoSVG.diff | 86 + source/blender/io/gpencil/CMakeLists.txt | 4 +- .../io/gpencil/intern/gpencil_io_import_svg.cc | 2 +- source/blender/io/gpencil/nanosvg/nanosvg.h | 3327 -------------------- 6 files changed, 3422 insertions(+), 3331 deletions(-) create mode 100644 extern/nanosvg/README.blender create mode 100644 extern/nanosvg/nanosvg.h create mode 100644 extern/nanosvg/patches/NanoSVG.diff delete mode 100644 source/blender/io/gpencil/nanosvg/nanosvg.h diff --git a/extern/nanosvg/README.blender b/extern/nanosvg/README.blender new file mode 100644 index 00000000000..3772cc106c8 --- /dev/null +++ b/extern/nanosvg/README.blender @@ -0,0 +1,7 @@ +Project: NanoSVG +URL: https://github.com/memononen/nanosvg +License: zlib +Upstream version: +Local modifications: Added some functionality to manage grease pencil layers + +Added a fix to SVG import arc and float errors (https://developer.blender.org/rB11dc674c78b49fc4e0b7c134c375b6c8b8eacbcc) \ No newline at end of file diff --git a/extern/nanosvg/nanosvg.h b/extern/nanosvg/nanosvg.h new file mode 100644 index 00000000000..94dad37861a --- /dev/null +++ b/extern/nanosvg/nanosvg.h @@ -0,0 +1,3327 @@ +/* + * Copyright (c) 2013-14 `Mikko Mononen ` + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example + * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/) + * + * Arc calculation code based on canvg (https://code.google.com/p/canvg/) + * + * Bounding box calculation based on + * http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html + * + * This is a modified version for Blender used by importers. + * + */ + +#ifndef NANOSVG_H +#define NANOSVG_H + +#ifndef NANOSVG_CPLUSPLUS +# ifdef __cplusplus +extern "C" { +# endif +#endif + +// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of +// cubic bezier shapes. +// +// The library suits well for anything from rendering scalable icons in your editor application to +// prototyping a game. +// +// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create +// a pull request! +// +// The shapes in the SVG images are transformed by the viewBox and converted to specified units. +// That is, you should get the same looking data as your designed in your favorite app. +// +// NanoSVG can return the paths in few different units. For example if you want to render an image, +// you may choose to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you +// may want to use millimeters. +// +// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. +// DPI (dots-per-inch) controls how the unit conversion is done. +// +// If you don't know or care about the units stuff, "px" and 96 should get you going. + +/* Example Usage: + // Load SVG + NSVGimage* image; + image = nsvgParseFromFile("test.svg", "px", 96); + printf("size: %f x %f\n", image->width, image->height); + // Use... + for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { + for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { + for (int i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); + } + } + } + // Delete + nsvgDelete(image); +*/ + +enum NSVGpaintType { + NSVG_PAINT_NONE = 0, + NSVG_PAINT_COLOR = 1, + NSVG_PAINT_LINEAR_GRADIENT = 2, + NSVG_PAINT_RADIAL_GRADIENT = 3 +}; + +enum NSVGspreadType { NSVG_SPREAD_PAD = 0, NSVG_SPREAD_REFLECT = 1, NSVG_SPREAD_REPEAT = 2 }; + +enum NSVGlineJoin { NSVG_JOIN_MITER = 0, NSVG_JOIN_ROUND = 1, NSVG_JOIN_BEVEL = 2 }; + +enum NSVGlineCap { NSVG_CAP_BUTT = 0, NSVG_CAP_ROUND = 1, NSVG_CAP_SQUARE = 2 }; + +enum NSVGfillRule { NSVG_FILLRULE_NONZERO = 0, NSVG_FILLRULE_EVENODD = 1 }; + +enum NSVGflags { NSVG_FLAGS_VISIBLE = 0x01 }; + +typedef struct NSVGgradientStop { + unsigned int color; + float offset; +} NSVGgradientStop; + +typedef struct NSVGgradient { + float xform[6]; + char spread; + float fx, fy; + int nstops; + NSVGgradientStop stops[1]; +} NSVGgradient; + +typedef struct NSVGpaint { + char type; + union { + unsigned int color; + NSVGgradient *gradient; + }; +} NSVGpaint; + +typedef struct NSVGpath { + float *pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... + int npts; // Total number of bezier points. + char closed; // Flag indicating if shapes should be treated as closed. + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + struct NSVGpath *next; // Pointer to next path, or NULL if last element. +} NSVGpath; + +typedef struct NSVGshape { + char id[64]; // Optional 'id' attr of the shape or its group + /* Blender: Parent ID used for layer creation. */ + char id_parent[64]; + NSVGpaint fill; // Fill paint + NSVGpaint stroke; // Stroke paint + float opacity; // Opacity of the shape. + float strokeWidth; // Stroke width (scaled). + float strokeDashOffset; // Stroke dash offset (scaled). + float strokeDashArray[8]; // Stroke dash array (scaled). + char strokeDashCount; // Number of dash values in dash array. + char strokeLineJoin; // Stroke join type. + char strokeLineCap; // Stroke cap type. + float miterLimit; // Miter limit + char fillRule; // Fill rule, see NSVGfillRule. + unsigned char flags; // Logical or of NSVG_FLAGS_* flags + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + NSVGpath *paths; // Linked list of paths in the image. + struct NSVGshape *next; // Pointer to next shape, or NULL if last element. +} NSVGshape; + +typedef struct NSVGimage { + float width; // Width of the image. + float height; // Height of the image. + NSVGshape *shapes; // Linked list of shapes in the image. +} NSVGimage; + +// Parses SVG file from a file, returns SVG image as paths. +NSVGimage *nsvgParseFromFile(const char *filename, const char *units, float dpi); + +// Parses SVG file from a null terminated string, returns SVG image as paths. +// Important note: changes the string. +NSVGimage *nsvgParse(char *input, const char *units, float dpi); + +// Duplicates a path. +NSVGpath *nsvgDuplicatePath(NSVGpath *p); + +// Deletes an image. +void nsvgDelete(NSVGimage *image); + +#ifndef NANOSVG_CPLUSPLUS +# ifdef __cplusplus +} +# endif +#endif + +#endif // NANOSVG_H + +#ifdef NANOSVG_IMPLEMENTATION + +#include +#include +#include + +#define NSVG_PI (3.14159265358979323846264338327f) +#define NSVG_KAPPA90 \ + (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. + +#define NSVG_ALIGN_MIN 0 +#define NSVG_ALIGN_MID 1 +#define NSVG_ALIGN_MAX 2 +#define NSVG_ALIGN_NONE 0 +#define NSVG_ALIGN_MEET 1 +#define NSVG_ALIGN_SLICE 2 + +#define NSVG_NOTUSED(v) \ + do { \ + (void)(1 ? (void)0 : ((void)(v))); \ + } while (0) +#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) + +#ifdef _MSC_VER +# pragma warning(disable : 4996) // Switch off security warnings +# pragma warning(disable : 4100) // Switch off unreferenced formal parameter warnings +# ifdef __cplusplus +# define NSVG_INLINE inline +# else +# define NSVG_INLINE +# endif +#else +# define NSVG_INLINE inline +#endif + +static int nsvg__isspace(char c) +{ + return strchr(" \t\n\v\f\r", c) != 0; +} + +static int nsvg__isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static NSVG_INLINE float nsvg__minf(float a, float b) +{ + return a < b ? a : b; +} +static NSVG_INLINE float nsvg__maxf(float a, float b) +{ + return a > b ? a : b; +} + +// Simple XML parser + +#define NSVG_XML_TAG 1 +#define NSVG_XML_CONTENT 2 +#define NSVG_XML_MAX_ATTRIBS 256 + +static void nsvg__parseContent(char *s, void (*contentCb)(void *ud, const char *s), void *ud) +{ + // Trim start white spaces + while (*s && nsvg__isspace(*s)) + s++; + if (!*s) + return; + + if (contentCb) + (*contentCb)(ud, s); +} + +static void nsvg__parseElement(char *s, + void (*startelCb)(void *ud, const char *el, const char **attr), + void (*endelCb)(void *ud, const char *el), + void *ud) +{ + const char *attr[NSVG_XML_MAX_ATTRIBS]; + int nattr = 0; + char *name; + int start = 0; + int end = 0; + char quote; + + // Skip white space after the '<' + while (*s && nsvg__isspace(*s)) + s++; + + // Check if the tag is end tag + if (*s == '/') { + s++; + end = 1; + } + else { + start = 1; + } + + // Skip comments, data and preprocessor stuff. + if (!*s || *s == '?' || *s == '!') + return; + + // Get tag name + name = s; + while (*s && !nsvg__isspace(*s)) + s++; + if (*s) { + *s++ = '\0'; + } + + // Get attribs + while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS - 3) { + char *name = NULL; + char *value = NULL; + + // Skip white space before the attrib name + while (*s && nsvg__isspace(*s)) + s++; + if (!*s) + break; + if (*s == '/') { + end = 1; + break; + } + name = s; + // Find end of the attrib name. + while (*s && !nsvg__isspace(*s) && *s != '=') + s++; + if (*s) { + *s++ = '\0'; + } + // Skip until the beginning of the value. + while (*s && *s != '\"' && *s != '\'') + s++; + if (!*s) + break; + quote = *s; + s++; + // Store value and find the end of it. + value = s; + while (*s && *s != quote) + s++; + if (*s) { + *s++ = '\0'; + } + + // Store only well formed attributes + if (name && value) { + attr[nattr++] = name; + attr[nattr++] = value; + } + } + + // List terminator + attr[nattr++] = 0; + attr[nattr++] = 0; + + // Call callbacks. + if (start && startelCb) + (*startelCb)(ud, name, attr); + if (end && endelCb) + (*endelCb)(ud, name); +} + +static int nsvg__parseXML(char *input, + void (*startelCb)(void *ud, const char *el, const char **attr), + void (*endelCb)(void *ud, const char *el), + void (*contentCb)(void *ud, const char *s), + void *ud) +{ + char *s = input; + char *mark = s; + int state = NSVG_XML_CONTENT; + while (*s) { + if (*s == '<' && state == NSVG_XML_CONTENT) { + // Start of a tag + *s++ = '\0'; + nsvg__parseContent(mark, contentCb, ud); + mark = s; + state = NSVG_XML_TAG; + } + else if (*s == '>' && state == NSVG_XML_TAG) { + // Start of a content or new tag. + *s++ = '\0'; + nsvg__parseElement(mark, startelCb, endelCb, ud); + mark = s; + state = NSVG_XML_CONTENT; + } + else { + s++; + } + } + + return 1; +} + +/* Simple SVG parser. */ + +#define NSVG_MAX_ATTR 128 +#define NSVG_MAX_BREADCRUMB 5 + +enum NSVGgradientUnits { NSVG_USER_SPACE = 0, NSVG_OBJECT_SPACE = 1 }; + +#define NSVG_MAX_DASHES 8 + +enum NSVGunits { + NSVG_UNITS_USER, + NSVG_UNITS_PX, + NSVG_UNITS_PT, + NSVG_UNITS_PC, + NSVG_UNITS_MM, + NSVG_UNITS_CM, + NSVG_UNITS_IN, + NSVG_UNITS_PERCENT, + NSVG_UNITS_EM, + NSVG_UNITS_EX +}; + +typedef struct NSVGcoordinate { + float value; + int units; +} NSVGcoordinate; + +typedef struct NSVGlinearData { + NSVGcoordinate x1, y1, x2, y2; +} NSVGlinearData; + +typedef struct NSVGradialData { + NSVGcoordinate cx, cy, r, fx, fy; +} NSVGradialData; + +typedef struct NSVGgradientData { + char id[64]; + char ref[64]; + char type; + union { + NSVGlinearData linear; + NSVGradialData radial; + }; + char spread; + char units; + float xform[6]; + int nstops; + NSVGgradientStop *stops; + struct NSVGgradientData *next; +} NSVGgradientData; + +typedef struct NSVGattrib { + char id[64]; + float xform[6]; + unsigned int fillColor; + unsigned int strokeColor; + float opacity; + float fillOpacity; + float strokeOpacity; + char fillGradient[64]; + char strokeGradient[64]; + float strokeWidth; + float strokeDashOffset; + float strokeDashArray[NSVG_MAX_DASHES]; + int strokeDashCount; + char strokeLineJoin; + char strokeLineCap; + float miterLimit; + char fillRule; + float fontSize; + unsigned int stopColor; + float stopOpacity; + float stopOffset; + char hasFill; + char hasStroke; + char visible; +} NSVGattrib; + +typedef struct NSVGparser { + NSVGattrib attr[NSVG_MAX_ATTR]; + int attrHead; + float *pts; + int npts; + int cpts; + NSVGpath *plist; + NSVGimage *image; + NSVGgradientData *gradients; + NSVGshape *shapesTail; + float viewMinx, viewMiny, viewWidth, viewHeight; + int alignX, alignY, alignType; + float dpi; + char pathFlag; + char defsFlag; + /** Blender breadcrumb for layers. */ + char breadcrumb[NSVG_MAX_BREADCRUMB][64]; + /** Blender number of elements in breadcrumb. */ + int breadcrumb_len; +} NSVGparser; + +static void nsvg__xformIdentity(float *t) +{ + t[0] = 1.0f; + t[1] = 0.0f; + t[2] = 0.0f; + t[3] = 1.0f; + t[4] = 0.0f; + t[5] = 0.0f; +} + +static void nsvg__xformSetTranslation(float *t, float tx, float ty) +{ + t[0] = 1.0f; + t[1] = 0.0f; + t[2] = 0.0f; + t[3] = 1.0f; + t[4] = tx; + t[5] = ty; +} + +static void nsvg__xformSetScale(float *t, float sx, float sy) +{ + t[0] = sx; + t[1] = 0.0f; + t[2] = 0.0f; + t[3] = sy; + t[4] = 0.0f; + t[5] = 0.0f; +} + +static void nsvg__xformSetSkewX(float *t, float a) +{ + t[0] = 1.0f; + t[1] = 0.0f; + t[2] = tanf(a); + t[3] = 1.0f; + t[4] = 0.0f; + t[5] = 0.0f; +} + +static void nsvg__xformSetSkewY(float *t, float a) +{ + t[0] = 1.0f; + t[1] = tanf(a); + t[2] = 0.0f; + t[3] = 1.0f; + t[4] = 0.0f; + t[5] = 0.0f; +} + +static void nsvg__xformSetRotation(float *t, float a) +{ + float cs = cosf(a), sn = sinf(a); + t[0] = cs; + t[1] = sn; + t[2] = -sn; + t[3] = cs; + t[4] = 0.0f; + t[5] = 0.0f; +} + +static void nsvg__xformMultiply(float *t, float *s) +{ + float t0 = t[0] * s[0] + t[1] * s[2]; + float t2 = t[2] * s[0] + t[3] * s[2]; + float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; + t[1] = t[0] * s[1] + t[1] * s[3]; + t[3] = t[2] * s[1] + t[3] * s[3]; + t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; + t[0] = t0; + t[2] = t2; + t[4] = t4; +} + +static void nsvg__xformInverse(float *inv, float *t) +{ + double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; + if (det > -1e-6 && det < 1e-6) { + nsvg__xformIdentity(t); + return; + } + invdet = 1.0 / det; + inv[0] = (float)(t[3] * invdet); + inv[2] = (float)(-t[2] * invdet); + inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); + inv[1] = (float)(-t[1] * invdet); + inv[3] = (float)(t[0] * invdet); + inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); +} + +static void nsvg__xformPremultiply(float *t, float *s) +{ + float s2[6]; + memcpy(s2, s, sizeof(float) * 6); + nsvg__xformMultiply(s2, t); + memcpy(t, s2, sizeof(float) * 6); +} + +static void nsvg__xformPoint(float *dx, float *dy, float x, float y, float *t) +{ + *dx = x * t[0] + y * t[2] + t[4]; + *dy = x * t[1] + y * t[3] + t[5]; +} + +static void nsvg__xformVec(float *dx, float *dy, float x, float y, float *t) +{ + *dx = x * t[0] + y * t[2]; + *dy = x * t[1] + y * t[3]; +} + +#define NSVG_EPSILON (1e-12) + +static int nsvg__ptInBounds(float *pt, float *bounds) +{ + return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; +} + +static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) +{ + double it = 1.0 - t; + return it * it * it * p0 + 3.0 * it * it * t * p1 + 3.0 * it * t * t * p2 + t * t * t * p3; +} + +static void nsvg__curveBounds(float *bounds, float *curve) +{ + int i, j, count; + double roots[2], a, b, c, b2ac, t, v; + float *v0 = &curve[0]; + float *v1 = &curve[2]; + float *v2 = &curve[4]; + float *v3 = &curve[6]; + + // Start the bounding box by end points + bounds[0] = nsvg__minf(v0[0], v3[0]); + bounds[1] = nsvg__minf(v0[1], v3[1]); + bounds[2] = nsvg__maxf(v0[0], v3[0]); + bounds[3] = nsvg__maxf(v0[1], v3[1]); + + // Bezier curve fits inside the convex hull of it's control points. + // If control points are inside the bounds, we're done. + if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) + return; + + // Add bezier curve inflection points in X and Y. + for (i = 0; i < 2; i++) { + a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; + b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; + c = 3.0 * v1[i] - 3.0 * v0[i]; + count = 0; + if (fabs(a) < NSVG_EPSILON) { + if (fabs(b) > NSVG_EPSILON) { + t = -c / b; + if (t > NSVG_EPSILON && t < 1.0 - NSVG_EPSILON) + roots[count++] = t; + } + } + else { + b2ac = b * b - 4.0 * c * a; + if (b2ac > NSVG_EPSILON) { + t = (-b + sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0 - NSVG_EPSILON) + roots[count++] = t; + t = (-b - sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0 - NSVG_EPSILON) + roots[count++] = t; + } + } + for (j = 0; j < count; j++) { + v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); + bounds[0 + i] = nsvg__minf(bounds[0 + i], (float)v); + bounds[2 + i] = nsvg__maxf(bounds[2 + i], (float)v); + } + } +} + +static NSVGparser *nsvg__createParser() +{ + NSVGparser *p; + p = (NSVGparser *)malloc(sizeof(NSVGparser)); + if (p == NULL) + goto error; + memset(p, 0, sizeof(NSVGparser)); + + p->image = (NSVGimage *)malloc(sizeof(NSVGimage)); + if (p->image == NULL) + goto error; + memset(p->image, 0, sizeof(NSVGimage)); + + // Init style + nsvg__xformIdentity(p->attr[0].xform); + memset(p->attr[0].id, 0, sizeof p->attr[0].id); + p->attr[0].fillColor = NSVG_RGB(0, 0, 0); + p->attr[0].strokeColor = NSVG_RGB(0, 0, 0); + p->attr[0].opacity = 1; + p->attr[0].fillOpacity = 1; + p->attr[0].strokeOpacity = 1; + p->attr[0].stopOpacity = 1; + p->attr[0].strokeWidth = 1; + p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; + p->attr[0].strokeLineCap = NSVG_CAP_BUTT; + p->attr[0].miterLimit = 4; + p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; + p->attr[0].hasFill = 1; + p->attr[0].visible = 1; + + return p; + +error: + if (p) { + if (p->image) + free(p->image); + free(p); + } + return NULL; +} + +static void nsvg__deletePaths(NSVGpath *path) +{ + while (path) { + NSVGpath *next = path->next; + if (path->pts != NULL) + free(path->pts); + free(path); + path = next; + } +} + +static void nsvg__deletePaint(NSVGpaint *paint) +{ + if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) + free(paint->gradient); +} + +static void nsvg__deleteGradientData(NSVGgradientData *grad) +{ + NSVGgradientData *next; + while (grad != NULL) { + next = grad->next; + free(grad->stops); + free(grad); + grad = next; + } +} + +static void nsvg__deleteParser(NSVGparser *p) +{ + if (p != NULL) { + nsvg__deletePaths(p->plist); + nsvg__deleteGradientData(p->gradients); + nsvgDelete(p->image); + free(p->pts); + free(p); + } +} + +static void nsvg__resetPath(NSVGparser *p) +{ + p->npts = 0; +} + +static void nsvg__addPoint(NSVGparser *p, float x, float y) +{ + if (p->npts + 1 > p->cpts) { + p->cpts = p->cpts ? p->cpts * 2 : 8; + p->pts = (float *)realloc(p->pts, p->cpts * 2 * sizeof(float)); + if (!p->pts) + return; + } + p->pts[p->npts * 2 + 0] = x; + p->pts[p->npts * 2 + 1] = y; + p->npts++; +} + +static void nsvg__moveTo(NSVGparser *p, float x, float y) +{ + if (p->npts > 0) { + p->pts[(p->npts - 1) * 2 + 0] = x; + p->pts[(p->npts - 1) * 2 + 1] = y; + } + else { + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__lineTo(NSVGparser *p, float x, float y) +{ + float px, py, dx, dy; + if (p->npts > 0) { + px = p->pts[(p->npts - 1) * 2 + 0]; + py = p->pts[(p->npts - 1) * 2 + 1]; + dx = x - px; + dy = y - py; + nsvg__addPoint(p, px + dx / 3.0f, py + dy / 3.0f); + nsvg__addPoint(p, x - dx / 3.0f, y - dy / 3.0f); + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__cubicBezTo( + NSVGparser *p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) +{ + if (p->npts > 0) { + nsvg__addPoint(p, cpx1, cpy1); + nsvg__addPoint(p, cpx2, cpy2); + nsvg__addPoint(p, x, y); + } +} + +static NSVGattrib *nsvg__getAttr(NSVGparser *p) +{ + return &p->attr[p->attrHead]; +} + +static void nsvg__pushAttr(NSVGparser *p) +{ + if (p->attrHead < NSVG_MAX_ATTR - 1) { + p->attrHead++; + memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead - 1], sizeof(NSVGattrib)); + } +} + +static void nsvg__popAttr(NSVGparser *p) +{ + if (p->attrHead > 0) + p->attrHead--; +} + +static float nsvg__actualOrigX(NSVGparser *p) +{ + return p->viewMinx; +} + +static float nsvg__actualOrigY(NSVGparser *p) +{ + return p->viewMiny; +} + +static float nsvg__actualWidth(NSVGparser *p) +{ + return p->viewWidth; +} + +static float nsvg__actualHeight(NSVGparser *p) +{ + return p->viewHeight; +} + +static float nsvg__actualLength(NSVGparser *p) +{ + float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); + return sqrtf(w * w + h * h) / sqrtf(2.0f); +} + +static float nsvg__convertToPixels(NSVGparser *p, NSVGcoordinate c, float orig, float length) +{ + NSVGattrib *attr = nsvg__getAttr(p); + switch (c.units) { + case NSVG_UNITS_USER: + return c.value; + case NSVG_UNITS_PX: + return c.value; + case NSVG_UNITS_PT: + return c.value / 72.0f * p->dpi; + case NSVG_UNITS_PC: + return c.value / 6.0f * p->dpi; + case NSVG_UNITS_MM: + return c.value / 25.4f * p->dpi; + case NSVG_UNITS_CM: + return c.value / 2.54f * p->dpi; + case NSVG_UNITS_IN: + return c.value * p->dpi; + case NSVG_UNITS_EM: + return c.value * attr->fontSize; + case NSVG_UNITS_EX: + return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. + case NSVG_UNITS_PERCENT: + return orig + c.value / 100.0f * length; + default: + return c.value; + } + return c.value; +} + +static NSVGgradientData *nsvg__findGradientData(NSVGparser *p, const char *id) +{ + NSVGgradientData *grad = p->gradients; + if (id == NULL || *id == '\0') + return NULL; + while (grad != NULL) { + if (strcmp(grad->id, id) == 0) + return grad; + grad = grad->next; + } + return NULL; +} + +static NSVGgradient *nsvg__createGradient(NSVGparser *p, + const char *id, + const float *localBounds, + char *paintType) +{ + NSVGattrib *attr = nsvg__getAttr(p); + NSVGgradientData *data = NULL; + NSVGgradientData *ref = NULL; + NSVGgradientStop *stops = NULL; + NSVGgradient *grad; + float ox, oy, sw, sh, sl; + int nstops = 0; + int refIter; + + data = nsvg__findGradientData(p, id); + if (data == NULL) + return NULL; + + // TODO: use ref to fill in all unset values too. + ref = data; + refIter = 0; + while (ref != NULL) { + NSVGgradientData *nextRef = NULL; + if (stops == NULL && ref->stops != NULL) { + stops = ref->stops; + nstops = ref->nstops; + break; + } + nextRef = nsvg__findGradientData(p, ref->ref); + if (nextRef == ref) + break; // prevent infite loops on malformed data + ref = nextRef; + refIter++; + if (refIter > 32) + break; // prevent infite loops on malformed data + } + if (stops == NULL) + return NULL; + + grad = (NSVGgradient *)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop) * (nstops - 1)); + if (grad == NULL) + return NULL; + + // The shape width and height. + if (data->units == NSVG_OBJECT_SPACE) { + ox = localBounds[0]; + oy = localBounds[1]; + sw = localBounds[2] - localBounds[0]; + sh = localBounds[3] - localBounds[1]; + } + else { + ox = nsvg__actualOrigX(p); + oy = nsvg__actualOrigY(p); + sw = nsvg__actualWidth(p); + sh = nsvg__actualHeight(p); + } + sl = sqrtf(sw * sw + sh * sh) / sqrtf(2.0f); + + if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { + float x1, y1, x2, y2, dx, dy; + x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); + y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); + x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); + y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); + // Calculate transform aligned to the line + dx = x2 - x1; + dy = y2 - y1; + grad->xform[0] = dy; + grad->xform[1] = -dx; + grad->xform[2] = dx; + grad->xform[3] = dy; + grad->xform[4] = x1; + grad->xform[5] = y1; + } + else { + float cx, cy, fx, fy, r; + cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); + cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); + fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); + fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); + r = nsvg__convertToPixels(p, data->radial.r, 0, sl); + // Calculate transform aligned to the circle + grad->xform[0] = r; + grad->xform[1] = 0; + grad->xform[2] = 0; + grad->xform[3] = r; + grad->xform[4] = cx; + grad->xform[5] = cy; + grad->fx = fx / r; + grad->fy = fy / r; + } + + nsvg__xformMultiply(grad->xform, data->xform); + nsvg__xformMultiply(grad->xform, attr->xform); + + grad->spread = data->spread; + memcpy(grad->stops, stops, nstops * sizeof(NSVGgradientStop)); + grad->nstops = nstops; + + *paintType = data->type; + + return grad; +} + +static float nsvg__getAverageScale(float *t) +{ + float sx = sqrtf(t[0] * t[0] + t[2] * t[2]); + float sy = sqrtf(t[1] * t[1] + t[3] * t[3]); + return (sx + sy) * 0.5f; +} + +static void nsvg__getLocalBounds(float *bounds, NSVGshape *shape, float *xform) +{ + NSVGpath *path; + float curve[4 * 2], curveBounds[4]; + int i, first = 1; + for (path = shape->paths; path != NULL; path = path->next) { + nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); + for (i = 0; i < path->npts - 1; i += 3) { + nsvg__xformPoint( + &curve[2], &curve[3], path->pts[(i + 1) * 2], path->pts[(i + 1) * 2 + 1], xform); + nsvg__xformPoint( + &curve[4], &curve[5], path->pts[(i + 2) * 2], path->pts[(i + 2) * 2 + 1], xform); + nsvg__xformPoint( + &curve[6], &curve[7], path->pts[(i + 3) * 2], path->pts[(i + 3) * 2 + 1], xform); + nsvg__curveBounds(curveBounds, curve); + if (first) { + bounds[0] = curveBounds[0]; + bounds[1] = curveBounds[1]; + bounds[2] = curveBounds[2]; + bounds[3] = curveBounds[3]; + first = 0; + } + else { + bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); + bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); + bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); + bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); + } + curve[0] = curve[6]; + curve[1] = curve[7]; + } + } +} + +static void nsvg__addShape(NSVGparser *p) +{ + NSVGattrib *attr = nsvg__getAttr(p); + float scale = 1.0f; + NSVGshape *shape; + NSVGpath *path; + int i; + + if (p->plist == NULL) + return; + + shape = (NSVGshape *)malloc(sizeof(NSVGshape)); + if (shape == NULL) + goto error; + memset(shape, 0, sizeof(NSVGshape)); + + memcpy(shape->id, attr->id, sizeof shape->id); + /* Copy parent id from breadcrumb. */ + if (p->breadcrumb_len > 0) { + memcpy(shape->id_parent, p->breadcrumb[0], sizeof shape->id_parent); + } + else { + memcpy(shape->id_parent, attr->id, sizeof shape->id_parent); + } + + scale = nsvg__getAverageScale(attr->xform); + shape->strokeWidth = attr->strokeWidth * scale; + shape->strokeDashOffset = attr->strokeDashOffset * scale; + shape->strokeDashCount = (char)attr->strokeDashCount; + for (i = 0; i < attr->strokeDashCount; i++) + shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; + shape->strokeLineJoin = attr->strokeLineJoin; + shape->strokeLineCap = attr->strokeLineCap; + shape->miterLimit = attr->miterLimit; + shape->fillRule = attr->fillRule; + shape->opacity = attr->opacity; + + shape->paths = p->plist; + p->plist = NULL; + + // Calculate shape bounds + shape->bounds[0] = shape->paths->bounds[0]; + shape->bounds[1] = shape->paths->bounds[1]; + shape->bounds[2] = shape->paths->bounds[2]; + shape->bounds[3] = shape->paths->bounds[3]; + for (path = shape->paths->next; path != NULL; path = path->next) { + shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); + shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); + shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); + shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); + } + + // Set fill + if (attr->hasFill == 0) { + shape->fill.type = NSVG_PAINT_NONE; + } + else if (attr->hasFill == 1) { + shape->fill.type = NSVG_PAINT_COLOR; + shape->fill.color = attr->fillColor; + shape->fill.color |= (unsigned int)(attr->fillOpacity * 255) << 24; + } + else if (attr->hasFill == 2) { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, attr->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->fill.gradient = nsvg__createGradient( + p, attr->fillGradient, localBounds, &shape->fill.type); + if (shape->fill.gradient == NULL) { + shape->fill.type = NSVG_PAINT_NONE; + } + } + + // Set stroke + if (attr->hasStroke == 0) { + shape->stroke.type = NSVG_PAINT_NONE; + } + else if (attr->hasStroke == 1) { + shape->stroke.type = NSVG_PAINT_COLOR; + shape->stroke.color = attr->strokeColor; + shape->stroke.color |= (unsigned int)(attr->strokeOpacity * 255) << 24; + } + else if (attr->hasStroke == 2) { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, attr->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->stroke.gradient = nsvg__createGradient( + p, attr->strokeGradient, localBounds, &shape->stroke.type); + if (shape->stroke.gradient == NULL) + shape->stroke.type = NSVG_PAINT_NONE; + } + + // Set flags + shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); + + // Add to tail + if (p->image->shapes == NULL) + p->image->shapes = shape; + else + p->shapesTail->next = shape; + p->shapesTail = shape; + + return; + +error: + if (shape) + free(shape); +} + +static void nsvg__addPath(NSVGparser *p, char closed) +{ + NSVGattrib *attr = nsvg__getAttr(p); + NSVGpath *path = NULL; + float bounds[4]; + float *curve; + int i; + + if (p->npts < 4) + return; + + if (closed) + nsvg__lineTo(p, p->pts[0], p->pts[1]); + + // Expect 1 + N*3 points (N = number of cubic bezier segments). + if ((p->npts % 3) != 1) + return; + + path = (NSVGpath *)malloc(sizeof(NSVGpath)); + if (path == NULL) + goto error; + memset(path, 0, sizeof(NSVGpath)); + + path->pts = (float *)malloc(p->npts * 2 * sizeof(float)); + if (path->pts == NULL) + goto error; + path->closed = closed; + path->npts = p->npts; + + // Transform path. + for (i = 0; i < p->npts; ++i) + nsvg__xformPoint( + &path->pts[i * 2], &path->pts[i * 2 + 1], p->pts[i * 2], p->pts[i * 2 + 1], attr->xform); + + // Find bounds + for (i = 0; i < path->npts - 1; i += 3) { + curve = &path->pts[i * 2]; + nsvg__curveBounds(bounds, curve); + if (i == 0) { + path->bounds[0] = bounds[0]; + path->bounds[1] = bounds[1]; + path->bounds[2] = bounds[2]; + path->bounds[3] = bounds[3]; + } + else { + path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); + path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); + path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); + path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); + } + } + + path->next = p->plist; + p->plist = path; + + return; + +error: + if (path != NULL) { + if (path->pts != NULL) + free(path->pts); + free(path); + } +} + +// We roll our own string to float because the std library one uses locale and messes things up. +static double nsvg__atof(const char *s) +{ + char *cur = (char *)s; + char *end = NULL; + double res = 0.0, sign = 1.0; + long long intPart = 0, fracPart = 0; + char hasIntPart = 0, hasFracPart = 0; + + // Parse optional sign + if (*cur == '+') { + cur++; + } + else if (*cur == '-') { + sign = -1; + cur++; + } + + // Parse integer part + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + intPart = strtoll(cur, &end, 10); + if (cur != end) { + res = (double)intPart; + hasIntPart = 1; + cur = end; + } + } + + // Parse fractional part. + if (*cur == '.') { + cur++; // Skip '.' + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + fracPart = strtoll(cur, &end, 10); + if (cur != end) { + res += (double)fracPart / pow(10.0, (double)(end - cur)); + hasFracPart = 1; + cur = end; + } + } + } + + // A valid number should have integer or fractional part. + if (!hasIntPart && !hasFracPart) + return 0.0; + + // Parse optional exponent + if (*cur == 'e' || *cur == 'E') { + long expPart = 0; + cur++; // skip 'E' + expPart = strtol(cur, &end, 10); // Parse digit sequence with sign + if (cur != end) { + res *= pow(10.0, (double)expPart); + } + } + + return res * sign; +} + +static const char *nsvg__parseNumber(const char *s, char *it, const int size) +{ + const int last = size - 1; + int i = 0; + + // sign + if (*s == '-' || *s == '+') { + if (i < last) + it[i++] = *s; + s++; + } + // integer part + while (*s && nsvg__isdigit(*s)) { + if (i < last) + it[i++] = *s; + s++; + } + if (*s == '.') { + // decimal point + if (i < last) + it[i++] = *s; + s++; + // fraction part + while (*s && nsvg__isdigit(*s)) { + if (i < last) + it[i++] = *s; + s++; + } + } + // exponent + if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) { + if (i < last) + it[i++] = *s; + s++; + if (*s == '-' || *s == '+') { + if (i < last) + it[i++] = *s; + s++; + } + while (*s && nsvg__isdigit(*s)) { + if (i < last) + it[i++] = *s; + s++; + } + } + it[i] = '\0'; + + return s; +} + +static const char *nsvg__getNextPathItem(const char *s, char *it, char cmd, int nargs) +{ + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) + s++; + if (!*s) + return s; + + /* Blender: Special case for arc command's 4th and 5th arguments. */ + if (ELEM(cmd, 'a', 'A') && ELEM(nargs, 3, 4)) { + it[0] = s[0]; + it[1] = '\0'; + s++; + return s; + } + + if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { + s = nsvg__parseNumber(s, it, 64); + } + else { + // Parse command + it[0] = *s++; + it[1] = '\0'; + return s; + } + + return s; +} + +static unsigned int nsvg__parseColorHex(const char *str) +{ + unsigned int c = 0, r = 0, g = 0, b = 0; + int n = 0; + str++; // skip # + // Calculate number of characters. + while (str[n] && !nsvg__isspace(str[n])) + n++; + if (n == 6) { + sscanf(str, "%x", &c); + } + else if (n == 3) { + sscanf(str, "%x", &c); + c = (c & 0xf) | ((c & 0xf0) << 4) | ((c & 0xf00) << 8); + c |= c << 4; + } + r = (c >> 16) & 0xff; + g = (c >> 8) & 0xff; + b = c & 0xff; + return NSVG_RGB(r, g, b); +} + +static unsigned int nsvg__parseColorRGB(const char *str) +{ + int r = -1, g = -1, b = -1; + char s1[32] = "", s2[32] = ""; + sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b); + if (strchr(s1, '%')) { + return NSVG_RGB((r * 255) / 100, (g * 255) / 100, (b * 255) / 100); + } + else { + return NSVG_RGB(r, g, b); + } +} + +typedef struct NSVGNamedColor { + const char *name; + unsigned int color; +} NSVGNamedColor; + +NSVGNamedColor nsvg__colors[] = { + + {"red", NSVG_RGB(255, 0, 0)}, + {"green", NSVG_RGB(0, 128, 0)}, + {"blue", NSVG_RGB(0, 0, 255)}, + {"yellow", NSVG_RGB(255, 255, 0)}, + {"cyan", NSVG_RGB(0, 255, 255)}, + {"magenta", NSVG_RGB(255, 0, 255)}, + {"black", NSVG_RGB(0, 0, 0)}, + {"grey", NSVG_RGB(128, 128, 128)}, + {"gray", NSVG_RGB(128, 128, 128)}, + {"white", NSVG_RGB(255, 255, 255)}, + +#ifdef NANOSVG_ALL_COLOR_KEYWORDS + {"aliceblue", NSVG_RGB(240, 248, 255)}, + {"antiquewhite", NSVG_RGB(250, 235, 215)}, + {"aqua", NSVG_RGB(0, 255, 255)}, + {"aquamarine", NSVG_RGB(127, 255, 212)}, + {"azure", NSVG_RGB(240, 255, 255)}, + {"beige", NSVG_RGB(245, 245, 220)}, + {"bisque", NSVG_RGB(255, 228, 196)}, + {"blanchedalmond", NSVG_RGB(255, 235, 205)}, + {"blueviolet", NSVG_RGB(138, 43, 226)}, + {"brown", NSVG_RGB(165, 42, 42)}, + {"burlywood", NSVG_RGB(222, 184, 135)}, + {"cadetblue", NSVG_RGB(95, 158, 160)}, + {"chartreuse", NSVG_RGB(127, 255, 0)}, + {"chocolate", NSVG_RGB(210, 105, 30)}, + {"coral", NSVG_RGB(255, 127, 80)}, + {"cornflowerblue", NSVG_RGB(100, 149, 237)}, + {"cornsilk", NSVG_RGB(255, 248, 220)}, + {"crimson", NSVG_RGB(220, 20, 60)}, + {"darkblue", NSVG_RGB(0, 0, 139)}, + {"darkcyan", NSVG_RGB(0, 139, 139)}, + {"darkgoldenrod", NSVG_RGB(184, 134, 11)}, + {"darkgray", NSVG_RGB(169, 169, 169)}, + {"darkgreen", NSVG_RGB(0, 100, 0)}, + {"darkgrey", NSVG_RGB(169, 169, 169)}, + {"darkkhaki", NSVG_RGB(189, 183, 107)}, + {"darkmagenta", NSVG_RGB(139, 0, 139)}, + {"darkolivegreen", NSVG_RGB(85, 107, 47)}, + {"darkorange", NSVG_RGB(255, 140, 0)}, + {"darkorchid", NSVG_RGB(153, 50, 204)}, + {"darkred", NSVG_RGB(139, 0, 0)}, + {"darksalmon", NSVG_RGB(233, 150, 122)}, + {"darkseagreen", NSVG_RGB(143, 188, 143)}, + {"darkslateblue", NSVG_RGB(72, 61, 139)}, + {"darkslategray", NSVG_RGB(47, 79, 79)}, + {"darkslategrey", NSVG_RGB(47, 79, 79)}, + {"darkturquoise", NSVG_RGB(0, 206, 209)}, + {"darkviolet", NSVG_RGB(148, 0, 211)}, + {"deeppink", NSVG_RGB(255, 20, 147)}, + {"deepskyblue", NSVG_RGB(0, 191, 255)}, + {"dimgray", NSVG_RGB(105, 105, 105)}, + {"dimgrey", NSVG_RGB(105, 105, 105)}, + {"dodgerblue", NSVG_RGB(30, 144, 255)}, + {"firebrick", NSVG_RGB(178, 34, 34)}, + {"floralwhite", NSVG_RGB(255, 250, 240)}, + {"forestgreen", NSVG_RGB(34, 139, 34)}, + {"fuchsia", NSVG_RGB(255, 0, 255)}, + {"gainsboro", NSVG_RGB(220, 220, 220)}, + {"ghostwhite", NSVG_RGB(248, 248, 255)}, + {"gold", NSVG_RGB(255, 215, 0)}, + {"goldenrod", NSVG_RGB(218, 165, 32)}, + {"greenyellow", NSVG_RGB(173, 255, 47)}, + {"honeydew", NSVG_RGB(240, 255, 240)}, + {"hotpink", NSVG_RGB(255, 105, 180)}, + {"indianred", NSVG_RGB(205, 92, 92)}, + {"indigo", NSVG_RGB(75, 0, 130)}, + {"ivory", NSVG_RGB(255, 255, 240)}, + {"khaki", NSVG_RGB(240, 230, 140)}, + {"lavender", NSVG_RGB(230, 230, 250)}, + {"lavenderblush", NSVG_RGB(255, 240, 245)}, + {"lawngreen", NSVG_RGB(124, 252, 0)}, + {"lemonchiffon", NSVG_RGB(255, 250, 205)}, + {"lightblue", NSVG_RGB(173, 216, 230)}, + {"lightcoral", NSVG_RGB(240, 128, 128)}, + {"lightcyan", NSVG_RGB(224, 255, 255)}, + {"lightgoldenrodyellow", NSVG_RGB(250, 250, 210)}, + {"lightgray", NSVG_RGB(211, 211, 211)}, + {"lightgreen", NSVG_RGB(144, 238, 144)}, + {"lightgrey", NSVG_RGB(211, 211, 211)}, + {"lightpink", NSVG_RGB(255, 182, 193)}, + {"lightsalmon", NSVG_RGB(255, 160, 122)}, + {"lightseagreen", NSVG_RGB(32, 178, 170)}, + {"lightskyblue", NSVG_RGB(135, 206, 250)}, + {"lightslategray", NSVG_RGB(119, 136, 153)}, + {"lightslategrey", NSVG_RGB(119, 136, 153)}, + {"lightsteelblue", NSVG_RGB(176, 196, 222)}, + {"lightyellow", NSVG_RGB(255, 255, 224)}, + {"lime", NSVG_RGB(0, 255, 0)}, + {"limegreen", NSVG_RGB(50, 205, 50)}, + {"linen", NSVG_RGB(250, 240, 230)}, + {"maroon", NSVG_RGB(128, 0, 0)}, + {"mediumaquamarine", NSVG_RGB(102, 205, 170)}, + {"mediumblue", NSVG_RGB(0, 0, 205)}, + {"mediumorchid", NSVG_RGB(186, 85, 211)}, + {"mediumpurple", NSVG_RGB(147, 112, 219)}, + {"mediumseagreen", NSVG_RGB(60, 179, 113)}, + {"mediumslateblue", NSVG_RGB(123, 104, 238)}, + {"mediumspringgreen", NSVG_RGB(0, 250, 154)}, + {"mediumturquoise", NSVG_RGB(72, 209, 204)}, + {"mediumvioletred", NSVG_RGB(199, 21, 133)}, + {"midnightblue", NSVG_RGB(25, 25, 112)}, + {"mintcream", NSVG_RGB(245, 255, 250)}, + {"mistyrose", NSVG_RGB(255, 228, 225)}, + {"moccasin", NSVG_RGB(255, 228, 181)}, + {"navajowhite", NSVG_RGB(255, 222, 173)}, + {"navy", NSVG_RGB(0, 0, 128)}, + {"oldlace", NSVG_RGB(253, 245, 230)}, + {"olive", NSVG_RGB(128, 128, 0)}, + {"olivedrab", NSVG_RGB(107, 142, 35)}, + {"orange", NSVG_RGB(255, 165, 0)}, + {"orangered", NSVG_RGB(255, 69, 0)}, + {"orchid", NSVG_RGB(218, 112, 214)}, + {"palegoldenrod", NSVG_RGB(238, 232, 170)}, + {"palegreen", NSVG_RGB(152, 251, 152)}, + {"paleturquoise", NSVG_RGB(175, 238, 238)}, + {"palevioletred", NSVG_RGB(219, 112, 147)}, + {"papayawhip", NSVG_RGB(255, 239, 213)}, + {"peachpuff", NSVG_RGB(255, 218, 185)}, + {"peru", NSVG_RGB(205, 133, 63)}, + {"pink", NSVG_RGB(255, 192, 203)}, + {"plum", NSVG_RGB(221, 160, 221)}, + {"powderblue", NSVG_RGB(176, 224, 230)}, + {"purple", NSVG_RGB(128, 0, 128)}, + {"rosybrown", NSVG_RGB(188, 143, 143)}, + {"royalblue", NSVG_RGB(65, 105, 225)}, + {"saddlebrown", NSVG_RGB(139, 69, 19)}, + {"salmon", NSVG_RGB(250, 128, 114)}, + {"sandybrown", NSVG_RGB(244, 164, 96)}, + {"seagreen", NSVG_RGB(46, 139, 87)}, + {"seashell", NSVG_RGB(255, 245, 238)}, + {"sienna", NSVG_RGB(160, 82, 45)}, + {"silver", NSVG_RGB(192, 192, 192)}, + {"skyblue", NSVG_RGB(135, 206, 235)}, + {"slateblue", NSVG_RGB(106, 90, 205)}, + {"slategray", NSVG_RGB(112, 128, 144)}, + {"slategrey", NSVG_RGB(112, 128, 144)}, + {"snow", NSVG_RGB(255, 250, 250)}, + {"springgreen", NSVG_RGB(0, 255, 127)}, + {"steelblue", NSVG_RGB(70, 130, 180)}, + {"tan", NSVG_RGB(210, 180, 140)}, + {"teal", NSVG_RGB(0, 128, 128)}, + {"thistle", NSVG_RGB(216, 191, 216)}, + {"tomato", NSVG_RGB(255, 99, 71)}, + {"turquoise", NSVG_RGB(64, 224, 208)}, + {"violet", NSVG_RGB(238, 130, 238)}, + {"wheat", NSVG_RGB(245, 222, 179)}, + {"whitesmoke", NSVG_RGB(245, 245, 245)}, + {"yellowgreen", NSVG_RGB(154, 205, 50)}, +#endif +}; + +static unsigned int nsvg__parseColorName(const char *str) +{ + int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); + + for (i = 0; i < ncolors; i++) { + if (strcmp(nsvg__colors[i].name, str) == 0) { + return nsvg__colors[i].color; + } + } + + return NSVG_RGB(128, 128, 128); +} + +static unsigned int nsvg__parseColor(const char *str) +{ + size_t len = 0; + while (*str == ' ') + ++str; + len = strlen(str); + if (len >= 1 && *str == '#') + return nsvg__parseColorHex(str); + else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') + return nsvg__parseColorRGB(str); + return nsvg__parseColorName(str); +} + +static float nsvg__parseOpacity(const char *str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) + val = 0.0f; + if (val > 1.0f) + val = 1.0f; + return val; +} + +static float nsvg__parseMiterLimit(const char *str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) + val = 0.0f; + return val; +} + +static int nsvg__parseUnits(const char *units) +{ + if (units[0] == 'p' && units[1] == 'x') + return NSVG_UNITS_PX; + else if (units[0] == 'p' && units[1] == 't') + return NSVG_UNITS_PT; + else if (units[0] == 'p' && units[1] == 'c') + return NSVG_UNITS_PC; + else if (units[0] == 'm' && units[1] == 'm') + return NSVG_UNITS_MM; + else if (units[0] == 'c' && units[1] == 'm') + return NSVG_UNITS_CM; + else if (units[0] == 'i' && units[1] == 'n') + return NSVG_UNITS_IN; + else if (units[0] == '%') + return NSVG_UNITS_PERCENT; + else if (units[0] == 'e' && units[1] == 'm') + return NSVG_UNITS_EM; + else if (units[0] == 'e' && units[1] == 'x') + return NSVG_UNITS_EX; + return NSVG_UNITS_USER; +} + +static int nsvg__isCoordinate(const char *s) +{ + // optional sign + if (*s == '-' || *s == '+') + s++; + // must have at least one digit, or start by a dot + return (nsvg__isdigit(*s) || *s == '.'); +} + +static NSVGcoordinate nsvg__parseCoordinateRaw(const char *str) +{ + NSVGcoordinate coord = {0, NSVG_UNITS_USER}; + char buf[64]; + coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); + coord.value = nsvg__atof(buf); + return coord; +} + +static NSVGcoordinate nsvg__coord(float v, int units) +{ + NSVGcoordinate coord = {v, units}; + return coord; +} + +static float nsvg__parseCoordinate(NSVGparser *p, const char *str, float orig, float length) +{ + NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); + return nsvg__convertToPixels(p, coord, orig, length); +} + +static int nsvg__parseTransformArgs(const char *str, float *args, int maxNa, int *na) +{ + const char *end; + const char *ptr; + char it[64]; + + *na = 0; + ptr = str; + while (*ptr && *ptr != '(') + ++ptr; + if (*ptr == 0) + return 1; + end = ptr; + while (*end && *end != ')') + ++end; + if (*end == 0) + return 1; + + while (ptr < end) { + if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { + if (*na >= maxNa) + return 0; + ptr = nsvg__parseNumber(ptr, it, 64); + args[(*na)++] = (float)nsvg__atof(it); + } + else { + ++ptr; + } + } + return (int)(end - str); +} + +static int nsvg__parseMatrix(float *xform, const char *str) +{ + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, t, 6, &na); + if (na != 6) + return len; + memcpy(xform, t, sizeof(float) * 6); + return len; +} + +static int nsvg__parseTranslate(float *xform, const char *str) +{ + float args[2]; + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) + args[1] = 0.0; + + nsvg__xformSetTranslation(t, args[0], args[1]); + memcpy(xform, t, sizeof(float) * 6); + return len; +} + +static int nsvg__parseScale(float *xform, const char *str) +{ + float args[2]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) + args[1] = args[0]; + nsvg__xformSetScale(t, args[0], args[1]); + memcpy(xform, t, sizeof(float) * 6); + return len; +} + +static int nsvg__parseSkewX(float *xform, const char *str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewX(t, args[0] / 180.0f * NSVG_PI); + memcpy(xform, t, sizeof(float) * 6); + return len; +} + +static int nsvg__parseSkewY(float *xform, const char *str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewY(t, args[0] / 180.0f * NSVG_PI); + memcpy(xform, t, sizeof(float) * 6); + return len; +} + +static int nsvg__parseRotate(float *xform, const char *str) +{ + float args[3]; + int na = 0; + float m[6]; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 3, &na); + if (na == 1) + args[1] = args[2] = 0.0f; + nsvg__xformIdentity(m); + + if (na > 1) { + nsvg__xformSetTranslation(t, -args[1], -args[2]); + nsvg__xformMultiply(m, t); + } + + nsvg__xformSetRotation(t, args[0] / 180.0f * NSVG_PI); + nsvg__xformMultiply(m, t); + + if (na > 1) { + nsvg__xformSetTranslation(t, args[1], args[2]); + nsvg__xformMultiply(m, t); + } + + memcpy(xform, m, sizeof(float) * 6); + + return len; +} + +static void nsvg__parseTransform(float *xform, const char *str) +{ + float t[6]; + int len; + nsvg__xformIdentity(xform); + while (*str) { + if (strncmp(str, "matrix", 6) == 0) + len = nsvg__parseMatrix(t, str); + else if (strncmp(str, "translate", 9) == 0) + len = nsvg__parseTranslate(t, str); + else if (strncmp(str, "scale", 5) == 0) + len = nsvg__parseScale(t, str); + else if (strncmp(str, "rotate", 6) == 0) + len = nsvg__parseRotate(t, str); + else if (strncmp(str, "skewX", 5) == 0) + len = nsvg__parseSkewX(t, str); + else if (strncmp(str, "skewY", 5) == 0) + len = nsvg__parseSkewY(t, str); + else { + ++str; + continue; + } + if (len != 0) { + str += len; + } + else { + ++str; + continue; + } + + nsvg__xformPremultiply(xform, t); + } +} + +static void nsvg__parseUrl(char *id, const char *str) +{ + int i = 0; + str += 4; // "url("; + if (*str == '#') + str++; + while (i < 63 && *str != ')') { + id[i] = *str++; + i++; + } + id[i] = '\0'; +} + +static char nsvg__parseLineCap(const char *str) +{ + if (strcmp(str, "butt") == 0) + return NSVG_CAP_BUTT; + else if (strcmp(str, "round") == 0) + return NSVG_CAP_ROUND; + else if (strcmp(str, "square") == 0) + return NSVG_CAP_SQUARE; + // TODO: handle inherit. + return NSVG_CAP_BUTT; +} + +static char nsvg__parseLineJoin(const char *str) +{ + if (strcmp(str, "miter") == 0) + return NSVG_JOIN_MITER; + else if (strcmp(str, "round") == 0) + return NSVG_JOIN_ROUND; + else if (strcmp(str, "bevel") == 0) + return NSVG_JOIN_BEVEL; + // TODO: handle inherit. + return NSVG_JOIN_MITER; +} + +static char nsvg__parseFillRule(const char *str) +{ + if (strcmp(str, "nonzero") == 0) + return NSVG_FILLRULE_NONZERO; + else if (strcmp(str, "evenodd") == 0) + return NSVG_FILLRULE_EVENODD; + // TODO: handle inherit. + return NSVG_FILLRULE_NONZERO; +} + +static const char *nsvg__getNextDashItem(const char *s, char *it) +{ + int n = 0; + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) + s++; + // Advance until whitespace, comma or end. + while (*s && (!nsvg__isspace(*s) && *s != ',')) { + if (n < 63) + it[n++] = *s; + s++; + } + it[n++] = '\0'; + return s; +} + +static int nsvg__parseStrokeDashArray(NSVGparser *p, const char *str, float *strokeDashArray) +{ + char item[64]; + int count = 0, i; + float sum = 0.0f; + + // Handle "none" + if (str[0] == 'n') + return 0; + + // Parse dashes + while (*str) { + str = nsvg__getNextDashItem(str, item); + if (!*item) + break; + if (count < NSVG_MAX_DASHES) + strokeDashArray[count++] = fabsf( + nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); + } + + for (i = 0; i < count; i++) + sum += strokeDashArray[i]; + if (sum <= 1e-6f) + count = 0; + + return count; +} + +static void nsvg__parseStyle(NSVGparser *p, const char *str); + +static int nsvg__parseAttr(NSVGparser *p, const char *name, const char *value) +{ + float xform[6]; + NSVGattrib *attr = nsvg__getAttr(p); + if (!attr) + return 0; + + if (strcmp(name, "style") == 0) { + nsvg__parseStyle(p, value); + } + else if (strcmp(name, "display") == 0) { + if (strcmp(value, "none") == 0) + attr->visible = 0; + // Don't reset ->visible on display:inline, one display:none hides the whole subtree + } + else if (strcmp(name, "fill") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasFill = 0; + } + else if (strncmp(value, "url(", 4) == 0) { + attr->hasFill = 2; + nsvg__parseUrl(attr->fillGradient, value); + } + else { + attr->hasFill = 1; + attr->fillColor = nsvg__parseColor(value); + } + } + else if (strcmp(name, "opacity") == 0) { + attr->opacity = nsvg__parseOpacity(value); + } + else if (strcmp(name, "fill-opacity") == 0) { + attr->fillOpacity = nsvg__parseOpacity(value); + } + else if (strcmp(name, "stroke") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasStroke = 0; + } + else if (strncmp(value, "url(", 4) == 0) { + attr->hasStroke = 2; + nsvg__parseUrl(attr->strokeGradient, value); + } + else { + attr->hasStroke = 1; + attr->strokeColor = nsvg__parseColor(value); + } + } + else if (strcmp(name, "stroke-width") == 0) { + attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } + else if (strcmp(name, "stroke-dasharray") == 0) { + attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); + } + else if (strcmp(name, "stroke-dashoffset") == 0) { + attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } + else if (strcmp(name, "stroke-opacity") == 0) { + attr->strokeOpacity = nsvg__parseOpacity(value); + } + else if (strcmp(name, "stroke-linecap") == 0) { + attr->strokeLineCap = nsvg__parseLineCap(value); + } + else if (strcmp(name, "stroke-linejoin") == 0) { + attr->strokeLineJoin = nsvg__parseLineJoin(value); + } + else if (strcmp(name, "stroke-miterlimit") == 0) { + attr->miterLimit = nsvg__parseMiterLimit(value); + } + else if (strcmp(name, "fill-rule") == 0) { + attr->fillRule = nsvg__parseFillRule(value); + } + else if (strcmp(name, "font-size") == 0) { + attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } + else if (strcmp(name, "transform") == 0) { + nsvg__parseTransform(xform, value); + nsvg__xformPremultiply(attr->xform, xform); + } + else if (strcmp(name, "stop-color") == 0) { + attr->stopColor = nsvg__parseColor(value); + } + else if (strcmp(name, "stop-opacity") == 0) { + attr->stopOpacity = nsvg__parseOpacity(value); + } + else if (strcmp(name, "offset") == 0) { + attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); + } + else if (strcmp(name, "id") == 0) { + strncpy(attr->id, value, 63); + attr->id[63] = '\0'; + } + else { + return 0; + } + return 1; +} + +static int nsvg__parseNameValue(NSVGparser *p, const char *start, const char *end) +{ + const char *str; + const char *val; + char name[512]; + char value[512]; + int n; + + str = start; + while (str < end && *str != ':') + ++str; + + val = str; + + // Right Trim + while (str > start && (*str == ':' || nsvg__isspace(*str))) + --str; + ++str; + + n = (int)(str - start); + if (n > 511) + n = 511; + if (n) + memcpy(name, start, n); + name[n] = 0; + + while (val < end && (*val == ':' || nsvg__isspace(*val))) + ++val; + + n = (int)(end - val); + if (n > 511) + n = 511; + if (n) + memcpy(value, val, n); + value[n] = 0; + + return nsvg__parseAttr(p, name, value); +} + +static void nsvg__parseStyle(NSVGparser *p, const char *str) +{ + const char *start; + const char *end; + + while (*str) { + // Left Trim + while (*str && nsvg__isspace(*str)) + ++str; + start = str; + while (*str && *str != ';') + ++str; + end = str; + + // Right Trim + while (end > start && (*end == ';' || nsvg__isspace(*end))) + --end; + ++end; + + nsvg__parseNameValue(p, start, end); + if (*str) + ++str; + } +} + +static void nsvg__parseAttribs(NSVGparser *p, const char **attr) +{ + int i; + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "style") == 0) + nsvg__parseStyle(p, attr[i + 1]); + else + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } +} + +static int nsvg__getArgsPerElement(char cmd) +{ + switch (cmd) { + case 'v': + case 'V': + case 'h': + case 'H': + return 1; + case 'm': + case 'M': + case 'l': + case 'L': + case 't': + case 'T': + return 2; + case 'q': + case 'Q': + case 's': + case 'S': + return 4; + case 'c': + case 'C': + return 6; + case 'a': + case 'A': + return 7; + case 'z': + case 'Z': + return 0; + } + return -1; +} + +static void nsvg__pathMoveTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } + else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__moveTo(p, *cpx, *cpy); +} + +static void nsvg__pathLineTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } + else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathHLineTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) +{ + if (rel) + *cpx += args[0]; + else + *cpx = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathVLineTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) +{ + if (rel) + *cpy += args[0]; + else + *cpy = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathCubicBezTo( + NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel) +{ + float x2, y2, cx1, cy1, cx2, cy2; + + if (rel) { + cx1 = *cpx + args[0]; + cy1 = *cpy + args[1]; + cx2 = *cpx + args[2]; + cy2 = *cpy + args[3]; + x2 = *cpx + args[4]; + y2 = *cpy + args[5]; + } + else { + cx1 = args[0]; + cy1 = args[1]; + cx2 = args[2]; + cy2 = args[3]; + x2 = args[4]; + y2 = args[5]; + } + + nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathCubicBezShortTo( + NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel) +{ + float x1, y1, x2, y2, cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx2 = *cpx + args[0]; + cy2 = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } + else { + cx2 = args[0]; + cy2 = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + cx1 = 2 * x1 - *cpx2; + cy1 = 2 * y1 - *cpy2; + + nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezTo( + NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx = *cpx + args[0]; + cy = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } + else { + cx = args[0]; + cy = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + // Convert to cubic bezier + cx1 = x1 + 2.0f / 3.0f * (cx - x1); + cy1 = y1 + 2.0f / 3.0f * (cy - y1); + cx2 = x2 + 2.0f / 3.0f * (cx - x2); + cy2 = y2 + 2.0f / 3.0f * (cy - y2); + + nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezShortTo( + NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + x2 = *cpx + args[0]; + y2 = *cpy + args[1]; + } + else { + x2 = args[0]; + y2 = args[1]; + } + + cx = 2 * x1 - *cpx2; + cy = 2 * y1 - *cpy2; + + // Convert to cubix bezier + cx1 = x1 + 2.0f / 3.0f * (cx - x1); + cy1 = y1 + 2.0f / 3.0f * (cy - y1); + cx2 = x2 + 2.0f / 3.0f * (cx - x2); + cy2 = y2 + 2.0f / 3.0f * (cy - y2); + + nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static float nsvg__sqr(float x) +{ + return x * x; +} +static float nsvg__vmag(float x, float y) +{ + return sqrtf(x * x + y * y); +} + +static float nsvg__vecrat(float ux, float uy, float vx, float vy) +{ + return (ux * vx + uy * vy) / (nsvg__vmag(ux, uy) * nsvg__vmag(vx, vy)); +} + +static float nsvg__vecang(float ux, float uy, float vx, float vy) +{ + float r = nsvg__vecrat(ux, uy, vx, vy); + if (r < -1.0f) + r = -1.0f; + if (r > 1.0f) + r = 1.0f; + return ((ux * vy < uy * vx) ? -1.0f : 1.0f) * acosf(r); +} + +static void nsvg__pathArcTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) +{ + // Ported from canvg (https://code.google.com/p/canvg/) + float rx, ry, rotx; + float x1, y1, x2, y2, cx, cy, dx, dy, d; + float x1p, y1p, cxp, cyp, s, sa, sb; + float ux, uy, vx, vy, a1, da; + float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; + float sinrx, cosrx; + int fa, fs; + int i, ndivs; + float hda, kappa; + + rx = fabsf(args[0]); // y radius + ry = fabsf(args[1]); // x radius + rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle + fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc + fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction + x1 = *cpx; // start point + y1 = *cpy; + if (rel) { // end point + x2 = *cpx + args[5]; + y2 = *cpy + args[6]; + } + else { + x2 = args[5]; + y2 = args[6]; + } + + dx = x1 - x2; + dy = y1 - y2; + d = sqrtf(dx * dx + dy * dy); + if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { + // The arc degenerates to a line + nsvg__lineTo(p, x2, y2); + *cpx = x2; + *cpy = y2; + return; + } + + sinrx = sinf(rotx); + cosrx = cosf(rotx); + + // Convert to center point parameterization. + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + // 1) Compute x1', y1' + x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; + y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; + d = nsvg__sqr(x1p) / nsvg__sqr(rx) + nsvg__sqr(y1p) / nsvg__sqr(ry); + if (d > 1) { + d = sqrtf(d); + rx *= d; + ry *= d; + } + // 2) Compute cx', cy' + s = 0.0f; + sa = nsvg__sqr(rx) * nsvg__sqr(ry) - nsvg__sqr(rx) * nsvg__sqr(y1p) - + nsvg__sqr(ry) * nsvg__sqr(x1p); + sb = nsvg__sqr(rx) * nsvg__sqr(y1p) + nsvg__sqr(ry) * nsvg__sqr(x1p); + if (sa < 0.0f) + sa = 0.0f; + if (sb > 0.0f) + s = sqrtf(sa / sb); + if (fa == fs) + s = -s; + cxp = s * rx * y1p / ry; + cyp = s * -ry * x1p / rx; + + // 3) Compute cx,cy from cx',cy' + cx = (x1 + x2) / 2.0f + cosrx * cxp - sinrx * cyp; + cy = (y1 + y2) / 2.0f + sinrx * cxp + cosrx * cyp; + + // 4) Calculate theta1, and delta theta. + ux = (x1p - cxp) / rx; + uy = (y1p - cyp) / ry; + vx = (-x1p - cxp) / rx; + vy = (-y1p - cyp) / ry; + a1 = nsvg__vecang(1.0f, 0.0f, ux, uy); // Initial angle + da = nsvg__vecang(ux, uy, vx, vy); // Delta angle + + // if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; + // if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; + + if (fs == 0 && da > 0) + da -= 2 * NSVG_PI; + else if (fs == 1 && da < 0) + da += 2 * NSVG_PI; + + // Approximate the arc using cubic spline segments. + t[0] = cosrx; + t[1] = sinrx; + t[2] = -sinrx; + t[3] = cosrx; + t[4] = cx; + t[5] = cy; + + // Split arc into max 90 degree segments. + // The loop assumes an iteration per end point (including start and end), this +1. + ndivs = (int)(fabsf(da) / (NSVG_PI * 0.5f) + 1.0f); + hda = (da / (float)ndivs) / 2.0f; + // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite) + if ((hda < 1e-3f) && (hda > -1e-3f)) + hda *= 0.5f; + else + hda = (1.0f - cosf(hda)) / sinf(hda); + kappa = fabsf(4.0f / 3.0f * hda); + if (da < 0.0f) + kappa = -kappa; + + for (i = 0; i <= ndivs; i++) { + a = a1 + da * ((float)i / (float)ndivs); + dx = cosf(a); + dy = sinf(a); + nsvg__xformPoint(&x, &y, dx * rx, dy * ry, t); // position + nsvg__xformVec(&tanx, &tany, -dy * rx * kappa, dx * ry * kappa, t); // tangent + if (i > 0) + nsvg__cubicBezTo(p, px + ptanx, py + ptany, x - tanx, y - tany, x, y); + px = x; + py = y; + ptanx = tanx; + ptany = tany; + } + + *cpx = x2; + *cpy = y2; +} + +static void nsvg__parsePath(NSVGparser *p, const char **attr) +{ + const char *s = NULL; + char cmd = '\0'; + float args[10]; + int nargs; + int rargs = 0; + char initPoint; + float cpx, cpy, cpx2, cpy2; + const char *tmp[4]; + char closedFlag; + int i; + char item[64]; + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "d") == 0) { + s = attr[i + 1]; + } + else { + tmp[0] = attr[i]; + tmp[1] = attr[i + 1]; + tmp[2] = 0; + tmp[3] = 0; + nsvg__parseAttribs(p, tmp); + } + } + + if (s) { + nsvg__resetPath(p); + cpx = 0; + cpy = 0; + cpx2 = 0; + cpy2 = 0; + initPoint = 0; + closedFlag = 0; + nargs = 0; + + while (*s) { + s = nsvg__getNextPathItem(s, item, cmd, nargs); + if (!*item) + break; + if (cmd != '\0' && nsvg__isCoordinate(item)) { + if (nargs < 10) + args[nargs++] = (float)nsvg__atof(item); + if (nargs >= rargs) { + switch (cmd) { + case 'm': + case 'M': + nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); + // Moveto can be followed by multiple coordinate pairs, + // which should be treated as linetos. + cmd = (cmd == 'm') ? 'l' : 'L'; + rargs = nsvg__getArgsPerElement(cmd); + cpx2 = cpx; + cpy2 = cpy; + initPoint = 1; + break; + case 'l': + case 'L': + nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); + cpx2 = cpx; + cpy2 = cpy; + break; + case 'H': + case 'h': + nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); + cpx2 = cpx; + cpy2 = cpy; + break; + case 'V': + case 'v': + nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); + cpx2 = cpx; + cpy2 = cpy; + break; + case 'C': + case 'c': + nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); + break; + case 'S': + case 's': + nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); + break; + case 'Q': + case 'q': + nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); + break; + case 'T': + case 't': + nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); + break; + case 'A': + case 'a': + nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); + cpx2 = cpx; + cpy2 = cpy; + break; + default: + if (nargs >= 2) { + cpx = args[nargs - 2]; + cpy = args[nargs - 1]; + cpx2 = cpx; + cpy2 = cpy; + } + break; + } + nargs = 0; + } + } + else { + cmd = item[0]; + if (cmd == 'M' || cmd == 'm') { + // Commit path. + if (p->npts > 0) + nsvg__addPath(p, closedFlag); + // Start new subpath. + nsvg__resetPath(p); + closedFlag = 0; + nargs = 0; + } + else if (initPoint == 0) { + // Do not allow other commands until initial point has been set (moveTo called once). + cmd = '\0'; + } + if (cmd == 'Z' || cmd == 'z') { + closedFlag = 1; + // Commit path. + if (p->npts > 0) { + // Move current point to first point + cpx = p->pts[0]; + cpy = p->pts[1]; + cpx2 = cpx; + cpy2 = cpy; + nsvg__addPath(p, closedFlag); + } + // Start new subpath. + nsvg__resetPath(p); + nsvg__moveTo(p, cpx, cpy); + closedFlag = 0; + nargs = 0; + } + rargs = nsvg__getArgsPerElement(cmd); + if (rargs == -1) { + // Command not recognized + cmd = '\0'; + rargs = 0; + } + } + } + // Commit path. + if (p->npts) + nsvg__addPath(p, closedFlag); + } + + nsvg__addShape(p); +} + +static void nsvg__parseRect(NSVGparser *p, const char **attr) +{ + float x = 0.0f; + float y = 0.0f; + float w = 0.0f; + float h = 0.0f; + float rx = -1.0f; // marks not set + float ry = -1.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x") == 0) + x = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y") == 0) + y = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "width") == 0) + w = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualWidth(p)); + if (strcmp(attr[i], "height") == 0) + h = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) + rx = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) + ry = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx < 0.0f && ry > 0.0f) + rx = ry; + if (ry < 0.0f && rx > 0.0f) + ry = rx; + if (rx < 0.0f) + rx = 0.0f; + if (ry < 0.0f) + ry = 0.0f; + if (rx > w / 2.0f) + rx = w / 2.0f; + if (ry > h / 2.0f) + ry = h / 2.0f; + + if (w != 0.0f && h != 0.0f) { + nsvg__resetPath(p); + + if (rx < 0.00001f || ry < 0.0001f) { + nsvg__moveTo(p, x, y); + nsvg__lineTo(p, x + w, y); + nsvg__lineTo(p, x + w, y + h); + nsvg__lineTo(p, x, y + h); + } + else { + // Rounded rectangle + nsvg__moveTo(p, x + rx, y); + nsvg__lineTo(p, x + w - rx, y); + nsvg__cubicBezTo(p, + x + w - rx * (1 - NSVG_KAPPA90), + y, + x + w, + y + ry * (1 - NSVG_KAPPA90), + x + w, + y + ry); + nsvg__lineTo(p, x + w, y + h - ry); + nsvg__cubicBezTo(p, + x + w, + y + h - ry * (1 - NSVG_KAPPA90), + x + w - rx * (1 - NSVG_KAPPA90), + y + h, + x + w - rx, + y + h); + nsvg__lineTo(p, x + rx, y + h); + nsvg__cubicBezTo(p, + x + rx * (1 - NSVG_KAPPA90), + y + h, + x, + y + h - ry * (1 - NSVG_KAPPA90), + x, + y + h - ry); + nsvg__lineTo(p, x, y + ry); + nsvg__cubicBezTo( + p, x, y + ry * (1 - NSVG_KAPPA90), x + rx * (1 - NSVG_KAPPA90), y, x + rx, y); + } + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseCircle(NSVGparser *p, const char **attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float r = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) + cx = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) + cy = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "r") == 0) + r = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualLength(p))); + } + } + + if (r > 0.0f) { + nsvg__resetPath(p); + + nsvg__moveTo(p, cx + r, cy); + nsvg__cubicBezTo(p, cx + r, cy + r * NSVG_KAPPA90, cx + r * NSVG_KAPPA90, cy + r, cx, cy + r); + nsvg__cubicBezTo(p, cx - r * NSVG_KAPPA90, cy + r, cx - r, cy + r * NSVG_KAPPA90, cx - r, cy); + nsvg__cubicBezTo(p, cx - r, cy - r * NSVG_KAPPA90, cx - r * NSVG_KAPPA90, cy - r, cx, cy - r); + nsvg__cubicBezTo(p, cx + r * NSVG_KAPPA90, cy - r, cx + r, cy - r * NSVG_KAPPA90, cx + r, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseEllipse(NSVGparser *p, const char **attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float rx = 0.0f; + float ry = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) + cx = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) + cy = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) + rx = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) + ry = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx > 0.0f && ry > 0.0f) { + + nsvg__resetPath(p); + + nsvg__moveTo(p, cx + rx, cy); + nsvg__cubicBezTo( + p, cx + rx, cy + ry * NSVG_KAPPA90, cx + rx * NSVG_KAPPA90, cy + ry, cx, cy + ry); + nsvg__cubicBezTo( + p, cx - rx * NSVG_KAPPA90, cy + ry, cx - rx, cy + ry * NSVG_KAPPA90, cx - rx, cy); + nsvg__cubicBezTo( + p, cx - rx, cy - ry * NSVG_KAPPA90, cx - rx * NSVG_KAPPA90, cy - ry, cx, cy - ry); + nsvg__cubicBezTo( + p, cx + rx * NSVG_KAPPA90, cy - ry, cx + rx, cy - ry * NSVG_KAPPA90, cx + rx, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseLine(NSVGparser *p, const char **attr) +{ + float x1 = 0.0; + float y1 = 0.0; + float x2 = 0.0; + float y2 = 0.0; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x1") == 0) + x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y1") == 0) + y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "x2") == 0) + x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y2") == 0) + y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + } + } + + nsvg__resetPath(p); + + nsvg__moveTo(p, x1, y1); + nsvg__lineTo(p, x2, y2); + + nsvg__addPath(p, 0); + + nsvg__addShape(p); +} + +static void nsvg__parsePoly(NSVGparser *p, const char **attr, int closeFlag) +{ + int i; + const char *s; + float args[2]; + int nargs, npts = 0; + char item[64]; + + nsvg__resetPath(p); + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "points") == 0) { + s = attr[i + 1]; + nargs = 0; + while (*s) { + s = nsvg__getNextPathItem(s, item, '\0', nargs); + args[nargs++] = (float)nsvg__atof(item); + if (nargs >= 2) { + if (npts == 0) + nsvg__moveTo(p, args[0], args[1]); + else + nsvg__lineTo(p, args[0], args[1]); + nargs = 0; + npts++; + } + } + } + } + } + + nsvg__addPath(p, (char)closeFlag); + + nsvg__addShape(p); +} + +static void nsvg__parseSVG(NSVGparser *p, const char **attr) +{ + int i; + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "width") == 0) { + p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); + } + else if (strcmp(attr[i], "height") == 0) { + p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); + } + else if (strcmp(attr[i], "viewBox") == 0) { + const char *s = attr[i + 1]; + char buf[64]; + s = nsvg__parseNumber(s, buf, 64); + p->viewMinx = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) + s++; + if (!*s) + return; + s = nsvg__parseNumber(s, buf, 64); + p->viewMiny = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) + s++; + if (!*s) + return; + s = nsvg__parseNumber(s, buf, 64); + p->viewWidth = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) + s++; + if (!*s) + return; + s = nsvg__parseNumber(s, buf, 64); + p->viewHeight = nsvg__atof(buf); + } + else if (strcmp(attr[i], "preserveAspectRatio") == 0) { + if (strstr(attr[i + 1], "none") != 0) { + // No uniform scaling + p->alignType = NSVG_ALIGN_NONE; + } + else { + // Parse X align + if (strstr(attr[i + 1], "xMin") != 0) + p->alignX = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "xMid") != 0) + p->alignX = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "xMax") != 0) + p->alignX = NSVG_ALIGN_MAX; + // Parse X align + if (strstr(attr[i + 1], "yMin") != 0) + p->alignY = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "yMid") != 0) + p->alignY = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "yMax") != 0) + p->alignY = NSVG_ALIGN_MAX; + // Parse meet/slice + p->alignType = NSVG_ALIGN_MEET; + if (strstr(attr[i + 1], "slice") != 0) + p->alignType = NSVG_ALIGN_SLICE; + } + } + } + } +} + +static void nsvg__parseGradient(NSVGparser *p, const char **attr, char type) +{ + int i; + NSVGgradientData *grad = (NSVGgradientData *)malloc(sizeof(NSVGgradientData)); + if (grad == NULL) + return; + memset(grad, 0, sizeof(NSVGgradientData)); + grad->units = NSVG_OBJECT_SPACE; + grad->type = type; + if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { + grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); + grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + } + else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { + grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + } + + nsvg__xformIdentity(grad->xform); + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "id") == 0) { + strncpy(grad->id, attr[i + 1], 63); + grad->id[63] = '\0'; + } + else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "gradientUnits") == 0) { + if (strcmp(attr[i + 1], "objectBoundingBox") == 0) + grad->units = NSVG_OBJECT_SPACE; + else + grad->units = NSVG_USER_SPACE; + } + else if (strcmp(attr[i], "gradientTransform") == 0) { + nsvg__parseTransform(grad->xform, attr[i + 1]); + } + else if (strcmp(attr[i], "cx") == 0) { + grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); + } + else if (strcmp(attr[i], "cy") == 0) { + grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); + } + else if (strcmp(attr[i], "r") == 0) { + grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); + } + else if (strcmp(attr[i], "fx") == 0) { + grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); + } + else if (strcmp(attr[i], "fy") == 0) { + grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); + } + else if (strcmp(attr[i], "x1") == 0) { + grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } + else if (strcmp(attr[i], "y1") == 0) { + grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } + else if (strcmp(attr[i], "x2") == 0) { + grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } + else if (strcmp(attr[i], "y2") == 0) { + grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } + else if (strcmp(attr[i], "spreadMethod") == 0) { + if (strcmp(attr[i + 1], "pad") == 0) + grad->spread = NSVG_SPREAD_PAD; + else if (strcmp(attr[i + 1], "reflect") == 0) + grad->spread = NSVG_SPREAD_REFLECT; + else if (strcmp(attr[i + 1], "repeat") == 0) + grad->spread = NSVG_SPREAD_REPEAT; + } + else if (strcmp(attr[i], "xlink:href") == 0) { + const char *href = attr[i + 1]; + strncpy(grad->ref, href + 1, 62); + grad->ref[62] = '\0'; + } + } + } + + grad->next = p->gradients; + p->gradients = grad; +} + +static void nsvg__parseGradientStop(NSVGparser *p, const char **attr) +{ + NSVGattrib *curAttr = nsvg__getAttr(p); + NSVGgradientData *grad; + NSVGgradientStop *stop; + int i, idx; + + curAttr->stopOffset = 0; + curAttr->stopColor = 0; + curAttr->stopOpacity = 1.0f; + + for (i = 0; attr[i]; i += 2) { + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } + + // Add stop to the last gradient. + grad = p->gradients; + if (grad == NULL) + return; + + grad->nstops++; + grad->stops = (NSVGgradientStop *)realloc(grad->stops, sizeof(NSVGgradientStop) * grad->nstops); + if (grad->stops == NULL) + return; + + // Insert + idx = grad->nstops - 1; + for (i = 0; i < grad->nstops - 1; i++) { + if (curAttr->stopOffset < grad->stops[i].offset) { + idx = i; + break; + } + } + if (idx != grad->nstops - 1) { + for (i = grad->nstops - 1; i > idx; i--) + grad->stops[i] = grad->stops[i - 1]; + } + + stop = &grad->stops[idx]; + stop->color = curAttr->stopColor; + stop->color |= (unsigned int)(curAttr->stopOpacity * 255) << 24; + stop->offset = curAttr->stopOffset; +} + +static void nsvg__startElement(void *ud, const char *el, const char **attr) +{ + NSVGparser *p = (NSVGparser *)ud; + + if (p->defsFlag) { + // Skip everything but gradients in defs + if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } + else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } + else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } + return; + } + + if (strcmp(el, "g") == 0) { + nsvg__pushAttr(p); + nsvg__parseAttribs(p, attr); + + /* Save the breadcrumb of groups. */ + if (p->breadcrumb_len < NSVG_MAX_BREADCRUMB) { + NSVGattrib *attr_id = nsvg__getAttr(p); + memcpy( + p->breadcrumb[p->breadcrumb_len], attr_id->id, sizeof(p->breadcrumb[p->breadcrumb_len])); + p->breadcrumb_len++; + } + } + else if (strcmp(el, "path") == 0) { + if (p->pathFlag) // Do not allow nested paths. + return; + nsvg__pushAttr(p); + nsvg__parsePath(p, attr); + nsvg__popAttr(p); + } + else if (strcmp(el, "rect") == 0) { + nsvg__pushAttr(p); + nsvg__parseRect(p, attr); + nsvg__popAttr(p); + } + else if (strcmp(el, "circle") == 0) { + nsvg__pushAttr(p); + nsvg__parseCircle(p, attr); + nsvg__popAttr(p); + } + else if (strcmp(el, "ellipse") == 0) { + nsvg__pushAttr(p); + nsvg__parseEllipse(p, attr); + nsvg__popAttr(p); + } + else if (strcmp(el, "line") == 0) { + nsvg__pushAttr(p); + nsvg__parseLine(p, attr); + nsvg__popAttr(p); + } + else if (strcmp(el, "polyline") == 0) { + nsvg__pushAttr(p); + nsvg__parsePoly(p, attr, 0); + nsvg__popAttr(p); + } + else if (strcmp(el, "polygon") == 0) { + nsvg__pushAttr(p); + nsvg__parsePoly(p, attr, 1); + nsvg__popAttr(p); + } + else if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } + else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } + else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } + else if (strcmp(el, "defs") == 0) { + p->defsFlag = 1; + } + else if (strcmp(el, "svg") == 0) { + nsvg__parseSVG(p, attr); + } +} + +static void nsvg__endElement(void *ud, const char *el) +{ + NSVGparser *p = (NSVGparser *)ud; + + if (strcmp(el, "g") == 0) { + /* Remove the breadcrumb level. */ + if (p->breadcrumb_len > 0) { + p->breadcrumb[p->breadcrumb_len - 1][0] = '\0'; + p->breadcrumb_len--; + } + + nsvg__popAttr(p); + } + else if (strcmp(el, "path") == 0) { + p->pathFlag = 0; + } + else if (strcmp(el, "defs") == 0) { + p->defsFlag = 0; + } +} + +static void nsvg__content(void *ud, const char *s) +{ + NSVG_NOTUSED(ud); + NSVG_NOTUSED(s); + // empty +} + +static void nsvg__imageBounds(NSVGparser *p, float *bounds) +{ + NSVGshape *shape; + shape = p->image->shapes; + if (shape == NULL) { + bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; + return; + } + bounds[0] = shape->bounds[0]; + bounds[1] = shape->bounds[1]; + bounds[2] = shape->bounds[2]; + bounds[3] = shape->bounds[3]; + for (shape = shape->next; shape != NULL; shape = shape->next) { + bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); + bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); + bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); + bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); + } +} + +static float nsvg__viewAlign(float content, float container, int type) +{ + if (type == NSVG_ALIGN_MIN) + return 0; + else if (type == NSVG_ALIGN_MAX) + return container - content; + // mid + return (container - content) * 0.5f; +} + +static void nsvg__scaleGradient(NSVGgradient *grad, float tx, float ty, float sx, float sy) +{ + float t[6]; + nsvg__xformSetTranslation(t, tx, ty); + nsvg__xformMultiply(grad->xform, t); + + nsvg__xformSetScale(t, sx, sy); + nsvg__xformMultiply(grad->xform, t); +} + +static void nsvg__scaleToViewbox(NSVGparser *p, const char *units) +{ + NSVGshape *shape; + NSVGpath *path; + float tx, ty, sx, sy, us, bounds[4], t[6], avgs; + int i; + float *pt; + + // Guess image size if not set completely. + nsvg__imageBounds(p, bounds); + + if (p->viewWidth == 0) { + if (p->image->width > 0) { + p->viewWidth = p->image->width; + } + else { + p->viewMinx = bounds[0]; + p->viewWidth = bounds[2] - bounds[0]; + } + } + if (p->viewHeight == 0) { + if (p->image->height > 0) { + p->viewHeight = p->image->height; + } + else { + p->viewMiny = bounds[1]; + p->viewHeight = bounds[3] - bounds[1]; + } + } + if (p->image->width == 0) + p->image->width = p->viewWidth; + if (p->image->height == 0) + p->image->height = p->viewHeight; + + tx = -p->viewMinx; + ty = -p->viewMiny; + sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; + sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; + // Unit scaling + us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); + + // Fix aspect ratio + if (p->alignType == NSVG_ALIGN_MEET) { + // fit whole image into viewbox + sx = sy = nsvg__minf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth * sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight * sy, p->image->height, p->alignY) / sy; + } + else if (p->alignType == NSVG_ALIGN_SLICE) { + // fill whole viewbox with image + sx = sy = nsvg__maxf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth * sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight * sy, p->image->height, p->alignY) / sy; + } + + // Transform + sx *= us; + sy *= us; + avgs = (sx + sy) / 2.0f; + for (shape = p->image->shapes; shape != NULL; shape = shape->next) { + shape->bounds[0] = (shape->bounds[0] + tx) * sx; + shape->bounds[1] = (shape->bounds[1] + ty) * sy; + shape->bounds[2] = (shape->bounds[2] + tx) * sx; + shape->bounds[3] = (shape->bounds[3] + ty) * sy; + for (path = shape->paths; path != NULL; path = path->next) { + path->bounds[0] = (path->bounds[0] + tx) * sx; + path->bounds[1] = (path->bounds[1] + ty) * sy; + path->bounds[2] = (path->bounds[2] + tx) * sx; + path->bounds[3] = (path->bounds[3] + ty) * sy; + for (i = 0; i < path->npts; i++) { + pt = &path->pts[i * 2]; + pt[0] = (pt[0] + tx) * sx; + pt[1] = (pt[1] + ty) * sy; + } + } + + if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || + shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->fill.gradient, tx, ty, sx, sy); + memcpy(t, shape->fill.gradient->xform, sizeof(float) * 6); + nsvg__xformInverse(shape->fill.gradient->xform, t); + } + if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || + shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->stroke.gradient, tx, ty, sx, sy); + memcpy(t, shape->stroke.gradient->xform, sizeof(float) * 6); + nsvg__xformInverse(shape->stroke.gradient->xform, t); + } + + shape->strokeWidth *= avgs; + shape->strokeDashOffset *= avgs; + for (i = 0; i < shape->strokeDashCount; i++) + shape->strokeDashArray[i] *= avgs; + } +} + +NSVGimage *nsvgParse(char *input, const char *units, float dpi) +{ + NSVGparser *p; + NSVGimage *ret = 0; + + p = nsvg__createParser(); + if (p == NULL) { + return NULL; + } + p->dpi = dpi; + + nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); + + // Scale to viewBox + nsvg__scaleToViewbox(p, units); + + ret = p->image; + p->image = NULL; + + nsvg__deleteParser(p); + + return ret; +} + +NSVGimage *nsvgParseFromFile(const char *filename, const char *units, float dpi) +{ + FILE *fp = NULL; + size_t size; + char *data = NULL; + NSVGimage *image = NULL; + + fp = fopen(filename, "rb"); + if (!fp) + goto error; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + data = (char *)malloc(size + 1); + if (data == NULL) + goto error; + if (fread(data, 1, size, fp) != size) + goto error; + data[size] = '\0'; // Must be null terminated. + fclose(fp); + image = nsvgParse(data, units, dpi); + free(data); + + return image; + +error: + if (fp) + fclose(fp); + if (data) + free(data); + if (image) + nsvgDelete(image); + return NULL; +} + +NSVGpath *nsvgDuplicatePath(NSVGpath *p) +{ + NSVGpath *res = NULL; + + if (p == NULL) + return NULL; + + res = (NSVGpath *)malloc(sizeof(NSVGpath)); + if (res == NULL) + goto error; + memset(res, 0, sizeof(NSVGpath)); + + res->pts = (float *)malloc(p->npts * 2 * sizeof(float)); + if (res->pts == NULL) + goto error; + memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); + res->npts = p->npts; + + memcpy(res->bounds, p->bounds, sizeof(p->bounds)); + + res->closed = p->closed; + + return res; + +error: + if (res != NULL) { + free(res->pts); + free(res); + } + return NULL; +} + +void nsvgDelete(NSVGimage *image) +{ + NSVGshape *snext, *shape; + if (image == NULL) + return; + shape = image->shapes; + while (shape != NULL) { + snext = shape->next; + nsvg__deletePaths(shape->paths); + nsvg__deletePaint(&shape->fill); + nsvg__deletePaint(&shape->stroke); + free(shape); + shape = snext; + } + free(image); +} + +#endif diff --git a/extern/nanosvg/patches/NanoSVG.diff b/extern/nanosvg/patches/NanoSVG.diff new file mode 100644 index 00000000000..68dbb18573b --- /dev/null +++ b/extern/nanosvg/patches/NanoSVG.diff @@ -0,0 +1,86 @@ +diff --git a/c:/tmp/nanosvg_original.h b/c:/tmp/nanosvg_modif.h +index 24a01a86d3d..eca0d07e79d 100644 +--- a/c:/tmp/nanosvg_original.h ++++ b/c:/tmp/nanosvg_modif.h +@@ -24,7 +24,8 @@ + * + * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html + * +- */ ++ * This is a modified version for Blender used by importers. ++ **/ + + #ifndef NANOSVG_H + #define NANOSVG_H +@@ -148,6 +149,8 @@ extern "C" { + typedef struct NSVGshape + { + char id[64]; // Optional 'id' attr of the shape or its group ++ /* Blender: Parent ID used for layer creation. */ ++ char id_parent[64]; + NSVGpaint fill; // Fill paint + NSVGpaint stroke; // Stroke paint + float opacity; // Opacity of the shape. +@@ -370,6 +373,7 @@ int nsvg__parseXML(char* input, + /* Simple SVG parser. */ + + #define NSVG_MAX_ATTR 128 ++#define NSVG_MAX_BREADCRUMB 5 + + enum NSVGgradientUnits + { +@@ -471,6 +475,10 @@ typedef struct NSVGparser + float dpi; + char pathFlag; + char defsFlag; ++ /** Blender breadcrumb for layers. */ ++ char breadcrumb[NSVG_MAX_BREADCRUMB][64]; ++ /** Blender number of elements in breadcrumb. */ ++ int breadcrumb_len; + } NSVGparser; + + static void nsvg__xformIdentity(float* t) +@@ -980,6 +988,14 @@ static void nsvg__addShape(NSVGparser* p) + memset(shape, 0, sizeof(NSVGshape)); + + memcpy(shape->id, attr->id, sizeof shape->id); ++ /* Copy parent id from breadcrumb. */ ++ if (p->breadcrumb_len > 0) { ++ memcpy(shape->id_parent, p->breadcrumb[0], sizeof shape->id_parent); ++ } ++ else { ++ memcpy(shape->id_parent, attr->id, sizeof shape->id_parent); ++ } ++ + scale = nsvg__getAverageScale(attr->xform); + shape->strokeWidth = attr->strokeWidth * scale; + shape->strokeDashOffset = attr->strokeDashOffset * scale; +@@ -2814,6 +2830,14 @@ static void nsvg__startElement(void* ud, const char* el, const char** attr) + if (strcmp(el, "g") == 0) { + nsvg__pushAttr(p); + nsvg__parseAttribs(p, attr); ++ ++ /* Save the breadcrumb of groups. */ ++ if (p->breadcrumb_len < NSVG_MAX_BREADCRUMB) { ++ NSVGattrib *attr_id = nsvg__getAttr(p); ++ memcpy( ++ p->breadcrumb[p->breadcrumb_len], attr_id->id, sizeof(p->breadcrumb[p->breadcrumb_len])); ++ p->breadcrumb_len++; ++ } + } + else if (strcmp(el, "path") == 0) { + if (p->pathFlag) // Do not allow nested paths. +@@ -2874,7 +2898,12 @@ static void nsvg__endElement(void* ud, const char* el) + NSVGparser* p = (NSVGparser*)ud; + + if (strcmp(el, "g") == 0) { +- nsvg__popAttr(p); ++ /* Remove the breadcrumb level. */ ++ if (p->breadcrumb_len > 0) { ++ p->breadcrumb[p->breadcrumb_len - 1][0] = '\0'; ++ p->breadcrumb_len--; ++ } ++ nsvg__popAttr(p); + } + else if (strcmp(el, "path") == 0) { + p->pathFlag = 0; diff --git a/source/blender/io/gpencil/CMakeLists.txt b/source/blender/io/gpencil/CMakeLists.txt index fec95be6aa8..4af8b506bd5 100644 --- a/source/blender/io/gpencil/CMakeLists.txt +++ b/source/blender/io/gpencil/CMakeLists.txt @@ -33,6 +33,7 @@ set(INC ../../../../intern/clog ../../../../intern/guardedalloc ../../../../intern/utfconv + ../../../../extern/nanosvg ) set(INC_SYS @@ -44,9 +45,6 @@ set(SRC intern/gpencil_io_import_base.cc intern/gpencil_io_import_svg.cc - # This line must be removed if NanoSVG is moved to extern - nanosvg/nanosvg.h - gpencil_io.h intern/gpencil_io_base.hh intern/gpencil_io_export_base.hh diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc index db6bbc7768e..941d1137f4d 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc @@ -42,7 +42,7 @@ #define NANOSVG_ALL_COLOR_KEYWORDS #define NANOSVG_IMPLEMENTATION -#include "nanosvg/nanosvg.h" +#include "nanosvg.h" using blender::MutableSpan; diff --git a/source/blender/io/gpencil/nanosvg/nanosvg.h b/source/blender/io/gpencil/nanosvg/nanosvg.h deleted file mode 100644 index 94dad37861a..00000000000 --- a/source/blender/io/gpencil/nanosvg/nanosvg.h +++ /dev/null @@ -1,3327 +0,0 @@ -/* - * Copyright (c) 2013-14 `Mikko Mononen ` - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example - * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/) - * - * Arc calculation code based on canvg (https://code.google.com/p/canvg/) - * - * Bounding box calculation based on - * http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html - * - * This is a modified version for Blender used by importers. - * - */ - -#ifndef NANOSVG_H -#define NANOSVG_H - -#ifndef NANOSVG_CPLUSPLUS -# ifdef __cplusplus -extern "C" { -# endif -#endif - -// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of -// cubic bezier shapes. -// -// The library suits well for anything from rendering scalable icons in your editor application to -// prototyping a game. -// -// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create -// a pull request! -// -// The shapes in the SVG images are transformed by the viewBox and converted to specified units. -// That is, you should get the same looking data as your designed in your favorite app. -// -// NanoSVG can return the paths in few different units. For example if you want to render an image, -// you may choose to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you -// may want to use millimeters. -// -// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. -// DPI (dots-per-inch) controls how the unit conversion is done. -// -// If you don't know or care about the units stuff, "px" and 96 should get you going. - -/* Example Usage: - // Load SVG - NSVGimage* image; - image = nsvgParseFromFile("test.svg", "px", 96); - printf("size: %f x %f\n", image->width, image->height); - // Use... - for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { - for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { - for (int i = 0; i < path->npts-1; i += 3) { - float* p = &path->pts[i*2]; - drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); - } - } - } - // Delete - nsvgDelete(image); -*/ - -enum NSVGpaintType { - NSVG_PAINT_NONE = 0, - NSVG_PAINT_COLOR = 1, - NSVG_PAINT_LINEAR_GRADIENT = 2, - NSVG_PAINT_RADIAL_GRADIENT = 3 -}; - -enum NSVGspreadType { NSVG_SPREAD_PAD = 0, NSVG_SPREAD_REFLECT = 1, NSVG_SPREAD_REPEAT = 2 }; - -enum NSVGlineJoin { NSVG_JOIN_MITER = 0, NSVG_JOIN_ROUND = 1, NSVG_JOIN_BEVEL = 2 }; - -enum NSVGlineCap { NSVG_CAP_BUTT = 0, NSVG_CAP_ROUND = 1, NSVG_CAP_SQUARE = 2 }; - -enum NSVGfillRule { NSVG_FILLRULE_NONZERO = 0, NSVG_FILLRULE_EVENODD = 1 }; - -enum NSVGflags { NSVG_FLAGS_VISIBLE = 0x01 }; - -typedef struct NSVGgradientStop { - unsigned int color; - float offset; -} NSVGgradientStop; - -typedef struct NSVGgradient { - float xform[6]; - char spread; - float fx, fy; - int nstops; - NSVGgradientStop stops[1]; -} NSVGgradient; - -typedef struct NSVGpaint { - char type; - union { - unsigned int color; - NSVGgradient *gradient; - }; -} NSVGpaint; - -typedef struct NSVGpath { - float *pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... - int npts; // Total number of bezier points. - char closed; // Flag indicating if shapes should be treated as closed. - float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. - struct NSVGpath *next; // Pointer to next path, or NULL if last element. -} NSVGpath; - -typedef struct NSVGshape { - char id[64]; // Optional 'id' attr of the shape or its group - /* Blender: Parent ID used for layer creation. */ - char id_parent[64]; - NSVGpaint fill; // Fill paint - NSVGpaint stroke; // Stroke paint - float opacity; // Opacity of the shape. - float strokeWidth; // Stroke width (scaled). - float strokeDashOffset; // Stroke dash offset (scaled). - float strokeDashArray[8]; // Stroke dash array (scaled). - char strokeDashCount; // Number of dash values in dash array. - char strokeLineJoin; // Stroke join type. - char strokeLineCap; // Stroke cap type. - float miterLimit; // Miter limit - char fillRule; // Fill rule, see NSVGfillRule. - unsigned char flags; // Logical or of NSVG_FLAGS_* flags - float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. - NSVGpath *paths; // Linked list of paths in the image. - struct NSVGshape *next; // Pointer to next shape, or NULL if last element. -} NSVGshape; - -typedef struct NSVGimage { - float width; // Width of the image. - float height; // Height of the image. - NSVGshape *shapes; // Linked list of shapes in the image. -} NSVGimage; - -// Parses SVG file from a file, returns SVG image as paths. -NSVGimage *nsvgParseFromFile(const char *filename, const char *units, float dpi); - -// Parses SVG file from a null terminated string, returns SVG image as paths. -// Important note: changes the string. -NSVGimage *nsvgParse(char *input, const char *units, float dpi); - -// Duplicates a path. -NSVGpath *nsvgDuplicatePath(NSVGpath *p); - -// Deletes an image. -void nsvgDelete(NSVGimage *image); - -#ifndef NANOSVG_CPLUSPLUS -# ifdef __cplusplus -} -# endif -#endif - -#endif // NANOSVG_H - -#ifdef NANOSVG_IMPLEMENTATION - -#include -#include -#include - -#define NSVG_PI (3.14159265358979323846264338327f) -#define NSVG_KAPPA90 \ - (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. - -#define NSVG_ALIGN_MIN 0 -#define NSVG_ALIGN_MID 1 -#define NSVG_ALIGN_MAX 2 -#define NSVG_ALIGN_NONE 0 -#define NSVG_ALIGN_MEET 1 -#define NSVG_ALIGN_SLICE 2 - -#define NSVG_NOTUSED(v) \ - do { \ - (void)(1 ? (void)0 : ((void)(v))); \ - } while (0) -#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) - -#ifdef _MSC_VER -# pragma warning(disable : 4996) // Switch off security warnings -# pragma warning(disable : 4100) // Switch off unreferenced formal parameter warnings -# ifdef __cplusplus -# define NSVG_INLINE inline -# else -# define NSVG_INLINE -# endif -#else -# define NSVG_INLINE inline -#endif - -static int nsvg__isspace(char c) -{ - return strchr(" \t\n\v\f\r", c) != 0; -} - -static int nsvg__isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static NSVG_INLINE float nsvg__minf(float a, float b) -{ - return a < b ? a : b; -} -static NSVG_INLINE float nsvg__maxf(float a, float b) -{ - return a > b ? a : b; -} - -// Simple XML parser - -#define NSVG_XML_TAG 1 -#define NSVG_XML_CONTENT 2 -#define NSVG_XML_MAX_ATTRIBS 256 - -static void nsvg__parseContent(char *s, void (*contentCb)(void *ud, const char *s), void *ud) -{ - // Trim start white spaces - while (*s && nsvg__isspace(*s)) - s++; - if (!*s) - return; - - if (contentCb) - (*contentCb)(ud, s); -} - -static void nsvg__parseElement(char *s, - void (*startelCb)(void *ud, const char *el, const char **attr), - void (*endelCb)(void *ud, const char *el), - void *ud) -{ - const char *attr[NSVG_XML_MAX_ATTRIBS]; - int nattr = 0; - char *name; - int start = 0; - int end = 0; - char quote; - - // Skip white space after the '<' - while (*s && nsvg__isspace(*s)) - s++; - - // Check if the tag is end tag - if (*s == '/') { - s++; - end = 1; - } - else { - start = 1; - } - - // Skip comments, data and preprocessor stuff. - if (!*s || *s == '?' || *s == '!') - return; - - // Get tag name - name = s; - while (*s && !nsvg__isspace(*s)) - s++; - if (*s) { - *s++ = '\0'; - } - - // Get attribs - while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS - 3) { - char *name = NULL; - char *value = NULL; - - // Skip white space before the attrib name - while (*s && nsvg__isspace(*s)) - s++; - if (!*s) - break; - if (*s == '/') { - end = 1; - break; - } - name = s; - // Find end of the attrib name. - while (*s && !nsvg__isspace(*s) && *s != '=') - s++; - if (*s) { - *s++ = '\0'; - } - // Skip until the beginning of the value. - while (*s && *s != '\"' && *s != '\'') - s++; - if (!*s) - break; - quote = *s; - s++; - // Store value and find the end of it. - value = s; - while (*s && *s != quote) - s++; - if (*s) { - *s++ = '\0'; - } - - // Store only well formed attributes - if (name && value) { - attr[nattr++] = name; - attr[nattr++] = value; - } - } - - // List terminator - attr[nattr++] = 0; - attr[nattr++] = 0; - - // Call callbacks. - if (start && startelCb) - (*startelCb)(ud, name, attr); - if (end && endelCb) - (*endelCb)(ud, name); -} - -static int nsvg__parseXML(char *input, - void (*startelCb)(void *ud, const char *el, const char **attr), - void (*endelCb)(void *ud, const char *el), - void (*contentCb)(void *ud, const char *s), - void *ud) -{ - char *s = input; - char *mark = s; - int state = NSVG_XML_CONTENT; - while (*s) { - if (*s == '<' && state == NSVG_XML_CONTENT) { - // Start of a tag - *s++ = '\0'; - nsvg__parseContent(mark, contentCb, ud); - mark = s; - state = NSVG_XML_TAG; - } - else if (*s == '>' && state == NSVG_XML_TAG) { - // Start of a content or new tag. - *s++ = '\0'; - nsvg__parseElement(mark, startelCb, endelCb, ud); - mark = s; - state = NSVG_XML_CONTENT; - } - else { - s++; - } - } - - return 1; -} - -/* Simple SVG parser. */ - -#define NSVG_MAX_ATTR 128 -#define NSVG_MAX_BREADCRUMB 5 - -enum NSVGgradientUnits { NSVG_USER_SPACE = 0, NSVG_OBJECT_SPACE = 1 }; - -#define NSVG_MAX_DASHES 8 - -enum NSVGunits { - NSVG_UNITS_USER, - NSVG_UNITS_PX, - NSVG_UNITS_PT, - NSVG_UNITS_PC, - NSVG_UNITS_MM, - NSVG_UNITS_CM, - NSVG_UNITS_IN, - NSVG_UNITS_PERCENT, - NSVG_UNITS_EM, - NSVG_UNITS_EX -}; - -typedef struct NSVGcoordinate { - float value; - int units; -} NSVGcoordinate; - -typedef struct NSVGlinearData { - NSVGcoordinate x1, y1, x2, y2; -} NSVGlinearData; - -typedef struct NSVGradialData { - NSVGcoordinate cx, cy, r, fx, fy; -} NSVGradialData; - -typedef struct NSVGgradientData { - char id[64]; - char ref[64]; - char type; - union { - NSVGlinearData linear; - NSVGradialData radial; - }; - char spread; - char units; - float xform[6]; - int nstops; - NSVGgradientStop *stops; - struct NSVGgradientData *next; -} NSVGgradientData; - -typedef struct NSVGattrib { - char id[64]; - float xform[6]; - unsigned int fillColor; - unsigned int strokeColor; - float opacity; - float fillOpacity; - float strokeOpacity; - char fillGradient[64]; - char strokeGradient[64]; - float strokeWidth; - float strokeDashOffset; - float strokeDashArray[NSVG_MAX_DASHES]; - int strokeDashCount; - char strokeLineJoin; - char strokeLineCap; - float miterLimit; - char fillRule; - float fontSize; - unsigned int stopColor; - float stopOpacity; - float stopOffset; - char hasFill; - char hasStroke; - char visible; -} NSVGattrib; - -typedef struct NSVGparser { - NSVGattrib attr[NSVG_MAX_ATTR]; - int attrHead; - float *pts; - int npts; - int cpts; - NSVGpath *plist; - NSVGimage *image; - NSVGgradientData *gradients; - NSVGshape *shapesTail; - float viewMinx, viewMiny, viewWidth, viewHeight; - int alignX, alignY, alignType; - float dpi; - char pathFlag; - char defsFlag; - /** Blender breadcrumb for layers. */ - char breadcrumb[NSVG_MAX_BREADCRUMB][64]; - /** Blender number of elements in breadcrumb. */ - int breadcrumb_len; -} NSVGparser; - -static void nsvg__xformIdentity(float *t) -{ - t[0] = 1.0f; - t[1] = 0.0f; - t[2] = 0.0f; - t[3] = 1.0f; - t[4] = 0.0f; - t[5] = 0.0f; -} - -static void nsvg__xformSetTranslation(float *t, float tx, float ty) -{ - t[0] = 1.0f; - t[1] = 0.0f; - t[2] = 0.0f; - t[3] = 1.0f; - t[4] = tx; - t[5] = ty; -} - -static void nsvg__xformSetScale(float *t, float sx, float sy) -{ - t[0] = sx; - t[1] = 0.0f; - t[2] = 0.0f; - t[3] = sy; - t[4] = 0.0f; - t[5] = 0.0f; -} - -static void nsvg__xformSetSkewX(float *t, float a) -{ - t[0] = 1.0f; - t[1] = 0.0f; - t[2] = tanf(a); - t[3] = 1.0f; - t[4] = 0.0f; - t[5] = 0.0f; -} - -static void nsvg__xformSetSkewY(float *t, float a) -{ - t[0] = 1.0f; - t[1] = tanf(a); - t[2] = 0.0f; - t[3] = 1.0f; - t[4] = 0.0f; - t[5] = 0.0f; -} - -static void nsvg__xformSetRotation(float *t, float a) -{ - float cs = cosf(a), sn = sinf(a); - t[0] = cs; - t[1] = sn; - t[2] = -sn; - t[3] = cs; - t[4] = 0.0f; - t[5] = 0.0f; -} - -static void nsvg__xformMultiply(float *t, float *s) -{ - float t0 = t[0] * s[0] + t[1] * s[2]; - float t2 = t[2] * s[0] + t[3] * s[2]; - float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; - t[1] = t[0] * s[1] + t[1] * s[3]; - t[3] = t[2] * s[1] + t[3] * s[3]; - t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; - t[0] = t0; - t[2] = t2; - t[4] = t4; -} - -static void nsvg__xformInverse(float *inv, float *t) -{ - double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; - if (det > -1e-6 && det < 1e-6) { - nsvg__xformIdentity(t); - return; - } - invdet = 1.0 / det; - inv[0] = (float)(t[3] * invdet); - inv[2] = (float)(-t[2] * invdet); - inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); - inv[1] = (float)(-t[1] * invdet); - inv[3] = (float)(t[0] * invdet); - inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); -} - -static void nsvg__xformPremultiply(float *t, float *s) -{ - float s2[6]; - memcpy(s2, s, sizeof(float) * 6); - nsvg__xformMultiply(s2, t); - memcpy(t, s2, sizeof(float) * 6); -} - -static void nsvg__xformPoint(float *dx, float *dy, float x, float y, float *t) -{ - *dx = x * t[0] + y * t[2] + t[4]; - *dy = x * t[1] + y * t[3] + t[5]; -} - -static void nsvg__xformVec(float *dx, float *dy, float x, float y, float *t) -{ - *dx = x * t[0] + y * t[2]; - *dy = x * t[1] + y * t[3]; -} - -#define NSVG_EPSILON (1e-12) - -static int nsvg__ptInBounds(float *pt, float *bounds) -{ - return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; -} - -static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) -{ - double it = 1.0 - t; - return it * it * it * p0 + 3.0 * it * it * t * p1 + 3.0 * it * t * t * p2 + t * t * t * p3; -} - -static void nsvg__curveBounds(float *bounds, float *curve) -{ - int i, j, count; - double roots[2], a, b, c, b2ac, t, v; - float *v0 = &curve[0]; - float *v1 = &curve[2]; - float *v2 = &curve[4]; - float *v3 = &curve[6]; - - // Start the bounding box by end points - bounds[0] = nsvg__minf(v0[0], v3[0]); - bounds[1] = nsvg__minf(v0[1], v3[1]); - bounds[2] = nsvg__maxf(v0[0], v3[0]); - bounds[3] = nsvg__maxf(v0[1], v3[1]); - - // Bezier curve fits inside the convex hull of it's control points. - // If control points are inside the bounds, we're done. - if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) - return; - - // Add bezier curve inflection points in X and Y. - for (i = 0; i < 2; i++) { - a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; - b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; - c = 3.0 * v1[i] - 3.0 * v0[i]; - count = 0; - if (fabs(a) < NSVG_EPSILON) { - if (fabs(b) > NSVG_EPSILON) { - t = -c / b; - if (t > NSVG_EPSILON && t < 1.0 - NSVG_EPSILON) - roots[count++] = t; - } - } - else { - b2ac = b * b - 4.0 * c * a; - if (b2ac > NSVG_EPSILON) { - t = (-b + sqrt(b2ac)) / (2.0 * a); - if (t > NSVG_EPSILON && t < 1.0 - NSVG_EPSILON) - roots[count++] = t; - t = (-b - sqrt(b2ac)) / (2.0 * a); - if (t > NSVG_EPSILON && t < 1.0 - NSVG_EPSILON) - roots[count++] = t; - } - } - for (j = 0; j < count; j++) { - v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); - bounds[0 + i] = nsvg__minf(bounds[0 + i], (float)v); - bounds[2 + i] = nsvg__maxf(bounds[2 + i], (float)v); - } - } -} - -static NSVGparser *nsvg__createParser() -{ - NSVGparser *p; - p = (NSVGparser *)malloc(sizeof(NSVGparser)); - if (p == NULL) - goto error; - memset(p, 0, sizeof(NSVGparser)); - - p->image = (NSVGimage *)malloc(sizeof(NSVGimage)); - if (p->image == NULL) - goto error; - memset(p->image, 0, sizeof(NSVGimage)); - - // Init style - nsvg__xformIdentity(p->attr[0].xform); - memset(p->attr[0].id, 0, sizeof p->attr[0].id); - p->attr[0].fillColor = NSVG_RGB(0, 0, 0); - p->attr[0].strokeColor = NSVG_RGB(0, 0, 0); - p->attr[0].opacity = 1; - p->attr[0].fillOpacity = 1; - p->attr[0].strokeOpacity = 1; - p->attr[0].stopOpacity = 1; - p->attr[0].strokeWidth = 1; - p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; - p->attr[0].strokeLineCap = NSVG_CAP_BUTT; - p->attr[0].miterLimit = 4; - p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; - p->attr[0].hasFill = 1; - p->attr[0].visible = 1; - - return p; - -error: - if (p) { - if (p->image) - free(p->image); - free(p); - } - return NULL; -} - -static void nsvg__deletePaths(NSVGpath *path) -{ - while (path) { - NSVGpath *next = path->next; - if (path->pts != NULL) - free(path->pts); - free(path); - path = next; - } -} - -static void nsvg__deletePaint(NSVGpaint *paint) -{ - if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) - free(paint->gradient); -} - -static void nsvg__deleteGradientData(NSVGgradientData *grad) -{ - NSVGgradientData *next; - while (grad != NULL) { - next = grad->next; - free(grad->stops); - free(grad); - grad = next; - } -} - -static void nsvg__deleteParser(NSVGparser *p) -{ - if (p != NULL) { - nsvg__deletePaths(p->plist); - nsvg__deleteGradientData(p->gradients); - nsvgDelete(p->image); - free(p->pts); - free(p); - } -} - -static void nsvg__resetPath(NSVGparser *p) -{ - p->npts = 0; -} - -static void nsvg__addPoint(NSVGparser *p, float x, float y) -{ - if (p->npts + 1 > p->cpts) { - p->cpts = p->cpts ? p->cpts * 2 : 8; - p->pts = (float *)realloc(p->pts, p->cpts * 2 * sizeof(float)); - if (!p->pts) - return; - } - p->pts[p->npts * 2 + 0] = x; - p->pts[p->npts * 2 + 1] = y; - p->npts++; -} - -static void nsvg__moveTo(NSVGparser *p, float x, float y) -{ - if (p->npts > 0) { - p->pts[(p->npts - 1) * 2 + 0] = x; - p->pts[(p->npts - 1) * 2 + 1] = y; - } - else { - nsvg__addPoint(p, x, y); - } -} - -static void nsvg__lineTo(NSVGparser *p, float x, float y) -{ - float px, py, dx, dy; - if (p->npts > 0) { - px = p->pts[(p->npts - 1) * 2 + 0]; - py = p->pts[(p->npts - 1) * 2 + 1]; - dx = x - px; - dy = y - py; - nsvg__addPoint(p, px + dx / 3.0f, py + dy / 3.0f); - nsvg__addPoint(p, x - dx / 3.0f, y - dy / 3.0f); - nsvg__addPoint(p, x, y); - } -} - -static void nsvg__cubicBezTo( - NSVGparser *p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) -{ - if (p->npts > 0) { - nsvg__addPoint(p, cpx1, cpy1); - nsvg__addPoint(p, cpx2, cpy2); - nsvg__addPoint(p, x, y); - } -} - -static NSVGattrib *nsvg__getAttr(NSVGparser *p) -{ - return &p->attr[p->attrHead]; -} - -static void nsvg__pushAttr(NSVGparser *p) -{ - if (p->attrHead < NSVG_MAX_ATTR - 1) { - p->attrHead++; - memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead - 1], sizeof(NSVGattrib)); - } -} - -static void nsvg__popAttr(NSVGparser *p) -{ - if (p->attrHead > 0) - p->attrHead--; -} - -static float nsvg__actualOrigX(NSVGparser *p) -{ - return p->viewMinx; -} - -static float nsvg__actualOrigY(NSVGparser *p) -{ - return p->viewMiny; -} - -static float nsvg__actualWidth(NSVGparser *p) -{ - return p->viewWidth; -} - -static float nsvg__actualHeight(NSVGparser *p) -{ - return p->viewHeight; -} - -static float nsvg__actualLength(NSVGparser *p) -{ - float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); - return sqrtf(w * w + h * h) / sqrtf(2.0f); -} - -static float nsvg__convertToPixels(NSVGparser *p, NSVGcoordinate c, float orig, float length) -{ - NSVGattrib *attr = nsvg__getAttr(p); - switch (c.units) { - case NSVG_UNITS_USER: - return c.value; - case NSVG_UNITS_PX: - return c.value; - case NSVG_UNITS_PT: - return c.value / 72.0f * p->dpi; - case NSVG_UNITS_PC: - return c.value / 6.0f * p->dpi; - case NSVG_UNITS_MM: - return c.value / 25.4f * p->dpi; - case NSVG_UNITS_CM: - return c.value / 2.54f * p->dpi; - case NSVG_UNITS_IN: - return c.value * p->dpi; - case NSVG_UNITS_EM: - return c.value * attr->fontSize; - case NSVG_UNITS_EX: - return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. - case NSVG_UNITS_PERCENT: - return orig + c.value / 100.0f * length; - default: - return c.value; - } - return c.value; -} - -static NSVGgradientData *nsvg__findGradientData(NSVGparser *p, const char *id) -{ - NSVGgradientData *grad = p->gradients; - if (id == NULL || *id == '\0') - return NULL; - while (grad != NULL) { - if (strcmp(grad->id, id) == 0) - return grad; - grad = grad->next; - } - return NULL; -} - -static NSVGgradient *nsvg__createGradient(NSVGparser *p, - const char *id, - const float *localBounds, - char *paintType) -{ - NSVGattrib *attr = nsvg__getAttr(p); - NSVGgradientData *data = NULL; - NSVGgradientData *ref = NULL; - NSVGgradientStop *stops = NULL; - NSVGgradient *grad; - float ox, oy, sw, sh, sl; - int nstops = 0; - int refIter; - - data = nsvg__findGradientData(p, id); - if (data == NULL) - return NULL; - - // TODO: use ref to fill in all unset values too. - ref = data; - refIter = 0; - while (ref != NULL) { - NSVGgradientData *nextRef = NULL; - if (stops == NULL && ref->stops != NULL) { - stops = ref->stops; - nstops = ref->nstops; - break; - } - nextRef = nsvg__findGradientData(p, ref->ref); - if (nextRef == ref) - break; // prevent infite loops on malformed data - ref = nextRef; - refIter++; - if (refIter > 32) - break; // prevent infite loops on malformed data - } - if (stops == NULL) - return NULL; - - grad = (NSVGgradient *)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop) * (nstops - 1)); - if (grad == NULL) - return NULL; - - // The shape width and height. - if (data->units == NSVG_OBJECT_SPACE) { - ox = localBounds[0]; - oy = localBounds[1]; - sw = localBounds[2] - localBounds[0]; - sh = localBounds[3] - localBounds[1]; - } - else { - ox = nsvg__actualOrigX(p); - oy = nsvg__actualOrigY(p); - sw = nsvg__actualWidth(p); - sh = nsvg__actualHeight(p); - } - sl = sqrtf(sw * sw + sh * sh) / sqrtf(2.0f); - - if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { - float x1, y1, x2, y2, dx, dy; - x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); - y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); - x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); - y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); - // Calculate transform aligned to the line - dx = x2 - x1; - dy = y2 - y1; - grad->xform[0] = dy; - grad->xform[1] = -dx; - grad->xform[2] = dx; - grad->xform[3] = dy; - grad->xform[4] = x1; - grad->xform[5] = y1; - } - else { - float cx, cy, fx, fy, r; - cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); - cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); - fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); - fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); - r = nsvg__convertToPixels(p, data->radial.r, 0, sl); - // Calculate transform aligned to the circle - grad->xform[0] = r; - grad->xform[1] = 0; - grad->xform[2] = 0; - grad->xform[3] = r; - grad->xform[4] = cx; - grad->xform[5] = cy; - grad->fx = fx / r; - grad->fy = fy / r; - } - - nsvg__xformMultiply(grad->xform, data->xform); - nsvg__xformMultiply(grad->xform, attr->xform); - - grad->spread = data->spread; - memcpy(grad->stops, stops, nstops * sizeof(NSVGgradientStop)); - grad->nstops = nstops; - - *paintType = data->type; - - return grad; -} - -static float nsvg__getAverageScale(float *t) -{ - float sx = sqrtf(t[0] * t[0] + t[2] * t[2]); - float sy = sqrtf(t[1] * t[1] + t[3] * t[3]); - return (sx + sy) * 0.5f; -} - -static void nsvg__getLocalBounds(float *bounds, NSVGshape *shape, float *xform) -{ - NSVGpath *path; - float curve[4 * 2], curveBounds[4]; - int i, first = 1; - for (path = shape->paths; path != NULL; path = path->next) { - nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); - for (i = 0; i < path->npts - 1; i += 3) { - nsvg__xformPoint( - &curve[2], &curve[3], path->pts[(i + 1) * 2], path->pts[(i + 1) * 2 + 1], xform); - nsvg__xformPoint( - &curve[4], &curve[5], path->pts[(i + 2) * 2], path->pts[(i + 2) * 2 + 1], xform); - nsvg__xformPoint( - &curve[6], &curve[7], path->pts[(i + 3) * 2], path->pts[(i + 3) * 2 + 1], xform); - nsvg__curveBounds(curveBounds, curve); - if (first) { - bounds[0] = curveBounds[0]; - bounds[1] = curveBounds[1]; - bounds[2] = curveBounds[2]; - bounds[3] = curveBounds[3]; - first = 0; - } - else { - bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); - bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); - bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); - bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); - } - curve[0] = curve[6]; - curve[1] = curve[7]; - } - } -} - -static void nsvg__addShape(NSVGparser *p) -{ - NSVGattrib *attr = nsvg__getAttr(p); - float scale = 1.0f; - NSVGshape *shape; - NSVGpath *path; - int i; - - if (p->plist == NULL) - return; - - shape = (NSVGshape *)malloc(sizeof(NSVGshape)); - if (shape == NULL) - goto error; - memset(shape, 0, sizeof(NSVGshape)); - - memcpy(shape->id, attr->id, sizeof shape->id); - /* Copy parent id from breadcrumb. */ - if (p->breadcrumb_len > 0) { - memcpy(shape->id_parent, p->breadcrumb[0], sizeof shape->id_parent); - } - else { - memcpy(shape->id_parent, attr->id, sizeof shape->id_parent); - } - - scale = nsvg__getAverageScale(attr->xform); - shape->strokeWidth = attr->strokeWidth * scale; - shape->strokeDashOffset = attr->strokeDashOffset * scale; - shape->strokeDashCount = (char)attr->strokeDashCount; - for (i = 0; i < attr->strokeDashCount; i++) - shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; - shape->strokeLineJoin = attr->strokeLineJoin; - shape->strokeLineCap = attr->strokeLineCap; - shape->miterLimit = attr->miterLimit; - shape->fillRule = attr->fillRule; - shape->opacity = attr->opacity; - - shape->paths = p->plist; - p->plist = NULL; - - // Calculate shape bounds - shape->bounds[0] = shape->paths->bounds[0]; - shape->bounds[1] = shape->paths->bounds[1]; - shape->bounds[2] = shape->paths->bounds[2]; - shape->bounds[3] = shape->paths->bounds[3]; - for (path = shape->paths->next; path != NULL; path = path->next) { - shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); - shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); - shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); - shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); - } - - // Set fill - if (attr->hasFill == 0) { - shape->fill.type = NSVG_PAINT_NONE; - } - else if (attr->hasFill == 1) { - shape->fill.type = NSVG_PAINT_COLOR; - shape->fill.color = attr->fillColor; - shape->fill.color |= (unsigned int)(attr->fillOpacity * 255) << 24; - } - else if (attr->hasFill == 2) { - float inv[6], localBounds[4]; - nsvg__xformInverse(inv, attr->xform); - nsvg__getLocalBounds(localBounds, shape, inv); - shape->fill.gradient = nsvg__createGradient( - p, attr->fillGradient, localBounds, &shape->fill.type); - if (shape->fill.gradient == NULL) { - shape->fill.type = NSVG_PAINT_NONE; - } - } - - // Set stroke - if (attr->hasStroke == 0) { - shape->stroke.type = NSVG_PAINT_NONE; - } - else if (attr->hasStroke == 1) { - shape->stroke.type = NSVG_PAINT_COLOR; - shape->stroke.color = attr->strokeColor; - shape->stroke.color |= (unsigned int)(attr->strokeOpacity * 255) << 24; - } - else if (attr->hasStroke == 2) { - float inv[6], localBounds[4]; - nsvg__xformInverse(inv, attr->xform); - nsvg__getLocalBounds(localBounds, shape, inv); - shape->stroke.gradient = nsvg__createGradient( - p, attr->strokeGradient, localBounds, &shape->stroke.type); - if (shape->stroke.gradient == NULL) - shape->stroke.type = NSVG_PAINT_NONE; - } - - // Set flags - shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); - - // Add to tail - if (p->image->shapes == NULL) - p->image->shapes = shape; - else - p->shapesTail->next = shape; - p->shapesTail = shape; - - return; - -error: - if (shape) - free(shape); -} - -static void nsvg__addPath(NSVGparser *p, char closed) -{ - NSVGattrib *attr = nsvg__getAttr(p); - NSVGpath *path = NULL; - float bounds[4]; - float *curve; - int i; - - if (p->npts < 4) - return; - - if (closed) - nsvg__lineTo(p, p->pts[0], p->pts[1]); - - // Expect 1 + N*3 points (N = number of cubic bezier segments). - if ((p->npts % 3) != 1) - return; - - path = (NSVGpath *)malloc(sizeof(NSVGpath)); - if (path == NULL) - goto error; - memset(path, 0, sizeof(NSVGpath)); - - path->pts = (float *)malloc(p->npts * 2 * sizeof(float)); - if (path->pts == NULL) - goto error; - path->closed = closed; - path->npts = p->npts; - - // Transform path. - for (i = 0; i < p->npts; ++i) - nsvg__xformPoint( - &path->pts[i * 2], &path->pts[i * 2 + 1], p->pts[i * 2], p->pts[i * 2 + 1], attr->xform); - - // Find bounds - for (i = 0; i < path->npts - 1; i += 3) { - curve = &path->pts[i * 2]; - nsvg__curveBounds(bounds, curve); - if (i == 0) { - path->bounds[0] = bounds[0]; - path->bounds[1] = bounds[1]; - path->bounds[2] = bounds[2]; - path->bounds[3] = bounds[3]; - } - else { - path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); - path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); - path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); - path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); - } - } - - path->next = p->plist; - p->plist = path; - - return; - -error: - if (path != NULL) { - if (path->pts != NULL) - free(path->pts); - free(path); - } -} - -// We roll our own string to float because the std library one uses locale and messes things up. -static double nsvg__atof(const char *s) -{ - char *cur = (char *)s; - char *end = NULL; - double res = 0.0, sign = 1.0; - long long intPart = 0, fracPart = 0; - char hasIntPart = 0, hasFracPart = 0; - - // Parse optional sign - if (*cur == '+') { - cur++; - } - else if (*cur == '-') { - sign = -1; - cur++; - } - - // Parse integer part - if (nsvg__isdigit(*cur)) { - // Parse digit sequence - intPart = strtoll(cur, &end, 10); - if (cur != end) { - res = (double)intPart; - hasIntPart = 1; - cur = end; - } - } - - // Parse fractional part. - if (*cur == '.') { - cur++; // Skip '.' - if (nsvg__isdigit(*cur)) { - // Parse digit sequence - fracPart = strtoll(cur, &end, 10); - if (cur != end) { - res += (double)fracPart / pow(10.0, (double)(end - cur)); - hasFracPart = 1; - cur = end; - } - } - } - - // A valid number should have integer or fractional part. - if (!hasIntPart && !hasFracPart) - return 0.0; - - // Parse optional exponent - if (*cur == 'e' || *cur == 'E') { - long expPart = 0; - cur++; // skip 'E' - expPart = strtol(cur, &end, 10); // Parse digit sequence with sign - if (cur != end) { - res *= pow(10.0, (double)expPart); - } - } - - return res * sign; -} - -static const char *nsvg__parseNumber(const char *s, char *it, const int size) -{ - const int last = size - 1; - int i = 0; - - // sign - if (*s == '-' || *s == '+') { - if (i < last) - it[i++] = *s; - s++; - } - // integer part - while (*s && nsvg__isdigit(*s)) { - if (i < last) - it[i++] = *s; - s++; - } - if (*s == '.') { - // decimal point - if (i < last) - it[i++] = *s; - s++; - // fraction part - while (*s && nsvg__isdigit(*s)) { - if (i < last) - it[i++] = *s; - s++; - } - } - // exponent - if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) { - if (i < last) - it[i++] = *s; - s++; - if (*s == '-' || *s == '+') { - if (i < last) - it[i++] = *s; - s++; - } - while (*s && nsvg__isdigit(*s)) { - if (i < last) - it[i++] = *s; - s++; - } - } - it[i] = '\0'; - - return s; -} - -static const char *nsvg__getNextPathItem(const char *s, char *it, char cmd, int nargs) -{ - it[0] = '\0'; - // Skip white spaces and commas - while (*s && (nsvg__isspace(*s) || *s == ',')) - s++; - if (!*s) - return s; - - /* Blender: Special case for arc command's 4th and 5th arguments. */ - if (ELEM(cmd, 'a', 'A') && ELEM(nargs, 3, 4)) { - it[0] = s[0]; - it[1] = '\0'; - s++; - return s; - } - - if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { - s = nsvg__parseNumber(s, it, 64); - } - else { - // Parse command - it[0] = *s++; - it[1] = '\0'; - return s; - } - - return s; -} - -static unsigned int nsvg__parseColorHex(const char *str) -{ - unsigned int c = 0, r = 0, g = 0, b = 0; - int n = 0; - str++; // skip # - // Calculate number of characters. - while (str[n] && !nsvg__isspace(str[n])) - n++; - if (n == 6) { - sscanf(str, "%x", &c); - } - else if (n == 3) { - sscanf(str, "%x", &c); - c = (c & 0xf) | ((c & 0xf0) << 4) | ((c & 0xf00) << 8); - c |= c << 4; - } - r = (c >> 16) & 0xff; - g = (c >> 8) & 0xff; - b = c & 0xff; - return NSVG_RGB(r, g, b); -} - -static unsigned int nsvg__parseColorRGB(const char *str) -{ - int r = -1, g = -1, b = -1; - char s1[32] = "", s2[32] = ""; - sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b); - if (strchr(s1, '%')) { - return NSVG_RGB((r * 255) / 100, (g * 255) / 100, (b * 255) / 100); - } - else { - return NSVG_RGB(r, g, b); - } -} - -typedef struct NSVGNamedColor { - const char *name; - unsigned int color; -} NSVGNamedColor; - -NSVGNamedColor nsvg__colors[] = { - - {"red", NSVG_RGB(255, 0, 0)}, - {"green", NSVG_RGB(0, 128, 0)}, - {"blue", NSVG_RGB(0, 0, 255)}, - {"yellow", NSVG_RGB(255, 255, 0)}, - {"cyan", NSVG_RGB(0, 255, 255)}, - {"magenta", NSVG_RGB(255, 0, 255)}, - {"black", NSVG_RGB(0, 0, 0)}, - {"grey", NSVG_RGB(128, 128, 128)}, - {"gray", NSVG_RGB(128, 128, 128)}, - {"white", NSVG_RGB(255, 255, 255)}, - -#ifdef NANOSVG_ALL_COLOR_KEYWORDS - {"aliceblue", NSVG_RGB(240, 248, 255)}, - {"antiquewhite", NSVG_RGB(250, 235, 215)}, - {"aqua", NSVG_RGB(0, 255, 255)}, - {"aquamarine", NSVG_RGB(127, 255, 212)}, - {"azure", NSVG_RGB(240, 255, 255)}, - {"beige", NSVG_RGB(245, 245, 220)}, - {"bisque", NSVG_RGB(255, 228, 196)}, - {"blanchedalmond", NSVG_RGB(255, 235, 205)}, - {"blueviolet", NSVG_RGB(138, 43, 226)}, - {"brown", NSVG_RGB(165, 42, 42)}, - {"burlywood", NSVG_RGB(222, 184, 135)}, - {"cadetblue", NSVG_RGB(95, 158, 160)}, - {"chartreuse", NSVG_RGB(127, 255, 0)}, - {"chocolate", NSVG_RGB(210, 105, 30)}, - {"coral", NSVG_RGB(255, 127, 80)}, - {"cornflowerblue", NSVG_RGB(100, 149, 237)}, - {"cornsilk", NSVG_RGB(255, 248, 220)}, - {"crimson", NSVG_RGB(220, 20, 60)}, - {"darkblue", NSVG_RGB(0, 0, 139)}, - {"darkcyan", NSVG_RGB(0, 139, 139)}, - {"darkgoldenrod", NSVG_RGB(184, 134, 11)}, - {"darkgray", NSVG_RGB(169, 169, 169)}, - {"darkgreen", NSVG_RGB(0, 100, 0)}, - {"darkgrey", NSVG_RGB(169, 169, 169)}, - {"darkkhaki", NSVG_RGB(189, 183, 107)}, - {"darkmagenta", NSVG_RGB(139, 0, 139)}, - {"darkolivegreen", NSVG_RGB(85, 107, 47)}, - {"darkorange", NSVG_RGB(255, 140, 0)}, - {"darkorchid", NSVG_RGB(153, 50, 204)}, - {"darkred", NSVG_RGB(139, 0, 0)}, - {"darksalmon", NSVG_RGB(233, 150, 122)}, - {"darkseagreen", NSVG_RGB(143, 188, 143)}, - {"darkslateblue", NSVG_RGB(72, 61, 139)}, - {"darkslategray", NSVG_RGB(47, 79, 79)}, - {"darkslategrey", NSVG_RGB(47, 79, 79)}, - {"darkturquoise", NSVG_RGB(0, 206, 209)}, - {"darkviolet", NSVG_RGB(148, 0, 211)}, - {"deeppink", NSVG_RGB(255, 20, 147)}, - {"deepskyblue", NSVG_RGB(0, 191, 255)}, - {"dimgray", NSVG_RGB(105, 105, 105)}, - {"dimgrey", NSVG_RGB(105, 105, 105)}, - {"dodgerblue", NSVG_RGB(30, 144, 255)}, - {"firebrick", NSVG_RGB(178, 34, 34)}, - {"floralwhite", NSVG_RGB(255, 250, 240)}, - {"forestgreen", NSVG_RGB(34, 139, 34)}, - {"fuchsia", NSVG_RGB(255, 0, 255)}, - {"gainsboro", NSVG_RGB(220, 220, 220)}, - {"ghostwhite", NSVG_RGB(248, 248, 255)}, - {"gold", NSVG_RGB(255, 215, 0)}, - {"goldenrod", NSVG_RGB(218, 165, 32)}, - {"greenyellow", NSVG_RGB(173, 255, 47)}, - {"honeydew", NSVG_RGB(240, 255, 240)}, - {"hotpink", NSVG_RGB(255, 105, 180)}, - {"indianred", NSVG_RGB(205, 92, 92)}, - {"indigo", NSVG_RGB(75, 0, 130)}, - {"ivory", NSVG_RGB(255, 255, 240)}, - {"khaki", NSVG_RGB(240, 230, 140)}, - {"lavender", NSVG_RGB(230, 230, 250)}, - {"lavenderblush", NSVG_RGB(255, 240, 245)}, - {"lawngreen", NSVG_RGB(124, 252, 0)}, - {"lemonchiffon", NSVG_RGB(255, 250, 205)}, - {"lightblue", NSVG_RGB(173, 216, 230)}, - {"lightcoral", NSVG_RGB(240, 128, 128)}, - {"lightcyan", NSVG_RGB(224, 255, 255)}, - {"lightgoldenrodyellow", NSVG_RGB(250, 250, 210)}, - {"lightgray", NSVG_RGB(211, 211, 211)}, - {"lightgreen", NSVG_RGB(144, 238, 144)}, - {"lightgrey", NSVG_RGB(211, 211, 211)}, - {"lightpink", NSVG_RGB(255, 182, 193)}, - {"lightsalmon", NSVG_RGB(255, 160, 122)}, - {"lightseagreen", NSVG_RGB(32, 178, 170)}, - {"lightskyblue", NSVG_RGB(135, 206, 250)}, - {"lightslategray", NSVG_RGB(119, 136, 153)}, - {"lightslategrey", NSVG_RGB(119, 136, 153)}, - {"lightsteelblue", NSVG_RGB(176, 196, 222)}, - {"lightyellow", NSVG_RGB(255, 255, 224)}, - {"lime", NSVG_RGB(0, 255, 0)}, - {"limegreen", NSVG_RGB(50, 205, 50)}, - {"linen", NSVG_RGB(250, 240, 230)}, - {"maroon", NSVG_RGB(128, 0, 0)}, - {"mediumaquamarine", NSVG_RGB(102, 205, 170)}, - {"mediumblue", NSVG_RGB(0, 0, 205)}, - {"mediumorchid", NSVG_RGB(186, 85, 211)}, - {"mediumpurple", NSVG_RGB(147, 112, 219)}, - {"mediumseagreen", NSVG_RGB(60, 179, 113)}, - {"mediumslateblue", NSVG_RGB(123, 104, 238)}, - {"mediumspringgreen", NSVG_RGB(0, 250, 154)}, - {"mediumturquoise", NSVG_RGB(72, 209, 204)}, - {"mediumvioletred", NSVG_RGB(199, 21, 133)}, - {"midnightblue", NSVG_RGB(25, 25, 112)}, - {"mintcream", NSVG_RGB(245, 255, 250)}, - {"mistyrose", NSVG_RGB(255, 228, 225)}, - {"moccasin", NSVG_RGB(255, 228, 181)}, - {"navajowhite", NSVG_RGB(255, 222, 173)}, - {"navy", NSVG_RGB(0, 0, 128)}, - {"oldlace", NSVG_RGB(253, 245, 230)}, - {"olive", NSVG_RGB(128, 128, 0)}, - {"olivedrab", NSVG_RGB(107, 142, 35)}, - {"orange", NSVG_RGB(255, 165, 0)}, - {"orangered", NSVG_RGB(255, 69, 0)}, - {"orchid", NSVG_RGB(218, 112, 214)}, - {"palegoldenrod", NSVG_RGB(238, 232, 170)}, - {"palegreen", NSVG_RGB(152, 251, 152)}, - {"paleturquoise", NSVG_RGB(175, 238, 238)}, - {"palevioletred", NSVG_RGB(219, 112, 147)}, - {"papayawhip", NSVG_RGB(255, 239, 213)}, - {"peachpuff", NSVG_RGB(255, 218, 185)}, - {"peru", NSVG_RGB(205, 133, 63)}, - {"pink", NSVG_RGB(255, 192, 203)}, - {"plum", NSVG_RGB(221, 160, 221)}, - {"powderblue", NSVG_RGB(176, 224, 230)}, - {"purple", NSVG_RGB(128, 0, 128)}, - {"rosybrown", NSVG_RGB(188, 143, 143)}, - {"royalblue", NSVG_RGB(65, 105, 225)}, - {"saddlebrown", NSVG_RGB(139, 69, 19)}, - {"salmon", NSVG_RGB(250, 128, 114)}, - {"sandybrown", NSVG_RGB(244, 164, 96)}, - {"seagreen", NSVG_RGB(46, 139, 87)}, - {"seashell", NSVG_RGB(255, 245, 238)}, - {"sienna", NSVG_RGB(160, 82, 45)}, - {"silver", NSVG_RGB(192, 192, 192)}, - {"skyblue", NSVG_RGB(135, 206, 235)}, - {"slateblue", NSVG_RGB(106, 90, 205)}, - {"slategray", NSVG_RGB(112, 128, 144)}, - {"slategrey", NSVG_RGB(112, 128, 144)}, - {"snow", NSVG_RGB(255, 250, 250)}, - {"springgreen", NSVG_RGB(0, 255, 127)}, - {"steelblue", NSVG_RGB(70, 130, 180)}, - {"tan", NSVG_RGB(210, 180, 140)}, - {"teal", NSVG_RGB(0, 128, 128)}, - {"thistle", NSVG_RGB(216, 191, 216)}, - {"tomato", NSVG_RGB(255, 99, 71)}, - {"turquoise", NSVG_RGB(64, 224, 208)}, - {"violet", NSVG_RGB(238, 130, 238)}, - {"wheat", NSVG_RGB(245, 222, 179)}, - {"whitesmoke", NSVG_RGB(245, 245, 245)}, - {"yellowgreen", NSVG_RGB(154, 205, 50)}, -#endif -}; - -static unsigned int nsvg__parseColorName(const char *str) -{ - int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); - - for (i = 0; i < ncolors; i++) { - if (strcmp(nsvg__colors[i].name, str) == 0) { - return nsvg__colors[i].color; - } - } - - return NSVG_RGB(128, 128, 128); -} - -static unsigned int nsvg__parseColor(const char *str) -{ - size_t len = 0; - while (*str == ' ') - ++str; - len = strlen(str); - if (len >= 1 && *str == '#') - return nsvg__parseColorHex(str); - else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') - return nsvg__parseColorRGB(str); - return nsvg__parseColorName(str); -} - -static float nsvg__parseOpacity(const char *str) -{ - float val = nsvg__atof(str); - if (val < 0.0f) - val = 0.0f; - if (val > 1.0f) - val = 1.0f; - return val; -} - -static float nsvg__parseMiterLimit(const char *str) -{ - float val = nsvg__atof(str); - if (val < 0.0f) - val = 0.0f; - return val; -} - -static int nsvg__parseUnits(const char *units) -{ - if (units[0] == 'p' && units[1] == 'x') - return NSVG_UNITS_PX; - else if (units[0] == 'p' && units[1] == 't') - return NSVG_UNITS_PT; - else if (units[0] == 'p' && units[1] == 'c') - return NSVG_UNITS_PC; - else if (units[0] == 'm' && units[1] == 'm') - return NSVG_UNITS_MM; - else if (units[0] == 'c' && units[1] == 'm') - return NSVG_UNITS_CM; - else if (units[0] == 'i' && units[1] == 'n') - return NSVG_UNITS_IN; - else if (units[0] == '%') - return NSVG_UNITS_PERCENT; - else if (units[0] == 'e' && units[1] == 'm') - return NSVG_UNITS_EM; - else if (units[0] == 'e' && units[1] == 'x') - return NSVG_UNITS_EX; - return NSVG_UNITS_USER; -} - -static int nsvg__isCoordinate(const char *s) -{ - // optional sign - if (*s == '-' || *s == '+') - s++; - // must have at least one digit, or start by a dot - return (nsvg__isdigit(*s) || *s == '.'); -} - -static NSVGcoordinate nsvg__parseCoordinateRaw(const char *str) -{ - NSVGcoordinate coord = {0, NSVG_UNITS_USER}; - char buf[64]; - coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); - coord.value = nsvg__atof(buf); - return coord; -} - -static NSVGcoordinate nsvg__coord(float v, int units) -{ - NSVGcoordinate coord = {v, units}; - return coord; -} - -static float nsvg__parseCoordinate(NSVGparser *p, const char *str, float orig, float length) -{ - NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); - return nsvg__convertToPixels(p, coord, orig, length); -} - -static int nsvg__parseTransformArgs(const char *str, float *args, int maxNa, int *na) -{ - const char *end; - const char *ptr; - char it[64]; - - *na = 0; - ptr = str; - while (*ptr && *ptr != '(') - ++ptr; - if (*ptr == 0) - return 1; - end = ptr; - while (*end && *end != ')') - ++end; - if (*end == 0) - return 1; - - while (ptr < end) { - if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { - if (*na >= maxNa) - return 0; - ptr = nsvg__parseNumber(ptr, it, 64); - args[(*na)++] = (float)nsvg__atof(it); - } - else { - ++ptr; - } - } - return (int)(end - str); -} - -static int nsvg__parseMatrix(float *xform, const char *str) -{ - float t[6]; - int na = 0; - int len = nsvg__parseTransformArgs(str, t, 6, &na); - if (na != 6) - return len; - memcpy(xform, t, sizeof(float) * 6); - return len; -} - -static int nsvg__parseTranslate(float *xform, const char *str) -{ - float args[2]; - float t[6]; - int na = 0; - int len = nsvg__parseTransformArgs(str, args, 2, &na); - if (na == 1) - args[1] = 0.0; - - nsvg__xformSetTranslation(t, args[0], args[1]); - memcpy(xform, t, sizeof(float) * 6); - return len; -} - -static int nsvg__parseScale(float *xform, const char *str) -{ - float args[2]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 2, &na); - if (na == 1) - args[1] = args[0]; - nsvg__xformSetScale(t, args[0], args[1]); - memcpy(xform, t, sizeof(float) * 6); - return len; -} - -static int nsvg__parseSkewX(float *xform, const char *str) -{ - float args[1]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 1, &na); - nsvg__xformSetSkewX(t, args[0] / 180.0f * NSVG_PI); - memcpy(xform, t, sizeof(float) * 6); - return len; -} - -static int nsvg__parseSkewY(float *xform, const char *str) -{ - float args[1]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 1, &na); - nsvg__xformSetSkewY(t, args[0] / 180.0f * NSVG_PI); - memcpy(xform, t, sizeof(float) * 6); - return len; -} - -static int nsvg__parseRotate(float *xform, const char *str) -{ - float args[3]; - int na = 0; - float m[6]; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 3, &na); - if (na == 1) - args[1] = args[2] = 0.0f; - nsvg__xformIdentity(m); - - if (na > 1) { - nsvg__xformSetTranslation(t, -args[1], -args[2]); - nsvg__xformMultiply(m, t); - } - - nsvg__xformSetRotation(t, args[0] / 180.0f * NSVG_PI); - nsvg__xformMultiply(m, t); - - if (na > 1) { - nsvg__xformSetTranslation(t, args[1], args[2]); - nsvg__xformMultiply(m, t); - } - - memcpy(xform, m, sizeof(float) * 6); - - return len; -} - -static void nsvg__parseTransform(float *xform, const char *str) -{ - float t[6]; - int len; - nsvg__xformIdentity(xform); - while (*str) { - if (strncmp(str, "matrix", 6) == 0) - len = nsvg__parseMatrix(t, str); - else if (strncmp(str, "translate", 9) == 0) - len = nsvg__parseTranslate(t, str); - else if (strncmp(str, "scale", 5) == 0) - len = nsvg__parseScale(t, str); - else if (strncmp(str, "rotate", 6) == 0) - len = nsvg__parseRotate(t, str); - else if (strncmp(str, "skewX", 5) == 0) - len = nsvg__parseSkewX(t, str); - else if (strncmp(str, "skewY", 5) == 0) - len = nsvg__parseSkewY(t, str); - else { - ++str; - continue; - } - if (len != 0) { - str += len; - } - else { - ++str; - continue; - } - - nsvg__xformPremultiply(xform, t); - } -} - -static void nsvg__parseUrl(char *id, const char *str) -{ - int i = 0; - str += 4; // "url("; - if (*str == '#') - str++; - while (i < 63 && *str != ')') { - id[i] = *str++; - i++; - } - id[i] = '\0'; -} - -static char nsvg__parseLineCap(const char *str) -{ - if (strcmp(str, "butt") == 0) - return NSVG_CAP_BUTT; - else if (strcmp(str, "round") == 0) - return NSVG_CAP_ROUND; - else if (strcmp(str, "square") == 0) - return NSVG_CAP_SQUARE; - // TODO: handle inherit. - return NSVG_CAP_BUTT; -} - -static char nsvg__parseLineJoin(const char *str) -{ - if (strcmp(str, "miter") == 0) - return NSVG_JOIN_MITER; - else if (strcmp(str, "round") == 0) - return NSVG_JOIN_ROUND; - else if (strcmp(str, "bevel") == 0) - return NSVG_JOIN_BEVEL; - // TODO: handle inherit. - return NSVG_JOIN_MITER; -} - -static char nsvg__parseFillRule(const char *str) -{ - if (strcmp(str, "nonzero") == 0) - return NSVG_FILLRULE_NONZERO; - else if (strcmp(str, "evenodd") == 0) - return NSVG_FILLRULE_EVENODD; - // TODO: handle inherit. - return NSVG_FILLRULE_NONZERO; -} - -static const char *nsvg__getNextDashItem(const char *s, char *it) -{ - int n = 0; - it[0] = '\0'; - // Skip white spaces and commas - while (*s && (nsvg__isspace(*s) || *s == ',')) - s++; - // Advance until whitespace, comma or end. - while (*s && (!nsvg__isspace(*s) && *s != ',')) { - if (n < 63) - it[n++] = *s; - s++; - } - it[n++] = '\0'; - return s; -} - -static int nsvg__parseStrokeDashArray(NSVGparser *p, const char *str, float *strokeDashArray) -{ - char item[64]; - int count = 0, i; - float sum = 0.0f; - - // Handle "none" - if (str[0] == 'n') - return 0; - - // Parse dashes - while (*str) { - str = nsvg__getNextDashItem(str, item); - if (!*item) - break; - if (count < NSVG_MAX_DASHES) - strokeDashArray[count++] = fabsf( - nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); - } - - for (i = 0; i < count; i++) - sum += strokeDashArray[i]; - if (sum <= 1e-6f) - count = 0; - - return count; -} - -static void nsvg__parseStyle(NSVGparser *p, const char *str); - -static int nsvg__parseAttr(NSVGparser *p, const char *name, const char *value) -{ - float xform[6]; - NSVGattrib *attr = nsvg__getAttr(p); - if (!attr) - return 0; - - if (strcmp(name, "style") == 0) { - nsvg__parseStyle(p, value); - } - else if (strcmp(name, "display") == 0) { - if (strcmp(value, "none") == 0) - attr->visible = 0; - // Don't reset ->visible on display:inline, one display:none hides the whole subtree - } - else if (strcmp(name, "fill") == 0) { - if (strcmp(value, "none") == 0) { - attr->hasFill = 0; - } - else if (strncmp(value, "url(", 4) == 0) { - attr->hasFill = 2; - nsvg__parseUrl(attr->fillGradient, value); - } - else { - attr->hasFill = 1; - attr->fillColor = nsvg__parseColor(value); - } - } - else if (strcmp(name, "opacity") == 0) { - attr->opacity = nsvg__parseOpacity(value); - } - else if (strcmp(name, "fill-opacity") == 0) { - attr->fillOpacity = nsvg__parseOpacity(value); - } - else if (strcmp(name, "stroke") == 0) { - if (strcmp(value, "none") == 0) { - attr->hasStroke = 0; - } - else if (strncmp(value, "url(", 4) == 0) { - attr->hasStroke = 2; - nsvg__parseUrl(attr->strokeGradient, value); - } - else { - attr->hasStroke = 1; - attr->strokeColor = nsvg__parseColor(value); - } - } - else if (strcmp(name, "stroke-width") == 0) { - attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } - else if (strcmp(name, "stroke-dasharray") == 0) { - attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); - } - else if (strcmp(name, "stroke-dashoffset") == 0) { - attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } - else if (strcmp(name, "stroke-opacity") == 0) { - attr->strokeOpacity = nsvg__parseOpacity(value); - } - else if (strcmp(name, "stroke-linecap") == 0) { - attr->strokeLineCap = nsvg__parseLineCap(value); - } - else if (strcmp(name, "stroke-linejoin") == 0) { - attr->strokeLineJoin = nsvg__parseLineJoin(value); - } - else if (strcmp(name, "stroke-miterlimit") == 0) { - attr->miterLimit = nsvg__parseMiterLimit(value); - } - else if (strcmp(name, "fill-rule") == 0) { - attr->fillRule = nsvg__parseFillRule(value); - } - else if (strcmp(name, "font-size") == 0) { - attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } - else if (strcmp(name, "transform") == 0) { - nsvg__parseTransform(xform, value); - nsvg__xformPremultiply(attr->xform, xform); - } - else if (strcmp(name, "stop-color") == 0) { - attr->stopColor = nsvg__parseColor(value); - } - else if (strcmp(name, "stop-opacity") == 0) { - attr->stopOpacity = nsvg__parseOpacity(value); - } - else if (strcmp(name, "offset") == 0) { - attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); - } - else if (strcmp(name, "id") == 0) { - strncpy(attr->id, value, 63); - attr->id[63] = '\0'; - } - else { - return 0; - } - return 1; -} - -static int nsvg__parseNameValue(NSVGparser *p, const char *start, const char *end) -{ - const char *str; - const char *val; - char name[512]; - char value[512]; - int n; - - str = start; - while (str < end && *str != ':') - ++str; - - val = str; - - // Right Trim - while (str > start && (*str == ':' || nsvg__isspace(*str))) - --str; - ++str; - - n = (int)(str - start); - if (n > 511) - n = 511; - if (n) - memcpy(name, start, n); - name[n] = 0; - - while (val < end && (*val == ':' || nsvg__isspace(*val))) - ++val; - - n = (int)(end - val); - if (n > 511) - n = 511; - if (n) - memcpy(value, val, n); - value[n] = 0; - - return nsvg__parseAttr(p, name, value); -} - -static void nsvg__parseStyle(NSVGparser *p, const char *str) -{ - const char *start; - const char *end; - - while (*str) { - // Left Trim - while (*str && nsvg__isspace(*str)) - ++str; - start = str; - while (*str && *str != ';') - ++str; - end = str; - - // Right Trim - while (end > start && (*end == ';' || nsvg__isspace(*end))) - --end; - ++end; - - nsvg__parseNameValue(p, start, end); - if (*str) - ++str; - } -} - -static void nsvg__parseAttribs(NSVGparser *p, const char **attr) -{ - int i; - for (i = 0; attr[i]; i += 2) { - if (strcmp(attr[i], "style") == 0) - nsvg__parseStyle(p, attr[i + 1]); - else - nsvg__parseAttr(p, attr[i], attr[i + 1]); - } -} - -static int nsvg__getArgsPerElement(char cmd) -{ - switch (cmd) { - case 'v': - case 'V': - case 'h': - case 'H': - return 1; - case 'm': - case 'M': - case 'l': - case 'L': - case 't': - case 'T': - return 2; - case 'q': - case 'Q': - case 's': - case 'S': - return 4; - case 'c': - case 'C': - return 6; - case 'a': - case 'A': - return 7; - case 'z': - case 'Z': - return 0; - } - return -1; -} - -static void nsvg__pathMoveTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) -{ - if (rel) { - *cpx += args[0]; - *cpy += args[1]; - } - else { - *cpx = args[0]; - *cpy = args[1]; - } - nsvg__moveTo(p, *cpx, *cpy); -} - -static void nsvg__pathLineTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) -{ - if (rel) { - *cpx += args[0]; - *cpy += args[1]; - } - else { - *cpx = args[0]; - *cpy = args[1]; - } - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathHLineTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) -{ - if (rel) - *cpx += args[0]; - else - *cpx = args[0]; - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathVLineTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) -{ - if (rel) - *cpy += args[0]; - else - *cpy = args[0]; - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathCubicBezTo( - NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel) -{ - float x2, y2, cx1, cy1, cx2, cy2; - - if (rel) { - cx1 = *cpx + args[0]; - cy1 = *cpy + args[1]; - cx2 = *cpx + args[2]; - cy2 = *cpy + args[3]; - x2 = *cpx + args[4]; - y2 = *cpy + args[5]; - } - else { - cx1 = args[0]; - cy1 = args[1]; - cx2 = args[2]; - cy2 = args[3]; - x2 = args[4]; - y2 = args[5]; - } - - nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); - - *cpx2 = cx2; - *cpy2 = cy2; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathCubicBezShortTo( - NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel) -{ - float x1, y1, x2, y2, cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - cx2 = *cpx + args[0]; - cy2 = *cpy + args[1]; - x2 = *cpx + args[2]; - y2 = *cpy + args[3]; - } - else { - cx2 = args[0]; - cy2 = args[1]; - x2 = args[2]; - y2 = args[3]; - } - - cx1 = 2 * x1 - *cpx2; - cy1 = 2 * y1 - *cpy2; - - nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); - - *cpx2 = cx2; - *cpy2 = cy2; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathQuadBezTo( - NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel) -{ - float x1, y1, x2, y2, cx, cy; - float cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - cx = *cpx + args[0]; - cy = *cpy + args[1]; - x2 = *cpx + args[2]; - y2 = *cpy + args[3]; - } - else { - cx = args[0]; - cy = args[1]; - x2 = args[2]; - y2 = args[3]; - } - - // Convert to cubic bezier - cx1 = x1 + 2.0f / 3.0f * (cx - x1); - cy1 = y1 + 2.0f / 3.0f * (cy - y1); - cx2 = x2 + 2.0f / 3.0f * (cx - x2); - cy2 = y2 + 2.0f / 3.0f * (cy - y2); - - nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); - - *cpx2 = cx; - *cpy2 = cy; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathQuadBezShortTo( - NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel) -{ - float x1, y1, x2, y2, cx, cy; - float cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - x2 = *cpx + args[0]; - y2 = *cpy + args[1]; - } - else { - x2 = args[0]; - y2 = args[1]; - } - - cx = 2 * x1 - *cpx2; - cy = 2 * y1 - *cpy2; - - // Convert to cubix bezier - cx1 = x1 + 2.0f / 3.0f * (cx - x1); - cy1 = y1 + 2.0f / 3.0f * (cy - y1); - cx2 = x2 + 2.0f / 3.0f * (cx - x2); - cy2 = y2 + 2.0f / 3.0f * (cy - y2); - - nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); - - *cpx2 = cx; - *cpy2 = cy; - *cpx = x2; - *cpy = y2; -} - -static float nsvg__sqr(float x) -{ - return x * x; -} -static float nsvg__vmag(float x, float y) -{ - return sqrtf(x * x + y * y); -} - -static float nsvg__vecrat(float ux, float uy, float vx, float vy) -{ - return (ux * vx + uy * vy) / (nsvg__vmag(ux, uy) * nsvg__vmag(vx, vy)); -} - -static float nsvg__vecang(float ux, float uy, float vx, float vy) -{ - float r = nsvg__vecrat(ux, uy, vx, vy); - if (r < -1.0f) - r = -1.0f; - if (r > 1.0f) - r = 1.0f; - return ((ux * vy < uy * vx) ? -1.0f : 1.0f) * acosf(r); -} - -static void nsvg__pathArcTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) -{ - // Ported from canvg (https://code.google.com/p/canvg/) - float rx, ry, rotx; - float x1, y1, x2, y2, cx, cy, dx, dy, d; - float x1p, y1p, cxp, cyp, s, sa, sb; - float ux, uy, vx, vy, a1, da; - float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; - float sinrx, cosrx; - int fa, fs; - int i, ndivs; - float hda, kappa; - - rx = fabsf(args[0]); // y radius - ry = fabsf(args[1]); // x radius - rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle - fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc - fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction - x1 = *cpx; // start point - y1 = *cpy; - if (rel) { // end point - x2 = *cpx + args[5]; - y2 = *cpy + args[6]; - } - else { - x2 = args[5]; - y2 = args[6]; - } - - dx = x1 - x2; - dy = y1 - y2; - d = sqrtf(dx * dx + dy * dy); - if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { - // The arc degenerates to a line - nsvg__lineTo(p, x2, y2); - *cpx = x2; - *cpy = y2; - return; - } - - sinrx = sinf(rotx); - cosrx = cosf(rotx); - - // Convert to center point parameterization. - // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes - // 1) Compute x1', y1' - x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; - y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; - d = nsvg__sqr(x1p) / nsvg__sqr(rx) + nsvg__sqr(y1p) / nsvg__sqr(ry); - if (d > 1) { - d = sqrtf(d); - rx *= d; - ry *= d; - } - // 2) Compute cx', cy' - s = 0.0f; - sa = nsvg__sqr(rx) * nsvg__sqr(ry) - nsvg__sqr(rx) * nsvg__sqr(y1p) - - nsvg__sqr(ry) * nsvg__sqr(x1p); - sb = nsvg__sqr(rx) * nsvg__sqr(y1p) + nsvg__sqr(ry) * nsvg__sqr(x1p); - if (sa < 0.0f) - sa = 0.0f; - if (sb > 0.0f) - s = sqrtf(sa / sb); - if (fa == fs) - s = -s; - cxp = s * rx * y1p / ry; - cyp = s * -ry * x1p / rx; - - // 3) Compute cx,cy from cx',cy' - cx = (x1 + x2) / 2.0f + cosrx * cxp - sinrx * cyp; - cy = (y1 + y2) / 2.0f + sinrx * cxp + cosrx * cyp; - - // 4) Calculate theta1, and delta theta. - ux = (x1p - cxp) / rx; - uy = (y1p - cyp) / ry; - vx = (-x1p - cxp) / rx; - vy = (-y1p - cyp) / ry; - a1 = nsvg__vecang(1.0f, 0.0f, ux, uy); // Initial angle - da = nsvg__vecang(ux, uy, vx, vy); // Delta angle - - // if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; - // if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; - - if (fs == 0 && da > 0) - da -= 2 * NSVG_PI; - else if (fs == 1 && da < 0) - da += 2 * NSVG_PI; - - // Approximate the arc using cubic spline segments. - t[0] = cosrx; - t[1] = sinrx; - t[2] = -sinrx; - t[3] = cosrx; - t[4] = cx; - t[5] = cy; - - // Split arc into max 90 degree segments. - // The loop assumes an iteration per end point (including start and end), this +1. - ndivs = (int)(fabsf(da) / (NSVG_PI * 0.5f) + 1.0f); - hda = (da / (float)ndivs) / 2.0f; - // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite) - if ((hda < 1e-3f) && (hda > -1e-3f)) - hda *= 0.5f; - else - hda = (1.0f - cosf(hda)) / sinf(hda); - kappa = fabsf(4.0f / 3.0f * hda); - if (da < 0.0f) - kappa = -kappa; - - for (i = 0; i <= ndivs; i++) { - a = a1 + da * ((float)i / (float)ndivs); - dx = cosf(a); - dy = sinf(a); - nsvg__xformPoint(&x, &y, dx * rx, dy * ry, t); // position - nsvg__xformVec(&tanx, &tany, -dy * rx * kappa, dx * ry * kappa, t); // tangent - if (i > 0) - nsvg__cubicBezTo(p, px + ptanx, py + ptany, x - tanx, y - tany, x, y); - px = x; - py = y; - ptanx = tanx; - ptany = tany; - } - - *cpx = x2; - *cpy = y2; -} - -static void nsvg__parsePath(NSVGparser *p, const char **attr) -{ - const char *s = NULL; - char cmd = '\0'; - float args[10]; - int nargs; - int rargs = 0; - char initPoint; - float cpx, cpy, cpx2, cpy2; - const char *tmp[4]; - char closedFlag; - int i; - char item[64]; - - for (i = 0; attr[i]; i += 2) { - if (strcmp(attr[i], "d") == 0) { - s = attr[i + 1]; - } - else { - tmp[0] = attr[i]; - tmp[1] = attr[i + 1]; - tmp[2] = 0; - tmp[3] = 0; - nsvg__parseAttribs(p, tmp); - } - } - - if (s) { - nsvg__resetPath(p); - cpx = 0; - cpy = 0; - cpx2 = 0; - cpy2 = 0; - initPoint = 0; - closedFlag = 0; - nargs = 0; - - while (*s) { - s = nsvg__getNextPathItem(s, item, cmd, nargs); - if (!*item) - break; - if (cmd != '\0' && nsvg__isCoordinate(item)) { - if (nargs < 10) - args[nargs++] = (float)nsvg__atof(item); - if (nargs >= rargs) { - switch (cmd) { - case 'm': - case 'M': - nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); - // Moveto can be followed by multiple coordinate pairs, - // which should be treated as linetos. - cmd = (cmd == 'm') ? 'l' : 'L'; - rargs = nsvg__getArgsPerElement(cmd); - cpx2 = cpx; - cpy2 = cpy; - initPoint = 1; - break; - case 'l': - case 'L': - nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); - cpx2 = cpx; - cpy2 = cpy; - break; - case 'H': - case 'h': - nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); - cpx2 = cpx; - cpy2 = cpy; - break; - case 'V': - case 'v': - nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); - cpx2 = cpx; - cpy2 = cpy; - break; - case 'C': - case 'c': - nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); - break; - case 'S': - case 's': - nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); - break; - case 'Q': - case 'q': - nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); - break; - case 'T': - case 't': - nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); - break; - case 'A': - case 'a': - nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); - cpx2 = cpx; - cpy2 = cpy; - break; - default: - if (nargs >= 2) { - cpx = args[nargs - 2]; - cpy = args[nargs - 1]; - cpx2 = cpx; - cpy2 = cpy; - } - break; - } - nargs = 0; - } - } - else { - cmd = item[0]; - if (cmd == 'M' || cmd == 'm') { - // Commit path. - if (p->npts > 0) - nsvg__addPath(p, closedFlag); - // Start new subpath. - nsvg__resetPath(p); - closedFlag = 0; - nargs = 0; - } - else if (initPoint == 0) { - // Do not allow other commands until initial point has been set (moveTo called once). - cmd = '\0'; - } - if (cmd == 'Z' || cmd == 'z') { - closedFlag = 1; - // Commit path. - if (p->npts > 0) { - // Move current point to first point - cpx = p->pts[0]; - cpy = p->pts[1]; - cpx2 = cpx; - cpy2 = cpy; - nsvg__addPath(p, closedFlag); - } - // Start new subpath. - nsvg__resetPath(p); - nsvg__moveTo(p, cpx, cpy); - closedFlag = 0; - nargs = 0; - } - rargs = nsvg__getArgsPerElement(cmd); - if (rargs == -1) { - // Command not recognized - cmd = '\0'; - rargs = 0; - } - } - } - // Commit path. - if (p->npts) - nsvg__addPath(p, closedFlag); - } - - nsvg__addShape(p); -} - -static void nsvg__parseRect(NSVGparser *p, const char **attr) -{ - float x = 0.0f; - float y = 0.0f; - float w = 0.0f; - float h = 0.0f; - float rx = -1.0f; // marks not set - float ry = -1.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "x") == 0) - x = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y") == 0) - y = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "width") == 0) - w = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualWidth(p)); - if (strcmp(attr[i], "height") == 0) - h = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualHeight(p)); - if (strcmp(attr[i], "rx") == 0) - rx = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualWidth(p))); - if (strcmp(attr[i], "ry") == 0) - ry = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualHeight(p))); - } - } - - if (rx < 0.0f && ry > 0.0f) - rx = ry; - if (ry < 0.0f && rx > 0.0f) - ry = rx; - if (rx < 0.0f) - rx = 0.0f; - if (ry < 0.0f) - ry = 0.0f; - if (rx > w / 2.0f) - rx = w / 2.0f; - if (ry > h / 2.0f) - ry = h / 2.0f; - - if (w != 0.0f && h != 0.0f) { - nsvg__resetPath(p); - - if (rx < 0.00001f || ry < 0.0001f) { - nsvg__moveTo(p, x, y); - nsvg__lineTo(p, x + w, y); - nsvg__lineTo(p, x + w, y + h); - nsvg__lineTo(p, x, y + h); - } - else { - // Rounded rectangle - nsvg__moveTo(p, x + rx, y); - nsvg__lineTo(p, x + w - rx, y); - nsvg__cubicBezTo(p, - x + w - rx * (1 - NSVG_KAPPA90), - y, - x + w, - y + ry * (1 - NSVG_KAPPA90), - x + w, - y + ry); - nsvg__lineTo(p, x + w, y + h - ry); - nsvg__cubicBezTo(p, - x + w, - y + h - ry * (1 - NSVG_KAPPA90), - x + w - rx * (1 - NSVG_KAPPA90), - y + h, - x + w - rx, - y + h); - nsvg__lineTo(p, x + rx, y + h); - nsvg__cubicBezTo(p, - x + rx * (1 - NSVG_KAPPA90), - y + h, - x, - y + h - ry * (1 - NSVG_KAPPA90), - x, - y + h - ry); - nsvg__lineTo(p, x, y + ry); - nsvg__cubicBezTo( - p, x, y + ry * (1 - NSVG_KAPPA90), x + rx * (1 - NSVG_KAPPA90), y, x + rx, y); - } - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseCircle(NSVGparser *p, const char **attr) -{ - float cx = 0.0f; - float cy = 0.0f; - float r = 0.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "cx") == 0) - cx = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "cy") == 0) - cy = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "r") == 0) - r = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualLength(p))); - } - } - - if (r > 0.0f) { - nsvg__resetPath(p); - - nsvg__moveTo(p, cx + r, cy); - nsvg__cubicBezTo(p, cx + r, cy + r * NSVG_KAPPA90, cx + r * NSVG_KAPPA90, cy + r, cx, cy + r); - nsvg__cubicBezTo(p, cx - r * NSVG_KAPPA90, cy + r, cx - r, cy + r * NSVG_KAPPA90, cx - r, cy); - nsvg__cubicBezTo(p, cx - r, cy - r * NSVG_KAPPA90, cx - r * NSVG_KAPPA90, cy - r, cx, cy - r); - nsvg__cubicBezTo(p, cx + r * NSVG_KAPPA90, cy - r, cx + r, cy - r * NSVG_KAPPA90, cx + r, cy); - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseEllipse(NSVGparser *p, const char **attr) -{ - float cx = 0.0f; - float cy = 0.0f; - float rx = 0.0f; - float ry = 0.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "cx") == 0) - cx = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "cy") == 0) - cy = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "rx") == 0) - rx = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualWidth(p))); - if (strcmp(attr[i], "ry") == 0) - ry = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualHeight(p))); - } - } - - if (rx > 0.0f && ry > 0.0f) { - - nsvg__resetPath(p); - - nsvg__moveTo(p, cx + rx, cy); - nsvg__cubicBezTo( - p, cx + rx, cy + ry * NSVG_KAPPA90, cx + rx * NSVG_KAPPA90, cy + ry, cx, cy + ry); - nsvg__cubicBezTo( - p, cx - rx * NSVG_KAPPA90, cy + ry, cx - rx, cy + ry * NSVG_KAPPA90, cx - rx, cy); - nsvg__cubicBezTo( - p, cx - rx, cy - ry * NSVG_KAPPA90, cx - rx * NSVG_KAPPA90, cy - ry, cx, cy - ry); - nsvg__cubicBezTo( - p, cx + rx * NSVG_KAPPA90, cy - ry, cx + rx, cy - ry * NSVG_KAPPA90, cx + rx, cy); - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseLine(NSVGparser *p, const char **attr) -{ - float x1 = 0.0; - float y1 = 0.0; - float x2 = 0.0; - float y2 = 0.0; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "x1") == 0) - x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y1") == 0) - y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "x2") == 0) - x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y2") == 0) - y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - } - } - - nsvg__resetPath(p); - - nsvg__moveTo(p, x1, y1); - nsvg__lineTo(p, x2, y2); - - nsvg__addPath(p, 0); - - nsvg__addShape(p); -} - -static void nsvg__parsePoly(NSVGparser *p, const char **attr, int closeFlag) -{ - int i; - const char *s; - float args[2]; - int nargs, npts = 0; - char item[64]; - - nsvg__resetPath(p); - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "points") == 0) { - s = attr[i + 1]; - nargs = 0; - while (*s) { - s = nsvg__getNextPathItem(s, item, '\0', nargs); - args[nargs++] = (float)nsvg__atof(item); - if (nargs >= 2) { - if (npts == 0) - nsvg__moveTo(p, args[0], args[1]); - else - nsvg__lineTo(p, args[0], args[1]); - nargs = 0; - npts++; - } - } - } - } - } - - nsvg__addPath(p, (char)closeFlag); - - nsvg__addShape(p); -} - -static void nsvg__parseSVG(NSVGparser *p, const char **attr) -{ - int i; - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "width") == 0) { - p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); - } - else if (strcmp(attr[i], "height") == 0) { - p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); - } - else if (strcmp(attr[i], "viewBox") == 0) { - const char *s = attr[i + 1]; - char buf[64]; - s = nsvg__parseNumber(s, buf, 64); - p->viewMinx = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) - s++; - if (!*s) - return; - s = nsvg__parseNumber(s, buf, 64); - p->viewMiny = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) - s++; - if (!*s) - return; - s = nsvg__parseNumber(s, buf, 64); - p->viewWidth = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) - s++; - if (!*s) - return; - s = nsvg__parseNumber(s, buf, 64); - p->viewHeight = nsvg__atof(buf); - } - else if (strcmp(attr[i], "preserveAspectRatio") == 0) { - if (strstr(attr[i + 1], "none") != 0) { - // No uniform scaling - p->alignType = NSVG_ALIGN_NONE; - } - else { - // Parse X align - if (strstr(attr[i + 1], "xMin") != 0) - p->alignX = NSVG_ALIGN_MIN; - else if (strstr(attr[i + 1], "xMid") != 0) - p->alignX = NSVG_ALIGN_MID; - else if (strstr(attr[i + 1], "xMax") != 0) - p->alignX = NSVG_ALIGN_MAX; - // Parse X align - if (strstr(attr[i + 1], "yMin") != 0) - p->alignY = NSVG_ALIGN_MIN; - else if (strstr(attr[i + 1], "yMid") != 0) - p->alignY = NSVG_ALIGN_MID; - else if (strstr(attr[i + 1], "yMax") != 0) - p->alignY = NSVG_ALIGN_MAX; - // Parse meet/slice - p->alignType = NSVG_ALIGN_MEET; - if (strstr(attr[i + 1], "slice") != 0) - p->alignType = NSVG_ALIGN_SLICE; - } - } - } - } -} - -static void nsvg__parseGradient(NSVGparser *p, const char **attr, char type) -{ - int i; - NSVGgradientData *grad = (NSVGgradientData *)malloc(sizeof(NSVGgradientData)); - if (grad == NULL) - return; - memset(grad, 0, sizeof(NSVGgradientData)); - grad->units = NSVG_OBJECT_SPACE; - grad->type = type; - if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { - grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); - grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - } - else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { - grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - } - - nsvg__xformIdentity(grad->xform); - - for (i = 0; attr[i]; i += 2) { - if (strcmp(attr[i], "id") == 0) { - strncpy(grad->id, attr[i + 1], 63); - grad->id[63] = '\0'; - } - else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "gradientUnits") == 0) { - if (strcmp(attr[i + 1], "objectBoundingBox") == 0) - grad->units = NSVG_OBJECT_SPACE; - else - grad->units = NSVG_USER_SPACE; - } - else if (strcmp(attr[i], "gradientTransform") == 0) { - nsvg__parseTransform(grad->xform, attr[i + 1]); - } - else if (strcmp(attr[i], "cx") == 0) { - grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "cy") == 0) { - grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "r") == 0) { - grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "fx") == 0) { - grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "fy") == 0) { - grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "x1") == 0) { - grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "y1") == 0) { - grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "x2") == 0) { - grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "y2") == 0) { - grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "spreadMethod") == 0) { - if (strcmp(attr[i + 1], "pad") == 0) - grad->spread = NSVG_SPREAD_PAD; - else if (strcmp(attr[i + 1], "reflect") == 0) - grad->spread = NSVG_SPREAD_REFLECT; - else if (strcmp(attr[i + 1], "repeat") == 0) - grad->spread = NSVG_SPREAD_REPEAT; - } - else if (strcmp(attr[i], "xlink:href") == 0) { - const char *href = attr[i + 1]; - strncpy(grad->ref, href + 1, 62); - grad->ref[62] = '\0'; - } - } - } - - grad->next = p->gradients; - p->gradients = grad; -} - -static void nsvg__parseGradientStop(NSVGparser *p, const char **attr) -{ - NSVGattrib *curAttr = nsvg__getAttr(p); - NSVGgradientData *grad; - NSVGgradientStop *stop; - int i, idx; - - curAttr->stopOffset = 0; - curAttr->stopColor = 0; - curAttr->stopOpacity = 1.0f; - - for (i = 0; attr[i]; i += 2) { - nsvg__parseAttr(p, attr[i], attr[i + 1]); - } - - // Add stop to the last gradient. - grad = p->gradients; - if (grad == NULL) - return; - - grad->nstops++; - grad->stops = (NSVGgradientStop *)realloc(grad->stops, sizeof(NSVGgradientStop) * grad->nstops); - if (grad->stops == NULL) - return; - - // Insert - idx = grad->nstops - 1; - for (i = 0; i < grad->nstops - 1; i++) { - if (curAttr->stopOffset < grad->stops[i].offset) { - idx = i; - break; - } - } - if (idx != grad->nstops - 1) { - for (i = grad->nstops - 1; i > idx; i--) - grad->stops[i] = grad->stops[i - 1]; - } - - stop = &grad->stops[idx]; - stop->color = curAttr->stopColor; - stop->color |= (unsigned int)(curAttr->stopOpacity * 255) << 24; - stop->offset = curAttr->stopOffset; -} - -static void nsvg__startElement(void *ud, const char *el, const char **attr) -{ - NSVGparser *p = (NSVGparser *)ud; - - if (p->defsFlag) { - // Skip everything but gradients in defs - if (strcmp(el, "linearGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); - } - else if (strcmp(el, "radialGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); - } - else if (strcmp(el, "stop") == 0) { - nsvg__parseGradientStop(p, attr); - } - return; - } - - if (strcmp(el, "g") == 0) { - nsvg__pushAttr(p); - nsvg__parseAttribs(p, attr); - - /* Save the breadcrumb of groups. */ - if (p->breadcrumb_len < NSVG_MAX_BREADCRUMB) { - NSVGattrib *attr_id = nsvg__getAttr(p); - memcpy( - p->breadcrumb[p->breadcrumb_len], attr_id->id, sizeof(p->breadcrumb[p->breadcrumb_len])); - p->breadcrumb_len++; - } - } - else if (strcmp(el, "path") == 0) { - if (p->pathFlag) // Do not allow nested paths. - return; - nsvg__pushAttr(p); - nsvg__parsePath(p, attr); - nsvg__popAttr(p); - } - else if (strcmp(el, "rect") == 0) { - nsvg__pushAttr(p); - nsvg__parseRect(p, attr); - nsvg__popAttr(p); - } - else if (strcmp(el, "circle") == 0) { - nsvg__pushAttr(p); - nsvg__parseCircle(p, attr); - nsvg__popAttr(p); - } - else if (strcmp(el, "ellipse") == 0) { - nsvg__pushAttr(p); - nsvg__parseEllipse(p, attr); - nsvg__popAttr(p); - } - else if (strcmp(el, "line") == 0) { - nsvg__pushAttr(p); - nsvg__parseLine(p, attr); - nsvg__popAttr(p); - } - else if (strcmp(el, "polyline") == 0) { - nsvg__pushAttr(p); - nsvg__parsePoly(p, attr, 0); - nsvg__popAttr(p); - } - else if (strcmp(el, "polygon") == 0) { - nsvg__pushAttr(p); - nsvg__parsePoly(p, attr, 1); - nsvg__popAttr(p); - } - else if (strcmp(el, "linearGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); - } - else if (strcmp(el, "radialGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); - } - else if (strcmp(el, "stop") == 0) { - nsvg__parseGradientStop(p, attr); - } - else if (strcmp(el, "defs") == 0) { - p->defsFlag = 1; - } - else if (strcmp(el, "svg") == 0) { - nsvg__parseSVG(p, attr); - } -} - -static void nsvg__endElement(void *ud, const char *el) -{ - NSVGparser *p = (NSVGparser *)ud; - - if (strcmp(el, "g") == 0) { - /* Remove the breadcrumb level. */ - if (p->breadcrumb_len > 0) { - p->breadcrumb[p->breadcrumb_len - 1][0] = '\0'; - p->breadcrumb_len--; - } - - nsvg__popAttr(p); - } - else if (strcmp(el, "path") == 0) { - p->pathFlag = 0; - } - else if (strcmp(el, "defs") == 0) { - p->defsFlag = 0; - } -} - -static void nsvg__content(void *ud, const char *s) -{ - NSVG_NOTUSED(ud); - NSVG_NOTUSED(s); - // empty -} - -static void nsvg__imageBounds(NSVGparser *p, float *bounds) -{ - NSVGshape *shape; - shape = p->image->shapes; - if (shape == NULL) { - bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; - return; - } - bounds[0] = shape->bounds[0]; - bounds[1] = shape->bounds[1]; - bounds[2] = shape->bounds[2]; - bounds[3] = shape->bounds[3]; - for (shape = shape->next; shape != NULL; shape = shape->next) { - bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); - bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); - bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); - bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); - } -} - -static float nsvg__viewAlign(float content, float container, int type) -{ - if (type == NSVG_ALIGN_MIN) - return 0; - else if (type == NSVG_ALIGN_MAX) - return container - content; - // mid - return (container - content) * 0.5f; -} - -static void nsvg__scaleGradient(NSVGgradient *grad, float tx, float ty, float sx, float sy) -{ - float t[6]; - nsvg__xformSetTranslation(t, tx, ty); - nsvg__xformMultiply(grad->xform, t); - - nsvg__xformSetScale(t, sx, sy); - nsvg__xformMultiply(grad->xform, t); -} - -static void nsvg__scaleToViewbox(NSVGparser *p, const char *units) -{ - NSVGshape *shape; - NSVGpath *path; - float tx, ty, sx, sy, us, bounds[4], t[6], avgs; - int i; - float *pt; - - // Guess image size if not set completely. - nsvg__imageBounds(p, bounds); - - if (p->viewWidth == 0) { - if (p->image->width > 0) { - p->viewWidth = p->image->width; - } - else { - p->viewMinx = bounds[0]; - p->viewWidth = bounds[2] - bounds[0]; - } - } - if (p->viewHeight == 0) { - if (p->image->height > 0) { - p->viewHeight = p->image->height; - } - else { - p->viewMiny = bounds[1]; - p->viewHeight = bounds[3] - bounds[1]; - } - } - if (p->image->width == 0) - p->image->width = p->viewWidth; - if (p->image->height == 0) - p->image->height = p->viewHeight; - - tx = -p->viewMinx; - ty = -p->viewMiny; - sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; - sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; - // Unit scaling - us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); - - // Fix aspect ratio - if (p->alignType == NSVG_ALIGN_MEET) { - // fit whole image into viewbox - sx = sy = nsvg__minf(sx, sy); - tx += nsvg__viewAlign(p->viewWidth * sx, p->image->width, p->alignX) / sx; - ty += nsvg__viewAlign(p->viewHeight * sy, p->image->height, p->alignY) / sy; - } - else if (p->alignType == NSVG_ALIGN_SLICE) { - // fill whole viewbox with image - sx = sy = nsvg__maxf(sx, sy); - tx += nsvg__viewAlign(p->viewWidth * sx, p->image->width, p->alignX) / sx; - ty += nsvg__viewAlign(p->viewHeight * sy, p->image->height, p->alignY) / sy; - } - - // Transform - sx *= us; - sy *= us; - avgs = (sx + sy) / 2.0f; - for (shape = p->image->shapes; shape != NULL; shape = shape->next) { - shape->bounds[0] = (shape->bounds[0] + tx) * sx; - shape->bounds[1] = (shape->bounds[1] + ty) * sy; - shape->bounds[2] = (shape->bounds[2] + tx) * sx; - shape->bounds[3] = (shape->bounds[3] + ty) * sy; - for (path = shape->paths; path != NULL; path = path->next) { - path->bounds[0] = (path->bounds[0] + tx) * sx; - path->bounds[1] = (path->bounds[1] + ty) * sy; - path->bounds[2] = (path->bounds[2] + tx) * sx; - path->bounds[3] = (path->bounds[3] + ty) * sy; - for (i = 0; i < path->npts; i++) { - pt = &path->pts[i * 2]; - pt[0] = (pt[0] + tx) * sx; - pt[1] = (pt[1] + ty) * sy; - } - } - - if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || - shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { - nsvg__scaleGradient(shape->fill.gradient, tx, ty, sx, sy); - memcpy(t, shape->fill.gradient->xform, sizeof(float) * 6); - nsvg__xformInverse(shape->fill.gradient->xform, t); - } - if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || - shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { - nsvg__scaleGradient(shape->stroke.gradient, tx, ty, sx, sy); - memcpy(t, shape->stroke.gradient->xform, sizeof(float) * 6); - nsvg__xformInverse(shape->stroke.gradient->xform, t); - } - - shape->strokeWidth *= avgs; - shape->strokeDashOffset *= avgs; - for (i = 0; i < shape->strokeDashCount; i++) - shape->strokeDashArray[i] *= avgs; - } -} - -NSVGimage *nsvgParse(char *input, const char *units, float dpi) -{ - NSVGparser *p; - NSVGimage *ret = 0; - - p = nsvg__createParser(); - if (p == NULL) { - return NULL; - } - p->dpi = dpi; - - nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); - - // Scale to viewBox - nsvg__scaleToViewbox(p, units); - - ret = p->image; - p->image = NULL; - - nsvg__deleteParser(p); - - return ret; -} - -NSVGimage *nsvgParseFromFile(const char *filename, const char *units, float dpi) -{ - FILE *fp = NULL; - size_t size; - char *data = NULL; - NSVGimage *image = NULL; - - fp = fopen(filename, "rb"); - if (!fp) - goto error; - fseek(fp, 0, SEEK_END); - size = ftell(fp); - fseek(fp, 0, SEEK_SET); - data = (char *)malloc(size + 1); - if (data == NULL) - goto error; - if (fread(data, 1, size, fp) != size) - goto error; - data[size] = '\0'; // Must be null terminated. - fclose(fp); - image = nsvgParse(data, units, dpi); - free(data); - - return image; - -error: - if (fp) - fclose(fp); - if (data) - free(data); - if (image) - nsvgDelete(image); - return NULL; -} - -NSVGpath *nsvgDuplicatePath(NSVGpath *p) -{ - NSVGpath *res = NULL; - - if (p == NULL) - return NULL; - - res = (NSVGpath *)malloc(sizeof(NSVGpath)); - if (res == NULL) - goto error; - memset(res, 0, sizeof(NSVGpath)); - - res->pts = (float *)malloc(p->npts * 2 * sizeof(float)); - if (res->pts == NULL) - goto error; - memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); - res->npts = p->npts; - - memcpy(res->bounds, p->bounds, sizeof(p->bounds)); - - res->closed = p->closed; - - return res; - -error: - if (res != NULL) { - free(res->pts); - free(res); - } - return NULL; -} - -void nsvgDelete(NSVGimage *image) -{ - NSVGshape *snext, *shape; - if (image == NULL) - return; - shape = image->shapes; - while (shape != NULL) { - snext = shape->next; - nsvg__deletePaths(shape->paths); - nsvg__deletePaint(&shape->fill); - nsvg__deletePaint(&shape->stroke); - free(shape); - shape = snext; - } - free(image); -} - -#endif -- cgit v1.2.3 From 335379d8fc70b2ce64b1f84862b2895494b5fe11 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 6 Aug 2021 15:32:28 +0200 Subject: Cleanup: Add missing newline at end of file --- extern/nanosvg/README.blender | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/nanosvg/README.blender b/extern/nanosvg/README.blender index 3772cc106c8..701b5100ca5 100644 --- a/extern/nanosvg/README.blender +++ b/extern/nanosvg/README.blender @@ -4,4 +4,4 @@ License: zlib Upstream version: Local modifications: Added some functionality to manage grease pencil layers -Added a fix to SVG import arc and float errors (https://developer.blender.org/rB11dc674c78b49fc4e0b7c134c375b6c8b8eacbcc) \ No newline at end of file +Added a fix to SVG import arc and float errors (https://developer.blender.org/rB11dc674c78b49fc4e0b7c134c375b6c8b8eacbcc) -- cgit v1.2.3 From 69c9363e39957a47f7d18e8a24d048cbb99df3b5 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 6 Aug 2021 15:53:05 +0200 Subject: Cleanup: Use conventional naming for private Session members Makes it consistent with the guidelines and the Cycles X branch, and allows to backport fix for the viewport update from the branch. Will cause a merge conflict, which should be simple accept-ours in the branch. --- intern/cycles/render/session.cpp | 252 +++++++++++++++++++-------------------- intern/cycles/render/session.h | 42 +++---- 2 files changed, 147 insertions(+), 147 deletions(-) diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 19d4a66353d..6b50522b660 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -57,23 +57,23 @@ Session::Session(const SessionParams ¶ms_) stats(), profiler() { - device_use_gl = ((params.device.type != DEVICE_CPU) && !params.background); + device_use_gl_ = ((params.device.type != DEVICE_CPU) && !params.background); TaskScheduler::init(params.threads); - session_thread = NULL; + session_thread_ = NULL; scene = NULL; - reset_time = 0.0; - last_update_time = 0.0; + reset_time_ = 0.0; + last_update_time_ = 0.0; - delayed_reset.do_reset = false; - delayed_reset.samples = 0; + delayed_reset_.do_reset = false; + delayed_reset_.samples = 0; - display_outdated = false; - gpu_draw_ready = false; - gpu_need_display_buffer_update = false; - pause = false; + display_outdated_ = false; + gpu_draw_ready_ = false; + gpu_need_display_buffer_update_ = false; + pause_ = false; buffers = NULL; display = NULL; @@ -127,25 +127,25 @@ Session::~Session() void Session::start() { - if (!session_thread) { - session_thread = new thread(function_bind(&Session::run, this)); + if (!session_thread_) { + session_thread_ = new thread(function_bind(&Session::run, this)); } } void Session::cancel() { - if (session_thread) { + if (session_thread_) { /* wait for session thread to end */ progress.set_cancel("Exiting"); - gpu_need_display_buffer_update = false; - gpu_need_display_buffer_update_cond.notify_all(); + gpu_need_display_buffer_update_ = false; + gpu_need_display_buffer_update_cond_.notify_all(); { - thread_scoped_lock pause_lock(pause_mutex); - pause = false; + thread_scoped_lock pause_lock(pause_mutex_); + pause_ = false; } - pause_cond.notify_all(); + pause_cond_.notify_all(); wait(); } @@ -153,9 +153,9 @@ void Session::cancel() bool Session::ready_to_reset() { - double dt = time_dt() - reset_time; + double dt = time_dt() - reset_time_; - if (!display_outdated) + if (!display_outdated_) return (dt > params.reset_timeout); else return (dt > params.cancel_timeout); @@ -165,48 +165,48 @@ bool Session::ready_to_reset() void Session::reset_gpu(BufferParams &buffer_params, int samples) { - thread_scoped_lock pause_lock(pause_mutex); + thread_scoped_lock pause_lock(pause_mutex_); /* block for buffer access and reset immediately. we can't do this * in the thread, because we need to allocate an OpenGL buffer, and * that only works in the main thread */ - thread_scoped_lock display_lock(display_mutex); - thread_scoped_lock buffers_lock(buffers_mutex); + thread_scoped_lock display_lock(display_mutex_); + thread_scoped_lock buffers_lock(buffers_mutex_); - display_outdated = true; - reset_time = time_dt(); + display_outdated_ = true; + reset_time_ = time_dt(); reset_(buffer_params, samples); - gpu_need_display_buffer_update = false; - gpu_need_display_buffer_update_cond.notify_all(); + gpu_need_display_buffer_update_ = false; + gpu_need_display_buffer_update_cond_.notify_all(); - pause_cond.notify_all(); + pause_cond_.notify_all(); } bool Session::draw_gpu(BufferParams &buffer_params, DeviceDrawParams &draw_params) { /* block for buffer access */ - thread_scoped_lock display_lock(display_mutex); + thread_scoped_lock display_lock(display_mutex_); /* first check we already rendered something */ - if (gpu_draw_ready) { + if (gpu_draw_ready_) { /* then verify the buffers have the expected size, so we don't * draw previous results in a resized window */ if (buffer_params.width == display->params.width && buffer_params.height == display->params.height) { /* for CUDA we need to do tone-mapping still, since we can * only access GL buffers from the main thread. */ - if (gpu_need_display_buffer_update) { - thread_scoped_lock buffers_lock(buffers_mutex); + if (gpu_need_display_buffer_update_) { + thread_scoped_lock buffers_lock(buffers_mutex_); copy_to_display_buffer(tile_manager.state.sample); - gpu_need_display_buffer_update = false; - gpu_need_display_buffer_update_cond.notify_all(); + gpu_need_display_buffer_update_ = false; + gpu_need_display_buffer_update_cond_.notify_all(); } display->draw(device, draw_params); - if (display_outdated && (time_dt() - reset_time) > params.text_timeout) + if (display_outdated_ && (time_dt() - reset_time_) > params.text_timeout) return false; return true; @@ -220,9 +220,9 @@ void Session::run_gpu() { bool tiles_written = false; - reset_time = time_dt(); - last_update_time = time_dt(); - last_display_time = last_update_time; + reset_time_ = time_dt(); + last_update_time_ = time_dt(); + last_display_time_ = last_update_time_; progress.set_render_start_time(); @@ -255,7 +255,7 @@ void Session::run_gpu() /* buffers mutex is locked entirely while rendering each * sample, and released/reacquired on each iteration to allow * reset and draw in between */ - thread_scoped_lock buffers_lock(buffers_mutex); + thread_scoped_lock buffers_lock(buffers_mutex_); /* update status and timing */ update_status_time(); @@ -273,17 +273,17 @@ void Session::run_gpu() /* update status and timing */ update_status_time(); - gpu_need_display_buffer_update = !delayed_denoise; - gpu_draw_ready = true; + gpu_need_display_buffer_update_ = !delayed_denoise; + gpu_draw_ready_ = true; progress.set_update(); /* wait for until display buffer is updated */ if (!params.background) { - while (gpu_need_display_buffer_update) { + while (gpu_need_display_buffer_update_) { if (progress.get_cancel()) break; - gpu_need_display_buffer_update_cond.wait(buffers_lock); + gpu_need_display_buffer_update_cond_.wait(buffers_lock); } } @@ -305,23 +305,23 @@ void Session::run_gpu() void Session::reset_cpu(BufferParams &buffer_params, int samples) { - thread_scoped_lock reset_lock(delayed_reset.mutex); - thread_scoped_lock pause_lock(pause_mutex); + thread_scoped_lock reset_lock(delayed_reset_.mutex); + thread_scoped_lock pause_lock(pause_mutex_); - display_outdated = true; - reset_time = time_dt(); + display_outdated_ = true; + reset_time_ = time_dt(); - delayed_reset.params = buffer_params; - delayed_reset.samples = samples; - delayed_reset.do_reset = true; + delayed_reset_.params = buffer_params; + delayed_reset_.samples = samples; + delayed_reset_.do_reset = true; device->task_cancel(); - pause_cond.notify_all(); + pause_cond_.notify_all(); } bool Session::draw_cpu(BufferParams &buffer_params, DeviceDrawParams &draw_params) { - thread_scoped_lock display_lock(display_mutex); + thread_scoped_lock display_lock(display_mutex_); /* first check we already rendered something */ if (display->draw_ready()) { @@ -331,7 +331,7 @@ bool Session::draw_cpu(BufferParams &buffer_params, DeviceDrawParams &draw_param buffer_params.height == display->params.height) { display->draw(device, draw_params); - if (display_outdated && (time_dt() - reset_time) > params.text_timeout) + if (display_outdated_ && (time_dt() - reset_time_) > params.text_timeout) return false; return true; @@ -345,46 +345,46 @@ bool Session::steal_tile(RenderTile &rtile, Device *tile_device, thread_scoped_l { /* Devices that can get their tiles stolen don't steal tiles themselves. * Additionally, if there are no stealable tiles in flight, give up here. */ - if (tile_device->info.type == DEVICE_CPU || stealable_tiles == 0) { + if (tile_device->info.type == DEVICE_CPU || stealable_tiles_ == 0) { return false; } /* Wait until no other thread is trying to steal a tile. */ - while (tile_stealing_state != NOT_STEALING && stealable_tiles > 0) { + while (tile_stealing_state_ != NOT_STEALING && stealable_tiles_ > 0) { /* Someone else is currently trying to get a tile. * Wait on the condition variable and try later. */ - tile_steal_cond.wait(tile_lock); + tile_steal_cond_.wait(tile_lock); } /* If another thread stole the last stealable tile in the meantime, give up. */ - if (stealable_tiles == 0) { + if (stealable_tiles_ == 0) { return false; } /* There are stealable tiles in flight, so signal that one should be released. */ - tile_stealing_state = WAITING_FOR_TILE; + tile_stealing_state_ = WAITING_FOR_TILE; /* Wait until a device notices the signal and releases its tile. */ - while (tile_stealing_state != GOT_TILE && stealable_tiles > 0) { - tile_steal_cond.wait(tile_lock); + while (tile_stealing_state_ != GOT_TILE && stealable_tiles_ > 0) { + tile_steal_cond_.wait(tile_lock); } /* If the last stealable tile finished on its own, give up. */ - if (tile_stealing_state != GOT_TILE) { - tile_stealing_state = NOT_STEALING; + if (tile_stealing_state_ != GOT_TILE) { + tile_stealing_state_ = NOT_STEALING; return false; } /* Successfully stole a tile, now move it to the new device. */ - rtile = stolen_tile; + rtile = stolen_tile_; rtile.buffers->buffer.move_device(tile_device); rtile.buffer = rtile.buffers->buffer.device_pointer; rtile.stealing_state = RenderTile::NO_STEALING; rtile.num_samples -= (rtile.sample - rtile.start_sample); rtile.start_sample = rtile.sample; - tile_stealing_state = NOT_STEALING; + tile_stealing_state_ = NOT_STEALING; /* Poke any threads which might be waiting for NOT_STEALING above. */ - tile_steal_cond.notify_one(); + tile_steal_cond_.notify_one(); return true; } @@ -394,7 +394,7 @@ bool Session::get_tile_stolen() /* If tile_stealing_state is WAITING_FOR_TILE, atomically set it to RELEASING_TILE * and return true. */ TileStealingState expected = WAITING_FOR_TILE; - return tile_stealing_state.compare_exchange_weak(expected, RELEASING_TILE); + return tile_stealing_state_.compare_exchange_weak(expected, RELEASING_TILE); } bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_types) @@ -406,7 +406,7 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ } } - thread_scoped_lock tile_lock(tile_mutex); + thread_scoped_lock tile_lock(tile_mutex_); /* get next tile from manager */ Tile *tile; @@ -423,7 +423,7 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ /* Wait for denoising tiles to become available */ if ((tile_types & RenderTile::DENOISE) && !progress.get_cancel() && tile_manager.has_tiles()) { - denoising_cond.wait(tile_lock); + denoising_cond_.wait(tile_lock); continue; } @@ -446,7 +446,7 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ } else { if (tile_device->info.type == DEVICE_CPU) { - stealable_tiles++; + stealable_tiles_++; rtile.stealing_state = RenderTile::CAN_BE_STOLEN; } @@ -515,7 +515,7 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ /* This will read any passes needed as input for baking. */ if (tile_manager.state.sample == tile_manager.range_start_sample) { { - thread_scoped_lock tile_lock(tile_mutex); + thread_scoped_lock tile_lock(tile_mutex_); read_bake_tile_cb(rtile); } rtile.buffers->buffer.copy_to_device(); @@ -533,7 +533,7 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ void Session::update_tile_sample(RenderTile &rtile) { - thread_scoped_lock tile_lock(tile_mutex); + thread_scoped_lock tile_lock(tile_mutex_); if (update_render_tile_cb) { if (params.progressive_refine == false) { @@ -548,25 +548,25 @@ void Session::update_tile_sample(RenderTile &rtile) void Session::release_tile(RenderTile &rtile, const bool need_denoise) { - thread_scoped_lock tile_lock(tile_mutex); + thread_scoped_lock tile_lock(tile_mutex_); if (rtile.stealing_state != RenderTile::NO_STEALING) { - stealable_tiles--; + stealable_tiles_--; if (rtile.stealing_state == RenderTile::WAS_STOLEN) { /* If the tile is being stolen, don't release it here - the new device will pick up where * the old one left off. */ - assert(tile_stealing_state == RELEASING_TILE); + assert(tile_stealing_state_ == RELEASING_TILE); assert(rtile.sample < rtile.start_sample + rtile.num_samples); - tile_stealing_state = GOT_TILE; - stolen_tile = rtile; - tile_steal_cond.notify_all(); + tile_stealing_state_ = GOT_TILE; + stolen_tile_ = rtile; + tile_steal_cond_.notify_all(); return; } - else if (stealable_tiles == 0) { + else if (stealable_tiles_ == 0) { /* If this was the last stealable tile, wake up any threads still waiting for one. */ - tile_steal_cond.notify_all(); + tile_steal_cond_.notify_all(); } } @@ -595,12 +595,12 @@ void Session::release_tile(RenderTile &rtile, const bool need_denoise) update_status_time(); /* Notify denoising thread that a tile was finished. */ - denoising_cond.notify_all(); + denoising_cond_.notify_all(); } void Session::map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device) { - thread_scoped_lock tile_lock(tile_mutex); + thread_scoped_lock tile_lock(tile_mutex_); const int4 image_region = make_int4( tile_manager.state.buffer.full_x, @@ -677,7 +677,7 @@ void Session::map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_de void Session::unmap_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device) { - thread_scoped_lock tile_lock(tile_mutex); + thread_scoped_lock tile_lock(tile_mutex_); device->unmap_neighbor_tiles(tile_device, neighbors); } @@ -685,8 +685,8 @@ void Session::run_cpu() { bool tiles_written = false; - last_update_time = time_dt(); - last_display_time = last_update_time; + last_update_time_ = time_dt(); + last_display_time_ = last_update_time_; while (!progress.get_cancel()) { const bool no_tiles = !run_update_for_next_iteration(); @@ -718,7 +718,7 @@ void Session::run_cpu() /* buffers mutex is locked entirely while rendering each * sample, and released/reacquired on each iteration to allow * reset and draw in between */ - thread_scoped_lock buffers_lock(buffers_mutex); + thread_scoped_lock buffers_lock(buffers_mutex_); /* update status and timing */ update_status_time(); @@ -741,14 +741,14 @@ void Session::run_cpu() device->task_wait(); { - thread_scoped_lock reset_lock(delayed_reset.mutex); - thread_scoped_lock buffers_lock(buffers_mutex); - thread_scoped_lock display_lock(display_mutex); + thread_scoped_lock reset_lock(delayed_reset_.mutex); + thread_scoped_lock buffers_lock(buffers_mutex_); + thread_scoped_lock display_lock(display_mutex_); - if (delayed_reset.do_reset) { + if (delayed_reset_.do_reset) { /* reset rendering if request from main thread */ - delayed_reset.do_reset = false; - reset_(delayed_reset.params, delayed_reset.samples); + delayed_reset_.do_reset = false; + reset_(delayed_reset_.params, delayed_reset_.samples); } else if (need_copy_to_display_buffer) { /* Only copy to display_buffer if we do not reset, we don't @@ -783,7 +783,7 @@ void Session::run() /* reset number of rendered samples */ progress.reset_sample(); - if (device_use_gl) + if (device_use_gl_) run_gpu(); else run_cpu(); @@ -801,12 +801,12 @@ void Session::run() bool Session::run_update_for_next_iteration() { thread_scoped_lock scene_lock(scene->mutex); - thread_scoped_lock reset_lock(delayed_reset.mutex); + thread_scoped_lock reset_lock(delayed_reset_.mutex); - if (delayed_reset.do_reset) { - thread_scoped_lock buffers_lock(buffers_mutex); - reset_(delayed_reset.params, delayed_reset.samples); - delayed_reset.do_reset = false; + if (delayed_reset_.do_reset) { + thread_scoped_lock buffers_lock(buffers_mutex_); + reset_(delayed_reset_.params, delayed_reset_.samples); + delayed_reset_.do_reset = false; } const bool have_tiles = tile_manager.next(); @@ -829,25 +829,25 @@ bool Session::run_wait_for_work(bool no_tiles) return false; } - thread_scoped_lock pause_lock(pause_mutex); + thread_scoped_lock pause_lock(pause_mutex_); - if (!pause && !no_tiles) { + if (!pause_ && !no_tiles) { return false; } - update_status_time(pause, no_tiles); + update_status_time(pause_, no_tiles); while (true) { scoped_timer pause_timer; - pause_cond.wait(pause_lock); - if (pause) { + pause_cond_.wait(pause_lock); + if (pause_) { progress.add_skip_time(pause_timer, params.background); } - update_status_time(pause, no_tiles); + update_status_time(pause_, no_tiles); progress.set_update(); - if (!pause) { + if (!pause_) { break; } } @@ -857,7 +857,7 @@ bool Session::run_wait_for_work(bool no_tiles) bool Session::draw(BufferParams &buffer_params, DeviceDrawParams &draw_params) { - if (device_use_gl) + if (device_use_gl_) return draw_gpu(buffer_params, draw_params); else return draw_cpu(buffer_params, draw_params); @@ -866,7 +866,7 @@ bool Session::draw(BufferParams &buffer_params, DeviceDrawParams &draw_params) void Session::reset_(BufferParams &buffer_params, int samples) { if (buffers && buffer_params.modified(tile_manager.params)) { - gpu_draw_ready = false; + gpu_draw_ready_ = false; buffers->reset(buffer_params); if (display) { display->reset(buffer_params); @@ -874,8 +874,8 @@ void Session::reset_(BufferParams &buffer_params, int samples) } tile_manager.reset(buffer_params, samples); - stealable_tiles = 0; - tile_stealing_state = NOT_STEALING; + stealable_tiles_ = 0; + tile_stealing_state_ = NOT_STEALING; progress.reset_sample(); bool show_progress = params.background || tile_manager.get_num_effective_samples() != INT_MAX; @@ -888,7 +888,7 @@ void Session::reset_(BufferParams &buffer_params, int samples) void Session::reset(BufferParams &buffer_params, int samples) { - if (device_use_gl) + if (device_use_gl_) reset_gpu(buffer_params, samples); else reset_cpu(buffer_params, samples); @@ -900,26 +900,26 @@ void Session::set_samples(int samples) params.samples = samples; tile_manager.set_samples(samples); - pause_cond.notify_all(); + pause_cond_.notify_all(); } } -void Session::set_pause(bool pause_) +void Session::set_pause(bool pause) { bool notify = false; { - thread_scoped_lock pause_lock(pause_mutex); + thread_scoped_lock pause_lock(pause_mutex_); if (pause != pause_) { - pause = pause_; + pause_ = pause; notify = true; } } - if (session_thread) { + if (session_thread_) { if (notify) { - pause_cond.notify_all(); + pause_cond_.notify_all(); } } else if (pause_) { @@ -932,7 +932,7 @@ void Session::set_denoising(const DenoiseParams &denoising) bool need_denoise = denoising.need_denoising_task(); /* Lock buffers so no denoising operation is triggered while the settings are changed here. */ - thread_scoped_lock buffers_lock(buffers_mutex); + thread_scoped_lock buffers_lock(buffers_mutex_); params.denoising = denoising; if (!(params.device.denoisers & denoising.type)) { @@ -957,18 +957,18 @@ void Session::set_denoising_start_sample(int sample) if (sample != params.denoising.start_sample) { params.denoising.start_sample = sample; - pause_cond.notify_all(); + pause_cond_.notify_all(); } } void Session::wait() { - if (session_thread) { - session_thread->join(); - delete session_thread; + if (session_thread_) { + session_thread_->join(); + delete session_thread_; } - session_thread = NULL; + session_thread_ = NULL; } bool Session::update_scene() @@ -1099,7 +1099,7 @@ bool Session::render_need_denoise(bool &delayed) /* Avoid excessive denoising in viewport after reaching a certain amount of samples. */ delayed = (tile_manager.state.sample >= 20 && - (time_dt() - last_display_time) < params.progressive_update_timeout); + (time_dt() - last_display_time_) < params.progressive_update_timeout); return !delayed; } @@ -1197,10 +1197,10 @@ void Session::copy_to_display_buffer(int sample) /* set display to new size */ display->draw_set(task.w, task.h); - last_display_time = time_dt(); + last_display_time_ = time_dt(); } - display_outdated = false; + display_outdated_ = false; } bool Session::update_progressive_refine(bool cancel) @@ -1210,7 +1210,7 @@ bool Session::update_progressive_refine(bool cancel) double current_time = time_dt(); - if (current_time - last_update_time < params.progressive_update_timeout) { + if (current_time - last_update_time_ < params.progressive_update_timeout) { /* If last sample was processed, we need to write buffers anyway. */ if (!write && sample != 1) return false; @@ -1241,7 +1241,7 @@ bool Session::update_progressive_refine(bool cancel) } } - last_update_time = current_time; + last_update_time_ = current_time; return write; } diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index bc3b8366c05..fe043994197 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -174,7 +174,7 @@ class Session { bool do_reset; BufferParams params; int samples; - } delayed_reset; + } delayed_reset_; void run(); @@ -207,38 +207,38 @@ class Session { void map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device); void unmap_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device); - bool device_use_gl; + bool device_use_gl_; - thread *session_thread; + thread *session_thread_; - volatile bool display_outdated; + volatile bool display_outdated_; - volatile bool gpu_draw_ready; - volatile bool gpu_need_display_buffer_update; - thread_condition_variable gpu_need_display_buffer_update_cond; + volatile bool gpu_draw_ready_; + volatile bool gpu_need_display_buffer_update_; + thread_condition_variable gpu_need_display_buffer_update_cond_; - bool pause; - thread_condition_variable pause_cond; - thread_mutex pause_mutex; - thread_mutex tile_mutex; - thread_mutex buffers_mutex; - thread_mutex display_mutex; - thread_condition_variable denoising_cond; - thread_condition_variable tile_steal_cond; + bool pause_; + thread_condition_variable pause_cond_; + thread_mutex pause_mutex_; + thread_mutex tile_mutex_; + thread_mutex buffers_mutex_; + thread_mutex display_mutex_; + thread_condition_variable denoising_cond_; + thread_condition_variable tile_steal_cond_; - double reset_time; - double last_update_time; - double last_display_time; + double reset_time_; + double last_update_time_; + double last_display_time_; - RenderTile stolen_tile; + RenderTile stolen_tile_; typedef enum { NOT_STEALING, /* There currently is no tile stealing in progress. */ WAITING_FOR_TILE, /* A device is waiting for another device to release a tile. */ RELEASING_TILE, /* A device has releasing a stealable tile. */ GOT_TILE /* A device has released a stealable tile, which is now stored in stolen_tile. */ } TileStealingState; - std::atomic tile_stealing_state; - int stealable_tiles; + std::atomic tile_stealing_state_; + int stealable_tiles_; /* progressive refine */ bool update_progressive_refine(bool cancel); -- cgit v1.2.3 From 4f64fa4f8628aa514ec1f14d798b2f406a3bf6ef Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 6 Aug 2021 16:25:18 +0200 Subject: Cycles: Fix for possible viewport dead-lock This is a backport of recent development in the Cycles X branch. Fixes possible dead-lock in viewport rendering when exiting at an exact bad moment (couldn't reproduce in master branch, but in the cycles-x branch it was happening every now and then). Differential Revision: https://developer.blender.org/D12154 --- intern/cycles/render/session.cpp | 37 ++++++++++++++++++++++++++++--------- intern/cycles/render/session.h | 3 +++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 6b50522b660..1a08d8f52d6 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -73,7 +73,10 @@ Session::Session(const SessionParams ¶ms_) display_outdated_ = false; gpu_draw_ready_ = false; gpu_need_display_buffer_update_ = false; + pause_ = false; + cancel_ = false; + new_work_added_ = false; buffers = NULL; display = NULL; @@ -144,6 +147,7 @@ void Session::cancel() { thread_scoped_lock pause_lock(pause_mutex_); pause_ = false; + cancel_ = true; } pause_cond_.notify_all(); @@ -832,26 +836,34 @@ bool Session::run_wait_for_work(bool no_tiles) thread_scoped_lock pause_lock(pause_mutex_); if (!pause_ && !no_tiles) { + /* Rendering is not paused and there is work to be done. No need to wait for anything. */ return false; } update_status_time(pause_, no_tiles); - while (true) { + /* Only leave the loop when rendering is not paused. But even if the current render is un-paused + * but there is nothing to render keep waiting until new work is added. */ + while (!cancel_) { scoped_timer pause_timer; + + if (!pause_ && (!no_tiles || new_work_added_ || delayed_reset_.do_reset)) { + break; + } + + /* Wait for either pause state changed, or extra samples added to render. */ pause_cond_.wait(pause_lock); + if (pause_) { progress.add_skip_time(pause_timer, params.background); } update_status_time(pause_, no_tiles); progress.set_update(); - - if (!pause_) { - break; - } } + new_work_added_ = false; + return no_tiles; } @@ -896,12 +908,19 @@ void Session::reset(BufferParams &buffer_params, int samples) void Session::set_samples(int samples) { - if (samples != params.samples) { - params.samples = samples; - tile_manager.set_samples(samples); + if (samples == params.samples) { + return; + } - pause_cond_.notify_all(); + params.samples = samples; + tile_manager.set_samples(samples); + + { + thread_scoped_lock pause_lock(pause_mutex_); + new_work_added_ = true; } + + pause_cond_.notify_all(); } void Session::set_pause(bool pause) diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index fe043994197..05025c10f9c 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -218,6 +218,9 @@ class Session { thread_condition_variable gpu_need_display_buffer_update_cond_; bool pause_; + bool cancel_; + bool new_work_added_; + thread_condition_variable pause_cond_; thread_mutex pause_mutex_; thread_mutex tile_mutex_; -- cgit v1.2.3 From 3fab16fe8eb4a99607e00d5716ba88bf24020354 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 6 Aug 2021 11:44:15 -0300 Subject: Fix T90477: Cursor vertex snapping not working in UV editor `t->obedit_type` is read to indicate if we are in uv edit mode. Also fixes a crash introduced in {rBdd14ea18190ff27082009f73a556569a43377a71}. --- source/blender/editors/transform/transform.c | 3 +++ source/blender/editors/transform/transform.h | 2 +- .../blender/editors/transform/transform_convert.c | 14 ++--------- .../blender/editors/transform/transform_generics.c | 3 +-- source/blender/editors/transform/transform_snap.c | 29 ++++++++++------------ 5 files changed, 20 insertions(+), 31 deletions(-) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 96b84bd2a35..4069a72a8fc 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -78,6 +78,9 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2]); bool transdata_check_local_islands(TransInfo *t, short around) { + if (t->options & (CTX_CURSOR | CTX_TEXTURE_SPACE)) { + return false; + } return ((around == V3D_AROUND_LOCAL_ORIGINS) && (ELEM(t->obedit_type, OB_MESH, OB_GPENCIL))); } diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 1a61a594f37..1fa123e8507 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -582,7 +582,7 @@ typedef struct TransInfo { short around; /** space-type where transforming is. */ char spacetype; - /** Avoid looking inside #TransDataContainer.obedit. */ + /** Type of active object being edited. */ short obedit_type; /** translation, to show for widget. */ diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index e77fedfe143..3f730956dd0 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -1072,7 +1072,8 @@ static void init_proportional_edit(TransInfo *t) else if (convert_type == TC_MESH_UV && t->flag & T_PROP_CONNECTED) { /* Already calculated by uv_set_connectivity_distance. */ } - else if (convert_type == TC_CURVE_VERTS && t->obedit_type == OB_CURVE) { + else if (convert_type == TC_CURVE_VERTS) { + BLI_assert(t->obedit_type == OB_CURVE); set_prop_dist(t, false); } else { @@ -1371,7 +1372,6 @@ void createTransData(bContext *C, TransInfo *t) switch (t->data_type) { case TC_ACTION_DATA: - t->obedit_type = -1; createTransActionData(C, t); break; case TC_POSE: @@ -1394,7 +1394,6 @@ void createTransData(bContext *C, TransInfo *t) createTransCurveVerts(t); break; case TC_GRAPH_EDIT_DATA: - t->obedit_type = -1; createTransGraphEditData(C, t); break; case TC_GPENCIL: @@ -1404,9 +1403,6 @@ void createTransData(bContext *C, TransInfo *t) createTransLatticeVerts(t); break; case TC_MASKING_DATA: - if (t->spacetype == SPACE_CLIP) { - t->obedit_type = -1; - } createTransMaskingData(C, t); break; case TC_MBALL_VERTS: @@ -1425,11 +1421,9 @@ void createTransData(bContext *C, TransInfo *t) createTransUVs(C, t); break; case TC_NLA_DATA: - t->obedit_type = -1; createTransNlaData(C, t); break; case TC_NODE_DATA: - t->obedit_type = -1; createTransNodeData(t); break; case TC_OBJECT: @@ -1473,12 +1467,10 @@ void createTransData(bContext *C, TransInfo *t) createTransSculpt(C, t); break; case TC_SEQ_DATA: - t->obedit_type = -1; t->num.flag |= NUM_NO_FRACTION; /* sequencer has no use for floating point transform. */ createTransSeqData(t); break; case TC_TRACKING_DATA: - t->obedit_type = -1; createTransTrackingData(C, t); break; case TC_NONE: @@ -1492,8 +1484,6 @@ void createTransData(bContext *C, TransInfo *t) countAndCleanTransDataContainer(t); init_proportional_edit(t); - - BLI_assert((!(t->flag & T_EDIT)) == (!(t->obedit_type != -1))); } /** \} */ diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index e89ab6729d2..be8e551a1e8 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -152,8 +152,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->flag = 0; - if (obact && !(t->options & (CTX_CURSOR | CTX_TEXTURE_SPACE)) && - ELEM(object_mode, OB_MODE_EDIT, OB_MODE_EDIT_GPENCIL)) { + if (obact && ELEM(object_mode, OB_MODE_EDIT, OB_MODE_EDIT_GPENCIL)) { t->obedit_type = obact->type; } else { diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 2619fdf3403..656a1e5d9c7 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -575,7 +575,7 @@ static void initSnappingMode(TransInfo *t) { ToolSettings *ts = t->settings; /* All obedit types will match. */ - const int obedit_type = t->data_container->obedit ? t->data_container->obedit->type : -1; + const int obedit_type = t->obedit_type; ViewLayer *view_layer = t->view_layer; Base *base_act = view_layer->basact; @@ -609,15 +609,22 @@ static void initSnappingMode(TransInfo *t) } } - if ((t->spacetype == SPACE_VIEW3D || t->spacetype == SPACE_IMAGE) && - (t->options & CTX_CAMERA) == 0) { + if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) { /* Only 3D view or UV. */ /* Not with camera selected in camera view. */ setSnappingCallback(t); - if ((obedit_type != -1) && - ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) { + if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) { + /* In "Edit Strokes" mode, + * snap tool can perform snap to selected or active objects (see T49632) + * TODO: perform self snap in gpencil_strokes. + * + * When we're moving the origins, allow snapping onto our own geometry (see T69132). */ + t->tsnap.modeSelect = SNAP_ALL; + } + else if ((obedit_type != -1) && + ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) { /* Edit mode */ /* Temporary limited to edit mode meshes, armature, curves, metaballs. */ @@ -636,17 +643,7 @@ static void initSnappingMode(TransInfo *t) } else if (obedit_type == -1) { /* Object mode */ - if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) { - /* In "Edit Strokes" mode, - * snap tool can perform snap to selected or active objects (see T49632) - * TODO: perform self snap in gpencil_strokes. - * - * When we're moving the origins, allow snapping onto our own geometry (see T69132). */ - t->tsnap.modeSelect = SNAP_ALL; - } - else { - t->tsnap.modeSelect = SNAP_NOT_SELECTED; - } + t->tsnap.modeSelect = SNAP_NOT_SELECTED; } else { /* Increment if snap is not possible */ -- cgit v1.2.3 From d245782b80a63f9ff56ea49660cc9c287eb1bbd6 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Fri, 6 Aug 2021 13:54:49 -0400 Subject: Windows: Add support to compile python api docs from make file This adds support to compile the html python api docs from the command line by running `make doc_py` matching support between windows and unix. This patch also makes it so the compiler is not needed if you set the `blender_bin` variable, this affects icon generation as well. In the future, I want to move away from generating the build output in the build directory but that can come in a later change. Reviewed By: LazyDodo Differential Revision: https://developer.blender.org/D12144 --- build_files/windows/doc_py.cmd | 34 +++++++++++++++++++++++++++++++++ build_files/windows/find_sphinx.cmd | 23 ++++++++++++++++++++++ build_files/windows/parse_arguments.cmd | 3 +++ build_files/windows/reset_variables.cmd | 3 ++- build_files/windows/show_help.cmd | 4 ++++ make.bat | 15 +++++++++++++++ 6 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 build_files/windows/doc_py.cmd create mode 100644 build_files/windows/find_sphinx.cmd diff --git a/build_files/windows/doc_py.cmd b/build_files/windows/doc_py.cmd new file mode 100644 index 00000000000..d33a0289083 --- /dev/null +++ b/build_files/windows/doc_py.cmd @@ -0,0 +1,34 @@ +set SOURCEDIR=%BLENDER_DIR%/doc/python_api/sphinx-in +set BUILDDIR=%BLENDER_DIR%/doc/python_api/sphinx-out +if "%BF_LANG%" == "" set BF_LANG=en +set SPHINXOPTS=-j auto -D language=%BF_LANG% + +call "%~dp0\find_sphinx.cmd" + +if EXIST "%SPHINX_BIN%" ( + goto detect_sphinx_done +) + +echo unable to locate sphinx-build, run "set sphinx_BIN=full_path_to_sphinx-build.exe" +exit /b 1 + +:detect_sphinx_done + +call "%~dp0\find_blender.cmd" + +if EXIST "%BLENDER_BIN%" ( + goto detect_blender_done +) + +echo unable to locate blender, run "set BLENDER_BIN=full_path_to_blender.exe" +exit /b 1 + +:detect_blender_done + +%BLENDER_BIN% ^ + --background -noaudio --factory-startup ^ + --python %BLENDER_DIR%/doc/python_api/sphinx_doc_gen.py + +"%SPHINX_BIN%" -b html %SPHINXOPTS% %O% %SOURCEDIR% %BUILDDIR% + +:EOF diff --git a/build_files/windows/find_sphinx.cmd b/build_files/windows/find_sphinx.cmd new file mode 100644 index 00000000000..24238e96768 --- /dev/null +++ b/build_files/windows/find_sphinx.cmd @@ -0,0 +1,23 @@ +REM First see if there is an environment variable set +if EXIST "%SPHINX_BIN%" ( + goto detect_sphinx_done +) + +REM Then see if inkscape is available in the path +for %%X in (sphinx-build.exe) do (set SPHINX_BIN=%%~$PATH:X) +if EXIST "%SPHINX_BIN%" ( + goto detect_sphinx_done +) + +echo.The 'sphinx-build' command was not found. Make sure you have Sphinx +echo.installed, then set the SPHINX_BIN environment variable to point +echo.to the full path of the 'sphinx-build' executable. Alternatively you +echo.may add the Sphinx directory to PATH. +echo. +echo.If you don't have Sphinx installed, grab it from +echo.http://sphinx-doc.org/ + +REM If still not found clear the variable +set SPHINX_BIN= + +:detect_sphinx_done diff --git a/build_files/windows/parse_arguments.cmd b/build_files/windows/parse_arguments.cmd index c71093f2394..c63f062dfef 100644 --- a/build_files/windows/parse_arguments.cmd +++ b/build_files/windows/parse_arguments.cmd @@ -113,6 +113,9 @@ if NOT "%1" == "" ( ) else if "%1" == "icons_geom" ( set ICONS_GEOM=1 goto EOF + ) else if "%1" == "doc_py" ( + set DOC_PY=1 + goto EOF ) else ( echo Command "%1" unknown, aborting! goto ERR diff --git a/build_files/windows/reset_variables.cmd b/build_files/windows/reset_variables.cmd index 590d4ca246a..8ba7b4d3307 100644 --- a/build_files/windows/reset_variables.cmd +++ b/build_files/windows/reset_variables.cmd @@ -32,4 +32,5 @@ set FORMAT= set TEST= set BUILD_WITH_SCCACHE= set ICONS= -set ICONS_GEOM= \ No newline at end of file +set ICONS_GEOM= +set DOC_PY= diff --git a/build_files/windows/show_help.cmd b/build_files/windows/show_help.cmd index ee5e9d9dbbd..d914ecab2b8 100644 --- a/build_files/windows/show_help.cmd +++ b/build_files/windows/show_help.cmd @@ -31,6 +31,10 @@ echo - 2019 ^(build with visual studio 2019^) echo - 2019pre ^(build with visual studio 2019 pre-release^) echo - 2019b ^(build with visual studio 2019 Build Tools^) +echo. +echo Documentation Targets ^(Not associated with building^) +echo - doc_py ^(Generate sphinx python api docs^) + echo. echo Experimental options echo - with_opengl_tests ^(enable both the render and draw opengl test suites^) diff --git a/make.bat b/make.bat index 75d424202ae..0fda7594e25 100644 --- a/make.bat +++ b/make.bat @@ -13,6 +13,14 @@ if errorlevel 1 goto EOF call "%BLENDER_DIR%\build_files\windows\parse_arguments.cmd" %* if errorlevel 1 goto EOF +REM if it is one of the convenience targets and BLENDER_BIN is set +REM skip compiler detection +if "%ICONS%%ICONS_GEOM%%DOC_PY%" == "1" ( + if EXIST "%BLENDER_BIN%" ( + goto convenience_targets + ) +) + call "%BLENDER_DIR%\build_files\windows\find_dependencies.cmd" if errorlevel 1 goto EOF @@ -58,6 +66,8 @@ if "%BUILD_UPDATE%" == "1" ( call "%BLENDER_DIR%\build_files\windows\set_build_dir.cmd" +:convenience_targets + if "%ICONS%" == "1" ( call "%BLENDER_DIR%\build_files\windows\icons.cmd" goto EOF @@ -68,6 +78,11 @@ if "%ICONS_GEOM%" == "1" ( goto EOF ) +if "%DOC_PY%" == "1" ( + call "%BLENDER_DIR%\build_files\windows\doc_py.cmd" + goto EOF +) + echo Building blender with VS%BUILD_VS_YEAR% for %BUILD_ARCH% in %BUILD_DIR% call "%BLENDER_DIR%\build_files\windows\check_libraries.cmd" -- cgit v1.2.3 From b33b70ed077eed2c885fd91dd920087c3491b938 Mon Sep 17 00:00:00 2001 From: nutti Date: Fri, 6 Aug 2021 20:17:59 -0400 Subject: PyDoc: Fix poll_message_set API documentation to consistent with Python style Fix poll_message_set API documentation to consistent with Python style Reviewed By: Blendify Differential Revision: https://developer.blender.org/D12150 --- source/blender/python/intern/bpy_rna_operator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c index 490d9aa5212..d3ec54fc12d 100644 --- a/source/blender/python/intern/bpy_rna_operator.c +++ b/source/blender/python/intern/bpy_rna_operator.c @@ -91,7 +91,7 @@ static void pyop_poll_message_free_fn(bContext *UNUSED(C), void *user_data) } PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc, - ".. method:: poll_message_set(message, ...)\n" + ".. method:: poll_message_set(message, *args)\n" "\n" " Set the message to show in the tool-tip when poll fails.\n" "\n" -- cgit v1.2.3 From a7bb537a61c7c680bc01896ccf27a02c9e7a688a Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Sat, 7 Aug 2021 21:30:15 +0900 Subject: Cleanup: unnecessary double pointers in XR module No functional changes. --- intern/ghost/GHOST_C-api.h | 4 ++-- intern/ghost/intern/GHOST_C-api.cpp | 4 ++-- intern/ghost/intern/GHOST_XrAction.cpp | 8 ++++---- intern/ghost/intern/GHOST_XrAction.h | 4 ++-- intern/ghost/intern/GHOST_XrSession.cpp | 4 ++-- intern/ghost/intern/GHOST_XrSession.h | 4 ++-- source/blender/makesrna/intern/rna_xr.c | 9 +++------ source/blender/windowmanager/WM_api.h | 6 +++--- source/blender/windowmanager/xr/intern/wm_xr_action.c | 12 ++++++------ source/blender/windowmanager/xr/intern/wm_xr_intern.h | 4 ++-- 10 files changed, 28 insertions(+), 31 deletions(-) diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index 83c67f83908..b78aac6f5eb 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -1102,7 +1102,7 @@ int GHOST_XrSyncActions(GHOST_XrContextHandle xr_context, const char *action_set int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_context, const char *action_set_name, const char *action_name, - const char **subaction_path, + const char *subaction_path, const int64_t *duration, const float *frequency, const float *amplitude); @@ -1113,7 +1113,7 @@ int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_context, void GHOST_XrStopHapticAction(GHOST_XrContextHandle xr_context, const char *action_set_name, const char *action_name, - const char **subaction_path); + const char *subaction_path); /** * Get action set custom data (owned by Blender, not GHOST). diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index b1af5c131ab..a2871b46222 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -1005,7 +1005,7 @@ int GHOST_XrSyncActions(GHOST_XrContextHandle xr_contexthandle, const char *acti int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name, const char *action_name, - const char **subaction_path, + const char *subaction_path, const int64_t *duration, const float *frequency, const float *amplitude) @@ -1022,7 +1022,7 @@ int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_contexthandle, void GHOST_XrStopHapticAction(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name, const char *action_name, - const char **subaction_path) + const char *subaction_path) { GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; GHOST_XrSession *xr_session = xr_context->getSession(); diff --git a/intern/ghost/intern/GHOST_XrAction.cpp b/intern/ghost/intern/GHOST_XrAction.cpp index 07eb42c14e6..704b1ce9fac 100644 --- a/intern/ghost/intern/GHOST_XrAction.cpp +++ b/intern/ghost/intern/GHOST_XrAction.cpp @@ -375,7 +375,7 @@ void GHOST_XrAction::updateState(XrSession session, void GHOST_XrAction::applyHapticFeedback(XrSession session, const char *action_name, - const char **subaction_path_str, + const char *subaction_path_str, const int64_t &duration, const float &frequency, const float &litude) @@ -390,7 +390,7 @@ void GHOST_XrAction::applyHapticFeedback(XrSession session, haptic_info.action = m_action; if (subaction_path_str != nullptr) { - SubactionIndexMap::iterator it = m_subaction_indices.find(*subaction_path_str); + SubactionIndexMap::iterator it = m_subaction_indices.find(subaction_path_str); if (it != m_subaction_indices.end()) { haptic_info.subactionPath = m_subaction_paths[it->second]; CHECK_XR( @@ -410,13 +410,13 @@ void GHOST_XrAction::applyHapticFeedback(XrSession session, void GHOST_XrAction::stopHapticFeedback(XrSession session, const char *action_name, - const char **subaction_path_str) + const char *subaction_path_str) { XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO}; haptic_info.action = m_action; if (subaction_path_str != nullptr) { - SubactionIndexMap::iterator it = m_subaction_indices.find(*subaction_path_str); + SubactionIndexMap::iterator it = m_subaction_indices.find(subaction_path_str); if (it != m_subaction_indices.end()) { haptic_info.subactionPath = m_subaction_paths[it->second]; CHECK_XR(xrStopHapticFeedback(session, &haptic_info), diff --git a/intern/ghost/intern/GHOST_XrAction.h b/intern/ghost/intern/GHOST_XrAction.h index 70eaa694ae9..3e2224fe3ff 100644 --- a/intern/ghost/intern/GHOST_XrAction.h +++ b/intern/ghost/intern/GHOST_XrAction.h @@ -103,11 +103,11 @@ class GHOST_XrAction { const XrTime &predicted_display_time); void applyHapticFeedback(XrSession session, const char *action_name, - const char **subaction_path, + const char *subaction_path, const int64_t &duration, const float &frequency, const float &litude); - void stopHapticFeedback(XrSession session, const char *action_name, const char **subaction_path); + void stopHapticFeedback(XrSession session, const char *action_name, const char *subaction_path); void *getCustomdata(); void getBindings(std::map> &r_bindings) const; diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index a63ce5c9344..4cab22ee676 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -754,7 +754,7 @@ bool GHOST_XrSession::syncActions(const char *action_set_name) bool GHOST_XrSession::applyHapticAction(const char *action_set_name, const char *action_name, - const char **subaction_path, + const char *subaction_path, const int64_t &duration, const float &frequency, const float &litude) @@ -777,7 +777,7 @@ bool GHOST_XrSession::applyHapticAction(const char *action_set_name, void GHOST_XrSession::stopHapticAction(const char *action_set_name, const char *action_name, - const char **subaction_path) + const char *subaction_path) { GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); if (action_set == nullptr) { diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h index ec15897058f..a76e11aede1 100644 --- a/intern/ghost/intern/GHOST_XrSession.h +++ b/intern/ghost/intern/GHOST_XrSession.h @@ -76,13 +76,13 @@ class GHOST_XrSession { bool syncActions(const char *action_set_name = nullptr); bool applyHapticAction(const char *action_set_name, const char *action_name, - const char **subaction_path, + const char *subaction_path, const int64_t &duration, const float &frequency, const float &litude); void stopHapticAction(const char *action_set_name, const char *action_name, - const char **subaction_path); + const char *subaction_path); /* Custom data (owned by Blender, not GHOST) accessors. */ void *getActionSetCustomdata(const char *action_set_name); diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 88e285d7cc2..4cab92ad878 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -612,7 +612,6 @@ static bool rna_XrSessionState_action_create(bContext *C, const bool is_button_action = (is_float_action || ami->type == XR_BOOLEAN_INPUT); wmOperatorType *ot = NULL; IDProperty *op_properties = NULL; - const char *haptic_name = NULL; int64_t haptic_duration_msec; if (is_button_action) { @@ -625,7 +624,6 @@ static bool rna_XrSessionState_action_create(bContext *C, } } - haptic_name = &ami->haptic_name[0]; haptic_duration_msec = (int64_t)(ami->haptic_duration * 1000.0f); } @@ -637,7 +635,7 @@ static bool rna_XrSessionState_action_create(bContext *C, subaction_paths, ot, op_properties, - is_button_action ? &haptic_name : NULL, + is_button_action ? ami->haptic_name : NULL, is_button_action ? &haptic_duration_msec : NULL, is_button_action ? &ami->haptic_frequency : NULL, is_button_action ? &ami->haptic_amplitude : NULL, @@ -793,7 +791,7 @@ bool rna_XrSessionState_haptic_action_apply(bContext *C, return WM_xr_haptic_action_apply(&wm->xr, action_set_name, action_name, - user_path[0] ? &user_path : NULL, + user_path[0] ? user_path : NULL, &duration_msec, &frequency, &litude); @@ -810,8 +808,7 @@ void rna_XrSessionState_haptic_action_stop(bContext *C, { # ifdef WITH_XR_OPENXR wmWindowManager *wm = CTX_wm_manager(C); - WM_xr_haptic_action_stop( - &wm->xr, action_set_name, action_name, user_path[0] ? &user_path : NULL); + WM_xr_haptic_action_stop(&wm->xr, action_set_name, action_name, user_path[0] ? user_path : NULL); # else UNUSED_VARS(C, action_set_name, action_name, user_path); # endif diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 02e8d42e0ff..3027df41e77 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -987,7 +987,7 @@ bool WM_xr_action_create(wmXrData *xr, const char **subaction_paths, struct wmOperatorType *ot, struct IDProperty *op_properties, - const char **haptic_name, + const char *haptic_name, const int64_t *haptic_duration, const float *haptic_frequency, const float *haptic_amplitude, @@ -1027,14 +1027,14 @@ bool WM_xr_action_state_get(const wmXrData *xr, bool WM_xr_haptic_action_apply(wmXrData *xr, const char *action_set_name, const char *action_name, - const char **subaction_path, + const char *subaction_path, const int64_t *duration, const float *frequency, const float *amplitude); void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name, - const char **subaction_path); + const char *subaction_path); /* wm_xr_actionmap.c */ XrActionMap *WM_xr_actionmap_new(struct wmXrRuntimeData *runtime, diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c index 2712fde51a8..ba347c537ec 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_action.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -74,7 +74,7 @@ static wmXrAction *action_create(const char *action_name, const char **subaction_paths, wmOperatorType *ot, IDProperty *op_properties, - const char **haptic_name, + const char *haptic_name, const int64_t *haptic_duration, const float *haptic_frequency, const float *haptic_amplitude, @@ -134,8 +134,8 @@ static wmXrAction *action_create(const char *action_name, if (haptic_name) { BLI_assert(is_button_action); - action->haptic_name = MEM_mallocN(strlen(*haptic_name) + 1, "XrAction_HapticName"); - strcpy(action->haptic_name, *haptic_name); + action->haptic_name = MEM_mallocN(strlen(haptic_name) + 1, "XrAction_HapticName"); + strcpy(action->haptic_name, haptic_name); action->haptic_duration = *haptic_duration; action->haptic_frequency = *haptic_frequency; action->haptic_amplitude = *haptic_amplitude; @@ -232,7 +232,7 @@ bool WM_xr_action_create(wmXrData *xr, const char **subaction_paths, wmOperatorType *ot, IDProperty *op_properties, - const char **haptic_name, + const char *haptic_name, const int64_t *haptic_duration, const float *haptic_frequency, const float *haptic_amplitude, @@ -502,7 +502,7 @@ bool WM_xr_action_state_get(const wmXrData *xr, bool WM_xr_haptic_action_apply(wmXrData *xr, const char *action_set_name, const char *action_name, - const char **subaction_path, + const char *subaction_path, const int64_t *duration, const float *frequency, const float *amplitude) @@ -521,7 +521,7 @@ bool WM_xr_haptic_action_apply(wmXrData *xr, void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name, - const char **subaction_path) + const char *subaction_path) { GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name, subaction_path); } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 4b1605d0f68..4d4df43f796 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -134,7 +134,7 @@ typedef struct wmXrAction { eXrAxisFlag *axis_flags; /** The currently active subaction path (if any) for modal actions. */ - char **active_modal_path; + const char *active_modal_path; /** Operator to be called on XR events. */ struct wmOperatorType *ot; @@ -155,7 +155,7 @@ typedef struct wmXrAction { typedef struct wmXrHapticAction { struct wmXrHapticAction *next, *prev; wmXrAction *action; - const char **subaction_path; + const char *subaction_path; int64_t time_start; } wmXrHapticAction; -- cgit v1.2.3 From a7aeec26550e24fa8b9d8f678afa65c48a5524d5 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Sun, 8 Aug 2021 14:53:05 +0200 Subject: GPencil: New Select Random operator Select strokes or points randomly (similar to meshes). Reviewed By: pepeland Differential Revision: https://developer.blender.org/D12157 --- release/scripts/startup/bl_ui/space_view3d.py | 1 + source/blender/editors/gpencil/gpencil_intern.h | 1 + source/blender/editors/gpencil/gpencil_ops.c | 1 + source/blender/editors/gpencil/gpencil_select.c | 233 ++++++++++++++++++++++++ 4 files changed, 236 insertions(+) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 20b706f5004..c6bc6d9b5d3 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -1854,6 +1854,7 @@ class VIEW3D_MT_select_gpencil(Menu): layout.operator("gpencil.select_linked", text="Linked") layout.operator("gpencil.select_alternate") + layout.operator("gpencil.select_random") layout.operator_menu_enum("gpencil.select_grouped", "type", text="Grouped") if context.mode == 'VERTEX_GPENCIL': diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index d1a1e417d9e..b6730cb123b 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -375,6 +375,7 @@ void GPENCIL_OT_select_less(struct wmOperatorType *ot); void GPENCIL_OT_select_first(struct wmOperatorType *ot); void GPENCIL_OT_select_last(struct wmOperatorType *ot); void GPENCIL_OT_select_alternate(struct wmOperatorType *ot); +void GPENCIL_OT_select_random(struct wmOperatorType *ot); void GPENCIL_OT_select_vertex_color(struct wmOperatorType *ot); void GPENCIL_OT_duplicate(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 35640cf3b66..8c78a402e81 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -555,6 +555,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_select_first); WM_operatortype_append(GPENCIL_OT_select_last); WM_operatortype_append(GPENCIL_OT_select_alternate); + WM_operatortype_append(GPENCIL_OT_select_random); WM_operatortype_append(GPENCIL_OT_select_vertex_color); WM_operatortype_append(GPENCIL_OT_duplicate); diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 69734fa1ba8..4865cd3ecc4 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -33,6 +33,7 @@ #include "BLI_ghash.h" #include "BLI_lasso_2d.h" #include "BLI_math_vector.h" +#include "BLI_rand.h" #include "BLI_utildefines.h" #include "DNA_gpencil_types.h" @@ -193,6 +194,28 @@ static void deselect_all_selected(bContext *C) CTX_DATA_END; } +static void select_all_stroke_points(bGPdata *gpd, bGPDstroke *gps, bool select) +{ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + if (select) { + pt->flag |= GP_SPOINT_SELECT; + } + else { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + + if (select) { + gps->flag |= GP_STROKE_SELECT; + BKE_gpencil_stroke_select_index_set(gpd, gps); + } + else { + gps->flag &= ~GP_STROKE_SELECT; + BKE_gpencil_stroke_select_index_reset(gps); + } +} + static void select_all_curve_points(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gpc, bool deselect) { for (int i = 0; i < gpc->tot_curve_points; i++) { @@ -512,6 +535,216 @@ void GPENCIL_OT_select_alternate(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Select Random Operator + * \{ */ + +static int gpencil_select_random_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + if ((gpd == NULL) || (GPENCIL_NONE_EDIT_MODE(gpd))) { + return OPERATOR_CANCELLED; + } + + const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends"); + const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT); + const float randfac = RNA_float_get(op->ptr, "ratio"); + const int seed = WM_operator_properties_select_random_seed_increment_get(op); + const int start = (unselect_ends) ? 1 : 0; + const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + + int selectmode; + if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) { + selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt); + } + else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) { + selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex); + } + else { + selectmode = ts->gpencil_selectmode_edit; + } + + bool changed = false; + int seed_iter = seed; + int stroke_idx = 0; + + if (is_curve_edit) { + GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc) + { + /* Only apply to unselected strokes (if select). */ + if (select) { + if ((gps->flag & GP_STROKE_SELECT) || (gps->totpoints == 0)) { + continue; + } + } + else { + if (((gps->flag & GP_STROKE_SELECT) == 0) || (gps->totpoints == 0)) { + continue; + } + } + + /* Different seed by stroke. */ + seed_iter += gps->totpoints + stroke_idx; + stroke_idx++; + + if (selectmode == GP_SELECTMODE_STROKE) { + RNG *rng = BLI_rng_new(seed_iter); + const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints; + bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false; + select_stroke ^= select; + /* Curve function has select paremter inverted. */ + select_all_curve_points(gpd, gps, gps->editcurve, !select_stroke); + changed = true; + } + else { + int elem_map_len = 0; + bGPDcurve_point **elem_map = MEM_mallocN(sizeof(*elem_map) * gpc->tot_curve_points, + __func__); + bGPDcurve_point *ptc; + for (int i = start; i < gpc->tot_curve_points; i++) { + bGPDcurve_point *gpc_pt = &gpc->curve_points[i]; + elem_map[elem_map_len++] = gpc_pt; + } + + BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter); + const int count_select = elem_map_len * randfac; + for (int i = 0; i < count_select; i++) { + ptc = elem_map[i]; + if (select) { + ptc->flag |= GP_SPOINT_SELECT; + BEZT_SEL_ALL(&ptc->bezt); + } + else { + ptc->flag &= ~GP_SPOINT_SELECT; + BEZT_DESEL_ALL(&ptc->bezt); + } + } + MEM_freeN(elem_map); + + /* unselect start and end points */ + if (unselect_ends) { + bGPDcurve_point *gpc_pt = &gpc->curve_points[0]; + gpc_pt->flag &= ~GP_SPOINT_SELECT; + BEZT_DESEL_ALL(&gpc_pt->bezt); + + gpc_pt = &gpc->curve_points[gpc->tot_curve_points - 1]; + gpc_pt->flag &= ~GP_SPOINT_SELECT; + BEZT_DESEL_ALL(&gpc_pt->bezt); + } + + BKE_gpencil_curve_sync_selection(gpd, gps); + } + + changed = true; + } + GP_EDITABLE_CURVES_END(gps_iter); + } + else { + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + /* Only apply to unselected strokes (if select). */ + if (select) { + if ((gps->flag & GP_STROKE_SELECT) || (gps->totpoints == 0)) { + continue; + } + } + else { + if (((gps->flag & GP_STROKE_SELECT) == 0) || (gps->totpoints == 0)) { + continue; + } + } + + /* Different seed by stroke. */ + seed_iter += gps->totpoints + stroke_idx; + stroke_idx++; + + if (selectmode == GP_SELECTMODE_STROKE) { + RNG *rng = BLI_rng_new(seed_iter); + const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints; + bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false; + select_stroke ^= select; + select_all_stroke_points(gpd, gps, select_stroke); + changed = true; + } + else { + int elem_map_len = 0; + bGPDspoint **elem_map = MEM_mallocN(sizeof(*elem_map) * gps->totpoints, __func__); + bGPDspoint *pt; + for (int i = start; i < gps->totpoints; i++) { + pt = &gps->points[i]; + elem_map[elem_map_len++] = pt; + } + + BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter); + const int count_select = elem_map_len * randfac; + for (int i = 0; i < count_select; i++) { + pt = elem_map[i]; + if (select) { + pt->flag |= GP_SPOINT_SELECT; + } + else { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + MEM_freeN(elem_map); + + /* unselect start and end points */ + if (unselect_ends) { + pt = &gps->points[0]; + pt->flag &= ~GP_SPOINT_SELECT; + + pt = &gps->points[gps->totpoints - 1]; + pt->flag &= ~GP_SPOINT_SELECT; + } + + BKE_gpencil_stroke_sync_selection(gpd, gps); + } + + changed = true; + } + CTX_DATA_END; + } + + if (changed) { + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_select_random(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Random"; + ot->idname = "GPENCIL_OT_select_random"; + ot->description = "Select random points for non selected strokes"; + + /* callbacks */ + ot->exec = gpencil_select_random_exec; + ot->poll = gpencil_select_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_select_random(ot); + RNA_def_boolean(ot->srna, + "unselect_ends", + false, + "Unselect Ends", + "Do not select the first and last point of the stroke"); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Select Grouped Operator * \{ */ -- cgit v1.2.3 From b541b5f87588a98a1610ee99998795dd4eb1c33e Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Sun, 8 Aug 2021 15:09:34 +0200 Subject: GPencil: Fix memory leak in previous commit --- source/blender/editors/gpencil/gpencil_select.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 4865cd3ecc4..7e95e360998 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -597,6 +597,7 @@ static int gpencil_select_random_exec(bContext *C, wmOperator *op) /* Curve function has select paremter inverted. */ select_all_curve_points(gpd, gps, gps->editcurve, !select_stroke); changed = true; + BLI_rng_free(rng); } else { int elem_map_len = 0; @@ -666,6 +667,7 @@ static int gpencil_select_random_exec(bContext *C, wmOperator *op) select_stroke ^= select; select_all_stroke_points(gpd, gps, select_stroke); changed = true; + BLI_rng_free(rng); } else { int elem_map_len = 0; -- cgit v1.2.3 From 3f1873111ed8dc3f0f42f16e157ac4be2c948696 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Sun, 8 Aug 2021 15:11:50 +0200 Subject: Cleanup: Fix comment typo --- source/blender/editors/gpencil/gpencil_select.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 7e95e360998..93bae7d3614 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -594,7 +594,7 @@ static int gpencil_select_random_exec(bContext *C, wmOperator *op) const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints; bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false; select_stroke ^= select; - /* Curve function has select paremter inverted. */ + /* Curve function has select parameter inverted. */ select_all_curve_points(gpd, gps, gps->editcurve, !select_stroke); changed = true; BLI_rng_free(rng); -- cgit v1.2.3 From 76e24609fd6133023ddff5d16fea8cbc74e45207 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 8 Aug 2021 13:29:07 -0400 Subject: Cleanup: Remove stale/dead code This seems to be really old code from 2.4 or earlier. I was unable to find when it was removed gitk and git blame both couldnt find anything. However, it is safe to say that this code is long gone. --- release/scripts/startup/bl_ui/space_image.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 0fceb864ac2..061c5b8cd34 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -826,12 +826,6 @@ class IMAGE_HT_header(Header): row = layout.row() row.prop(sima, "display_channels", icon_only=True) - row = layout.row(align=True) - if ima.type == 'COMPOSITE': - row.operator("image.record_composite", icon='REC') - if ima.type == 'COMPOSITE' and ima.source in {'MOVIE', 'SEQUENCE'}: - row.operator("image.play_composite", icon='PLAY') - class IMAGE_MT_editor_menus(Menu): bl_idname = "IMAGE_MT_editor_menus" -- cgit v1.2.3 From 0be26f563e63dcf9fa725c27bf713b0b09bc4372 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 8 Aug 2021 13:30:30 -0400 Subject: UI: Sequencer: Fix placement of display options in sequencer & preview mode This commit does two things, first it removes the proportional editing tool settings. This is not accessible code and is has not been used since the grease pencil/annotations changes in 2.8. Second, this patch reorders the if statements so that the display options are always shown on the rightside. Reviewed By: antoniov Differential Revision: https://developer.blender.org/D12163 --- release/scripts/startup/bl_ui/space_sequencer.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 30467521c3d..20fb39e8c1f 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -148,20 +148,6 @@ class SEQUENCER_HT_header(Header): layout.separator_spacer() - if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}: - layout.prop(st, "display_mode", text="", icon_only=True) - layout.prop(st, "preview_channels", text="", icon_only=True) - - gpd = context.gpencil_data - tool_settings = context.tool_settings - - # Proportional editing - if gpd and gpd.use_stroke_edit_mode: - row = layout.row(align=True) - row.prop(tool_settings, "use_proportional_edit", icon_only=True) - if tool_settings.use_proportional_edit: - row.prop(tool_settings, "proportional_edit_falloff", icon_only=True) - if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: tool_settings = context.tool_settings row = layout.row(align=True) @@ -170,6 +156,10 @@ class SEQUENCER_HT_header(Header): sub.popover(panel="SEQUENCER_PT_snapping") layout.separator_spacer() + if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}: + layout.prop(st, "display_mode", text="", icon_only=True) + layout.prop(st, "preview_channels", text="", icon_only=True) + row = layout.row(align=True) row.prop(st, "show_strip_overlay", text="", icon='OVERLAY') sub = row.row(align=True) -- cgit v1.2.3 From 4c26bb02327f0e836e55929d900b2f50d303f433 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 8 Aug 2021 16:09:23 -0400 Subject: UI: Show Mask Display Options Conistently This commit makes the display options for mask only show in the header for the clip and image editors. Prior to this change they would display in the header for the clip editor and in the sidebar for the image editors. --- release/scripts/startup/bl_ui/properties_mask_common.py | 3 +-- release/scripts/startup/bl_ui/space_clip.py | 11 +++++------ release/scripts/startup/bl_ui/space_image.py | 5 ++--- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_mask_common.py b/release/scripts/startup/bl_ui/properties_mask_common.py index 09a1f40d3a9..40a704a65dd 100644 --- a/release/scripts/startup/bl_ui/properties_mask_common.py +++ b/release/scripts/startup/bl_ui/properties_mask_common.py @@ -237,9 +237,8 @@ class MASK_PT_point: class MASK_PT_display: # subclasses must define... # ~ bl_space_type = 'CLIP_EDITOR' - # ~ bl_region_type = 'UI' + # ~ bl_region_type = 'HEADER' bl_label = "Mask Display" - bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index afbc3abf302..ae106f893d3 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -1156,12 +1156,6 @@ class CLIP_PT_mask_layers(MASK_PT_layers, Panel): bl_category = "Mask" -class CLIP_PT_mask_display(MASK_PT_display, Panel): - bl_space_type = 'CLIP_EDITOR' - bl_region_type = 'HEADER' - bl_category = "Mask" - - class CLIP_PT_active_mask_spline(MASK_PT_spline, Panel): bl_space_type = 'CLIP_EDITOR' bl_region_type = 'UI' @@ -1191,6 +1185,11 @@ class CLIP_PT_tools_mask_tools(MASK_PT_tools, Panel): bl_region_type = 'TOOLS' bl_category = "Mask" + +class CLIP_PT_mask_display(MASK_PT_display, Panel): + bl_space_type = 'CLIP_EDITOR' + bl_region_type = 'HEADER' + # --- end mask --- diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 061c5b8cd34..dcb0ab2e9e5 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -817,7 +817,7 @@ class IMAGE_HT_header(Header): row.prop(sima, "show_stereo_3d", text="") if show_maskedit: row = layout.row() - row.popover(panel='CLIP_PT_mask_display') + row.popover(panel='IMAGE_PT_mask_display') # layers. layout.template_image_layers(ima, iuser) @@ -911,8 +911,7 @@ class IMAGE_PT_active_mask_point(MASK_PT_point, Panel): class IMAGE_PT_mask_display(MASK_PT_display, Panel): bl_space_type = 'IMAGE_EDITOR' - bl_region_type = 'UI' - bl_category = "Mask" + bl_region_type = 'HEADER' # --- end mask --- -- cgit v1.2.3 From 3ea6cf7d41f66d0cb817d9fba072dfcef3705fd4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 9 Aug 2021 12:01:11 +1000 Subject: Cleanup: avoid using context in versioning code Also extract versioning into a function that makes it's purpose clear. --- .../startup/bl_app_templates_system/Video_Editing/__init__.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py b/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py index ae3fdee56ac..c61acf2ce71 100644 --- a/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py +++ b/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py @@ -20,10 +20,8 @@ import bpy from bpy.app.handlers import persistent -@persistent -def load_handler(_): - from bpy import context - screen = context.screen +def do_version_file_browser(): + screen = bpy.data.screens["Video Editing"] for area in screen.areas: if area.type == 'FILE_BROWSER': space = area.spaces.active @@ -31,6 +29,11 @@ def load_handler(_): params.use_filter_folder = True +@persistent +def load_handler(_): + do_version_file_browser() + + def register(): bpy.app.handlers.load_factory_startup_post.append(load_handler) -- cgit v1.2.3 From ff9bc901f43668f27ea36e0d156a916aa08e8877 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 9 Aug 2021 12:36:19 +1000 Subject: Cleanup: grease pencil app-template versioning - Remove check for screens being None as this would raise an error. - Replace loop over `area.spaces` with `area.spaces.active`. - Loop over grease pencil data directly instead of accessing through the scenes objects. - Split versioning into functions. - Use `update_factory_startup_*` prefix for function names as this isn't versioning existing files. --- .../2D_Animation/__init__.py | 69 ++++++++++------------ .../Video_Editing/__init__.py | 4 +- 2 files changed, 34 insertions(+), 39 deletions(-) diff --git a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py index 40dd0729fec..be47890a002 100644 --- a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py +++ b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py @@ -21,48 +21,43 @@ import bpy from bpy.app.handlers import persistent - -@persistent -def load_handler(_): - import bpy - - # 2D Animation - screen = bpy.data.screens['2D Animation'] - if screen: - for area in screen.areas: +def update_factory_startup_screens(): + # 2D Animation. + screen = bpy.data.screens["2D Animation"] + for area in screen.areas: + if area.type == 'PROPERTIES': # Set Tool settings as default in properties panel. - if area.type == 'PROPERTIES': - for space in area.spaces: - if space.type != 'PROPERTIES': - continue - space.context = 'TOOL' - + space = area.spaces.active + space.context = 'TOOL' + elif area.type == 'DOPESHEET_EDITOR': # Open sidebar in Dopesheet. - elif area.type == 'DOPESHEET_EDITOR': - for space in area.spaces: - if space.type != 'DOPESHEET_EDITOR': - continue - space.show_region_ui = True + space = area.spaces.active + space.show_region_ui = True + + # 2D Full Canvas. + screen = bpy.data.screens["2D Full Canvas"] + for area in screen.areas: + if area.type == 'VIEW_3D': + space = area.spaces.active + space.shading.type = 'MATERIAL' + space.shading.use_scene_world = True - # 2D Full Canvas - screen = bpy.data.screens['2D Full Canvas'] - if screen: - for area in screen.areas: - if area.type == 'VIEW_3D': - for space in area.spaces: - if space.type != 'VIEW_3D': - continue - space.shading.type = 'MATERIAL' - space.shading.use_scene_world = True - # Grease pencil object - scene = bpy.data.scenes[0] - if scene: +def update_factory_startup_scenes(): + for scene in bpy.data.scenes: scene.tool_settings.use_keyframe_insert_auto = True - for ob in scene.objects: - if ob.type == 'GPENCIL': - gpd = ob.data - gpd.onion_keyframe_type = 'ALL' + + +def update_factory_startup_grease_pencils(): + for gpd in bpy.data.grease_pencils: + gpd.onion_keyframe_type = 'ALL' + + +@persistent +def load_handler(_): + update_factory_startup_screens() + update_factory_startup_scenes() + update_factory_startup_grease_pencils() def register(): diff --git a/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py b/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py index c61acf2ce71..247a1ec342e 100644 --- a/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py +++ b/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py @@ -20,7 +20,7 @@ import bpy from bpy.app.handlers import persistent -def do_version_file_browser(): +def update_factory_startup_screens(): screen = bpy.data.screens["Video Editing"] for area in screen.areas: if area.type == 'FILE_BROWSER': @@ -31,7 +31,7 @@ def do_version_file_browser(): @persistent def load_handler(_): - do_version_file_browser() + update_factory_startup_screens() def register(): -- cgit v1.2.3 From 8830cfe5416084caf98fa7c7981a0c337f35548b Mon Sep 17 00:00:00 2001 From: Yuki Hashimoto Date: Mon, 9 Aug 2021 14:27:35 +1000 Subject: Fix text object inserting multiple characters with a selection `bpy.ops.font.text_insert(text="multiple characters")` wasn't working. When the text is selected does not correctly insert multiple characters. - When the text was selected from left to right, the cursor only move one position next to the selected text. - When the text is selected from right to left, a part of the selected text remain. Ref D12161 --- source/blender/editors/curve/editfont.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index e43e4194c51..39fb2882e4b 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -439,37 +439,27 @@ static void text_update_edited(bContext *C, Object *obedit, int mode) WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); } -static int kill_selection(Object *obedit, int ins) /* 1 == new character */ +static int kill_selection(Object *obedit, int ins) /* ins == new character len */ { Curve *cu = obedit->data; EditFont *ef = cu->editfont; int selend, selstart, direction; - int offset = 0; int getfrom; direction = BKE_vfont_select_get(obedit, &selstart, &selend); if (direction) { int size; - if (ins) { - offset = 1; - } if (ef->pos >= selstart) { - ef->pos = selstart + offset; + ef->pos = selstart + ins; } if ((direction == -1) && ins) { - selstart++; - selend++; - } - getfrom = selend + offset; - if (ins == 0) { - getfrom++; - } - size = (ef->len * sizeof(*ef->textbuf)) - (selstart * sizeof(*ef->textbuf)) + - (offset * sizeof(*ef->textbuf)); - memmove(ef->textbuf + selstart, ef->textbuf + getfrom, size); - memmove(ef->textbufinfo + selstart, - ef->textbufinfo + getfrom, - ((ef->len - selstart) + offset) * sizeof(CharInfo)); + selstart += ins; + selend += ins; + } + getfrom = selend + 1; + size = ef->len - selend; /* This is equivalent to: `(ef->len - getfrom) + 1(null)`. */ + memmove(ef->textbuf + selstart, ef->textbuf + getfrom, sizeof(*ef->textbuf) * size); + memmove(ef->textbufinfo + selstart, ef->textbufinfo + getfrom, sizeof(CharInfo) * size); ef->len -= ((selend - selstart) + 1); ef->selstart = ef->selend = 0; } @@ -1650,7 +1640,7 @@ static int insert_text_exec(bContext *C, wmOperator *op) MEM_freeN(inserted_text); MEM_freeN(inserted_utf8); - kill_selection(obedit, 1); + kill_selection(obedit, len); text_update_edited(C, obedit, FO_EDIT); return OPERATOR_FINISHED; -- cgit v1.2.3 From eb165f574b7433be39aa5cb9569e49b70ffb65c7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 9 Aug 2021 15:02:31 +1000 Subject: Cleanup: remove redundant imports The module was importing it's own functions. --- release/scripts/modules/bpy/utils/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py index 58b20d9e3c8..1fe73f50639 100644 --- a/release/scripts/modules/bpy/utils/__init__.py +++ b/release/scripts/modules/bpy/utils/__init__.py @@ -754,12 +754,10 @@ def register_classes_factory(classes): which simply registers and unregisters a sequence of classes. """ def register(): - from bpy.utils import register_class for cls in classes: register_class(cls) def unregister(): - from bpy.utils import unregister_class for cls in reversed(classes): unregister_class(cls) -- cgit v1.2.3 From d3a699925d77809572f103853423473432d17d9b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 9 Aug 2021 15:03:32 +1000 Subject: Cleanup: use 'cls' for class methods first argument --- release/scripts/modules/bl_i18n_utils/utils.py | 6 +++--- release/scripts/modules/bpy_types.py | 4 ++-- release/scripts/startup/bl_ui/space_view3d_toolbar.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/release/scripts/modules/bl_i18n_utils/utils.py b/release/scripts/modules/bl_i18n_utils/utils.py index fda93682dc5..95184853f73 100644 --- a/release/scripts/modules/bl_i18n_utils/utils.py +++ b/release/scripts/modules/bl_i18n_utils/utils.py @@ -1355,7 +1355,7 @@ class I18n: print(prefix.join(lines)) @classmethod - def check_py_module_has_translations(clss, src, settings=settings): + def check_py_module_has_translations(cls, src, settings=settings): """ Check whether a given src (a py module, either a directory or a py file) has some i18n translation data, and returns a tuple (src_file, translations_tuple) if yes, else (None, None). @@ -1367,11 +1367,11 @@ class I18n: if not fname.endswith(".py"): continue path = os.path.join(root, fname) - _1, txt, _2, has_trans = clss._parser_check_file(path) + _1, txt, _2, has_trans = cls._parser_check_file(path) if has_trans: txts.append((path, txt)) elif src.endswith(".py") and os.path.isfile(src): - _1, txt, _2, has_trans = clss._parser_check_file(src) + _1, txt, _2, has_trans = cls._parser_check_file(src) if has_trans: txts.append((src, txt)) for path, txt in txts: diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index 29b53aedf78..d60165f760c 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -761,9 +761,9 @@ class Macro(StructRNA): __slots__ = () @classmethod - def define(self, opname): + def define(cls, opname): from _bpy import ops - return ops.macro_define(self, opname) + return ops.macro_define(cls, opname) class PropertyGroup(StructRNA, metaclass=RNAMetaPropGroup): diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 46fed79332d..16b5ed33f3f 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1029,7 +1029,7 @@ class VIEW3D_PT_tools_vertexpaint_options(Panel, View3DPaintPanel): bl_options = {'DEFAULT_CLOSED'} @classmethod - def poll(self, _context): + def poll(cls, _context): # This is currently unused, since there aren't any Vertex Paint mode specific options. return False -- cgit v1.2.3 From 64d750fb730f978e27ad6e0ffefa25f5a872c15d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 9 Aug 2021 15:03:47 +1000 Subject: Cleanup: indentation --- .../keyconfig/keymap_data/industry_compatible_data.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 9d34b110afe..8e46e9c06df 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -139,12 +139,12 @@ def _template_items_basic_tools(*, connected=False): ] def _template_items_tool_select(params, operator, *, extend): - return [ - (operator, {"type": 'LEFTMOUSE', "value": 'PRESS'}, - {"properties": [("deselect_all", True)]}), - (operator, {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, - {"properties": [(extend, True)]}), - ] + return [ + (operator, {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": [("deselect_all", True)]}), + (operator, {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, + {"properties": [(extend, True)]}), + ] def _template_items_tool_select_actions(operator, *, type, value): -- cgit v1.2.3 From b417fb92513cf9c579d94218566067b20b1eeda0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 9 Aug 2021 15:05:04 +1000 Subject: Cleanup: return True/False from gpencil toolbar function This was returning None/False which could cause problems in the future. --- release/scripts/startup/bl_ui/space_toolsystem_toolbar.py | 1 + 1 file changed, 1 insertion(+) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 46a6944d2ea..bde710d8dbf 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1903,6 +1903,7 @@ class _defs_gpencil_paint: brush_basic__draw_color_selector(context, layout, brush, gp_settings, props) brush_basic_gpencil_paint_settings(layout, context, brush, compact=True) + return True @staticmethod def generate_from_brushes(context): -- cgit v1.2.3 From 52c349cfcd28f402e6e73450831078fbd3fbbf08 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 9 Aug 2021 10:26:58 +0200 Subject: Fix T90511: Cycles preview does not update once preview is done Caused by 4f64fa4f8628. Was a bad backport from the Cycles X branch: the fact that CPU and GPU has different reset code paths was not taken into account. --- intern/cycles/render/session.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 1a08d8f52d6..1b91c49f0ea 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -185,6 +185,8 @@ void Session::reset_gpu(BufferParams &buffer_params, int samples) gpu_need_display_buffer_update_ = false; gpu_need_display_buffer_update_cond_.notify_all(); + new_work_added_ = true; + pause_cond_.notify_all(); } -- cgit v1.2.3 From 909e0819ae939a8054bea2c9dcf389232744f3ae Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 9 Aug 2021 18:32:53 +1000 Subject: Fix T90532: Crash editing meshes with auto-smooth Caused by fix for T90256 and a misunderstanding in D11928. Don't skip tagging edges when the auto-smooth angle is 180 degrees since this skips topology checks which are needed for properly calculating edge loop normals. --- source/blender/bmesh/intern/bmesh_mesh_normals.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index 8eda38046a1..a5e41b74ee1 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -1116,8 +1116,6 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm, BMIter fiter; BMFace *f_curr; const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); - const bool check_angle = (split_angle < (float)M_PI); - const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f; MLoopNorSpaceArray _lnors_spacearr = {NULL}; @@ -1152,16 +1150,13 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm, do { BM_elem_index_set(l_curr, index_loop++); /* set_inline */ BM_elem_flag_disable(l_curr, BM_ELEM_TAG); - /* Needed for when #bm_mesh_edges_sharp_tag doesn't run. - * Mark smooth if there is no smoothing angle. */ - BM_elem_flag_enable(l_curr->e, BM_ELEM_TAG); } while ((l_curr = l_curr->next) != l_first); } bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); - if (split_angle_cos != -1.0f) { - bm_mesh_edges_sharp_tag(bm, fnos, has_clnors ? (float)M_PI : split_angle, false); - } + /* Always tag edges based on winding & sharp edge flag + * (even when the auto-smooth angle doesn't need to be calculated). */ + bm_mesh_edges_sharp_tag(bm, fnos, has_clnors ? (float)M_PI : split_angle, false); /* We now know edges that can be smoothed (they are tagged), * and edges that will be hard (they aren't). -- cgit v1.2.3 From 0986992dbd0d85363f86079a7390632df9cb9d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 9 Aug 2021 11:31:14 +0200 Subject: Cleanup: document `FileListReadJob::tmp_filelist` Add a comment to document what `FileListReadJob::tmp_filelist` is for, and how it's freed. No functional changes. --- source/blender/editors/space_file/filelist.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 4eb58642bba..fae28269349 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -3354,7 +3354,14 @@ typedef struct FileListReadJob { char main_name[FILE_MAX]; Main *current_main; struct FileList *filelist; - /** XXX We may use a simpler struct here... just a linked list and root path? */ + + /** Shallow copy of #filelist for thread-safe access. + * + * The job system calls #filelist_readjob_update which moves any read file from #tmp_filelist + * into #filelist in a thread-safe way. + * + * NOTE: #tmp_filelist is freed in #filelist_readjob_free, so any copied pointers need to be set + * to NULL to avoid double-freeing them. */ struct FileList *tmp_filelist; } FileListReadJob; -- cgit v1.2.3 From d6f162dfa946e8745276ba594c29ce48b299a760 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 9 Aug 2021 12:07:59 +0200 Subject: Fix T90540: NoneType object error with entering grease pencil draw mode The preview was not ready when the panel was displayed. Just need to check if None. --- release/scripts/startup/bl_ui/properties_paint_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 97a0c5abf24..ad963396022 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -1141,7 +1141,7 @@ def brush_basic__draw_color_selector(context, layout, brush, gp_settings, props) if not gp_settings.use_material_pin: ma = context.object.active_material icon_id = 0 - if ma: + if ma and ma.id_data.preview: icon_id = ma.id_data.preview.icon_id txt_ma = ma.name maxw = 25 -- cgit v1.2.3 From 7ea577eef3e51799efe9fc286847f32b3611373e Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 9 Aug 2021 12:04:12 +0200 Subject: Fix depsgraph check for tag during evaluation - Only do print when asked for tags debugging. - Add missing newline to the message. --- source/blender/depsgraph/intern/depsgraph_tag.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index ab93464d09a..dd96c5a3b2b 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -646,8 +646,8 @@ void graph_id_tag_update( { const int debug_flags = (graph != nullptr) ? DEG_debug_flags_get((::Depsgraph *)graph) : G.debug; if (graph != nullptr && graph->is_evaluating) { - if (debug_flags & G_DEBUG_DEPSGRAPH) { - printf("ID tagged for update during dependency graph evaluation."); + if (debug_flags & G_DEBUG_DEPSGRAPH_TAG) { + printf("ID tagged for update during dependency graph evaluation.\n"); } return; } -- cgit v1.2.3 From 0e4a250279ffc95636643a08462cca04b6dd17de Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 9 Aug 2021 12:05:11 +0200 Subject: Fix invalid helps and description of session UUID verification A copy-paste error. --- source/blender/blenkernel/BKE_global.h | 3 ++- source/creator/creator_args.c | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 31928b5e80a..89713e9ad0a 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -145,7 +145,8 @@ enum { G_DEBUG_DEPSGRAPH_TIME = (1 << 11), /* depsgraph timing statistics and messages */ G_DEBUG_DEPSGRAPH_NO_THREADS = (1 << 12), /* single threaded depsgraph */ G_DEBUG_DEPSGRAPH_PRETTY = (1 << 13), /* use pretty colors in depsgraph messages */ - G_DEBUG_DEPSGRAPH_UUID = (1 << 14), /* use pretty colors in depsgraph messages */ + G_DEBUG_DEPSGRAPH_UUID = (1 << 14), /* Verify validness of session-wide identifiers + * assigned to ID datablocks */ G_DEBUG_DEPSGRAPH = (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_EVAL | G_DEBUG_DEPSGRAPH_TAG | G_DEBUG_DEPSGRAPH_TIME | G_DEBUG_DEPSGRAPH_UUID), G_DEBUG_SIMDATA = (1 << 15), /* sim debug data display */ diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 0f450624691..85ba4eca307 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -994,6 +994,9 @@ static const char arg_handle_debug_mode_generic_set_doc_depsgraph_no_threads[] = static const char arg_handle_debug_mode_generic_set_doc_depsgraph_pretty[] = "\n\t" "Enable colors for dependency graph debug messages."; +static const char arg_handle_debug_mode_generic_set_doc_depsgraph_uuid[] = + "\n\t" + "Verify validness of session-wide identifiers assigned to ID datablocks."; static const char arg_handle_debug_mode_generic_set_doc_gpu_force_workarounds[] = "\n\t" "Enable workarounds for typical GPU issues and disable all GPU extensions."; @@ -2197,7 +2200,7 @@ void main_args_setup(bContext *C, bArgs *ba) BLI_args_add(ba, NULL, "--debug-depsgraph-uuid", - CB_EX(arg_handle_debug_mode_generic_set, depsgraph_build), + CB_EX(arg_handle_debug_mode_generic_set, depsgraph_uuid), (void *)G_DEBUG_DEPSGRAPH_UUID); BLI_args_add(ba, NULL, -- cgit v1.2.3 From 22ed1c7b61d54b5783552d99005be75a3967b8fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 9 Aug 2021 12:37:23 +0200 Subject: Cleanup: filelist, pass `FileListReadJob` to job functions Pass `FileListReadJob` to the `read_job_fn` callback, instead of exploding the struct into its individual fields, passing those as parameters, and marking a bunch of those as unused again. No functional changes. --- source/blender/editors/space_file/filelist.c | 126 ++++++++++----------------- 1 file changed, 48 insertions(+), 78 deletions(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index fae28269349..f8ae4be9471 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -379,6 +379,7 @@ enum { FLF_ASSETS_ONLY = 1 << 4, }; +struct FileListReadJob; typedef struct FileList { FileDirEntryArr filelist; @@ -415,8 +416,7 @@ typedef struct FileList { bool (*check_dir_fn)(struct FileList *, char *, const bool); /* Fill filelist (to be called by read job). */ - void (*read_job_fn)( - Main *, struct FileList *, const char *, short *, short *, float *, ThreadMutex *); + void (*read_job_fn)(struct FileListReadJob *, short *, short *, float *); /* Filter an entry of current filelist. */ bool (*filter_fn)(struct FileListInternEntry *, const char *, FileListFilter *); @@ -459,34 +459,22 @@ enum { static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX]; -static void filelist_readjob_main(Main *current_main, - FileList *filelist, - const char *main_name, +static void filelist_readjob_main(struct FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock); -static void filelist_readjob_lib(Main *current_main, - FileList *filelist, - const char *main_name, + float *progress); +static void filelist_readjob_lib(struct FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock); -static void filelist_readjob_dir(Main *current_main, - FileList *filelist, - const char *main_name, + float *progress); +static void filelist_readjob_dir(struct FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock); -static void filelist_readjob_main_assets(Main *current_main, - FileList *filelist, - const char *main_name, + float *progress); +static void filelist_readjob_main_assets(struct FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock); + float *progress); /* helper, could probably go in BKE actually? */ static int groupname_to_code(const char *group); @@ -3133,14 +3121,29 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist) } #endif +typedef struct FileListReadJob { + ThreadMutex lock; + char main_name[FILE_MAX]; + Main *current_main; + struct FileList *filelist; + + /** Shallow copy of #filelist for thread-safe access. + * + * The job system calls #filelist_readjob_update which moves any read file from #tmp_filelist + * into #filelist in a thread-safe way. + * + * NOTE: #tmp_filelist is freed in #filelist_readjob_free, so any copied pointers need to be set + * to NULL to avoid double-freeing them. */ + struct FileList *tmp_filelist; +} FileListReadJob; + static void filelist_readjob_do(const bool do_lib, - FileList *filelist, - const char *main_name, + FileListReadJob *job_params, const short *stop, short *do_update, - float *progress, - ThreadMutex *lock) + float *progress) { + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ ListBase entries = {0}; BLI_Stack *todo_dirs; TodoDir *td_dir; @@ -3164,7 +3167,7 @@ static void filelist_readjob_do(const bool do_lib, BLI_strncpy(dir, filelist->filelist.root, sizeof(dir)); BLI_strncpy(filter_glob, filelist->filter_data.filter_glob, sizeof(filter_glob)); - BLI_path_normalize_dir(main_name, dir); + BLI_path_normalize_dir(job_params->main_name, dir); td_dir->dir = BLI_strdup(dir); while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) { @@ -3199,7 +3202,7 @@ static void filelist_readjob_do(const bool do_lib, if (!nbr_entries) { is_lib = false; nbr_entries = filelist_readjob_list_dir( - subdir, &entries, filter_glob, do_lib, main_name, skip_currpar); + subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar); } for (entry = entries.first; entry; entry = entry->next) { @@ -3226,7 +3229,7 @@ static void filelist_readjob_do(const bool do_lib, else { /* We have a directory we want to list, add it to todo list! */ BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath); - BLI_path_normalize_dir(main_name, dir); + BLI_path_normalize_dir(job_params->main_name, dir); td_dir = BLI_stack_push_r(todo_dirs); td_dir->level = recursion_level + 1; td_dir->dir = BLI_strdup(dir); @@ -3236,14 +3239,14 @@ static void filelist_readjob_do(const bool do_lib, } if (nbr_entries) { - BLI_mutex_lock(lock); + BLI_mutex_lock(&job_params->lock); *do_update = true; BLI_movelisttolist(&filelist->filelist.entries, &entries); filelist->filelist.nbr_entries += nbr_entries; - BLI_mutex_unlock(lock); + BLI_mutex_unlock(&job_params->lock); } nbr_done_dirs++; @@ -3261,51 +3264,40 @@ static void filelist_readjob_do(const bool do_lib, BLI_stack_free(todo_dirs); } -static void filelist_readjob_dir(Main *UNUSED(current_main), - FileList *filelist, - const char *main_name, +static void filelist_readjob_dir(FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock) + float *progress) { - filelist_readjob_do(false, filelist, main_name, stop, do_update, progress, lock); + filelist_readjob_do(false, job_params, stop, do_update, progress); } -static void filelist_readjob_lib(Main *UNUSED(current_main), - FileList *filelist, - const char *main_name, +static void filelist_readjob_lib(FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock) + float *progress) { - filelist_readjob_do(true, filelist, main_name, stop, do_update, progress, lock); + filelist_readjob_do(true, job_params, stop, do_update, progress); } -static void filelist_readjob_main(Main *current_main, - FileList *filelist, - const char *main_name, +static void filelist_readjob_main(FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock) + float *progress) { /* TODO! */ - filelist_readjob_dir(current_main, filelist, main_name, stop, do_update, progress, lock); + filelist_readjob_dir(job_params, stop, do_update, progress); } /** * \warning Acts on main, so NOT thread-safe! */ -static void filelist_readjob_main_assets(Main *current_main, - FileList *filelist, - const char *UNUSED(main_name), +static void filelist_readjob_main_assets(FileListReadJob *job_params, short *UNUSED(stop), short *do_update, - float *UNUSED(progress), - ThreadMutex *UNUSED(lock)) + float *UNUSED(progress)) { + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); @@ -3317,7 +3309,7 @@ static void filelist_readjob_main_assets(Main *current_main, ID *id_iter; int nbr_entries = 0; - FOREACH_MAIN_ID_BEGIN (current_main, id_iter) { + FOREACH_MAIN_ID_BEGIN (job_params->current_main, id_iter) { if (!id_iter->asset_data) { continue; } @@ -3349,22 +3341,6 @@ static void filelist_readjob_main_assets(Main *current_main, } } -typedef struct FileListReadJob { - ThreadMutex lock; - char main_name[FILE_MAX]; - Main *current_main; - struct FileList *filelist; - - /** Shallow copy of #filelist for thread-safe access. - * - * The job system calls #filelist_readjob_update which moves any read file from #tmp_filelist - * into #filelist in a thread-safe way. - * - * NOTE: #tmp_filelist is freed in #filelist_readjob_free, so any copied pointers need to be set - * to NULL to avoid double-freeing them. */ - struct FileList *tmp_filelist; -} FileListReadJob; - static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update, float *progress) { FileListReadJob *flrj = flrjv; @@ -3392,13 +3368,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update BLI_mutex_unlock(&flrj->lock); - flrj->tmp_filelist->read_job_fn(flrj->current_main, - flrj->tmp_filelist, - flrj->main_name, - stop, - do_update, - progress, - &flrj->lock); + flrj->tmp_filelist->read_job_fn(flrj, stop, do_update, progress); } static void filelist_readjob_update(void *flrjv) -- cgit v1.2.3 From 73b047bcd431c57687bc0c501dea97f44bd28b22 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 9 Aug 2021 12:59:28 +0200 Subject: Cleanup: Changed keyframe_keylist to CPP. Just a straight-forward change. Not utilizing CPP features. --- source/blender/editors/animation/CMakeLists.txt | 2 +- .../blender/editors/animation/keyframes_keylist.c | 872 -------------------- .../blender/editors/animation/keyframes_keylist.cc | 876 +++++++++++++++++++++ 3 files changed, 877 insertions(+), 873 deletions(-) delete mode 100644 source/blender/editors/animation/keyframes_keylist.c create mode 100644 source/blender/editors/animation/keyframes_keylist.cc diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt index 7a53b54b5a4..d9f52d90766 100644 --- a/source/blender/editors/animation/CMakeLists.txt +++ b/source/blender/editors/animation/CMakeLists.txt @@ -47,7 +47,7 @@ set(SRC keyframes_draw.c keyframes_edit.c keyframes_general.c - keyframes_keylist.c + keyframes_keylist.cc keyframing.c keyingsets.c time_scrub_ui.c diff --git a/source/blender/editors/animation/keyframes_keylist.c b/source/blender/editors/animation/keyframes_keylist.c deleted file mode 100644 index 98aedb9cd0c..00000000000 --- a/source/blender/editors/animation/keyframes_keylist.c +++ /dev/null @@ -1,872 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung - * All rights reserved. - */ - -/** \file - * \ingroup edanimation - */ - -/* System includes ----------------------------------------------------- */ - -#include -#include -#include -#include - -#include "MEM_guardedalloc.h" - -#include "BLI_dlrbTree.h" -#include "BLI_listbase.h" -#include "BLI_range.h" -#include "BLI_utildefines.h" - -#include "DNA_anim_types.h" -#include "DNA_cachefile_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_mask_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "BKE_fcurve.h" - -#include "ED_anim_api.h" -#include "ED_keyframes_keylist.h" - -/* *************************** Keyframe Processing *************************** */ - -typedef struct AnimKeylist { - DLRBT_Tree keys; -} AnimKeylist; - -static void ED_keylist_init(AnimKeylist *keylist) -{ - BLI_dlrbTree_init(&keylist->keys); -} - -AnimKeylist *ED_keylist_create(void) -{ - AnimKeylist *keylist = MEM_callocN(sizeof(AnimKeylist), __func__); - ED_keylist_init(keylist); - return keylist; -} - -void ED_keylist_free(AnimKeylist *keylist) -{ - BLI_assert(keylist); - BLI_dlrbTree_free(&keylist->keys); - MEM_freeN(keylist); -} - -const ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, float cfra) -{ - return (const ActKeyColumn *)BLI_dlrbTree_search_exact( - &keylist->keys, compare_ak_cfraPtr, &cfra); -} - -const ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, float cfra) -{ - return (const ActKeyColumn *)BLI_dlrbTree_search_next(&keylist->keys, compare_ak_cfraPtr, &cfra); -} - -const ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, float cfra) -{ - return (const ActKeyColumn *)BLI_dlrbTree_search_prev(&keylist->keys, compare_ak_cfraPtr, &cfra); -} - -/* TODO(jbakker): Should we change this to use `ED_keylist_find_next(keys, min_fra)` and only check - * boundary of `max_fra`. */ -const ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, - const Range2f frame_range) -{ - for (const ActKeyColumn *ak = keylist->keys.root; ak; - ak = (ak->cfra < frame_range.min) ? ak->right : ak->left) { - if (range2f_in_range(&frame_range, ak->cfra)) { - return ak; - } - } - return NULL; -} - -bool ED_keylist_is_empty(const struct AnimKeylist *keylist) -{ - return keylist->keys.root == NULL; -} - -const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist) -{ - return (ListBase *)&keylist->keys; -} - -bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range) -{ - BLI_assert(r_frame_range); - - if (ED_keylist_is_empty(keylist)) { - return false; - } - - const ActKeyColumn *first_column = (const ActKeyColumn *)keylist->keys.first; - r_frame_range->min = first_column->cfra; - - const ActKeyColumn *last_column = (const ActKeyColumn *)keylist->keys.last; - r_frame_range->max = last_column->cfra; - - return true; -} -/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */ - -BLI_INLINE bool is_cfra_eq(const float a, const float b) -{ - return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH); -} - -BLI_INLINE bool is_cfra_lt(const float a, const float b) -{ - return (b - a) > BEZT_BINARYSEARCH_THRESH; -} - -/* Comparator callback used for ActKeyColumns and cframe float-value pointer */ -/* NOTE: this is exported to other modules that use the ActKeyColumns for finding keyframes */ -short compare_ak_cfraPtr(void *node, void *data) -{ - ActKeyColumn *ak = (ActKeyColumn *)node; - const float *cframe = data; - const float val = *cframe; - - if (is_cfra_eq(val, ak->cfra)) { - return 0; - } - - if (val < ak->cfra) { - return -1; - } - return 1; -} - -/* --------------- */ - -/* Set of references to three logically adjacent keys. */ -typedef struct BezTripleChain { - /* Current keyframe. */ - BezTriple *cur; - - /* Logical neighbors. May be NULL. */ - BezTriple *prev, *next; -} BezTripleChain; - -/* Categorize the interpolation & handle type of the keyframe. */ -static eKeyframeHandleDrawOpts bezt_handle_type(const BezTriple *bezt) -{ - if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) { - return KEYFRAME_HANDLE_AUTO_CLAMP; - } - if (ELEM(bezt->h1, HD_AUTO_ANIM, HD_AUTO) && ELEM(bezt->h2, HD_AUTO_ANIM, HD_AUTO)) { - return KEYFRAME_HANDLE_AUTO; - } - if (bezt->h1 == HD_VECT && bezt->h2 == HD_VECT) { - return KEYFRAME_HANDLE_VECTOR; - } - if (ELEM(HD_FREE, bezt->h1, bezt->h2)) { - return KEYFRAME_HANDLE_FREE; - } - return KEYFRAME_HANDLE_ALIGNED; -} - -/* Determine if the keyframe is an extreme by comparing with neighbors. - * Ends of fixed-value sections and of the whole curve are also marked. - */ -static eKeyframeExtremeDrawOpts bezt_extreme_type(const BezTripleChain *chain) -{ - if (chain->prev == NULL && chain->next == NULL) { - return KEYFRAME_EXTREME_NONE; - } - - /* Keyframe values for the current one and neighbors. */ - const float cur_y = chain->cur->vec[1][1]; - float prev_y = cur_y, next_y = cur_y; - - if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) { - prev_y = chain->prev->vec[1][1]; - } - if (chain->next && !IS_EQF(cur_y, chain->next->vec[1][1])) { - next_y = chain->next->vec[1][1]; - } - - /* Static hold. */ - if (prev_y == cur_y && next_y == cur_y) { - return KEYFRAME_EXTREME_FLAT; - } - - /* Middle of an incline. */ - if ((prev_y < cur_y && next_y > cur_y) || (prev_y > cur_y && next_y < cur_y)) { - return KEYFRAME_EXTREME_NONE; - } - - /* Bezier handle values for the overshoot check. */ - const bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ; - const bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ; - const float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y; - const float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y; - - /* Detect extremes. One of the neighbors is allowed to be equal to current. */ - if (prev_y < cur_y || next_y < cur_y) { - const bool is_overshoot = (handle_l > cur_y || handle_r > cur_y); - - return KEYFRAME_EXTREME_MAX | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0); - } - - if (prev_y > cur_y || next_y > cur_y) { - const bool is_overshoot = (handle_l < cur_y || handle_r < cur_y); - - return KEYFRAME_EXTREME_MIN | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0); - } - - return KEYFRAME_EXTREME_NONE; -} - -/* Comparator callback used for ActKeyColumns and BezTripleChain */ -static short compare_ak_bezt(void *node, void *data) -{ - BezTripleChain *chain = data; - - return compare_ak_cfraPtr(node, &chain->cur->vec[1][0]); -} - -/* New node callback used for building ActKeyColumns from BezTripleChain */ -static DLRBT_Node *nalloc_ak_bezt(void *data) -{ - ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); - const BezTripleChain *chain = data; - const BezTriple *bezt = chain->cur; - - /* store settings based on state of BezTriple */ - ak->cfra = bezt->vec[1][0]; - ak->sel = BEZT_ISSEL_ANY(bezt) ? SELECT : 0; - ak->key_type = BEZKEYTYPE(bezt); - ak->handle_type = bezt_handle_type(bezt); - ak->extreme_type = bezt_extreme_type(chain); - - /* count keyframes in this column */ - ak->totkey = 1; - - return (DLRBT_Node *)ak; -} - -/* Node updater callback used for building ActKeyColumns from BezTripleChain */ -static void nupdate_ak_bezt(void *node, void *data) -{ - ActKeyColumn *ak = node; - const BezTripleChain *chain = data; - const BezTriple *bezt = chain->cur; - - /* set selection status and 'touched' status */ - if (BEZT_ISSEL_ANY(bezt)) { - ak->sel = SELECT; - } - - /* count keyframes in this column */ - ak->totkey++; - - /* For keyframe type, 'proper' keyframes have priority over breakdowns - * (and other types for now). */ - if (BEZKEYTYPE(bezt) == BEZT_KEYTYPE_KEYFRAME) { - ak->key_type = BEZT_KEYTYPE_KEYFRAME; - } - - /* For interpolation type, select the highest value (enum is sorted). */ - ak->handle_type = MAX2(ak->handle_type, bezt_handle_type(bezt)); - - /* For extremes, detect when combining different states. */ - const char new_extreme = bezt_extreme_type(chain); - - if (new_extreme != ak->extreme_type) { - /* Replace the flat status without adding mixed. */ - if (ak->extreme_type == KEYFRAME_EXTREME_FLAT) { - ak->extreme_type = new_extreme; - } - else if (new_extreme != KEYFRAME_EXTREME_FLAT) { - ak->extreme_type |= (new_extreme | KEYFRAME_EXTREME_MIXED); - } - } -} - -/* ......... */ - -/* Comparator callback used for ActKeyColumns and GPencil frame */ -static short compare_ak_gpframe(void *node, void *data) -{ - const bGPDframe *gpf = (bGPDframe *)data; - - float frame = gpf->framenum; - return compare_ak_cfraPtr(node, &frame); -} - -/* New node callback used for building ActKeyColumns from GPencil frames */ -static DLRBT_Node *nalloc_ak_gpframe(void *data) -{ - ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"); - const bGPDframe *gpf = (bGPDframe *)data; - - /* store settings based on state of BezTriple */ - ak->cfra = gpf->framenum; - ak->sel = (gpf->flag & GP_FRAME_SELECT) ? SELECT : 0; - ak->key_type = gpf->key_type; - - /* count keyframes in this column */ - ak->totkey = 1; - /* Set as visible block. */ - ak->totblock = 1; - ak->block.sel = ak->sel; - ak->block.flag |= ACTKEYBLOCK_FLAG_GPENCIL; - - return (DLRBT_Node *)ak; -} - -/* Node updater callback used for building ActKeyColumns from GPencil frames */ -static void nupdate_ak_gpframe(void *node, void *data) -{ - ActKeyColumn *ak = (ActKeyColumn *)node; - const bGPDframe *gpf = (bGPDframe *)data; - - /* set selection status and 'touched' status */ - if (gpf->flag & GP_FRAME_SELECT) { - ak->sel = SELECT; - } - - /* count keyframes in this column */ - ak->totkey++; - - /* for keyframe type, 'proper' keyframes have priority over breakdowns - * (and other types for now). */ - if (gpf->key_type == BEZT_KEYTYPE_KEYFRAME) { - ak->key_type = BEZT_KEYTYPE_KEYFRAME; - } -} - -/* ......... */ - -/* Comparator callback used for ActKeyColumns and GPencil frame */ -static short compare_ak_masklayshape(void *node, void *data) -{ - const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; - - float frame = masklay_shape->frame; - return compare_ak_cfraPtr(node, &frame); -} - -/* New node callback used for building ActKeyColumns from GPencil frames */ -static DLRBT_Node *nalloc_ak_masklayshape(void *data) -{ - ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"); - const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; - - /* store settings based on state of BezTriple */ - ak->cfra = masklay_shape->frame; - ak->sel = (masklay_shape->flag & MASK_SHAPE_SELECT) ? SELECT : 0; - - /* count keyframes in this column */ - ak->totkey = 1; - - return (DLRBT_Node *)ak; -} - -/* Node updater callback used for building ActKeyColumns from GPencil frames */ -static void nupdate_ak_masklayshape(void *node, void *data) -{ - ActKeyColumn *ak = (ActKeyColumn *)node; - const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; - - /* set selection status and 'touched' status */ - if (masklay_shape->flag & MASK_SHAPE_SELECT) { - ak->sel = SELECT; - } - - /* count keyframes in this column */ - ak->totkey++; -} - -/* --------------- */ - -/* Add the given BezTriple to the given 'list' of Keyframes */ -static void add_bezt_to_keycolumns_list(AnimKeylist *keylist, BezTripleChain *bezt) -{ - if (ELEM(NULL, keylist, bezt)) { - return; - } - - BLI_dlrbTree_add(&keylist->keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt); -} - -/* Add the given GPencil Frame to the given 'list' of Keyframes */ -static void add_gpframe_to_keycolumns_list(AnimKeylist *keylist, bGPDframe *gpf) -{ - if (ELEM(NULL, keylist, gpf)) { - return; - } - - BLI_dlrbTree_add(&keylist->keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf); -} - -/* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */ -static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape *masklay_shape) -{ - if (ELEM(NULL, keylist, masklay_shape)) { - return; - } - - BLI_dlrbTree_add(&keylist->keys, - compare_ak_masklayshape, - nalloc_ak_masklayshape, - nupdate_ak_masklayshape, - masklay_shape); -} - -/* ActKeyBlocks (Long Keyframes) ------------------------------------------ */ - -static const ActKeyBlockInfo dummy_keyblock = {0}; - -static void compute_keyblock_data(ActKeyBlockInfo *info, - const BezTriple *prev, - const BezTriple *beztn) -{ - memset(info, 0, sizeof(ActKeyBlockInfo)); - - if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) { - /* Animator tagged a "moving hold" - * - Previous key must also be tagged as a moving hold, otherwise - * we're just dealing with the first of a pair, and we don't - * want to be creating any phantom holds... - */ - if (BEZKEYTYPE(prev) == BEZT_KEYTYPE_MOVEHOLD) { - info->flag |= ACTKEYBLOCK_FLAG_MOVING_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD; - } - } - - /* Check for same values... - * - Handles must have same central value as each other - * - Handles which control that section of the curve must be constant - */ - if (IS_EQF(beztn->vec[1][1], prev->vec[1][1])) { - bool hold; - - /* Only check handles in case of actual bezier interpolation. */ - if (prev->ipo == BEZT_IPO_BEZ) { - hold = IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) && - IS_EQF(prev->vec[1][1], prev->vec[2][1]); - } - /* This interpolation type induces movement even between identical keys. */ - else { - hold = !ELEM(prev->ipo, BEZT_IPO_ELASTIC); - } - - if (hold) { - info->flag |= ACTKEYBLOCK_FLAG_STATIC_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD; - } - } - - /* Remember non-bezier interpolation info. */ - if (prev->ipo != BEZT_IPO_BEZ) { - info->flag |= ACTKEYBLOCK_FLAG_NON_BEZIER; - } - - info->sel = BEZT_ISSEL_ANY(prev) || BEZT_ISSEL_ANY(beztn); -} - -static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block) -{ - /* New curve and block. */ - if (col->totcurve <= 1 && col->totblock == 0) { - memcpy(&col->block, block, sizeof(ActKeyBlockInfo)); - } - /* Existing curve. */ - else { - col->block.conflict |= (col->block.flag ^ block->flag); - col->block.flag |= block->flag; - col->block.sel |= block->sel; - } - - if (block->flag) { - col->totblock++; - } -} - -static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len) -{ - ActKeyColumn *col = keylist->keys.first; - - if (bezt && bezt_len >= 2) { - ActKeyBlockInfo block; - - /* Find the first key column while inserting dummy blocks. */ - for (; col != NULL && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) { - add_keyblock_info(col, &dummy_keyblock); - } - - BLI_assert(col != NULL); - - /* Insert real blocks. */ - for (int v = 1; col != NULL && v < bezt_len; v++, bezt++) { - /* Wrong order of bezier keys: resync position. */ - if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) { - /* Backtrack to find the right location. */ - if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) { - ActKeyColumn *newcol = (ActKeyColumn *)BLI_dlrbTree_search_exact( - &keylist->keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]); - - if (newcol != NULL) { - col = newcol; - - /* The previous keyblock is garbage too. */ - if (col->prev != NULL) { - add_keyblock_info(col->prev, &dummy_keyblock); - } - } - else { - BLI_assert(false); - } - } - - continue; - } - - /* Normal sequence */ - BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0])); - - compute_keyblock_data(&block, bezt, bezt + 1); - - for (; col != NULL && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) { - add_keyblock_info(col, &block); - } - - BLI_assert(col != NULL); - } - } - - /* Insert dummy blocks at the end. */ - for (; col != NULL; col = col->next) { - add_keyblock_info(col, &dummy_keyblock); - } -} - -/* Walk through columns and propagate blocks and totcurve. - * - * This must be called even by animation sources that don't generate - * keyblocks to keep the data structure consistent after adding columns. - */ -static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len) -{ - /* Recompute the prev/next linked list. */ - BLI_dlrbTree_linkedlist_sync(&keylist->keys); - - /* Find the curve count */ - int max_curve = 0; - - LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) { - max_curve = MAX2(max_curve, col->totcurve); - } - - /* Propagate blocks to inserted keys */ - ActKeyColumn *prev_ready = NULL; - - LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) { - /* Pre-existing column. */ - if (col->totcurve > 0) { - prev_ready = col; - } - /* Newly inserted column, so copy block data from previous. */ - else if (prev_ready != NULL) { - col->totblock = prev_ready->totblock; - memcpy(&col->block, &prev_ready->block, sizeof(ActKeyBlockInfo)); - } - - col->totcurve = max_curve + 1; - } - - /* Add blocks on top */ - add_bezt_to_keyblocks_list(keylist, bezt, bezt_len); -} - -/* --------- */ - -bool actkeyblock_is_valid(const ActKeyColumn *ac) -{ - return ac != NULL && ac->next != NULL && ac->totblock > 0; -} - -/* Checks if ActKeyBlock should exist... */ -int actkeyblock_get_valid_hold(const ActKeyColumn *ac) -{ - /* check that block is valid */ - if (!actkeyblock_is_valid(ac)) { - return 0; - } - - const int hold_mask = (ACTKEYBLOCK_FLAG_ANY_HOLD | ACTKEYBLOCK_FLAG_STATIC_HOLD); - return (ac->block.flag & ~ac->block.conflict) & hold_mask; -} - -/* *************************** Keyframe List Conversions *************************** */ - -void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, const int saction_flag) -{ - if (ac) { - ListBase anim_data = {NULL, NULL}; - - /* get F-Curves to take keyframes from */ - const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - - /* loop through each F-Curve, grabbing the keyframes */ - for (const bAnimListElem *ale = anim_data.first; ale; ale = ale->next) { - /* Why not use all #eAnim_KeyType here? - * All of the other key types are actually "summaries" themselves, - * and will just end up duplicating stuff that comes up through - * standard filtering of just F-Curves. Given the way that these work, - * there isn't really any benefit at all from including them. - Aligorith */ - - switch (ale->datatype) { - case ALE_FCURVE: - fcurve_to_keylist(ale->adt, ale->data, keylist, saction_flag); - break; - case ALE_MASKLAY: - mask_to_keylist(ac->ads, ale->data, keylist); - break; - case ALE_GPFRAME: - gpl_to_keylist(ac->ads, ale->data, keylist); - break; - default: - // printf("%s: datatype %d unhandled\n", __func__, ale->datatype); - break; - } - } - - ANIM_animdata_freelist(&anim_data); - } -} - -void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, const int saction_flag) -{ - bAnimContext ac = {NULL}; - ListBase anim_data = {NULL, NULL}; - - bAnimListElem dummychan = {NULL}; - - if (sce == NULL) { - return; - } - - /* create a dummy wrapper data to work with */ - dummychan.type = ANIMTYPE_SCENE; - dummychan.data = sce; - dummychan.id = &sce->id; - dummychan.adt = sce->adt; - - ac.ads = ads; - ac.data = &dummychan; - ac.datatype = ANIMCONT_CHANNEL; - - /* get F-Curves to take keyframes from */ - const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - /* loop through each F-Curve, grabbing the keyframes */ - for (const bAnimListElem *ale = anim_data.first; ale; ale = ale->next) { - fcurve_to_keylist(ale->adt, ale->data, keylist, saction_flag); - } - - ANIM_animdata_freelist(&anim_data); -} - -void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, const int saction_flag) -{ - bAnimContext ac = {NULL}; - ListBase anim_data = {NULL, NULL}; - - bAnimListElem dummychan = {NULL}; - Base dummybase = {NULL}; - - if (ob == NULL) { - return; - } - - /* create a dummy wrapper data to work with */ - dummybase.object = ob; - - dummychan.type = ANIMTYPE_OBJECT; - dummychan.data = &dummybase; - dummychan.id = &ob->id; - dummychan.adt = ob->adt; - - ac.ads = ads; - ac.data = &dummychan; - ac.datatype = ANIMCONT_CHANNEL; - - /* get F-Curves to take keyframes from */ - const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - /* loop through each F-Curve, grabbing the keyframes */ - for (const bAnimListElem *ale = anim_data.first; ale; ale = ale->next) { - fcurve_to_keylist(ale->adt, ale->data, keylist, saction_flag); - } - - ANIM_animdata_freelist(&anim_data); -} - -void cachefile_to_keylist(bDopeSheet *ads, - CacheFile *cache_file, - AnimKeylist *keylist, - const int saction_flag) -{ - if (cache_file == NULL) { - return; - } - - /* create a dummy wrapper data to work with */ - bAnimListElem dummychan = {NULL}; - dummychan.type = ANIMTYPE_DSCACHEFILE; - dummychan.data = cache_file; - dummychan.id = &cache_file->id; - dummychan.adt = cache_file->adt; - - bAnimContext ac = {NULL}; - ac.ads = ads; - ac.data = &dummychan; - ac.datatype = ANIMCONT_CHANNEL; - - /* get F-Curves to take keyframes from */ - ListBase anim_data = {NULL, NULL}; - const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - /* loop through each F-Curve, grabbing the keyframes */ - LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) { - fcurve_to_keylist(ale->adt, ale->data, keylist, saction_flag); - } - - ANIM_animdata_freelist(&anim_data); -} - -void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag) -{ - if (fcu && fcu->totvert && fcu->bezt) { - /* apply NLA-mapping (if applicable) */ - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); - } - - /* Check if the curve is cyclic. */ - bool is_cyclic = BKE_fcurve_is_cyclic(fcu) && (fcu->totvert >= 2); - bool do_extremes = (saction_flag & SACTION_SHOW_EXTREMES) != 0; - - /* loop through beztriples, making ActKeysColumns */ - BezTripleChain chain = {0}; - - for (int v = 0; v < fcu->totvert; v++) { - chain.cur = &fcu->bezt[v]; - - /* Neighbor keys, accounting for being cyclic. */ - if (do_extremes) { - chain.prev = (v > 0) ? &fcu->bezt[v - 1] : is_cyclic ? &fcu->bezt[fcu->totvert - 2] : NULL; - chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] : is_cyclic ? &fcu->bezt[1] : NULL; - } - - add_bezt_to_keycolumns_list(keylist, &chain); - } - - /* Update keyblocks. */ - update_keyblocks(keylist, fcu->bezt, fcu->totvert); - - /* unapply NLA-mapping if applicable */ - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); - } - } -} - -void agroup_to_keylist(AnimData *adt, - bActionGroup *agrp, - AnimKeylist *keylist, - const int saction_flag) -{ - FCurve *fcu; - - if (agrp) { - /* loop through F-Curves */ - for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next) { - fcurve_to_keylist(adt, fcu, keylist, saction_flag); - } - } -} - -void action_to_keylist(AnimData *adt, bAction *act, AnimKeylist *keylist, const int saction_flag) -{ - FCurve *fcu; - - if (act) { - /* loop through F-Curves */ - for (fcu = act->curves.first; fcu; fcu = fcu->next) { - fcurve_to_keylist(adt, fcu, keylist, saction_flag); - } - } -} - -void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, AnimKeylist *keylist, const bool active) -{ - bGPDlayer *gpl; - - if (gpd && keylist) { - /* for now, just aggregate out all the frames, but only for visible layers */ - for (gpl = gpd->layers.last; gpl; gpl = gpl->prev) { - if ((gpl->flag & GP_LAYER_HIDE) == 0) { - if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) { - gpl_to_keylist(ads, gpl, keylist); - } - } - } - } -} - -void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, AnimKeylist *keylist) -{ - bGPDframe *gpf; - - if (gpl && keylist) { - /* Although the frames should already be in an ordered list, - * they are not suitable for displaying yet. */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - add_gpframe_to_keycolumns_list(keylist, gpf); - } - - update_keyblocks(keylist, NULL, 0); - } -} - -void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, AnimKeylist *keylist) -{ - MaskLayerShape *masklay_shape; - - if (masklay && keylist) { - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { - add_masklay_to_keycolumns_list(keylist, masklay_shape); - } - - update_keyblocks(keylist, NULL, 0); - } -} diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc new file mode 100644 index 00000000000..85036efc983 --- /dev/null +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -0,0 +1,876 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + */ + +/** \file + * \ingroup edanimation + */ + +/* System includes ----------------------------------------------------- */ + +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_dlrbTree.h" +#include "BLI_listbase.h" +#include "BLI_range.h" +#include "BLI_utildefines.h" + +#include "DNA_anim_types.h" +#include "DNA_cachefile_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_mask_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_fcurve.h" + +#include "ED_anim_api.h" +#include "ED_keyframes_keylist.h" + +extern "C" { +/* *************************** Keyframe Processing *************************** */ + +struct AnimKeylist { + DLRBT_Tree keys; +}; + +static void ED_keylist_init(AnimKeylist *keylist) +{ + BLI_dlrbTree_init(&keylist->keys); +} + +AnimKeylist *ED_keylist_create(void) +{ + AnimKeylist *keylist = static_cast(MEM_callocN(sizeof(AnimKeylist), __func__)); + ED_keylist_init(keylist); + return keylist; +} + +void ED_keylist_free(AnimKeylist *keylist) +{ + BLI_assert(keylist); + BLI_dlrbTree_free(&keylist->keys); + MEM_freeN(keylist); +} + +const ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, float cfra) +{ + return (const ActKeyColumn *)BLI_dlrbTree_search_exact( + &keylist->keys, compare_ak_cfraPtr, &cfra); +} + +const ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, float cfra) +{ + return (const ActKeyColumn *)BLI_dlrbTree_search_next(&keylist->keys, compare_ak_cfraPtr, &cfra); +} + +const ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, float cfra) +{ + return (const ActKeyColumn *)BLI_dlrbTree_search_prev(&keylist->keys, compare_ak_cfraPtr, &cfra); +} + +/* TODO(jbakker): Should we change this to use `ED_keylist_find_next(keys, min_fra)` and only check + * boundary of `max_fra`. */ +const ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, + const Range2f frame_range) +{ + for (const ActKeyColumn *ak = static_cast(keylist->keys.root); ak; + ak = static_cast((ak->cfra < frame_range.min) ? ak->right : + ak->left)) { + if (range2f_in_range(&frame_range, ak->cfra)) { + return ak; + } + } + return nullptr; +} + +bool ED_keylist_is_empty(const struct AnimKeylist *keylist) +{ + return keylist->keys.root == nullptr; +} + +const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist) +{ + return (ListBase *)&keylist->keys; +} + +bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range) +{ + BLI_assert(r_frame_range); + + if (ED_keylist_is_empty(keylist)) { + return false; + } + + const ActKeyColumn *first_column = (const ActKeyColumn *)keylist->keys.first; + r_frame_range->min = first_column->cfra; + + const ActKeyColumn *last_column = (const ActKeyColumn *)keylist->keys.last; + r_frame_range->max = last_column->cfra; + + return true; +} +/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */ + +BLI_INLINE bool is_cfra_eq(const float a, const float b) +{ + return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH); +} + +BLI_INLINE bool is_cfra_lt(const float a, const float b) +{ + return (b - a) > BEZT_BINARYSEARCH_THRESH; +} + +/* Comparator callback used for ActKeyColumns and cframe float-value pointer */ +/* NOTE: this is exported to other modules that use the ActKeyColumns for finding keyframes */ +short compare_ak_cfraPtr(void *node, void *data) +{ + ActKeyColumn *ak = (ActKeyColumn *)node; + const float *cframe = static_cast(data); + const float val = *cframe; + + if (is_cfra_eq(val, ak->cfra)) { + return 0; + } + + if (val < ak->cfra) { + return -1; + } + return 1; +} + +/* --------------- */ + +/* Set of references to three logically adjacent keys. */ +struct BezTripleChain { + /* Current keyframe. */ + BezTriple *cur; + + /* Logical neighbors. May be nullptr. */ + BezTriple *prev, *next; +}; + +/* Categorize the interpolation & handle type of the keyframe. */ +static eKeyframeHandleDrawOpts bezt_handle_type(const BezTriple *bezt) +{ + if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) { + return KEYFRAME_HANDLE_AUTO_CLAMP; + } + if (ELEM(bezt->h1, HD_AUTO_ANIM, HD_AUTO) && ELEM(bezt->h2, HD_AUTO_ANIM, HD_AUTO)) { + return KEYFRAME_HANDLE_AUTO; + } + if (bezt->h1 == HD_VECT && bezt->h2 == HD_VECT) { + return KEYFRAME_HANDLE_VECTOR; + } + if (ELEM(HD_FREE, bezt->h1, bezt->h2)) { + return KEYFRAME_HANDLE_FREE; + } + return KEYFRAME_HANDLE_ALIGNED; +} + +/* Determine if the keyframe is an extreme by comparing with neighbors. + * Ends of fixed-value sections and of the whole curve are also marked. + */ +static eKeyframeExtremeDrawOpts bezt_extreme_type(const BezTripleChain *chain) +{ + if (chain->prev == nullptr && chain->next == nullptr) { + return KEYFRAME_EXTREME_NONE; + } + + /* Keyframe values for the current one and neighbors. */ + const float cur_y = chain->cur->vec[1][1]; + float prev_y = cur_y, next_y = cur_y; + + if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) { + prev_y = chain->prev->vec[1][1]; + } + if (chain->next && !IS_EQF(cur_y, chain->next->vec[1][1])) { + next_y = chain->next->vec[1][1]; + } + + /* Static hold. */ + if (prev_y == cur_y && next_y == cur_y) { + return KEYFRAME_EXTREME_FLAT; + } + + /* Middle of an incline. */ + if ((prev_y < cur_y && next_y > cur_y) || (prev_y > cur_y && next_y < cur_y)) { + return KEYFRAME_EXTREME_NONE; + } + + /* Bezier handle values for the overshoot check. */ + const bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ; + const bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ; + const float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y; + const float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y; + + /* Detect extremes. One of the neighbors is allowed to be equal to current. */ + if (prev_y < cur_y || next_y < cur_y) { + const bool is_overshoot = (handle_l > cur_y || handle_r > cur_y); + + return static_cast(KEYFRAME_EXTREME_MAX | + (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0)); + } + + if (prev_y > cur_y || next_y > cur_y) { + const bool is_overshoot = (handle_l < cur_y || handle_r < cur_y); + + return static_cast(KEYFRAME_EXTREME_MIN | + (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0)); + } + + return KEYFRAME_EXTREME_NONE; +} + +/* Comparator callback used for ActKeyColumns and BezTripleChain */ +static short compare_ak_bezt(void *node, void *data) +{ + BezTripleChain *chain = static_cast(data); + + return compare_ak_cfraPtr(node, &chain->cur->vec[1][0]); +} + +/* New node callback used for building ActKeyColumns from BezTripleChain */ +static DLRBT_Node *nalloc_ak_bezt(void *data) +{ + ActKeyColumn *ak = static_cast( + MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn")); + const BezTripleChain *chain = static_cast(data); + const BezTriple *bezt = chain->cur; + + /* store settings based on state of BezTriple */ + ak->cfra = bezt->vec[1][0]; + ak->sel = BEZT_ISSEL_ANY(bezt) ? SELECT : 0; + ak->key_type = BEZKEYTYPE(bezt); + ak->handle_type = bezt_handle_type(bezt); + ak->extreme_type = bezt_extreme_type(chain); + + /* count keyframes in this column */ + ak->totkey = 1; + + return (DLRBT_Node *)ak; +} + +/* Node updater callback used for building ActKeyColumns from BezTripleChain */ +static void nupdate_ak_bezt(void *node, void *data) +{ + ActKeyColumn *ak = static_cast(node); + const BezTripleChain *chain = static_cast(data); + const BezTriple *bezt = chain->cur; + + /* set selection status and 'touched' status */ + if (BEZT_ISSEL_ANY(bezt)) { + ak->sel = SELECT; + } + + /* count keyframes in this column */ + ak->totkey++; + + /* For keyframe type, 'proper' keyframes have priority over breakdowns + * (and other types for now). */ + if (BEZKEYTYPE(bezt) == BEZT_KEYTYPE_KEYFRAME) { + ak->key_type = BEZT_KEYTYPE_KEYFRAME; + } + + /* For interpolation type, select the highest value (enum is sorted). */ + ak->handle_type = MAX2(ak->handle_type, bezt_handle_type(bezt)); + + /* For extremes, detect when combining different states. */ + const char new_extreme = bezt_extreme_type(chain); + + if (new_extreme != ak->extreme_type) { + /* Replace the flat status without adding mixed. */ + if (ak->extreme_type == KEYFRAME_EXTREME_FLAT) { + ak->extreme_type = new_extreme; + } + else if (new_extreme != KEYFRAME_EXTREME_FLAT) { + ak->extreme_type |= (new_extreme | KEYFRAME_EXTREME_MIXED); + } + } +} + +/* ......... */ + +/* Comparator callback used for ActKeyColumns and GPencil frame */ +static short compare_ak_gpframe(void *node, void *data) +{ + const bGPDframe *gpf = (bGPDframe *)data; + + float frame = gpf->framenum; + return compare_ak_cfraPtr(node, &frame); +} + +/* New node callback used for building ActKeyColumns from GPencil frames */ +static DLRBT_Node *nalloc_ak_gpframe(void *data) +{ + ActKeyColumn *ak = static_cast( + MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF")); + const bGPDframe *gpf = (bGPDframe *)data; + + /* store settings based on state of BezTriple */ + ak->cfra = gpf->framenum; + ak->sel = (gpf->flag & GP_FRAME_SELECT) ? SELECT : 0; + ak->key_type = gpf->key_type; + + /* count keyframes in this column */ + ak->totkey = 1; + /* Set as visible block. */ + ak->totblock = 1; + ak->block.sel = ak->sel; + ak->block.flag |= ACTKEYBLOCK_FLAG_GPENCIL; + + return (DLRBT_Node *)ak; +} + +/* Node updater callback used for building ActKeyColumns from GPencil frames */ +static void nupdate_ak_gpframe(void *node, void *data) +{ + ActKeyColumn *ak = (ActKeyColumn *)node; + const bGPDframe *gpf = (bGPDframe *)data; + + /* set selection status and 'touched' status */ + if (gpf->flag & GP_FRAME_SELECT) { + ak->sel = SELECT; + } + + /* count keyframes in this column */ + ak->totkey++; + + /* for keyframe type, 'proper' keyframes have priority over breakdowns + * (and other types for now). */ + if (gpf->key_type == BEZT_KEYTYPE_KEYFRAME) { + ak->key_type = BEZT_KEYTYPE_KEYFRAME; + } +} + +/* ......... */ + +/* Comparator callback used for ActKeyColumns and GPencil frame */ +static short compare_ak_masklayshape(void *node, void *data) +{ + const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; + + float frame = masklay_shape->frame; + return compare_ak_cfraPtr(node, &frame); +} + +/* New node callback used for building ActKeyColumns from GPencil frames */ +static DLRBT_Node *nalloc_ak_masklayshape(void *data) +{ + ActKeyColumn *ak = static_cast( + MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF")); + const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; + + /* store settings based on state of BezTriple */ + ak->cfra = masklay_shape->frame; + ak->sel = (masklay_shape->flag & MASK_SHAPE_SELECT) ? SELECT : 0; + + /* count keyframes in this column */ + ak->totkey = 1; + + return (DLRBT_Node *)ak; +} + +/* Node updater callback used for building ActKeyColumns from GPencil frames */ +static void nupdate_ak_masklayshape(void *node, void *data) +{ + ActKeyColumn *ak = (ActKeyColumn *)node; + const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; + + /* set selection status and 'touched' status */ + if (masklay_shape->flag & MASK_SHAPE_SELECT) { + ak->sel = SELECT; + } + + /* count keyframes in this column */ + ak->totkey++; +} + +/* --------------- */ + +/* Add the given BezTriple to the given 'list' of Keyframes */ +static void add_bezt_to_keycolumns_list(AnimKeylist *keylist, BezTripleChain *bezt) +{ + if (ELEM(nullptr, keylist, bezt)) { + return; + } + + BLI_dlrbTree_add(&keylist->keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt); +} + +/* Add the given GPencil Frame to the given 'list' of Keyframes */ +static void add_gpframe_to_keycolumns_list(AnimKeylist *keylist, bGPDframe *gpf) +{ + if (ELEM(nullptr, keylist, gpf)) { + return; + } + + BLI_dlrbTree_add(&keylist->keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf); +} + +/* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */ +static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape *masklay_shape) +{ + if (ELEM(nullptr, keylist, masklay_shape)) { + return; + } + + BLI_dlrbTree_add(&keylist->keys, + compare_ak_masklayshape, + nalloc_ak_masklayshape, + nupdate_ak_masklayshape, + masklay_shape); +} + +/* ActKeyBlocks (Long Keyframes) ------------------------------------------ */ + +static const ActKeyBlockInfo dummy_keyblock = {0}; + +static void compute_keyblock_data(ActKeyBlockInfo *info, + const BezTriple *prev, + const BezTriple *beztn) +{ + memset(info, 0, sizeof(ActKeyBlockInfo)); + + if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) { + /* Animator tagged a "moving hold" + * - Previous key must also be tagged as a moving hold, otherwise + * we're just dealing with the first of a pair, and we don't + * want to be creating any phantom holds... + */ + if (BEZKEYTYPE(prev) == BEZT_KEYTYPE_MOVEHOLD) { + info->flag |= ACTKEYBLOCK_FLAG_MOVING_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD; + } + } + + /* Check for same values... + * - Handles must have same central value as each other + * - Handles which control that section of the curve must be constant + */ + if (IS_EQF(beztn->vec[1][1], prev->vec[1][1])) { + bool hold; + + /* Only check handles in case of actual bezier interpolation. */ + if (prev->ipo == BEZT_IPO_BEZ) { + hold = IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) && + IS_EQF(prev->vec[1][1], prev->vec[2][1]); + } + /* This interpolation type induces movement even between identical keys. */ + else { + hold = !ELEM(prev->ipo, BEZT_IPO_ELASTIC); + } + + if (hold) { + info->flag |= ACTKEYBLOCK_FLAG_STATIC_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD; + } + } + + /* Remember non-bezier interpolation info. */ + if (prev->ipo != BEZT_IPO_BEZ) { + info->flag |= ACTKEYBLOCK_FLAG_NON_BEZIER; + } + + info->sel = BEZT_ISSEL_ANY(prev) || BEZT_ISSEL_ANY(beztn); +} + +static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block) +{ + /* New curve and block. */ + if (col->totcurve <= 1 && col->totblock == 0) { + memcpy(&col->block, block, sizeof(ActKeyBlockInfo)); + } + /* Existing curve. */ + else { + col->block.conflict |= (col->block.flag ^ block->flag); + col->block.flag |= block->flag; + col->block.sel |= block->sel; + } + + if (block->flag) { + col->totblock++; + } +} + +static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len) +{ + ActKeyColumn *col = static_cast(keylist->keys.first); + + if (bezt && bezt_len >= 2) { + ActKeyBlockInfo block; + + /* Find the first key column while inserting dummy blocks. */ + for (; col != nullptr && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) { + add_keyblock_info(col, &dummy_keyblock); + } + + BLI_assert(col != nullptr); + + /* Insert real blocks. */ + for (int v = 1; col != nullptr && v < bezt_len; v++, bezt++) { + /* Wrong order of bezier keys: resync position. */ + if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) { + /* Backtrack to find the right location. */ + if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) { + ActKeyColumn *newcol = (ActKeyColumn *)BLI_dlrbTree_search_exact( + &keylist->keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]); + + if (newcol != nullptr) { + col = newcol; + + /* The previous keyblock is garbage too. */ + if (col->prev != nullptr) { + add_keyblock_info(col->prev, &dummy_keyblock); + } + } + else { + BLI_assert(false); + } + } + + continue; + } + + /* Normal sequence */ + BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0])); + + compute_keyblock_data(&block, bezt, bezt + 1); + + for (; col != nullptr && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) { + add_keyblock_info(col, &block); + } + + BLI_assert(col != nullptr); + } + } + + /* Insert dummy blocks at the end. */ + for (; col != nullptr; col = col->next) { + add_keyblock_info(col, &dummy_keyblock); + } +} + +/* Walk through columns and propagate blocks and totcurve. + * + * This must be called even by animation sources that don't generate + * keyblocks to keep the data structure consistent after adding columns. + */ +static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len) +{ + /* Recompute the prev/next linked list. */ + BLI_dlrbTree_linkedlist_sync(&keylist->keys); + + /* Find the curve count */ + int max_curve = 0; + + LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) { + max_curve = MAX2(max_curve, col->totcurve); + } + + /* Propagate blocks to inserted keys */ + ActKeyColumn *prev_ready = nullptr; + + LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) { + /* Pre-existing column. */ + if (col->totcurve > 0) { + prev_ready = col; + } + /* Newly inserted column, so copy block data from previous. */ + else if (prev_ready != nullptr) { + col->totblock = prev_ready->totblock; + memcpy(&col->block, &prev_ready->block, sizeof(ActKeyBlockInfo)); + } + + col->totcurve = max_curve + 1; + } + + /* Add blocks on top */ + add_bezt_to_keyblocks_list(keylist, bezt, bezt_len); +} + +/* --------- */ + +bool actkeyblock_is_valid(const ActKeyColumn *ac) +{ + return ac != nullptr && ac->next != nullptr && ac->totblock > 0; +} + +/* Checks if ActKeyBlock should exist... */ +int actkeyblock_get_valid_hold(const ActKeyColumn *ac) +{ + /* check that block is valid */ + if (!actkeyblock_is_valid(ac)) { + return 0; + } + + const int hold_mask = (ACTKEYBLOCK_FLAG_ANY_HOLD | ACTKEYBLOCK_FLAG_STATIC_HOLD); + return (ac->block.flag & ~ac->block.conflict) & hold_mask; +} + +/* *************************** Keyframe List Conversions *************************** */ + +void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, const int saction_flag) +{ + if (ac) { + ListBase anim_data = {nullptr, nullptr}; + + /* get F-Curves to take keyframes from */ + const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; + ANIM_animdata_filter( + ac, &anim_data, filter, ac->data, static_cast(ac->datatype)); + + /* loop through each F-Curve, grabbing the keyframes */ + LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) { + /* Why not use all #eAnim_KeyType here? + * All of the other key types are actually "summaries" themselves, + * and will just end up duplicating stuff that comes up through + * standard filtering of just F-Curves. Given the way that these work, + * there isn't really any benefit at all from including them. - Aligorith */ + switch (ale->datatype) { + case ALE_FCURVE: + fcurve_to_keylist(ale->adt, static_cast(ale->data), keylist, saction_flag); + break; + case ALE_MASKLAY: + mask_to_keylist(ac->ads, static_cast(ale->data), keylist); + break; + case ALE_GPFRAME: + gpl_to_keylist(ac->ads, static_cast(ale->data), keylist); + break; + default: + // printf("%s: datatype %d unhandled\n", __func__, ale->datatype); + break; + } + } + + ANIM_animdata_freelist(&anim_data); + } +} + +void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, const int saction_flag) +{ + bAnimContext ac = {nullptr}; + ListBase anim_data = {nullptr, nullptr}; + + bAnimListElem dummychan = {nullptr}; + + if (sce == nullptr) { + return; + } + + /* create a dummy wrapper data to work with */ + dummychan.type = ANIMTYPE_SCENE; + dummychan.data = sce; + dummychan.id = &sce->id; + dummychan.adt = sce->adt; + + ac.ads = ads; + ac.data = &dummychan; + ac.datatype = ANIMCONT_CHANNEL; + + /* get F-Curves to take keyframes from */ + const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ + ANIM_animdata_filter( + &ac, &anim_data, filter, ac.data, static_cast(ac.datatype)); + + /* loop through each F-Curve, grabbing the keyframes */ + LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) { + fcurve_to_keylist(ale->adt, static_cast(ale->data), keylist, saction_flag); + } + + ANIM_animdata_freelist(&anim_data); +} + +void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, const int saction_flag) +{ + bAnimContext ac = {nullptr}; + ListBase anim_data = {nullptr, nullptr}; + + bAnimListElem dummychan = {nullptr}; + Base dummybase = {nullptr}; + + if (ob == nullptr) { + return; + } + + /* create a dummy wrapper data to work with */ + dummybase.object = ob; + + dummychan.type = ANIMTYPE_OBJECT; + dummychan.data = &dummybase; + dummychan.id = &ob->id; + dummychan.adt = ob->adt; + + ac.ads = ads; + ac.data = &dummychan; + ac.datatype = ANIMCONT_CHANNEL; + + /* get F-Curves to take keyframes from */ + const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ + ANIM_animdata_filter( + &ac, &anim_data, filter, ac.data, static_cast(ac.datatype)); + + /* loop through each F-Curve, grabbing the keyframes */ + LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) { + fcurve_to_keylist(ale->adt, static_cast(ale->data), keylist, saction_flag); + } + + ANIM_animdata_freelist(&anim_data); +} + +void cachefile_to_keylist(bDopeSheet *ads, + CacheFile *cache_file, + AnimKeylist *keylist, + const int saction_flag) +{ + if (cache_file == nullptr) { + return; + } + + /* create a dummy wrapper data to work with */ + bAnimListElem dummychan = {nullptr}; + dummychan.type = ANIMTYPE_DSCACHEFILE; + dummychan.data = cache_file; + dummychan.id = &cache_file->id; + dummychan.adt = cache_file->adt; + + bAnimContext ac = {nullptr}; + ac.ads = ads; + ac.data = &dummychan; + ac.datatype = ANIMCONT_CHANNEL; + + /* get F-Curves to take keyframes from */ + ListBase anim_data = {nullptr, nullptr}; + const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ + ANIM_animdata_filter( + &ac, &anim_data, filter, ac.data, static_cast(ac.datatype)); + + /* loop through each F-Curve, grabbing the keyframes */ + LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) { + fcurve_to_keylist(ale->adt, static_cast(ale->data), keylist, saction_flag); + } + + ANIM_animdata_freelist(&anim_data); +} + +void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag) +{ + if (fcu && fcu->totvert && fcu->bezt) { + /* apply NLA-mapping (if applicable) */ + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, fcu, false, false); + } + + /* Check if the curve is cyclic. */ + bool is_cyclic = BKE_fcurve_is_cyclic(fcu) && (fcu->totvert >= 2); + bool do_extremes = (saction_flag & SACTION_SHOW_EXTREMES) != 0; + + /* loop through beztriples, making ActKeysColumns */ + BezTripleChain chain = {nullptr}; + + for (int v = 0; v < fcu->totvert; v++) { + chain.cur = &fcu->bezt[v]; + + /* Neighbor keys, accounting for being cyclic. */ + if (do_extremes) { + chain.prev = (v > 0) ? &fcu->bezt[v - 1] : + is_cyclic ? &fcu->bezt[fcu->totvert - 2] : + nullptr; + chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] : + is_cyclic ? &fcu->bezt[1] : + nullptr; + } + + add_bezt_to_keycolumns_list(keylist, &chain); + } + + /* Update keyblocks. */ + update_keyblocks(keylist, fcu->bezt, fcu->totvert); + + /* unapply NLA-mapping if applicable */ + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, fcu, true, false); + } + } +} + +void agroup_to_keylist(AnimData *adt, + bActionGroup *agrp, + AnimKeylist *keylist, + const int saction_flag) +{ + if (agrp) { + /* loop through F-Curves */ + LISTBASE_FOREACH (FCurve *, fcu, &agrp->channels) { + fcurve_to_keylist(adt, fcu, keylist, saction_flag); + } + } +} + +void action_to_keylist(AnimData *adt, bAction *act, AnimKeylist *keylist, const int saction_flag) +{ + if (act) { + /* loop through F-Curves */ + LISTBASE_FOREACH (FCurve *, fcu, &act->curves) { + fcurve_to_keylist(adt, fcu, keylist, saction_flag); + } + } +} + +void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, AnimKeylist *keylist, const bool active) +{ + if (gpd && keylist) { + /* for now, just aggregate out all the frames, but only for visible layers */ + LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl, &gpd->layers) { + if ((gpl->flag & GP_LAYER_HIDE) == 0) { + if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) { + gpl_to_keylist(ads, gpl, keylist); + } + } + } + } +} + +void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, AnimKeylist *keylist) +{ + if (gpl && keylist) { + /* Although the frames should already be in an ordered list, + * they are not suitable for displaying yet. */ + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + add_gpframe_to_keycolumns_list(keylist, gpf); + } + + update_keyblocks(keylist, nullptr, 0); + } +} + +void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, AnimKeylist *keylist) +{ + if (masklay && keylist) { + LISTBASE_FOREACH (MaskLayerShape *, masklay_shape, &masklay->splines_shapes) { + add_masklay_to_keycolumns_list(keylist, masklay_shape); + } + + update_keyblocks(keylist, nullptr, 0); + } +} +} -- cgit v1.2.3 From 3886ab05b449979959dfbb4950fea9ec473ecb83 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 9 Aug 2021 14:31:56 +0200 Subject: Cleanup: Spelling and typos in comment --- source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h index 255ea840088..d0bb841caab 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h @@ -68,11 +68,11 @@ ID *deg_expand_copy_on_write_datablock(const struct Depsgraph *depsgraph, ID *deg_update_copy_on_write_datablock(const struct Depsgraph *depsgraph, const IDNode *id_node); ID *deg_update_copy_on_write_datablock(const struct Depsgraph *depsgraph, struct ID *id_orig); -/* Helper function which frees memory used by copy-on-written databnlock. */ +/* Helper function which frees memory used by copy-on-written data-block. */ void deg_free_copy_on_write_datablock(struct ID *id_cow); /* Callback function for depsgraph operation node which ensures copy-on-write - * datablock is ready for use by further evaluation routines. + * data-block is ready for use by further evaluation routines. */ void deg_evaluate_copy_on_write(struct ::Depsgraph *depsgraph, const struct IDNode *id_node); @@ -80,16 +80,16 @@ void deg_evaluate_copy_on_write(struct ::Depsgraph *depsgraph, const struct IDNo * copies inside. */ bool deg_validate_copy_on_write_datablock(ID *id_cow); -/* Tag given ID block as being copy-on-wtritten. */ +/* Tag given ID block as being copy-on-written. */ void deg_tag_copy_on_write_id(struct ID *id_cow, const struct ID *id_orig); -/* Check whether ID datablock is expanded. +/* Check whether ID data-block is expanded. * * TODO(sergey): Make it an inline function or a macro. */ bool deg_copy_on_write_is_expanded(const struct ID *id_cow); -/* Check whether copy-on-write datablock is needed for given ID. +/* Check whether copy-on-write data-block is needed for given ID. * * There are some exceptions on data-blocks which are covered by dependency graph * but which we don't want to start duplicating. -- cgit v1.2.3 From e2a411570e5597eb5d84743a2b658b75fe1c37e9 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 9 Aug 2021 14:41:30 +0200 Subject: Cleanup/fixes in UI messages. --- release/scripts/modules/bl_i18n_utils/utils_spell_check.py | 9 +++++++-- release/scripts/startup/bl_operators/assets.py | 2 +- release/scripts/startup/bl_ui/space_spreadsheet.py | 2 +- source/blender/blenkernel/intern/lib_override.c | 2 +- source/blender/blenkernel/intern/object.c | 2 +- source/blender/editors/armature/pose_lib_2.c | 2 +- source/blender/editors/object/object_modes.c | 2 +- source/blender/editors/object/object_relations.c | 4 ++-- source/blender/editors/screen/screen_ops.c | 2 +- source/blender/editors/space_sequencer/sequencer_add.c | 2 +- source/blender/makesrna/intern/rna_asset.c | 2 +- source/blender/makesrna/intern/rna_gpencil.c | 2 +- source/blender/makesrna/intern/rna_userdef.c | 2 +- source/blender/windowmanager/intern/wm_files.c | 4 ++-- 14 files changed, 22 insertions(+), 17 deletions(-) diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index c310eee0b14..14fc81821c4 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -48,6 +48,7 @@ class SpellChecker: "equi", # equi-angular, etc. "fader", "globbing", + "haptics", "hasn", # hasn't "hetero", "hoc", # ad-hoc @@ -188,7 +189,7 @@ class SpellChecker: "reprojection", "reproject", "reprojecting", "resize", "restpose", - "resync", + "resync", "resynced", "retarget", "retargets", "retargeting", "retargeted", "retiming", "rigidbody", @@ -227,6 +228,7 @@ class SpellChecker: "un", "unassociate", "unassociated", "unbake", + "uncheck", "unclosed", "uncomment", "unculled", @@ -381,6 +383,7 @@ class SpellChecker: "albedo", "anamorphic", "anisotropic", "anisotropy", + "bimanual", # OpenXR? "bitangent", "boid", "boids", "ceil", @@ -430,6 +433,7 @@ class SpellChecker: "spacebar", "subtractive", "superellipse", + "thumbstick", "tooltip", "tooltips", "trackpad", "tuple", @@ -493,7 +497,7 @@ class SpellChecker: "pinlight", "qi", "radiosity", - "raycasting", + "raycast", "raycasting", "raytrace", "raytracing", "raytraced", "refractions", "remesher", "remeshing", "remesh", @@ -698,6 +702,7 @@ class SpellChecker: "msgid", "msgids", "mux", "ndof", + "pbr", # Physically Based Rendering "ppc", "precisa", "px", diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py index c782cd0646e..8c76018b7a1 100644 --- a/release/scripts/startup/bl_operators/assets.py +++ b/release/scripts/startup/bl_operators/assets.py @@ -130,7 +130,7 @@ class ASSET_OT_open_containing_blend_file(Operator): return {'RUNNING_MODAL'} if returncode: - self.report({'WARNING'}, "Blender subprocess exited with error code %d" % returncode) + self.report({'WARNING'}, "Blender sub-process exited with error code %d" % returncode) # TODO(Sybren): Replace this with a generic "reload assets" operator # that can run outside of the Asset Browser context. diff --git a/release/scripts/startup/bl_ui/space_spreadsheet.py b/release/scripts/startup/bl_ui/space_spreadsheet.py index afdbfea5091..51f8841419b 100644 --- a/release/scripts/startup/bl_ui/space_spreadsheet.py +++ b/release/scripts/startup/bl_ui/space_spreadsheet.py @@ -55,7 +55,7 @@ class SPREADSHEET_HT_header(bpy.types.Header): layout.operator("spreadsheet.toggle_pin", text="", icon=pin_icon, emboss=False) if space.object_eval_state == 'VIEWER_NODE' and len(context_path) < 3: - layout.label(text="No active viewer node.", icon='INFO') + layout.label(text="No active viewer node", icon='INFO') layout.separator_spacer() diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 67ed7d1b394..bebc49e090d 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1020,7 +1020,7 @@ bool BKE_lib_override_library_resync(Main *bmain, if (id_root_reference->tag & LIB_TAG_MISSING) { BKE_reportf(reports != NULL ? reports->reports : NULL, RPT_ERROR, - "impossible to resync data-block %s and its dependencies, as its linked reference " + "Impossible to resync data-block %s and its dependencies, as its linked reference " "is missing", id_root->name + 2); return false; diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 2e69782f6c0..23b9dca8371 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -914,7 +914,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) if (ob->id.lib) { BLO_reportf_wrap(reports, RPT_INFO, - TIP_("Can't find obdata of %s lib %s\n"), + TIP_("Can't find object data of %s lib %s\n"), ob->id.name + 2, ob->id.lib->filepath); } diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c index 5b6100c9785..91a5dc67a21 100644 --- a/source/blender/editors/armature/pose_lib_2.c +++ b/source/blender/editors/armature/pose_lib_2.c @@ -423,7 +423,7 @@ static void poselib_blend_cleanup(bContext *C, wmOperator *op) case POSE_BLEND_ORIGINAL: /* Cleanup should not be called directly from these states. */ BLI_assert_msg(0, "poselib_blend_cleanup: unexpected pose blend state"); - BKE_report(op->reports, RPT_ERROR, "Internal pose library error, cancelling operator"); + BKE_report(op->reports, RPT_ERROR, "Internal pose library error, canceling operator"); ATTR_FALLTHROUGH; case POSE_BLEND_CANCEL: ED_pose_backup_restore(pbd->pose_backup); diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index 36a4f002978..2c58ef02486 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -585,7 +585,7 @@ void OBJECT_OT_transfer_mode(wmOperatorType *ot) "use_flash_on_transfer", true, "Flash On Transfer", - "Flash the target object when transfering the mode"); + "Flash the target object when transferring the mode"); } /** \} */ diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 92d0deb49d0..a9a439c5084 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2742,11 +2742,11 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C, char *result; if (prev_mat) { - const char *tooltip = TIP_("Drop %s on %s (slot %d, replacing %s)."); + const char *tooltip = TIP_("Drop %s on %s (slot %d, replacing %s)"); result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot, prev_mat->id.name + 2); } else { - const char *tooltip = TIP_("Drop %s on %s (slot %d)."); + const char *tooltip = TIP_("Drop %s on %s (slot %d)"); result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot); } return result; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 6af18104336..cff3ecfbbd3 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -403,7 +403,7 @@ bool ED_operator_object_active_editable_ex(bContext *C, const Object *ob) } if (ed_object_hidden(ob)) { - CTX_wm_operator_poll_msg_set(C, "Cannot edit hidden obect"); + CTX_wm_operator_poll_msg_set(C, "Cannot edit hidden object"); return false; } diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index c4575b3403e..265a52ed1a6 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -159,7 +159,7 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) "set_view_transform", true, "Set View Transform", - "Set appropriate view transform based on media colorspace"); + "Set appropriate view transform based on media color space"); } } diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index 3b3a04e1128..1e583f4ca52 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -319,7 +319,7 @@ static void rna_def_asset_library_reference(BlenderRNA *brna) { StructRNA *srna = RNA_def_struct(brna, "AssetLibraryReference", NULL); RNA_def_struct_ui_text( - srna, "Asset Library Reference", "Identifier to refere to the asset library"); + srna, "Asset Library Reference", "Identifier to refer to the asset library"); } /** diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index aad6f1231dd..3e5dce64c7b 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -2117,7 +2117,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) prop = RNA_def_property(srna, "use_viewlayer_masks", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER); RNA_def_property_ui_text( - prop, "Use Masks in Render", "Include the mask layers when rendering the viewlayer"); + prop, "Use Masks in Render", "Include the mask layers when rendering the view-layer"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* blend mode */ diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index d29a90a1886..2d93715a438 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6155,7 +6155,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) prop, "Python Scripts Directory", "Alternate script path, matching the default layout with subdirectories: " - "startup, addons, modules, and presets (requires restart)"); + "startup, add-ons, modules, and presets (requires restart)"); /* TODO: editing should reset sys.path! */ prop = RNA_def_property(srna, "i18n_branches_directory", PROP_STRING, PROP_DIRPATH); diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 92f3eb67783..abf957a6396 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -803,7 +803,7 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports) node_lib = node_lib->next) { Library *library = node_lib->link; BKE_reportf( - bf_reports->reports, RPT_INFO, "Library %s needs overrides resync.", library->filepath); + bf_reports->reports, RPT_INFO, "Library %s needs overrides resync", library->filepath); } } @@ -3443,7 +3443,7 @@ static uiBlock *block_create__close_file_dialog(struct bContext *C, BLI_split_file_part(blendfile_pathpath, filename, sizeof(filename)); } else { - STRNCPY(filename, IFACE_("untitled.blend")); + STRNCPY(filename, "untitled.blend"); } uiItemL(layout, filename, ICON_NONE); -- cgit v1.2.3 From 6deb37474eb082400ccd9d0cac0459116f24aacf Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 9 Aug 2021 22:55:12 +1000 Subject: Cleanup: spelling in comments --- source/blender/blenkernel/BKE_bvhutils.h | 2 +- source/blender/editors/interface/interface_layout.c | 2 +- source/blender/editors/space_outliner/outliner_tools.c | 2 +- source/blender/python/intern/bpy_interface.c | 2 +- source/blender/python/intern/bpy_rna_id_collection.c | 2 +- source/blender/python/mathutils/mathutils_Vector.c | 4 ++-- source/blender/python/mathutils/mathutils_geometry.c | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/BKE_bvhutils.h b/source/blender/blenkernel/BKE_bvhutils.h index 8be2fcbdb83..06be8ec80fc 100644 --- a/source/blender/blenkernel/BKE_bvhutils.h +++ b/source/blender/blenkernel/BKE_bvhutils.h @@ -69,7 +69,7 @@ typedef struct BVHTreeFromMesh { BVHTree_NearestPointCallback nearest_callback; BVHTree_RayCastCallback raycast_callback; - /* Vertex array, so that callbacks have instante access to data */ + /* Vertex array, so that callbacks have instant access to data. */ const struct MVert *vert; const struct MEdge *edge; /* only used for BVHTreeFromMeshEdges */ const struct MFace *face; diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 03c67e9b046..ec5a30f7793 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -282,7 +282,7 @@ static int ui_layout_vary_direction(uiLayout *layout) static bool ui_layout_variable_size(uiLayout *layout) { - /* Note that this code is probably a bit flakey, we'd probably want to know whether it's + /* Note that this code is probably a bit flaky, we'd probably want to know whether it's * variable in X and/or Y, etc. But for now it mimics previous one, * with addition of variable flag set for children of grid-flow layouts. */ return ui_layout_vary_direction(layout) == UI_ITEM_VARY_X || layout->variable_size; diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index e3aec572bd3..a994368a0ec 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -845,7 +845,7 @@ static void id_override_library_create_fn(bContext *C, if (!ID_IS_LINKED(te->store_elem->id)) { break; } - /* If we'd need to override that arent ID, but it is not overridable, abort. */ + /* If we'd need to override that aren't ID, but it is not overridable, abort. */ if (!ID_IS_OVERRIDABLE_LIBRARY(te->store_elem->id)) { BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); BKE_reportf(reports, diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index f91ba4d362c..35450e3eaad 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -155,7 +155,7 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate) } else if (py_call_level == 0) { /* XXX: Calling classes currently won't store the context :\, - * can't set NULL because of this. but this is very flakey still. */ + * can't set NULL because of this. but this is very flaky still. */ #if 0 BPY_context_set(NULL); #endif diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c index 1bb68babc3c..7bdb0e30410 100644 --- a/source/blender/python/intern/bpy_rna_id_collection.c +++ b/source/blender/python/intern/bpy_rna_id_collection.c @@ -227,7 +227,7 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject * } if (!data_cb.is_subset && - /* We do not want to pre-add keys of flitered out types. */ + /* We do not want to pre-add keys of filtered out types. */ (key_types_bitmap == NULL || id_check_type(id, key_types_bitmap)) && /* We do not want to pre-add keys when we have filter on value types, * but not on key types. */ diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index c2223b023ad..efcaa9b6a51 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -1926,7 +1926,7 @@ static PyObject *Vector_imatmul(PyObject *v1, PyObject *v2) return NULL; } -/* divid: obj / obj */ +/* divide: obj / obj */ static PyObject *Vector_div(PyObject *v1, PyObject *v2) { float *vec = NULL, scalar; @@ -2939,7 +2939,7 @@ static int row_vector_multiplication(float r_vec[MAX_DIMENSIONS], memcpy(vec_cpy, vec->vec, vec_size * sizeof(float)); r_vec[3] = 1.0f; - /* muliplication */ + /* Multiplication. */ for (col = 0; col < mat->num_col; col++) { double dot = 0.0; for (row = 0; row < mat->num_row; row++) { diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index c73dea79aac..3c0ea9a9566 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -201,7 +201,7 @@ static PyObject *M_Geometry_intersect_line_line(PyObject *UNUSED(self), PyObject } if (result == 0) { - /* collinear */ + /* Co-linear. */ Py_RETURN_NONE; } -- cgit v1.2.3 From 6fe00939b0a471cc149ea5b3c63ca57b049b4a37 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 9 Aug 2021 22:55:41 +1000 Subject: PyAPI: resolve build error with Python 3.10 Resolves T89931 --- source/blender/python/mathutils/mathutils.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 5beca7bd71a..be7dae6871b 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -95,7 +95,11 @@ Py_hash_t mathutils_array_hash(const float *array, size_t array_len) x = 0x345678UL; i = 0; while (--len >= 0) { +#if PY_VERSION_HEX >= 0x30a0000 /* Version: 3.10. */ + y = _Py_HashDouble(NULL, (double)(array[i++])); +#else y = _Py_HashDouble((double)(array[i++])); +#endif if (y == -1) { return -1; } -- cgit v1.2.3 From 71e2c366f7d19d8fe4f160a60597e63692fdc7ca Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 9 Aug 2021 15:28:12 +0200 Subject: Cleanup: Fix compiler warning --- source/blender/editors/gpencil/gpencil_paint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index aaea1c0ddaf..9e96c40b2db 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -997,7 +997,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) gps->uv_scale = 1.0f; /* Set stroke caps. */ - gps->caps[0] = gps->caps[1] = brush->gpencil_settings->caps_type; + gps->caps[0] = gps->caps[1] = (short)brush->gpencil_settings->caps_type; /* allocate enough memory for a continuous array for storage points */ const int subdivide = brush->gpencil_settings->draw_subdivide; -- cgit v1.2.3 From ff594715b8347e8d5d6a9f60e48ec7b170661962 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 9 Aug 2021 16:46:57 +0200 Subject: Build: macOS library upgrade fixes * Revert back to OpenMP 9.0.1 due to bug causing cloth physics test to fail. * Skip flex build on macOS to avoid link error, only reason we build this is due to old flex version on Linux CentOS 7. * Fix PNG cmake argument that expects lowercase on instead of ON. Ref T90507, T88438 --- build_files/build_environment/CMakeLists.txt | 2 +- build_files/build_environment/cmake/ispc.cmake | 6 +++--- build_files/build_environment/cmake/openmp.cmake | 6 ++++++ build_files/build_environment/cmake/osl.cmake | 2 +- build_files/build_environment/cmake/png.cmake | 2 +- build_files/build_environment/cmake/versions.cmake | 14 ++++++++++--- build_files/build_environment/patches/openmp.diff | 23 ++++++++++++++++++++++ 7 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 build_files/build_environment/patches/openmp.diff diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt index 3a9574b4b2a..af1653de59a 100644 --- a/build_files/build_environment/CMakeLists.txt +++ b/build_files/build_environment/CMakeLists.txt @@ -84,7 +84,7 @@ include(cmake/openimageio.cmake) include(cmake/tiff.cmake) if(WIN32) include(cmake/flexbison.cmake) -else() +elseif(UNIX AND NOT APPLE) include(cmake/flex.cmake) endif() include(cmake/osl.cmake) diff --git a/build_files/build_environment/cmake/ispc.cmake b/build_files/build_environment/cmake/ispc.cmake index 93fc9dc4846..b1cd2cea0c7 100644 --- a/build_files/build_environment/cmake/ispc.cmake +++ b/build_files/build_environment/cmake/ispc.cmake @@ -29,13 +29,13 @@ elseif(APPLE) if("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64") set(ISPC_EXTRA_ARGS_APPLE -DBISON_EXECUTABLE=/opt/homebrew/opt/bison/bin/bison - -DFLEX_EXECUTABLE=${LIBDIR}/flex/bin/flex + -DFLEX_EXECUTABLE=/opt/homebrew/opt/flex/bin/flex -DARM_ENABLED=On ) else() set(ISPC_EXTRA_ARGS_APPLE -DBISON_EXECUTABLE=/usr/local/opt/bison/bin/bison - -DFLEX_EXECUTABLE=${LIBDIR}/flex/bin/flex + -DFLEX_EXECUTABLE=/usr/local/opt/flex/bin/flex -DARM_ENABLED=Off ) endif() @@ -84,7 +84,7 @@ if(WIN32) external_ispc external_flexbison ) -else() +elseif(UNIX AND NOT APPLE) add_dependencies( external_ispc external_flex diff --git a/build_files/build_environment/cmake/openmp.cmake b/build_files/build_environment/cmake/openmp.cmake index 0e5323ca513..d4d5e69a5ad 100644 --- a/build_files/build_environment/cmake/openmp.cmake +++ b/build_files/build_environment/cmake/openmp.cmake @@ -16,12 +16,18 @@ # # ***** END GPL LICENSE BLOCK ***** +if(APPLE) + set(OPENMP_PATCH_COMMAND ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/openmp/src/external_openmp < ${PATCH_DIR}/openmp.diff) +else() + set(OPENMP_PATCH_COMMAND) +endif() ExternalProject_Add(external_openmp URL file://${PACKAGE_DIR}/${OPENMP_FILE} DOWNLOAD_DIR ${DOWNLOAD_DIR} URL_HASH ${OPENMP_HASH_TYPE}=${OPENMP_HASH} PREFIX ${BUILD_DIR}/openmp + PATCH_COMMAND ${OPENMP_PATCH_COMMAND} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/openmp ${DEFAULT_CMAKE_FLAGS} INSTALL_COMMAND cd ${BUILD_DIR}/openmp/src/external_openmp-build && install_name_tool -id @rpath/libomp.dylib runtime/src/libomp.dylib && make install INSTALL_DIR ${LIBDIR}/openmp diff --git a/build_files/build_environment/cmake/osl.cmake b/build_files/build_environment/cmake/osl.cmake index 05aedb1f085..c20bf68c0ef 100644 --- a/build_files/build_environment/cmake/osl.cmake +++ b/build_files/build_environment/cmake/osl.cmake @@ -96,7 +96,7 @@ if(WIN32) external_osl external_flexbison ) -else() +elseif(UNIX AND NOT APPLE) add_dependencies( external_osl external_flex diff --git a/build_files/build_environment/cmake/png.cmake b/build_files/build_environment/cmake/png.cmake index 458d3a1fd98..3fc53b238c2 100644 --- a/build_files/build_environment/cmake/png.cmake +++ b/build_files/build_environment/cmake/png.cmake @@ -23,7 +23,7 @@ set(PNG_EXTRA_ARGS ) if(BLENDER_PLATFORM_ARM) - set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=ON -DCMAKE_SYSTEM_PROCESSOR="aarch64") + set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=on -DCMAKE_SYSTEM_PROCESSOR="aarch64") endif() ExternalProject_Add(external_png diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index bc220c596c1..d1675bdddfd 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -158,10 +158,18 @@ set(LLVM_HASH 5a4fab4d7fc84aefffb118ac2c8a4fc0) set(LLVM_HASH_TYPE MD5) set(LLVM_FILE llvm-project-${LLVM_VERSION}.src.tar.xz) -set(OPENMP_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/openmp-${LLVM_VERSION}.src.tar.xz) -set(OPENMP_HASH ac48ce3e4582ccb82f81ab59eb3fc9dc) +if(APPLE) + # Cloth physics test is crashing due to this bug: + # https://bugs.llvm.org/show_bug.cgi?id=50579 + set(OPENMP_VERSION 9.0.1) + set(OPENMP_HASH 6eade16057edbdecb3c4eef9daa2bfcf) +else() + set(OPENMP_VERSION ${LLVM_VERSION}) + set(OPENMP_HASH ac48ce3e4582ccb82f81ab59eb3fc9dc) +endif() +set(OPENMP_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${OPENMP_VERSION}/openmp-${OPENMP_VERSION}.src.tar.xz) set(OPENMP_HASH_TYPE MD5) -set(OPENMP_FILE openmp-${LLVM_VERSION}.src.tar.xz) +set(OPENMP_FILE openmp-${OPENMP_VERSION}.src.tar.xz) set(OPENIMAGEIO_VERSION 2.2.15.1) set(OPENIMAGEIO_URI https://github.com/OpenImageIO/oiio/archive/Release-${OPENIMAGEIO_VERSION}.tar.gz) diff --git a/build_files/build_environment/patches/openmp.diff b/build_files/build_environment/patches/openmp.diff new file mode 100644 index 00000000000..201ab5c7713 --- /dev/null +++ b/build_files/build_environment/patches/openmp.diff @@ -0,0 +1,23 @@ +diff --git a/runtime/src/z_Linux_asm.S b/runtime/src/z_Linux_asm.S +index 0d8885e..42aa5ad 100644 +--- a/runtime/src/z_Linux_asm.S ++++ b/runtime/src/z_Linux_asm.S +@@ -1540,10 +1540,12 @@ __kmp_unnamed_critical_addr: + .comm .gomp_critical_user_,32,8 + .data + .align 8 +- .global __kmp_unnamed_critical_addr +-__kmp_unnamed_critical_addr: ++ .global ___kmp_unnamed_critical_addr ++___kmp_unnamed_critical_addr: + .8byte .gomp_critical_user_ +- .size __kmp_unnamed_critical_addr,8 ++# if !(KMP_OS_DARWIN) ++ .size ___kmp_unnamed_critical_addr,8 ++# endif + #endif /* KMP_ARCH_PPC64 || KMP_ARCH_AARCH64 */ + + #if KMP_OS_LINUX + + + -- cgit v1.2.3 From fddd5eb6927cb348a6a0e2098908e641ee7f83a4 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Mon, 9 Aug 2021 11:52:51 -0400 Subject: UI: Image Editor: Fix missing 2D cursor in mask edit mode The 2D cursor should be visible in both mask and uv edit modes. This was likely and oversight when splitting the image editor into the UV and Image editors --- source/blender/draw/intern/draw_view.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c index ae2c66881ff..90bb3762473 100644 --- a/source/blender/draw/intern/draw_view.c +++ b/source/blender/draw/intern/draw_view.c @@ -211,8 +211,17 @@ static bool is_cursor_visible_2d(const DRWContextState *draw_ctx) return false; } SpaceImage *sima = (SpaceImage *)space_data; - if (sima->mode != SI_MODE_UV) { - return false; + switch (sima->mode) { + case SI_MODE_VIEW: + return false; + break; + case SI_MODE_PAINT: + return false; + break; + case SI_MODE_MASK: + break; + case SI_MODE_UV: + break; } return (sima->overlay.flag & SI_OVERLAY_SHOW_OVERLAYS) != 0; } -- cgit v1.2.3 From b04997cca45882526ff8eb3c65579046e618d9bd Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 9 Aug 2021 13:42:19 -0500 Subject: Fix T90547: Add node errors when compiled without OpenVDB or Bullet These were added in other places but were overlooked here. --- source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc | 2 ++ source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc | 3 +++ 2 files changed, 5 insertions(+) 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 91d569282c3..91e08d7777b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -304,6 +304,8 @@ static void geo_node_convex_hull_exec(GeoNodeExecParams params) } params.set_output("Convex Hull", GeometrySet::create_with_mesh(mesh)); #else + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without Bullet")); params.set_output("Convex Hull", geometry_set); #endif /* WITH_BULLET */ } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index 403f4906d07..4c1151bf6c2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -149,6 +149,9 @@ static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) #ifdef WITH_OPENVDB create_mesh_from_volume(geometry_set_in, geometry_set_out, params); +#else + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); #endif params.set_output("Geometry", geometry_set_out); -- cgit v1.2.3 From ce95a2b148ed498a7e8ac7fb6565d7e5f21fca6f Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Mon, 9 Aug 2021 15:25:27 -0400 Subject: UI: Clip Editor: Move Annotation Panel to new View tab To be consistent with all other editors the annotation layers pannel should be placed in a "View Tab". In my next commit, this tab will be expanded to include other options. --- release/scripts/startup/bl_ui/space_clip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index ae106f893d3..7bac0556fb9 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -1239,7 +1239,7 @@ class CLIP_PT_tools_scenesetup(Panel): class CLIP_PT_annotation(AnnotationDataPanel, CLIP_PT_clip_view_panel, Panel): bl_space_type = 'CLIP_EDITOR' bl_region_type = 'UI' - bl_category = "Annotation" + bl_category = "View" bl_options = set() # NOTE: this is just a wrapper around the generic GP Panel -- cgit v1.2.3 From 2d867426b1b7e28b58bf0c84119679c73f08175f Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Mon, 9 Aug 2021 15:30:55 -0400 Subject: UI: Clip Editor: Expose 2D Cursor Location to RNA and UI To be consistent with the image editors and 3D viewport the cursor location can be changed from the sidebar. This was missing from the clip editor, but support has been added in this commit. Previously, the only way to precisely set the cursor was to call the set cursor operator then use the redo panel to adjust the value. --- release/scripts/startup/bl_ui/space_clip.py | 26 ++++++++++++++++++++++++++ source/blender/makesrna/intern/rna_space.c | 22 ++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index 7bac0556fb9..a1e5b509295 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -1075,6 +1075,31 @@ class CLIP_PT_stabilization(CLIP_PT_reconstruction_panel, Panel): layout.prop(stab, "filter_type") +class CLIP_PT_2d_cursor(Panel): + bl_space_type = 'CLIP_EDITOR' + bl_region_type = 'UI' + bl_category = "View" + bl_label = "2D Cursor" + + @classmethod + def poll(cls, context): + sc = context.space_data + + if CLIP_PT_clip_view_panel.poll(context): + return sc.pivot_point == 'CURSOR' or sc.mode == 'MASK' + + def draw(self, context): + layout = self.layout + + sc = context.space_data + + layout.use_property_split = True + layout.use_property_decorate = False + + col = layout.column() + col.prop(sc, "cursor_location", text="Location") + + class CLIP_PT_proxy(CLIP_PT_clip_view_panel, Panel): bl_space_type = 'CLIP_EDITOR' bl_region_type = 'UI' @@ -1862,6 +1887,7 @@ classes = ( CLIP_PT_proxy, CLIP_PT_footage, CLIP_PT_stabilization, + CLIP_PT_2d_cursor, CLIP_PT_mask, CLIP_PT_mask_layers, CLIP_PT_mask_display, diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 8c62484f229..fe43237963d 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2477,6 +2477,18 @@ static void rna_SpaceClipEditor_mask_set(PointerRNA *ptr, ED_space_clip_set_mask(NULL, sc, (Mask *)value.data); } +static void rna_SpaceClipEditor_cursor_location_get(PointerRNA *ptr, float *values) +{ + SpaceClip *sc = (SpaceClip *)(ptr->data); + copy_v2_v2(values, sc->cursor); +} + +static void rna_SpaceClipEditor_cursor_location_set(PointerRNA *ptr, const float *values) +{ + SpaceClip *sc = (SpaceClip *)(ptr->data); + copy_v2_v2(sc->cursor, values); +} + static void rna_SpaceClipEditor_clip_mode_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) @@ -7330,6 +7342,16 @@ static void rna_def_space_clip(BlenderRNA *brna) RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MOVIECLIP); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL); + /* transform */ + prop = RNA_def_property(srna, "cursor_location", PROP_FLOAT, PROP_XYZ); + RNA_def_property_array(prop, 2); + RNA_def_property_float_funcs(prop, + "rna_SpaceClipEditor_cursor_location_get", + "rna_SpaceClipEditor_cursor_location_set", + NULL); + RNA_def_property_ui_text(prop, "2D Cursor Location", "2D cursor location for this view"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, NULL); + /* pivot point */ prop = RNA_def_property(srna, "pivot_point", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "around"); -- cgit v1.2.3 From 8e3dea27ecfbfb374a244195e2938cec6157b982 Mon Sep 17 00:00:00 2001 From: Rob Ranieri Date: Mon, 9 Aug 2021 15:36:06 -0400 Subject: Docs: Blend-File: Fix typo Reviewed By: Blendify Differential Revision: https://developer.blender.org/D12162 --- doc/blender_file_format/BlendFileReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/blender_file_format/BlendFileReader.py b/doc/blender_file_format/BlendFileReader.py index a2f214bc4fc..1e66fc55ab0 100644 --- a/doc/blender_file_format/BlendFileReader.py +++ b/doc/blender_file_format/BlendFileReader.py @@ -123,7 +123,7 @@ def Align(handle): class BlendFile: ''' Reads a blendfile and store the header, all the fileblocks, and catalogue - structs foound in the DNA fileblock + structs found in the DNA fileblock - BlendFile.Header (BlendFileHeader instance) - BlendFile.Blocks (list of BlendFileBlock instances) -- cgit v1.2.3 From 3b87fd376adb599877740ca52ee8098622ba56fb Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 9 Aug 2021 15:25:05 -0500 Subject: Cleanup: Remove unecessary RNA get and set functions --- source/blender/makesrna/intern/rna_space.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index fe43237963d..5ab13e7b44e 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2477,18 +2477,6 @@ static void rna_SpaceClipEditor_mask_set(PointerRNA *ptr, ED_space_clip_set_mask(NULL, sc, (Mask *)value.data); } -static void rna_SpaceClipEditor_cursor_location_get(PointerRNA *ptr, float *values) -{ - SpaceClip *sc = (SpaceClip *)(ptr->data); - copy_v2_v2(values, sc->cursor); -} - -static void rna_SpaceClipEditor_cursor_location_set(PointerRNA *ptr, const float *values) -{ - SpaceClip *sc = (SpaceClip *)(ptr->data); - copy_v2_v2(sc->cursor, values); -} - static void rna_SpaceClipEditor_clip_mode_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) @@ -7344,11 +7332,8 @@ static void rna_def_space_clip(BlenderRNA *brna) /* transform */ prop = RNA_def_property(srna, "cursor_location", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "cursor"); RNA_def_property_array(prop, 2); - RNA_def_property_float_funcs(prop, - "rna_SpaceClipEditor_cursor_location_get", - "rna_SpaceClipEditor_cursor_location_set", - NULL); RNA_def_property_ui_text(prop, "2D Cursor Location", "2D cursor location for this view"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, NULL); -- cgit v1.2.3 From cd692c6954629e977250ec7c12509ffd3f90bd07 Mon Sep 17 00:00:00 2001 From: Wannes Malfait Date: Mon, 9 Aug 2021 17:30:44 -0500 Subject: Geometry Nodes: Add labels for switch node texture sockets This makes texture sockets have a label by default. This can be changed by adding the SOCK_HIDE_LABEL flag to the socket. With this change the switch node now shows the labels "True" and "False" like for the other types of sockets. --- source/blender/editors/space_node/drawnode.cc | 22 ++++++++++++++++++++-- .../nodes/node_geo_attribute_sample_texture.cc | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 95eb1ccc025..b5acdede81b 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -3596,8 +3596,26 @@ static void std_node_socket_draw( break; } case SOCK_TEXTURE: { - uiTemplateID( - layout, C, ptr, "default_value", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr); + if (text == "") { + uiTemplateID(layout, + C, + ptr, + "default_value", + "texture.new", + nullptr, + nullptr, + 0, + ICON_NONE, + nullptr); + } + else { + /* 0.3 split ratio is inconsistent, but use it here because the "New" button is large. */ + uiLayout *row = uiLayoutSplit(layout, 0.3f, false); + uiItemL(row, text, 0); + uiTemplateID( + row, C, ptr, "default_value", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr); + } + break; } case SOCK_MATERIAL: { diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc index e0a3f5ad334..5f02061da97 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -30,7 +30,7 @@ static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_TEXTURE, N_("Texture")}, + {SOCK_TEXTURE, N_("Texture"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, PROP_NONE, SOCK_HIDE_LABEL}, {SOCK_STRING, N_("Mapping")}, {SOCK_STRING, N_("Result")}, {-1, ""}, -- cgit v1.2.3 From cb7b4064d6c7baba7269df94329422e307aa3880 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 10 Aug 2021 01:24:06 +0200 Subject: VSE: Fix cache invalidation Blend mode replace is done in preprocessing stage, but property update function invalidated composite cache. --- source/blender/makesrna/intern/rna_sequencer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 74fe2a26505..e7f074efb01 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1900,7 +1900,7 @@ static void rna_def_sequence(BlenderRNA *brna) /* stupid 0-100 -> 0-1 */ RNA_def_property_float_funcs(prop, "rna_Sequence_opacity_get", "rna_Sequence_opacity_set", NULL); RNA_def_property_update( - prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_composite_update"); + prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update"); prop = RNA_def_property(srna, "effect_fader", PROP_FLOAT, PROP_FACTOR); RNA_def_property_range(prop, 0.0f, 1.0f); -- cgit v1.2.3 From d9930cddfd6214221f930c1dc3f4de649781ac31 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 10 Aug 2021 11:46:10 +1000 Subject: Fix invalid string comparison in cd692c6954629e977250ec7c12509ffd3f9 --- source/blender/editors/space_node/drawnode.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index b5acdede81b..0f7a911e3ce 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -3596,7 +3596,7 @@ static void std_node_socket_draw( break; } case SOCK_TEXTURE: { - if (text == "") { + if (text[0] == '\0') { uiTemplateID(layout, C, ptr, -- cgit v1.2.3 From 26fea4de635f28d91cb81a54cf18382c839861cb Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 10 Aug 2021 11:57:33 +1000 Subject: Cleanup: unused function warning --- source/blender/makesrna/intern/rna_sequencer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index e7f074efb01..dad77b4aad5 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -148,9 +148,9 @@ static void rna_Sequence_invalidate_preprocessed_update(Main *UNUSED(bmain), } } -static void rna_Sequence_invalidate_composite_update(Main *UNUSED(bmain), - Scene *UNUSED(scene), - PointerRNA *ptr) +static void UNUSED_FUNCTION(rna_Sequence_invalidate_composite_update)(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; Editing *ed = SEQ_editing_get(scene, false); -- cgit v1.2.3 From 3335f852a12973d4cb462d1f73a5cd734a313494 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 10 Aug 2021 12:04:17 +1000 Subject: Cleanup: mixing enum/non-enum type warning in conditional expression --- source/blender/editors/animation/keyframes_keylist.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index 85036efc983..916d7de0635 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -294,7 +294,7 @@ static void nupdate_ak_bezt(void *node, void *data) } /* For interpolation type, select the highest value (enum is sorted). */ - ak->handle_type = MAX2(ak->handle_type, bezt_handle_type(bezt)); + ak->handle_type = MAX2((eKeyframeHandleDrawOpts)ak->handle_type, bezt_handle_type(bezt)); /* For extremes, detect when combining different states. */ const char new_extreme = bezt_extreme_type(chain); -- cgit v1.2.3 From 4ca19c715314b4db12af8963750ab1a0e9fdbf1d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 10 Aug 2021 14:37:39 +1000 Subject: Fix T90493: Undo a knife-project operation crashes The crash occurred calling because mesh_get_eval_final in edit-mode freed all derived mesh data without tagging the object for updating. However meshes in edit-mode weren't meant to be used as knife-project source-data, adding support for multi object edit-mode caused this. --- source/blender/editors/mesh/editmesh_knife_project.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 09b17acf56d..54c35c071d4 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -131,9 +131,11 @@ static int knifeproject_exec(bContext *C, wmOperator *op) LinkNode *polys = NULL; CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { - if (ob != obedit) { - polys = knifeproject_poly_from_object(C, scene, ob, polys); + if (BKE_object_is_in_editmode(ob)) { + continue; } + BLI_assert(ob != obedit); + polys = knifeproject_poly_from_object(C, scene, ob, polys); } CTX_DATA_END; -- cgit v1.2.3 From b83ee724a418bee98f4e81e0c6854d03bc2e1fa6 Mon Sep 17 00:00:00 2001 From: Ankit Meel Date: Tue, 10 Aug 2021 10:30:55 +0530 Subject: Fix T90418: macOS codesign fails with dylib next to executable Change the dylib folder relative to `Blender` executable to be the same as before rB652fbc200500497a67bd11d18b786587ba34e3d9 and same as bpy.so : `@loader_path/../Resources/${BLENDER_VERSION}/lib` --- build_files/cmake/platform/platform_apple.cmake | 13 +++---------- source/creator/CMakeLists.txt | 5 +---- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index a130d265dff..dceafb236de 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -500,17 +500,10 @@ endif() # makesdna, tests, etc.), we add an rpath to the OpenMP library dir through # CMAKE_BUILD_RPATH. This avoids having to make many copies of the dylib next to each binary. # -# For the installed Blender executable, CMAKE_INSTALL_RPATH will be used -# to locate the dylibs at @executable_path, next to the Blender executable. -# -# For the installed Python module, CMAKE_INSTALL_RPATH is modified to find the -# dylib in an adjacent folder. +# For the installed Python module and installed Blender executable, CMAKE_INSTALL_RPATH +# is modified to find the dylib in an adjacent folder. Install step puts the libraries there. set(CMAKE_SKIP_BUILD_RPATH FALSE) list(APPEND CMAKE_BUILD_RPATH "${OpenMP_LIBRARY_DIR}") set(CMAKE_SKIP_INSTALL_RPATH FALSE) -list(APPEND CMAKE_INSTALL_RPATH "@executable_path") - -if(WITH_PYTHON_MODULE) - list(APPEND CMAKE_INSTALL_RPATH "@loader_path/../Resources/${BLENDER_VERSION}/lib") -endif() +list(APPEND CMAKE_INSTALL_RPATH "@loader_path/../Resources/${BLENDER_VERSION}/lib") diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index e928be571a2..47fb2642da1 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -345,13 +345,10 @@ elseif(APPLE) set(TARGETDIR_VER "${PYTHON_LIBPATH}/Resources/${BLENDER_VERSION}") set(INSTALL_BPY_TO_SITE_PACKAGES ON) endif() - # Dylibs folder for bpy.so. - set(MAC_BLENDER_TARGET_DYLIBS_DIR "${TARGETDIR_VER}/lib") else() set(TARGETDIR_VER Blender.app/Contents/Resources/${BLENDER_VERSION}) - # Dylibs folder for Blender executable. @executable_path is an rpath. - set(MAC_BLENDER_TARGET_DYLIBS_DIR "$") endif() + set(MAC_BLENDER_TARGET_DYLIBS_DIR "${TARGETDIR_VER}/lib") # Skip relinking on cpack / install set_target_properties(blender PROPERTIES BUILD_WITH_INSTALL_RPATH true) endif() -- cgit v1.2.3 From 49ea8e4fea61bd8e7c205e5edc8d0e06e6d4049e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 10 Aug 2021 15:01:43 +1000 Subject: Edit Mesh: multi-object edit-mode support for knife project --- source/blender/editors/mesh/editmesh_knife.c | 41 ++++++++++++---------- .../blender/editors/mesh/editmesh_knife_project.c | 39 +++++++++++++------- source/blender/editors/mesh/mesh_intern.h | 5 ++- 3 files changed, 52 insertions(+), 33 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 73f6a3f3238..3e3593d18fd 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -2630,24 +2630,24 @@ static void knife_init_colors(KnifeColors *colors) } /* called when modal loop selection gets set up... */ -static void knifetool_init(bContext *C, +static void knifetool_init(ViewContext *vc, KnifeTool_OpData *kcd, const bool only_select, const bool cut_through, const bool is_interactive) { - Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); + kcd->vc = *vc; + + Scene *scene = vc->scene; + Object *obedit = vc->obedit; /* assign the drawing handle for drawing preview line... */ kcd->scene = scene; kcd->ob = obedit; - kcd->region = CTX_wm_region(C); + kcd->region = vc->region; invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat); - em_setup_viewcontext(C, &kcd->vc); - kcd->em = BKE_editmesh_from_object(kcd->ob); /* cut all the way through the mesh if use_occlude_geometry button not pushed */ @@ -2694,14 +2694,14 @@ static void knifetool_init(bContext *C, } /* called when modal loop selection is done... */ -static void knifetool_exit_ex(bContext *C, KnifeTool_OpData *kcd) +static void knifetool_exit_ex(KnifeTool_OpData *kcd) { if (!kcd) { return; } if (kcd->is_interactive) { - WM_cursor_modal_restore(CTX_wm_window(C)); + WM_cursor_modal_restore(kcd->vc.win); /* deactivate the extra drawing stuff in 3D-View */ ED_region_draw_cb_exit(kcd->region->type, kcd->draw_handle); @@ -2735,10 +2735,10 @@ static void knifetool_exit_ex(bContext *C, KnifeTool_OpData *kcd) /* destroy kcd itself */ MEM_freeN(kcd); } -static void knifetool_exit(bContext *C, wmOperator *op) +static void knifetool_exit(wmOperator *op) { KnifeTool_OpData *kcd = op->customdata; - knifetool_exit_ex(C, kcd); + knifetool_exit_ex(kcd); op->customdata = NULL; } @@ -2827,10 +2827,10 @@ static void knifetool_finish(wmOperator *op) /** \name Operator (#MESH_OT_knife_tool) * \{ */ -static void knifetool_cancel(bContext *C, wmOperator *op) +static void knifetool_cancel(bContext *UNUSED(C), wmOperator *op) { /* this is just a wrapper around exit() */ - knifetool_exit(C, op); + knifetool_exit(op); } wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf) @@ -2872,7 +2872,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) bool do_refresh = false; if (!obedit || obedit->type != OB_MESH || BKE_editmesh_from_object(obedit) != kcd->em) { - knifetool_exit(C, op); + knifetool_exit(op); ED_workspace_status_text(C, NULL); return OPERATOR_FINISHED; } @@ -2893,7 +2893,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) /* finish */ ED_region_tag_redraw(kcd->region); - knifetool_exit(C, op); + knifetool_exit(op); ED_workspace_status_text(C, NULL); return OPERATOR_CANCELLED; @@ -2902,7 +2902,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_region_tag_redraw(kcd->region); knifetool_finish(op); - knifetool_exit(C, op); + knifetool_exit(op); ED_workspace_status_text(C, NULL); return OPERATOR_FINISHED; @@ -3066,8 +3066,11 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry"); const bool wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input"); + ViewContext vc; KnifeTool_OpData *kcd; + em_setup_viewcontext(C, &vc); + if (only_select) { Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(obedit); @@ -3080,7 +3083,7 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* alloc new customdata */ kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__); - knifetool_init(C, kcd, only_select, cut_through, true); + knifetool_init(&vc, kcd, only_select, cut_through, true); op->flag |= OP_IS_MODAL_CURSOR_REGION; @@ -3165,7 +3168,7 @@ static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2]) /** * \param use_tag: When set, tag all faces inside the polylines. */ -void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_through) +void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through) { KnifeTool_OpData *kcd; @@ -3176,7 +3179,7 @@ void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_throug kcd = MEM_callocN(sizeof(KnifeTool_OpData), __func__); - knifetool_init(C, kcd, only_select, cut_through, is_interactive); + knifetool_init(vc, kcd, only_select, cut_through, is_interactive); kcd->ignore_edge_snapping = true; kcd->ignore_vert_snapping = true; @@ -3313,7 +3316,7 @@ void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_throug #undef F_ISECT_SET_OUTSIDE } - knifetool_exit_ex(C, kcd); + knifetool_exit_ex(kcd); kcd = NULL; } } diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 54c35c071d4..16661897e87 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -32,6 +32,7 @@ #include "BKE_curve.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" +#include "BKE_layer.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_object.h" @@ -124,8 +125,6 @@ static LinkNode *knifeproject_poly_from_object(const bContext *C, static int knifeproject_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); const bool cut_through = RNA_boolean_get(op->ptr, "cut_through"); LinkNode *polys = NULL; @@ -134,13 +133,31 @@ static int knifeproject_exec(bContext *C, wmOperator *op) if (BKE_object_is_in_editmode(ob)) { continue; } - BLI_assert(ob != obedit); polys = knifeproject_poly_from_object(C, scene, ob, polys); } CTX_DATA_END; - if (polys) { - EDBM_mesh_knife(C, polys, true, cut_through); + if (polys == NULL) { + BKE_report(op->reports, + RPT_ERROR, + "No other selected objects have wire or boundary edges to use for projection"); + return OPERATOR_CANCELLED; + } + + ViewContext vc; + em_setup_viewcontext(C, &vc); + + /* TODO: Ideally meshes would occlude each other, currently they don't + * since each knife-project runs as a separate operation. */ + uint objects_len; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + vc.view_layer, vc.v3d, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + ED_view3d_viewcontext_init_object(&vc, obedit); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + EDBM_mesh_knife(&vc, polys, true, cut_through); /* select only tagged faces */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); @@ -150,16 +167,12 @@ static int knifeproject_exec(bContext *C, wmOperator *op) BM_mesh_elem_hflag_enable_test(em->bm, BM_FACE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG); BM_mesh_select_mode_flush(em->bm); - - BLI_linklist_freeN(polys); - - return OPERATOR_FINISHED; } + MEM_freeN(objects); + + BLI_linklist_freeN(polys); - BKE_report(op->reports, - RPT_ERROR, - "No other selected objects have wire or boundary edges to use for projection"); - return OPERATOR_CANCELLED; + return OPERATOR_FINISHED; } void MESH_OT_knife_project(wmOperatorType *ot) diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index f25317e8e85..03c99e40d1e 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -150,7 +150,10 @@ void MESH_OT_face_split_by_edges(struct wmOperatorType *ot); /* *** editmesh_knife.c *** */ void MESH_OT_knife_tool(struct wmOperatorType *ot); void MESH_OT_knife_project(struct wmOperatorType *ot); -void EDBM_mesh_knife(struct bContext *C, struct LinkNode *polys, bool use_tag, bool cut_through); +void EDBM_mesh_knife(struct ViewContext *vc, + struct LinkNode *polys, + bool use_tag, + bool cut_through); struct wmKeyMap *knifetool_modal_keymap(struct wmKeyConfig *keyconf); -- cgit v1.2.3 From 692e926b1876aeb0aa789f5fa21caf525da1690e Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 10 Aug 2021 07:57:41 +0200 Subject: Silensed compilation warning in gpu test case. --- source/blender/gpu/tests/gpu_shader_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpu/tests/gpu_shader_test.cc b/source/blender/gpu/tests/gpu_shader_test.cc index 43ff86ebbd8..561858f421e 100644 --- a/source/blender/gpu/tests/gpu_shader_test.cc +++ b/source/blender/gpu/tests/gpu_shader_test.cc @@ -108,7 +108,7 @@ void main() { EXPECT_NE(shader, nullptr); /* Construct Texture. */ - GPUTexture *texture = GPU_texture_create_1d("gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, NULL); + GPUTexture *texture = GPU_texture_create_1d("gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, nullptr); EXPECT_NE(texture, nullptr); GPU_shader_bind(shader); -- cgit v1.2.3 From 128ca8c7f6508648d1ae33bb1d1a31e0e3e10e3b Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 10 Aug 2021 08:42:06 +0200 Subject: Fix T90551: Dopesheet displays keyframes differently. Regression introduced by {rB73b047bcd431}. Missing a check when converting the file to use LISTBASE_FOREACH. --- source/blender/editors/animation/keyframes_keylist.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index 916d7de0635..f6ade11a517 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -821,6 +821,9 @@ void agroup_to_keylist(AnimData *adt, if (agrp) { /* loop through F-Curves */ LISTBASE_FOREACH (FCurve *, fcu, &agrp->channels) { + if (fcu->grp != agrp) { + break; + } fcurve_to_keylist(adt, fcu, keylist, saction_flag); } } -- cgit v1.2.3 From 61040a36aa73a19c7aa63961d9d273f22770b0d3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 10 Aug 2021 17:14:55 +1000 Subject: PyDoc: fix for renamed context member Missing change from 9cff9f9f5df034ca27848875c25471dd952c34c4. --- doc/python_api/sphinx_doc_gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index d6c1d7b51b8..43d0319b73f 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1047,7 +1047,7 @@ context_type_map = { "annotation_data": ("GreasePencil", False), "annotation_data_owner": ("ID", False), "armature": ("Armature", False), - "asset_library": ("AssetLibraryReference", False), + "asset_library_ref": ("AssetLibraryReference", False), "bone": ("Bone", False), "brush": ("Brush", False), "camera": ("Camera", False), -- cgit v1.2.3 From 7c2c66cdb8db8f38edc57a890c0ec7ea28b9fad3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 10 Aug 2021 17:50:01 +1000 Subject: Fix T90268: Mesh.from_pydata error using numpy array for edges/faces Technically not a bug but worth supporting. --- release/scripts/modules/bpy_types.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index d60165f760c..8a1615ad99f 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -477,29 +477,34 @@ class Mesh(bpy_types.ID): face_lengths = tuple(map(len, faces)) - self.vertices.add(len(vertices)) - self.edges.add(len(edges)) + # NOTE: check non-empty lists by length because of how `numpy` handles truth tests, see: T90268. + vertices_len = len(vertices) + edges_len = len(edges) + faces_len = len(faces) + + self.vertices.add(vertices_len) + self.edges.add(edges_len) self.loops.add(sum(face_lengths)) - self.polygons.add(len(faces)) + self.polygons.add(faces_len) self.vertices.foreach_set("co", tuple(chain.from_iterable(vertices))) self.edges.foreach_set("vertices", tuple(chain.from_iterable(edges))) vertex_indices = tuple(chain.from_iterable(faces)) - loop_starts = tuple(islice(chain([0], accumulate(face_lengths)), len(faces))) + loop_starts = tuple(islice(chain([0], accumulate(face_lengths)), faces_len)) self.polygons.foreach_set("loop_total", face_lengths) self.polygons.foreach_set("loop_start", loop_starts) self.polygons.foreach_set("vertices", vertex_indices) - if edges or faces: + if edges_len or faces_len: self.update( # Needed to either: # - Calculate edges that don't exist for polygons. # - Assign edges to polygon loops. - calc_edges=bool(faces), + calc_edges=bool(faces_len), # Flag loose edges. - calc_edges_loose=bool(edges), + calc_edges_loose=bool(edges_len), ) @property -- cgit v1.2.3 From 76d52cbcb42381d2ceb71f6aacbf6753906e01d0 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 10 Aug 2021 10:04:57 +0200 Subject: Cleanup: Comment COW/LOCALIZED ID tags. This was really missing there (some COW tags behavior was also documented in some code using them, like in `sound.c`, but not in their definition). Ref. T88555. --- source/blender/makesdna/DNA_ID.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index f7f4b0e6104..3995e98f34d 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -563,13 +563,32 @@ enum { /** * The data-block is a copy-on-write/localized version. * + * RESET_NEVER + * * \warning This should not be cleared on existing data. * If support for this is needed, see T88026 as this flag controls memory ownership * of physics *shared* pointers. */ LIB_TAG_COPIED_ON_WRITE = 1 << 12, - + /** + * The data-block is not the original COW ID created by the depsgraph, but has be re-allocated + * during the evaluation process of another ID. + * + * RESET_NEVER + * + * Typical example is object data, when evaluating the object's modifier stack the final obdata + * can be different than the COW initial obdata ID. + */ LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT = 1 << 13, + + /** + * The data-block is fully outside of any ID management area, and should be considered as a + * purely independent data. + * + * RESET_NEVER + * + * NOTE: Only used by nodegroups currently. + */ LIB_TAG_LOCALIZED = 1 << 14, /* RESET_NEVER tag data-block for freeing etc. behavior -- cgit v1.2.3 From 182edd4c35c24f809c80674a0dbb9e6a5e3574bc Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 10 Aug 2021 09:27:05 +0200 Subject: Fix T89284: Greasepencil top bar draw tool settings missing Caused by {rBe3faef686d38}. Error was getting the preview [which wasnt there yet] These only appeared once the material tab in the Properties Editor was used (since this ensured a valid preview icon). Above commit changed behavior for RNA icon getter (this does not create data anymore), so ensure the preview by hand here. Maniphest Tasks: T89284 Differential Revision: https://developer.blender.org/D12178 --- .../scripts/startup/bl_ui/properties_paint_common.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index ad963396022..c038f5f906a 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -1141,14 +1141,15 @@ def brush_basic__draw_color_selector(context, layout, brush, gp_settings, props) if not gp_settings.use_material_pin: ma = context.object.active_material icon_id = 0 - if ma and ma.id_data.preview: - icon_id = ma.id_data.preview.icon_id - txt_ma = ma.name - maxw = 25 - if len(txt_ma) > maxw: - txt_ma = txt_ma[:maxw - 5] + '..' + txt_ma[-3:] - else: - txt_ma = "" + txt_ma = "" + if ma: + ma.id_data.preview_ensure() + if ma.id_data.preview: + icon_id = ma.id_data.preview.icon_id + txt_ma = ma.name + maxw = 25 + if len(txt_ma) > maxw: + txt_ma = txt_ma[:maxw - 5] + '..' + txt_ma[-3:] sub = row.row() sub.ui_units_x = 8 -- cgit v1.2.3 From 895d3cd11e19b2ec361daad0064155a366359f98 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 10 Aug 2021 19:50:58 +1000 Subject: Fix T89253: template_list allows arbitrary data changes Blender forbids property changes in .draw() methods. But they weren't caught after a call to .template_list() with a custom list type. Support nested calls that disallow writes. --- source/blender/python/intern/bpy_rna.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index ec2e70ad262..b83c13e1974 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -8681,6 +8681,8 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param } #ifdef USE_PEDANTIC_WRITE + /* Handle nested draw calls, see: T89253. */ + const bool rna_disallow_writes_prev = rna_disallow_writes; rna_disallow_writes = is_readonly ? true : false; #endif /* *** Main Caller *** */ @@ -8690,7 +8692,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param /* *** Done Calling *** */ #ifdef USE_PEDANTIC_WRITE - rna_disallow_writes = false; + rna_disallow_writes = rna_disallow_writes_prev; #endif RNA_parameter_list_end(&iter); -- cgit v1.2.3 From aab7540b7a4460fdc414857493fa1c6cc5d7d972 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 10 Aug 2021 12:00:27 +0200 Subject: Fix crash: mouse is over file space during startup. When blender starts and the mouse is over a file/asset browser it crashes. This is because blender wants to highlight a file, but the layout isn't initialized yet. --- source/blender/editors/space_file/file_ops.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 944eb9988fa..c7e5f744455 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1366,7 +1366,9 @@ int file_highlight_set(SpaceFile *sfile, ARegion *region, int mx, int my) FileSelectParams *params; int numfiles, origfile; - if (sfile == NULL || sfile->files == NULL) { + /* In case blender starts where the mouse is over a File broser, this operator can be invoked + * when the sfile or sfile->layout isn't initialized yet. */ + if (sfile == NULL || sfile->files == NULL || sfile->layout == NULL) { return 0; } -- cgit v1.2.3 From fe1740ae6a580f9618ae05cf03839da51dab0c4e Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 10 Aug 2021 12:07:13 +0200 Subject: Cleanup: use give_object_under_cursor when dragging materials. It used to invoke give_base_under_cursor, but only accessed the `object` from the base. --- source/blender/editors/object/object_relations.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index a9a439c5084..c0b954f3cff 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2727,16 +2727,14 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C, PointerRNA *properties, const wmEvent *event) { - Base *base = ED_view3d_give_base_under_cursor(C, event->mval); + Object *ob = ED_view3d_give_object_under_cursor(C, event->mval); + if (ob == NULL) { + return BLI_strdup(""); + } char name[MAX_ID_NAME - 2]; RNA_string_get(properties, "name", name); - if (base == NULL) { - return BLI_strdup(""); - } - - Object *ob = base->object; int active_mat_slot = max_ii(ob->actcol, 1); Material *prev_mat = BKE_object_material_get(ob, active_mat_slot); @@ -2755,25 +2753,23 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C, static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Main *bmain = CTX_data_main(C); - Base *base = ED_view3d_give_base_under_cursor(C, event->mval); + Object *ob = ED_view3d_give_object_under_cursor(C, event->mval); Material *ma; char name[MAX_ID_NAME - 2]; RNA_string_get(op->ptr, "name", name); ma = (Material *)BKE_libblock_find_name(bmain, ID_MA, name); - if (base == NULL || ma == NULL) { + if (ob == NULL || ma == NULL) { return OPERATOR_CANCELLED; } - Object *ob = base->object; const short active_mat_slot = ob->actcol; - BKE_object_material_assign( - CTX_data_main(C), base->object, ma, active_mat_slot, BKE_MAT_ASSIGN_USERPREF); + BKE_object_material_assign(CTX_data_main(C), ob, ma, active_mat_slot, BKE_MAT_ASSIGN_USERPREF); - DEG_id_tag_update(&base->object->id, ID_RECALC_TRANSFORM); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); - WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, base->object); + WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL); WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma); -- cgit v1.2.3 From 05879f2c36e9e59ca62011aafb266a7b8dd64656 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 10 Aug 2021 12:54:06 +0200 Subject: File/Asset Browser: Select/Activate File on Right Click for Context Menu Right clicking would spawn the context menu under the cursor, but some operators would actually act on the active asset/file which wasn't the one clicked on. When multiple files are selected and one of them is right-clicked on, selection is not changed to allow operations on multiple files. E.g. deletion. This makes the File/Asset Browser match the Outliner (in behavior, not implementation). For the right-click selection keymap: * The context menu still only spawns on W. * Bonus: Right click now does something, it actually selects files! I could have done additional changes here to avoid this, but it seems like a good addition. This is also a better alternative to rB5edfde58fe60, which didn't work properly either. Using rename from the context menu would only work if the clicked on file was also active... Differential Revision: https://developer.blender.org/D12065 Reviewed by: Campbell Barton --- .../keyconfig/keymap_data/blender_default.py | 4 ++ .../keymap_data/industry_compatible_data.py | 4 ++ source/blender/editors/space_file/file_ops.c | 43 +++++++++++++--------- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index eda9c6e9792..7abbbaddb5c 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1999,6 +1999,10 @@ def km_file_browser(params): {"properties": [("increment", -10)]}), ("file.filenum", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, {"properties": [("increment", -100)]}), + + # Select file under cursor before spawning the context menu. + ("file.select", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, + {"properties": [("open", False), ("only_activate_if_selected", params.select_mouse == 'LEFTMOUSE'), ("pass_through", True)]}), *_template_items_context_menu("FILEBROWSER_MT_context_menu", params.context_menu_event), *_template_items_context_menu("ASSETBROWSER_MT_context_menu", params.context_menu_event), ]) diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 8e46e9c06df..cb9970f600b 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -1248,6 +1248,10 @@ def km_file_browser(params): {"properties": [("increment", -10)]}), ("file.filenum", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, {"properties": [("increment", -100)]}), + + # Select file under cursor before spawning the context menu. + ("file.select", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, + {"properties": [("open", False), ("only_activate_if_selected", True), ("pass_through", True)]}), *_template_items_context_menu("FILEBROWSER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), *_template_items_context_menu("ASSETBROWSER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ]) diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index c7e5f744455..d1ef1b33023 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -546,6 +546,9 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) const bool fill = RNA_boolean_get(op->ptr, "fill"); const bool do_diropen = RNA_boolean_get(op->ptr, "open"); const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + const bool only_activate_if_selected = RNA_boolean_get(op->ptr, "only_activate_if_selected"); + /* Used so right mouse clicks can do both, activate and spawn the context menu. */ + const bool pass_through = RNA_boolean_get(op->ptr, "pass_through"); if (region->regiontype != RGN_TYPE_WINDOW) { return OPERATOR_CANCELLED; @@ -563,8 +566,13 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) int numfiles = filelist_files_ensure(sfile->files); if ((idx >= 0) && (idx < numfiles)) { + const bool is_selected = filelist_entry_select_index_get(sfile->files, idx, CHECK_ALL) & + FILE_SEL_SELECTED; + if (only_activate_if_selected && is_selected) { + /* Don't deselect other items. */ + } /* single select, deselect all selected first */ - if (!extend) { + else if (!extend) { file_select_deselect_all(sfile, FILE_SEL_SELECTED); } } @@ -593,7 +601,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) WM_event_add_mousemove(CTX_wm_window(C)); /* for directory changes */ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); - return OPERATOR_FINISHED; + return pass_through ? (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH) : OPERATOR_FINISHED; } void FILE_OT_select(wmOperatorType *ot) @@ -628,6 +636,20 @@ void FILE_OT_select(wmOperatorType *ot) "Deselect On Nothing", "Deselect all when nothing under the cursor"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, + "only_activate_if_selected", + false, + "Only Activate if Selected", + "Do not change selection if the item under the cursor is already " + "selected, only activate it"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, + "pass_through", + false, + "Pass Through", + "Even on successful execution, pass the event on so other operators can " + "execute on it as well"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } /** \} */ @@ -2752,20 +2774,6 @@ static void file_rename_state_activate(SpaceFile *sfile, int file_idx, bool requ } } -static int file_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) -{ - ScrArea *area = CTX_wm_area(C); - SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C); - FileSelectParams *params = ED_fileselect_get_active_params(sfile); - - if (params) { - file_rename_state_activate(sfile, params->active_file, true); - ED_area_tag_redraw(area); - } - - return OPERATOR_FINISHED; -} - static int file_rename_exec(bContext *C, wmOperator *UNUSED(op)) { ScrArea *area = CTX_wm_area(C); @@ -2773,7 +2781,7 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op)) FileSelectParams *params = ED_fileselect_get_active_params(sfile); if (params) { - file_rename_state_activate(sfile, params->highlight_file, false); + file_rename_state_activate(sfile, params->active_file, false); ED_area_tag_redraw(area); } @@ -2788,7 +2796,6 @@ void FILE_OT_rename(struct wmOperatorType *ot) ot->idname = "FILE_OT_rename"; /* api callbacks */ - ot->invoke = file_rename_invoke; ot->exec = file_rename_exec; /* File browsing only operator (not asset browsing). */ ot->poll = ED_operator_file_browsing_active; -- cgit v1.2.3 From 9c0f11344e77eb4062ddb08502317d287cce4bc8 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 10 Aug 2021 10:27:57 +0200 Subject: Fix T90564: Crash when linking 2 node inputs Caused by {rB37570a73170e}. Above commit wasnt taking into account that at this point the link could still be NULL. Maniphest Tasks: T90564 Differential Revision: https://developer.blender.org/D12180 --- source/blender/blenkernel/intern/node.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 4aec5a9e667..1bf95369794 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -2364,7 +2364,7 @@ bNodeLink *nodeAddLink( ntree->update |= NTREE_UPDATE_LINKS; } - if (link->tosock->flag & SOCK_MULTI_INPUT) { + if (link != nullptr && link->tosock->flag & SOCK_MULTI_INPUT) { link->multi_input_socket_index = node_count_links(ntree, link->tosock) - 1; } -- cgit v1.2.3 From e38de11f05710e08bab39019a9a1c28c24c703a8 Mon Sep 17 00:00:00 2001 From: Himanshi Kalra Date: Tue, 10 Aug 2021 19:16:36 +0530 Subject: Refactor: Custom data comparison in meshes Added the comparison of non-generic attributes with generic attributes in the same loop to avoid issues with different order in layer->types of the two meshes. Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D12149 --- source/blender/blenkernel/intern/mesh.c | 315 +++++++++++++++----------------- 1 file changed, 145 insertions(+), 170 deletions(-) diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 088026ef945..b04988a8035 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -1,4 +1,4 @@ -/* +/* * 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 @@ -430,57 +430,159 @@ static int customdata_compare( { const float thresh_sq = thresh * thresh; CustomDataLayer *l1, *l2; - int i1 = 0, i2 = 0, tot, j; + int layer_count1 = 0, layer_count2 = 0, j; + const uint64_t cd_mask_non_generic = CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MPOLY | + CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MDEFORMVERT; + const uint64_t cd_mask_all_attr = CD_MASK_PROP_ALL | cd_mask_non_generic; for (int i = 0; i < c1->totlayer; i++) { - if (ELEM(c1->layers[i].type, - CD_MVERT, - CD_MEDGE, - CD_MPOLY, - CD_MLOOPUV, - CD_MLOOPCOL, - CD_MDEFORMVERT)) { - i1++; + if (CD_TYPE_AS_MASK(c1->layers[i].type) & cd_mask_all_attr) { + layer_count1++; } } for (int i = 0; i < c2->totlayer; i++) { - if (ELEM(c2->layers[i].type, - CD_MVERT, - CD_MEDGE, - CD_MPOLY, - CD_MLOOPUV, - CD_MLOOPCOL, - CD_MDEFORMVERT)) { - i2++; + if (CD_TYPE_AS_MASK(c2->layers[i].type) & cd_mask_all_attr) { + layer_count2++; } } - if (i1 != i2) { + if (layer_count1 != layer_count2) { return MESHCMP_CDLAYERS_MISMATCH; } l1 = c1->layers; l2 = c2->layers; - for (i1 = 0; i1 < c1->totlayer; i1++) { + for (int i1 = 0; i1 < c1->totlayer; i1++) { l1 = c1->layers + i1; - if ((CD_TYPE_AS_MASK(l1->type) & CD_MASK_PROP_ALL) == 0) { - /* Skip non generic attribute layers. */ - continue; - } - - bool found_corresponding_layer = false; - for (i2 = 0; i2 < c2->totlayer; i2++) { + for (int i2 = 0; i2 < c2->totlayer; i2++) { l2 = c2->layers + i2; if (l1->type != l2->type || !STREQ(l1->name, l2->name)) { continue; } - found_corresponding_layer = true; /* At this point `l1` and `l2` have the same name and type, so they should be compared. */ switch (l1->type) { + case CD_MVERT: { + MVert *v1 = l1->data; + MVert *v2 = l2->data; + int vtot = m1->totvert; + + for (j = 0; j < vtot; j++, v1++, v2++) { + if (len_squared_v3v3(v1->co, v2->co) > thresh_sq) { + return MESHCMP_VERTCOMISMATCH; + } + /* I don't care about normals, let's just do coordinates. */ + } + break; + } + + /* We're order-agnostic for edges here. */ + case CD_MEDGE: { + MEdge *e1 = l1->data; + MEdge *e2 = l2->data; + int etot = m1->totedge; + EdgeHash *eh = BLI_edgehash_new_ex(__func__, etot); + + for (j = 0; j < etot; j++, e1++) { + BLI_edgehash_insert(eh, e1->v1, e1->v2, e1); + } + + for (j = 0; j < etot; j++, e2++) { + if (!BLI_edgehash_lookup(eh, e2->v1, e2->v2)) { + return MESHCMP_EDGEUNKNOWN; + } + } + BLI_edgehash_free(eh, NULL); + break; + } + case CD_MPOLY: { + MPoly *p1 = l1->data; + MPoly *p2 = l2->data; + int ptot = m1->totpoly; + + for (j = 0; j < ptot; j++, p1++, p2++) { + MLoop *lp1, *lp2; + int k; + + if (p1->totloop != p2->totloop) { + return MESHCMP_POLYMISMATCH; + } + + lp1 = m1->mloop + p1->loopstart; + lp2 = m2->mloop + p2->loopstart; + + for (k = 0; k < p1->totloop; k++, lp1++, lp2++) { + if (lp1->v != lp2->v) { + return MESHCMP_POLYVERTMISMATCH; + } + } + } + break; + } + case CD_MLOOP: { + MLoop *lp1 = l1->data; + MLoop *lp2 = l2->data; + int ltot = m1->totloop; + + for (j = 0; j < ltot; j++, lp1++, lp2++) { + if (lp1->v != lp2->v) { + return MESHCMP_LOOPMISMATCH; + } + } + break; + } + case CD_MLOOPUV: { + MLoopUV *lp1 = l1->data; + MLoopUV *lp2 = l2->data; + int ltot = m1->totloop; + + for (j = 0; j < ltot; j++, lp1++, lp2++) { + if (len_squared_v2v2(lp1->uv, lp2->uv) > thresh_sq) { + return MESHCMP_LOOPUVMISMATCH; + } + } + break; + } + case CD_MLOOPCOL: { + MLoopCol *lp1 = l1->data; + MLoopCol *lp2 = l2->data; + int ltot = m1->totloop; + + for (j = 0; j < ltot; j++, lp1++, lp2++) { + if (abs(lp1->r - lp2->r) > thresh || abs(lp1->g - lp2->g) > thresh || + abs(lp1->b - lp2->b) > thresh || abs(lp1->a - lp2->a) > thresh) { + return MESHCMP_LOOPCOLMISMATCH; + } + } + break; + } + case CD_MDEFORMVERT: { + MDeformVert *dv1 = l1->data; + MDeformVert *dv2 = l2->data; + int dvtot = m1->totvert; + + for (j = 0; j < dvtot; j++, dv1++, dv2++) { + int k; + MDeformWeight *dw1 = dv1->dw, *dw2 = dv2->dw; + + if (dv1->totweight != dv2->totweight) { + return MESHCMP_DVERT_TOTGROUPMISMATCH; + } + + for (k = 0; k < dv1->totweight; k++, dw1++, dw2++) { + if (dw1->def_nr != dw2->def_nr) { + return MESHCMP_DVERT_GROUPMISMATCH; + } + if (fabsf(dw1->weight - dw2->weight) > thresh) { + return MESHCMP_DVERT_WEIGHTMISMATCH; + } + } + } + break; + } case CD_PROP_FLOAT: { const float *l1_data = l1->data; const float *l2_data = l2->data; @@ -514,157 +616,30 @@ static int customdata_compare( } break; } - default: { - int element_size = CustomData_sizeof(l1->type); + case CD_PROP_INT32: { + const int *l1_data = l1->data; + const int *l2_data = l2->data; + for (int i = 0; i < total_length; i++) { - int offset = element_size * i; - if (!CustomData_data_equals(l1->type, - POINTER_OFFSET(l1->data, offset), - POINTER_OFFSET(l2->data, offset))) { + if (l1_data[i] != l2_data[i]) { return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; } } break; } - } - } - - if (!found_corresponding_layer) { - return MESHCMP_CDLAYERS_MISMATCH; - } - } - - l1 = c1->layers; - l2 = c2->layers; - tot = i1; - i1 = 0; - i2 = 0; - for (int i = 0; i < tot; i++) { - while ( - i1 < c1->totlayer && - !ELEM(l1->type, CD_MVERT, CD_MEDGE, CD_MPOLY, CD_MLOOPUV, CD_MLOOPCOL, CD_MDEFORMVERT)) { - i1++; - l1++; - } - - while ( - i2 < c2->totlayer && - !ELEM(l2->type, CD_MVERT, CD_MEDGE, CD_MPOLY, CD_MLOOPUV, CD_MLOOPCOL, CD_MDEFORMVERT)) { - i2++; - l2++; - } - - if (l1->type == CD_MVERT) { - MVert *v1 = l1->data; - MVert *v2 = l2->data; - int vtot = m1->totvert; - - for (j = 0; j < vtot; j++, v1++, v2++) { - if (len_squared_v3v3(v1->co, v2->co) > thresh_sq) { - return MESHCMP_VERTCOMISMATCH; - } - /* I don't care about normals, let's just do coordinates. */ - } - } - - /* We're order-agnostic for edges here. */ - if (l1->type == CD_MEDGE) { - MEdge *e1 = l1->data; - MEdge *e2 = l2->data; - int etot = m1->totedge; - EdgeHash *eh = BLI_edgehash_new_ex(__func__, etot); + case CD_PROP_BOOL: { + const bool *l1_data = l1->data; + const bool *l2_data = l2->data; - for (j = 0; j < etot; j++, e1++) { - BLI_edgehash_insert(eh, e1->v1, e1->v2, e1); - } - - for (j = 0; j < etot; j++, e2++) { - if (!BLI_edgehash_lookup(eh, e2->v1, e2->v2)) { - return MESHCMP_EDGEUNKNOWN; - } - } - BLI_edgehash_free(eh, NULL); - } - - if (l1->type == CD_MPOLY) { - MPoly *p1 = l1->data; - MPoly *p2 = l2->data; - int ptot = m1->totpoly; - - for (j = 0; j < ptot; j++, p1++, p2++) { - MLoop *lp1, *lp2; - int k; - - if (p1->totloop != p2->totloop) { - return MESHCMP_POLYMISMATCH; - } - - lp1 = m1->mloop + p1->loopstart; - lp2 = m2->mloop + p2->loopstart; - - for (k = 0; k < p1->totloop; k++, lp1++, lp2++) { - if (lp1->v != lp2->v) { - return MESHCMP_POLYVERTMISMATCH; + for (int i = 0; i < total_length; i++) { + if (l1_data[i] != l2_data[i]) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } } + break; } - } - } - if (l1->type == CD_MLOOP) { - MLoop *lp1 = l1->data; - MLoop *lp2 = l2->data; - int ltot = m1->totloop; - - for (j = 0; j < ltot; j++, lp1++, lp2++) { - if (lp1->v != lp2->v) { - return MESHCMP_LOOPMISMATCH; - } - } - } - if (l1->type == CD_MLOOPUV) { - MLoopUV *lp1 = l1->data; - MLoopUV *lp2 = l2->data; - int ltot = m1->totloop; - - for (j = 0; j < ltot; j++, lp1++, lp2++) { - if (len_squared_v2v2(lp1->uv, lp2->uv) > thresh_sq) { - return MESHCMP_LOOPUVMISMATCH; - } - } - } - - if (l1->type == CD_MLOOPCOL) { - MLoopCol *lp1 = l1->data; - MLoopCol *lp2 = l2->data; - int ltot = m1->totloop; - - for (j = 0; j < ltot; j++, lp1++, lp2++) { - if (abs(lp1->r - lp2->r) > thresh || abs(lp1->g - lp2->g) > thresh || - abs(lp1->b - lp2->b) > thresh || abs(lp1->a - lp2->a) > thresh) { - return MESHCMP_LOOPCOLMISMATCH; - } - } - } - - if (l1->type == CD_MDEFORMVERT) { - MDeformVert *dv1 = l1->data; - MDeformVert *dv2 = l2->data; - int dvtot = m1->totvert; - - for (j = 0; j < dvtot; j++, dv1++, dv2++) { - int k; - MDeformWeight *dw1 = dv1->dw, *dw2 = dv2->dw; - - if (dv1->totweight != dv2->totweight) { - return MESHCMP_DVERT_TOTGROUPMISMATCH; - } - - for (k = 0; k < dv1->totweight; k++, dw1++, dw2++) { - if (dw1->def_nr != dw2->def_nr) { - return MESHCMP_DVERT_GROUPMISMATCH; - } - if (fabsf(dw1->weight - dw2->weight) > thresh) { - return MESHCMP_DVERT_WEIGHTMISMATCH; - } + default: { + break; } } } -- cgit v1.2.3 From 45997489622869b206b8fdacba0586e14f402722 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 21 May 2021 18:08:51 +0200 Subject: UI: hide object instancing panel for object types that dont support instancing Basically, only meshes, empties and pointclouds support direct instancing atm., no need to have the panel for other types. note: prior to rB2eca054e14b1, collection instancing was possible on all types (but that was removed in said commit) note2: for empties, rna_Object_instance_type_itemf should also be tweaked so we dont get "Vertices" and "Faces" options, but that can be done in a separate commit Maniphest Tasks: T88443 Differential Revision: https://developer.blender.org/D11348 --- release/scripts/startup/bl_ui/properties_object.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py index 46a16a70dca..e93b0b0d0a1 100644 --- a/release/scripts/startup/bl_ui/properties_object.py +++ b/release/scripts/startup/bl_ui/properties_object.py @@ -262,6 +262,11 @@ class OBJECT_PT_instancing(ObjectButtonsPanel, Panel): bl_label = "Instancing" bl_options = {'DEFAULT_CLOSED'} + @classmethod + def poll(cls, context): + ob = context.object + return (ob.type in {'MESH', 'EMPTY', 'POINTCLOUD'}) + def draw(self, context): layout = self.layout -- cgit v1.2.3 From 6c0c766bcaa013007e12f2fd3e308d83cd95594c Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 21 May 2021 18:25:35 +0200 Subject: UI: hide instancing options for empties which cannot be used Empties can only instance a collection, instancing on "Vertices" or "Faces" does not make sense for empties, make that clear in the UI. ref D11348 Maniphest Tasks: T88443 Differential Revision: https://developer.blender.org/D11349 --- source/blender/makesrna/intern/rna_object.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index bef430b0314..0f6b89722a4 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -218,6 +218,12 @@ static EnumPropertyItem instance_items_pointcloud[] = { {OB_DUPLIVERTS, "POINTS", 0, "Points", "Instantiate child objects on all points"}, {0, NULL, 0, NULL, NULL}, }; + +static EnumPropertyItem instance_items_empty[] = { + {0, "NONE", 0, "None", ""}, + INSTANCE_ITEM_COLLECTION, + {0, NULL, 0, NULL, NULL}, +}; #endif #undef INSTANCE_ITEMS_SHARED #undef INSTANCE_ITEM_COLLECTION @@ -751,7 +757,7 @@ static const EnumPropertyItem *rna_Object_instance_type_itemf(bContext *UNUSED(C const EnumPropertyItem *item; if (ob->type == OB_EMPTY) { - item = instance_items; + item = instance_items_empty; } else if (ob->type == OB_POINTCLOUD) { item = instance_items_pointcloud; -- cgit v1.2.3 From 0116a567dd06a4d53aa8d0117e49841963923742 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 10 Aug 2021 15:23:47 +0200 Subject: Compositor: Full frame Sun Beams node Adds full frame implementation to this node operation. No functional changes. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D11694 --- .../compositor/operations/COM_SunBeamsOperation.cc | 53 ++++++++++++++++++---- .../compositor/operations/COM_SunBeamsOperation.h | 12 ++++- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc index bd82b6397ad..ad96e3a02ba 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc @@ -30,7 +30,7 @@ SunBeamsOperation::SunBeamsOperation() this->flags.complex = true; } -void SunBeamsOperation::initExecution() +void SunBeamsOperation::calc_rays_common_data() { /* convert to pixels */ this->m_source_px[0] = this->m_data.source[0] * this->getWidth(); @@ -38,6 +38,11 @@ void SunBeamsOperation::initExecution() this->m_ray_length_px = this->m_data.ray_length * MAX2(this->getWidth(), this->getHeight()); } +void SunBeamsOperation::initExecution() +{ + calc_rays_common_data(); +} + /** * Defines a line accumulator for a specific sector, * given by the four matrix entries that rotate from buffer space into the sector @@ -140,7 +145,7 @@ template struct BufferLineAccumulator { falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f; - float *iter = input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS * (x + input->getWidth() * y); + float *iter = input->getBuffer() + input->get_coords_offset(x, y); return iter; } @@ -159,7 +164,6 @@ template struct BufferLineAccumulator { float dist_max) { const rcti &rect = input->get_rect(); - int buffer_width = input->getWidth(); int x, y, num; float v, dv; float falloff_factor; @@ -168,9 +172,7 @@ template struct BufferLineAccumulator { zero_v4(output); if ((int)(co[0] - source[0]) == 0 && (int)(co[1] - source[1]) == 0) { - copy_v4_v4(output, - input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS * - ((int)source[0] + input->getWidth() * (int)source[1])); + copy_v4_v4(output, input->get_elem(source[0], source[1])); return; } @@ -210,7 +212,7 @@ template struct BufferLineAccumulator { /* decrement u */ x -= fxu; y -= fyu; - buffer -= (fxu + fyu * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS; + buffer -= fxu * input->elem_stride + fyu * input->row_stride; /* decrement v (in steps of dv < 1) */ v_local -= dv; @@ -219,7 +221,7 @@ template struct BufferLineAccumulator { x -= fxv; y -= fyv; - buffer -= (fxv + fyv * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS; + buffer -= fxv * input->elem_stride + fyv * input->row_stride; } } @@ -356,4 +358,39 @@ bool SunBeamsOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&rect, readOperation, output); } +void SunBeamsOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS(input_idx); + calc_rays_common_data(); + + r_input_area = output_area; + /* Enlarges the rect by moving each corner toward the source. + * This is the maximum distance that pixels can influence each other + * and gives a rect that contains all possible accumulated pixels. */ + calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymin, m_source_px, m_ray_length_px); + calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymax, m_source_px, m_ray_length_px); + calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymin, m_source_px, m_ray_length_px); + calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymax, m_source_px, m_ray_length_px); +} + +void SunBeamsOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + MemoryBuffer *input = inputs[0]; + float coords[2]; + for (int y = area.ymin; y < area.ymax; y++) { + coords[1] = y; + float *out_elem = output->get_elem(area.xmin, y); + for (int x = area.xmin; x < area.xmax; x++) { + coords[0] = x; + accumulate_line(input, out_elem, coords, m_source_px, 0.0f, m_ray_length_px); + out_elem += output->elem_stride; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.h b/source/blender/compositor/operations/COM_SunBeamsOperation.h index d3725021cde..71fc04453fe 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.h +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.h @@ -17,11 +17,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class SunBeamsOperation : public NodeOperation { +class SunBeamsOperation : public MultiThreadedOperation { public: SunBeamsOperation(); @@ -40,6 +40,14 @@ class SunBeamsOperation : public NodeOperation { m_data = data; } + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + + private: + void calc_rays_common_data(); + private: NodeSunBeams m_data; -- cgit v1.2.3 From 079f35572b1dc74427509476c413fb2becdafa14 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 10 Aug 2021 15:24:05 +0200 Subject: Compositor: Full frame Bilateral Blur node Adds full frame implementation to this node operation. No functional changes. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D11634 --- source/blender/compositor/COM_defines.h | 1 + .../operations/COM_BilateralBlurOperation.cc | 86 +++++++++++++++++++++- .../operations/COM_BilateralBlurOperation.h | 13 +++- 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index 900f29db44c..5b826d8fc08 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -68,6 +68,7 @@ constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType constexpr float COM_COLOR_TRANSPARENT[4] = {0.0f, 0.0f, 0.0f, 0.0f}; constexpr float COM_VECTOR_ZERO[3] = {0.0f, 0.0f, 0.0f}; +constexpr float COM_COLOR_BLACK[4] = {0.0f, 0.0f, 0.0f, 1.0f}; constexpr float COM_VALUE_ZERO[1] = {0.0f}; constexpr float COM_VALUE_ONE[1] = {1.0f}; diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc index 64448e2ae95..0c1bb688d4e 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc @@ -38,7 +38,6 @@ void BilateralBlurOperation::initExecution() { this->m_inputColorProgram = getInputSocketReader(0); this->m_inputDeterminatorProgram = getInputSocketReader(1); - this->m_space = this->m_data->sigma_space + this->m_data->iter; QualityStepHelper::initExecution(COM_QH_INCREASE); } @@ -115,4 +114,89 @@ bool BilateralBlurOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void BilateralBlurOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &output_area, + rcti &r_input_area) +{ + const int add = ceil(this->m_space) + 1; + + r_input_area.xmax = output_area.xmax + (add); + r_input_area.xmin = output_area.xmin - (add); + r_input_area.ymax = output_area.ymax + (add); + r_input_area.ymin = output_area.ymin - (add); +} + +struct PixelCursor { + MemoryBuffer *input_determinator; + MemoryBuffer *input_color; + int step; + float sigma_color; + const float *determ_reference_color; + float temp_color[4]; + float *out; + int min_x, max_x; + int min_y, max_y; +}; + +static void blur_pixel(PixelCursor &p) +{ + float blur_divider = 0.0f; + zero_v4(p.out); + + /* TODO(sergey): This isn't really good bilateral filter, it should be + * using gaussian bell for weights. Also sigma_color doesn't seem to be + * used correct at all. + */ + for (int yi = p.min_y; yi < p.max_y; yi += p.step) { + for (int xi = p.min_x; xi < p.max_x; xi += p.step) { + p.input_determinator->read(p.temp_color, xi, yi); + /* Do not take the alpha channel into account. */ + const float delta_color = (fabsf(p.determ_reference_color[0] - p.temp_color[0]) + + fabsf(p.determ_reference_color[1] - p.temp_color[1]) + + fabsf(p.determ_reference_color[2] - p.temp_color[2])); + if (delta_color < p.sigma_color) { + /* Add this to the blur. */ + p.input_color->read(p.temp_color, xi, yi); + add_v4_v4(p.out, p.temp_color); + blur_divider += 1.0f; + } + } + } + + if (blur_divider > 0.0f) { + mul_v4_fl(p.out, 1.0f / blur_divider); + } + else { + copy_v4_v4(p.out, COM_COLOR_BLACK); + } +} + +void BilateralBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + PixelCursor p = {}; + p.step = QualityStepHelper::getStep(); + p.sigma_color = this->m_data->sigma_color; + p.input_color = inputs[0]; + p.input_determinator = inputs[1]; + const float space = this->m_space; + for (int y = area.ymin; y < area.ymax; y++) { + p.out = output->get_elem(area.xmin, y); + /* This will be used as the reference color for the determinator. */ + p.determ_reference_color = p.input_determinator->get_elem(area.xmin, y); + p.min_y = floor(y - space); + p.max_y = ceil(y + space); + for (int x = area.xmin; x < area.xmax; x++) { + p.min_x = floor(x - space); + p.max_x = ceil(x + space); + + blur_pixel(p); + + p.determ_reference_color += p.input_determinator->elem_stride; + p.out += output->elem_stride; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.h b/source/blender/compositor/operations/COM_BilateralBlurOperation.h index c56cef35050..4819715deb0 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.h +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "COM_QualityStepHelper.h" namespace blender::compositor { -class BilateralBlurOperation : public NodeOperation, public QualityStepHelper { +class BilateralBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: SocketReader *m_inputColorProgram; SocketReader *m_inputDeterminatorProgram; @@ -55,7 +55,16 @@ class BilateralBlurOperation : public NodeOperation, public QualityStepHelper { void setData(NodeBilateralBlurData *data) { this->m_data = data; + this->m_space = data->sigma_space + data->iter; } + + void get_area_of_interest(int input_idx, + const rcti &output_area, + rcti &r_input_area) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor -- cgit v1.2.3 From 5deb3229a0757e2637163c60d7915888cc50c9d8 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 10 Aug 2021 15:24:17 +0200 Subject: Compositor: Full frame Mask node Adds full frame implementation to this node operations. No functional changes. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D11751 --- .../compositor/operations/COM_MaskOperation.cc | 37 ++++++++++++++++++++++ .../compositor/operations/COM_MaskOperation.h | 11 +++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc index c7763f08e71..84992f23924 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.cc +++ b/source/blender/compositor/operations/COM_MaskOperation.cc @@ -161,4 +161,41 @@ void MaskOperation::executePixelSampled(float output[4], } } +void MaskOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span UNUSED(inputs)) +{ + Vector handles = get_non_null_handles(); + if (handles.size() == 0) { + output->fill(area, COM_VALUE_ZERO); + return; + } + + float xy[2]; + for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { + xy[0] = it.x * m_maskWidthInv + m_mask_px_ofs[0]; + xy[1] = it.y * m_maskHeightInv + m_mask_px_ofs[1]; + *it.out = 0.0f; + for (MaskRasterHandle *handle : handles) { + *it.out += BKE_maskrasterize_handle_sample(handle, xy); + } + + /* Until we get better falloff. */ + *it.out /= m_rasterMaskHandleTot; + } +} + +Vector MaskOperation::get_non_null_handles() const +{ + Vector handles; + for (int i = 0; i < m_rasterMaskHandleTot; i++) { + MaskRasterHandle *handle = m_rasterMaskHandles[i]; + if (handle == nullptr) { + continue; + } + handles.append(handle); + } + return handles; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MaskOperation.h b/source/blender/compositor/operations/COM_MaskOperation.h index e8cd9c722df..81e344c0451 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.h +++ b/source/blender/compositor/operations/COM_MaskOperation.h @@ -19,7 +19,7 @@ #pragma once #include "BLI_listbase.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_mask_types.h" #include "IMB_imbuf_types.h" @@ -31,7 +31,7 @@ namespace blender::compositor { /** * Class with implementation of mask rasterization */ -class MaskOperation : public NodeOperation { +class MaskOperation : public MultiThreadedOperation { protected: Mask *m_mask; @@ -98,6 +98,13 @@ class MaskOperation : public NodeOperation { } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; + + private: + Vector get_non_null_handles() const; }; } // namespace blender::compositor -- cgit v1.2.3 From 1a9b9dd64df72401df0263ca1e30306e5c675c6d Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 10 Aug 2021 15:24:28 +0200 Subject: Compositor: Full frame input nodes Adds full frame implementation to "Bokeh Image" node, "Track Position" node, `SetVectorOperation` and `MovieClipAttribute`. The other nodes in "Input" submenu are implemented separately. `MovieClipAttribute` needs resolution to calculate its constant value, it can't be constant folded, which requires it to be a `ConstantOperation`. Now `ConstantOperation` contemplate this case and any operation that is always constant without depending on inputs should implement it. If in the future an operation needs to get an input constant element during `determineResolution` it must first determine its input resolution. The nodes have no functional changes. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12090 --- .../compositor/intern/COM_BufferOperation.cc | 1 + .../compositor/intern/COM_ConstantFolder.cc | 4 +++- source/blender/compositor/intern/COM_Debug.cc | 16 ++++++++----- .../blender/compositor/intern/COM_MemoryBuffer.h | 5 +++++ .../operations/COM_BokehImageOperation.cc | 25 +++++++++++++++++++++ .../operations/COM_BokehImageOperation.h | 8 +++++-- .../compositor/operations/COM_ConstantOperation.cc | 17 ++++++++++++++ .../compositor/operations/COM_ConstantOperation.h | 16 +++++++++++-- .../operations/COM_MovieClipAttributeOperation.cc | 19 ++++++++++++++++ .../operations/COM_MovieClipAttributeOperation.h | 9 +++++++- .../compositor/operations/COM_SetColorOperation.cc | 10 --------- .../compositor/operations/COM_SetColorOperation.h | 4 ---- .../compositor/operations/COM_SetValueOperation.cc | 10 --------- .../compositor/operations/COM_SetValueOperation.h | 3 --- .../operations/COM_TrackPositionOperation.cc | 26 ++++++++++++++++++++++ .../operations/COM_TrackPositionOperation.h | 11 +++++++-- 16 files changed, 144 insertions(+), 40 deletions(-) diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc index 90c97f2a9c7..e1f566b19db 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.cc +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -32,6 +32,7 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) setResolution(resolution); addOutputSocket(data_type); flags.is_constant_operation = buffer_->is_a_single_elem(); + flags.is_fullframe_operation = false; } const float *BufferOperation::get_constant_elem() diff --git a/source/blender/compositor/intern/COM_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc index 5b48ff8fc08..445a9ce7433 100644 --- a/source/blender/compositor/intern/COM_ConstantFolder.cc +++ b/source/blender/compositor/intern/COM_ConstantFolder.cc @@ -44,7 +44,9 @@ static bool is_constant_foldable(NodeOperation *operation) { if (operation->get_flags().can_be_constant && !operation->get_flags().is_constant_operation) { for (int i = 0; i < operation->getNumberOfInputSockets(); i++) { - if (!operation->get_input_operation(i)->get_flags().is_constant_operation) { + NodeOperation *input = operation->get_input_operation(i); + if (!input->get_flags().is_constant_operation || + !static_cast(input)->can_get_constant_elem()) { return false; } } diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index 5443974cbb0..a0333cf96cf 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -178,21 +178,27 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system, } len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "", socket); switch (socket->getDataType()) { - case DataType::Value: - if (typeid(*operation) == typeid(SetValueOperation)) { - const float value = ((SetValueOperation *)operation)->getValue(); + case DataType::Value: { + ConstantOperation *constant = operation->get_flags().is_constant_operation ? + static_cast(operation) : + nullptr; + if (constant && constant->can_get_constant_elem()) { + const float value = *constant->get_constant_elem(); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value\\n%12.4g", value); } else { len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value"); } break; - case DataType::Vector: + } + case DataType::Vector: { len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Vector"); break; - case DataType::Color: + } + case DataType::Color: { len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Color"); break; + } } } len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}"); diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index ae12c444dc1..310e87b6a4b 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -260,6 +260,11 @@ class MemoryBuffer { return this->m_num_channels; } + uint8_t get_elem_bytes_len() const + { + return this->m_num_channels * sizeof(float); + } + /** * Get all buffer elements as a range with no offsets. */ diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc index 63f283b6acc..bd5b25b5af8 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.cc +++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc @@ -110,6 +110,31 @@ void BokehImageOperation::executePixelSampled(float output[4], output[3] = (insideBokehMax + insideBokehMed + insideBokehMin) / 3.0f; } +void BokehImageOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span UNUSED(inputs)) +{ + const float shift = this->m_data->lensshift; + const float shift2 = shift / 2.0f; + const float distance = this->m_circularDistance; + for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { + const float insideBokehMax = isInsideBokeh(distance, it.x, it.y); + const float insideBokehMed = isInsideBokeh(distance - fabsf(shift2 * distance), it.x, it.y); + const float insideBokehMin = isInsideBokeh(distance - fabsf(shift * distance), it.x, it.y); + if (shift < 0) { + it.out[0] = insideBokehMax; + it.out[1] = insideBokehMed; + it.out[2] = insideBokehMin; + } + else { + it.out[0] = insideBokehMin; + it.out[1] = insideBokehMed; + it.out[2] = insideBokehMax; + } + it.out[3] = (insideBokehMax + insideBokehMed + insideBokehMin) / 3.0f; + } +} + void BokehImageOperation::deinitExecution() { if (this->m_deleteData) { diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h index 2e0bc8a34dc..2527233fabd 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.h +++ b/source/blender/compositor/operations/COM_BokehImageOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -49,7 +49,7 @@ namespace blender::compositor { * With a simple compare it can be detected if the evaluated pixel is between the outer and inner *edge. */ -class BokehImageOperation : public NodeOperation { +class BokehImageOperation : public MultiThreadedOperation { private: /** * \brief Settings of the bokeh image @@ -151,6 +151,10 @@ class BokehImageOperation : public NodeOperation { { this->m_deleteData = true; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConstantOperation.cc b/source/blender/compositor/operations/COM_ConstantOperation.cc index f905edbde76..33d51cca432 100644 --- a/source/blender/compositor/operations/COM_ConstantOperation.cc +++ b/source/blender/compositor/operations/COM_ConstantOperation.cc @@ -22,7 +22,24 @@ namespace blender::compositor { ConstantOperation::ConstantOperation() { + needs_resolution_to_get_constant_ = false; flags.is_constant_operation = true; + flags.is_fullframe_operation = true; +} + +bool ConstantOperation::can_get_constant_elem() const +{ + return !needs_resolution_to_get_constant_ || this->flags.is_resolution_set; +} + +void ConstantOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span UNUSED(inputs)) +{ + BLI_assert(output->is_a_single_elem()); + const float *constant = get_constant_elem(); + float *out = output->get_elem(area.xmin, area.ymin); + memcpy(out, constant, output->get_elem_bytes_len()); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConstantOperation.h b/source/blender/compositor/operations/COM_ConstantOperation.h index 2709efeebd8..31b8d30254b 100644 --- a/source/blender/compositor/operations/COM_ConstantOperation.h +++ b/source/blender/compositor/operations/COM_ConstantOperation.h @@ -22,15 +22,27 @@ namespace blender::compositor { +/* TODO(manzanilla): After removing tiled implementation, implement a default #determineResolution + * for all constant operations and make all initialization and deinitilization methods final. */ /** - * Base class for primitive constant operations (Color/Vector/Value). The rest of operations that - * can be constant are evaluated into primitives during constant folding. + * Base class for operations that are always constant. Operations that can be constant only when + * all their inputs are so, are evaluated into primitive constants (Color/Vector/Value) during + * constant folding. */ class ConstantOperation : public NodeOperation { + protected: + bool needs_resolution_to_get_constant_; + public: ConstantOperation(); + /** May require resolution to be already determined. */ virtual const float *get_constant_elem() = 0; + bool can_get_constant_elem() const; + + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span inputs) final; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc index a9f187258b2..e36e93984fb 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc @@ -29,10 +29,21 @@ MovieClipAttributeOperation::MovieClipAttributeOperation() this->m_framenumber = 0; this->m_attribute = MCA_X; this->m_invert = false; + needs_resolution_to_get_constant_ = true; + is_value_calculated_ = false; } void MovieClipAttributeOperation::initExecution() { + if (!is_value_calculated_) { + calc_value(); + } +} + +void MovieClipAttributeOperation::calc_value() +{ + BLI_assert(this->get_flags().is_resolution_set); + is_value_calculated_ = true; if (this->m_clip == nullptr) { return; } @@ -83,4 +94,12 @@ void MovieClipAttributeOperation::determineResolution(unsigned int resolution[2] resolution[1] = preferredResolution[1]; } +const float *MovieClipAttributeOperation::get_constant_elem() +{ + if (!is_value_calculated_) { + calc_value(); + } + return &m_value; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h index 8507e98d08f..28c39d4dad3 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h @@ -18,6 +18,7 @@ #pragma once +#include "COM_ConstantOperation.h" #include "COM_NodeOperation.h" #include "DNA_movieclip_types.h" @@ -33,13 +34,14 @@ typedef enum MovieClipAttribute { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class MovieClipAttributeOperation : public NodeOperation { +class MovieClipAttributeOperation : public ConstantOperation { private: MovieClip *m_clip; float m_value; int m_framenumber; bool m_invert; MovieClipAttribute m_attribute; + bool is_value_calculated_; public: /** @@ -56,6 +58,8 @@ class MovieClipAttributeOperation : public NodeOperation { void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; + const float *get_constant_elem() override; + void setMovieClip(MovieClip *clip) { this->m_clip = clip; @@ -72,6 +76,9 @@ class MovieClipAttributeOperation : public NodeOperation { { this->m_invert = invert; } + + private: + void calc_value(); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc index bfe735aab15..dbe45fa60db 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.cc +++ b/source/blender/compositor/operations/COM_SetColorOperation.cc @@ -24,7 +24,6 @@ SetColorOperation::SetColorOperation() { this->addOutputSocket(DataType::Color); flags.is_set_operation = true; - flags.is_fullframe_operation = true; } void SetColorOperation::executePixelSampled(float output[4], @@ -42,13 +41,4 @@ void SetColorOperation::determineResolution(unsigned int resolution[2], resolution[1] = preferredResolution[1]; } -void SetColorOperation::update_memory_buffer(MemoryBuffer *output, - const rcti &area, - Span UNUSED(inputs)) -{ - BLI_assert(output->is_a_single_elem()); - float *out_elem = output->get_elem(area.xmin, area.ymin); - copy_v4_v4(out_elem, m_color); -} - } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h index f4c0948ee1b..f546d5e7668 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.h +++ b/source/blender/compositor/operations/COM_SetColorOperation.h @@ -85,10 +85,6 @@ class SetColorOperation : public ConstantOperation { void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; - - void update_memory_buffer(MemoryBuffer *output, - const rcti &area, - Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc index c12fb106afd..ef43cf64653 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.cc +++ b/source/blender/compositor/operations/COM_SetValueOperation.cc @@ -24,7 +24,6 @@ SetValueOperation::SetValueOperation() { this->addOutputSocket(DataType::Value); flags.is_set_operation = true; - flags.is_fullframe_operation = true; } void SetValueOperation::executePixelSampled(float output[4], @@ -42,13 +41,4 @@ void SetValueOperation::determineResolution(unsigned int resolution[2], resolution[1] = preferredResolution[1]; } -void SetValueOperation::update_memory_buffer(MemoryBuffer *output, - const rcti &area, - Span UNUSED(inputs)) -{ - BLI_assert(output->is_a_single_elem()); - float *out_elem = output->get_elem(area.xmin, area.ymin); - *out_elem = m_value; -} - } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h index f18d44d9554..726624c1c6a 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.h +++ b/source/blender/compositor/operations/COM_SetValueOperation.h @@ -56,9 +56,6 @@ class SetValueOperation : public ConstantOperation { void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; - void update_memory_buffer(MemoryBuffer *output, - const rcti &area, - Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc index 993410e3e84..0f4be16a620 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc @@ -42,14 +42,24 @@ TrackPositionOperation::TrackPositionOperation() this->m_relativeFrame = 0; this->m_speed_output = false; flags.is_set_operation = true; + is_track_position_calculated_ = false; } void TrackPositionOperation::initExecution() { + if (!is_track_position_calculated_) { + calc_track_position(); + } +} + +void TrackPositionOperation::calc_track_position() +{ + is_track_position_calculated_ = true; MovieTracking *tracking = nullptr; MovieClipUser user = {0}; MovieTrackingObject *object; + track_position_ = 0; zero_v2(this->m_markerPos); zero_v2(this->m_relativePos); @@ -114,6 +124,14 @@ void TrackPositionOperation::initExecution() } } } + + track_position_ = this->m_markerPos[this->m_axis] - this->m_relativePos[this->m_axis]; + if (this->m_axis == 0) { + track_position_ *= this->m_width; + } + else { + track_position_ *= this->m_height; + } } void TrackPositionOperation::executePixelSampled(float output[4], @@ -131,6 +149,14 @@ void TrackPositionOperation::executePixelSampled(float output[4], } } +const float *TrackPositionOperation::get_constant_elem() +{ + if (!is_track_position_calculated_) { + calc_track_position(); + } + return &track_position_; +} + void TrackPositionOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h index b0b0a123bd6..f716bd97737 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.h +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h @@ -20,7 +20,7 @@ #include -#include "COM_NodeOperation.h" +#include "COM_ConstantOperation.h" #include "DNA_movieclip_types.h" #include "DNA_tracking_types.h" @@ -33,7 +33,7 @@ namespace blender::compositor { /** * Class with implementation of green screen gradient rasterization */ -class TrackPositionOperation : public NodeOperation { +class TrackPositionOperation : public ConstantOperation { protected: MovieClip *m_movieClip; int m_framenumber; @@ -47,6 +47,8 @@ class TrackPositionOperation : public NodeOperation { int m_width, m_height; float m_markerPos[2]; float m_relativePos[2]; + float track_position_; + bool is_track_position_calculated_; /** * Determine the output resolution. The resolution is retrieved from the Renderer @@ -93,6 +95,11 @@ class TrackPositionOperation : public NodeOperation { void initExecution() override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + const float *get_constant_elem() override; + + private: + void calc_track_position(); }; } // namespace blender::compositor -- cgit v1.2.3 From b81d88a8e24574028e5a5c01e8decf6ae80a7de5 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 10 Aug 2021 15:24:38 +0200 Subject: Compositor: Fix memory leaks when initializing tiles multi-threaded It was only affecting tiled fallback on full frame mode. If tiles from a constant operation were multi-thread initialized, its buffer was inflated multiple times. --- source/blender/compositor/intern/COM_BufferOperation.cc | 12 ++++++++++++ source/blender/compositor/intern/COM_BufferOperation.h | 1 + 2 files changed, 13 insertions(+) diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc index e1f566b19db..cafdff89c8e 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.cc +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -41,20 +41,32 @@ const float *BufferOperation::get_constant_elem() return buffer_->getBuffer(); } +void BufferOperation::initExecution() +{ + if (buffer_->is_a_single_elem()) { + initMutex(); + } +} + void *BufferOperation::initializeTileData(rcti * /*rect*/) { if (buffer_->is_a_single_elem() == false) { return buffer_; } + lockMutex(); if (!inflated_buffer_) { inflated_buffer_ = buffer_->inflate(); } + unlockMutex(); return inflated_buffer_; } void BufferOperation::deinitExecution() { + if (buffer_->is_a_single_elem()) { + deinitMutex(); + } delete inflated_buffer_; } diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h index 705264c37b7..b4cbc0a56b6 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.h +++ b/source/blender/compositor/intern/COM_BufferOperation.h @@ -32,6 +32,7 @@ class BufferOperation : public ConstantOperation { const float *get_constant_elem() override; void *initializeTileData(rcti *rect) override; + void initExecution() override; void deinitExecution() override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override; -- cgit v1.2.3 From 8f6cc16490843bec88806c2f76c0aa012db938dd Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 10 Aug 2021 15:25:00 +0200 Subject: Compositor: Full frame curve nodes Adds full frame implementation to "RGB Curves", "Vector Curves" and "Hue Correct" nodes. No functional changes. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12093 --- .../operations/COM_ColorCurveOperation.cc | 53 ++++++++++++++++++++++ .../operations/COM_ColorCurveOperation.h | 8 ++++ .../operations/COM_CurveBaseOperation.cc | 1 + .../compositor/operations/COM_CurveBaseOperation.h | 4 +- .../COM_HueSaturationValueCorrectOperation.cc | 27 +++++++++++ .../COM_HueSaturationValueCorrectOperation.h | 4 ++ .../operations/COM_VectorCurveOperation.cc | 10 ++++ .../operations/COM_VectorCurveOperation.h | 4 ++ 8 files changed, 109 insertions(+), 2 deletions(-) diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc index cb0565a81a2..1b7ad0ea608 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc @@ -98,6 +98,36 @@ void ColorCurveOperation::deinitExecution() this->m_inputWhiteProgram = nullptr; } +void ColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + CurveMapping *cumap = this->m_curveMapping; + float bwmul[3]; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + /* Local versions of `cumap->black` and `cumap->white`. */ + const float *black = it.in(2); + const float *white = it.in(3); + /* Get a local `bwmul` value, it's not threadsafe using `cumap->bwmul` and others. */ + BKE_curvemapping_set_black_white_ex(black, white, bwmul); + + const float fac = *it.in(0); + const float *image = it.in(1); + if (fac >= 1.0f) { + BKE_curvemapping_evaluate_premulRGBF_ex(cumap, it.out, image, black, bwmul); + } + else if (fac <= 0.0f) { + copy_v3_v3(it.out, image); + } + else { + float col[4]; + BKE_curvemapping_evaluate_premulRGBF_ex(cumap, col, image, black, bwmul); + interp_v3_v3v3(it.out, image, col, fac); + } + it.out[3] = image[3]; + } +} + // Constant level curve mapping ConstantLevelColorCurveOperation::ConstantLevelColorCurveOperation() @@ -154,4 +184,27 @@ void ConstantLevelColorCurveOperation::deinitExecution() this->m_inputImageProgram = nullptr; } +void ConstantLevelColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + CurveMapping *cumap = this->m_curveMapping; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float fac = *it.in(0); + const float *image = it.in(1); + if (fac >= 1.0f) { + BKE_curvemapping_evaluate_premulRGBF(cumap, it.out, image); + } + else if (fac <= 0.0f) { + copy_v3_v3(it.out, image); + } + else { + float col[4]; + BKE_curvemapping_evaluate_premulRGBF(cumap, col, image); + interp_v3_v3v3(it.out, image, col, fac); + } + it.out[3] = image[3]; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.h b/source/blender/compositor/operations/COM_ColorCurveOperation.h index 6fc7759b8d2..d8271e56d1d 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.h +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.h @@ -51,6 +51,10 @@ class ColorCurveOperation : public CurveBaseOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; class ConstantLevelColorCurveOperation : public CurveBaseOperation { @@ -89,6 +93,10 @@ class ConstantLevelColorCurveOperation : public CurveBaseOperation { { copy_v3_v3(this->m_white, white); } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc index 8f655964570..3c4b27aa4cf 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc @@ -25,6 +25,7 @@ namespace blender::compositor { CurveBaseOperation::CurveBaseOperation() { this->m_curveMapping = nullptr; + this->flags.can_be_constant = true; } CurveBaseOperation::~CurveBaseOperation() diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.h b/source/blender/compositor/operations/COM_CurveBaseOperation.h index fff0f3168ba..da665e7ea60 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.h +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_color_types.h" namespace blender::compositor { -class CurveBaseOperation : public NodeOperation { +class CurveBaseOperation : public MultiThreadedOperation { protected: /** * Cached reference to the inputProgram diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc index e341a88ff71..5ae868c5964 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc @@ -73,4 +73,31 @@ void HueSaturationValueCorrectOperation::deinitExecution() this->m_inputProgram = nullptr; } +void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + float hsv[4]; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + copy_v4_v4(hsv, it.in(0)); + + /* Adjust hue, scaling returned default 0.5 up to 1. */ + float f = BKE_curvemapping_evaluateF(this->m_curveMapping, 0, hsv[0]); + hsv[0] += f - 0.5f; + + /* Adjust saturation, scaling returned default 0.5 up to 1. */ + f = BKE_curvemapping_evaluateF(this->m_curveMapping, 1, hsv[0]); + hsv[1] *= (f * 2.0f); + + /* Adjust value, scaling returned default 0.5 up to 1. */ + f = BKE_curvemapping_evaluateF(this->m_curveMapping, 2, hsv[0]); + hsv[2] *= (f * 2.0f); + + hsv[0] = hsv[0] - floorf(hsv[0]); /* Mod 1.0. */ + CLAMP(hsv[1], 0.0f, 1.0f); + + copy_v4_v4(it.out, hsv); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h index 703b2894bdb..6c1b66aba1f 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h @@ -47,6 +47,10 @@ class HueSaturationValueCorrectOperation : public CurveBaseOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.cc b/source/blender/compositor/operations/COM_VectorCurveOperation.cc index 9d53ed5d8ee..c2087fd071e 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.cc @@ -53,4 +53,14 @@ void VectorCurveOperation::deinitExecution() this->m_inputProgram = nullptr; } +void VectorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + CurveMapping *curve_map = this->m_curveMapping; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + BKE_curvemapping_evaluate_premulRGBF(curve_map, it.out, it.in(0)); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.h b/source/blender/compositor/operations/COM_VectorCurveOperation.h index 8cbb80e27c7..27b3ad69e17 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.h +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.h @@ -47,6 +47,10 @@ class VectorCurveOperation : public CurveBaseOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor -- cgit v1.2.3 From d481c6651d149bdc83a8c0bbb675d3d296f7c530 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 10 Aug 2021 15:25:10 +0200 Subject: Compositor: Full frame color nodes Adds full frame implementation to "Alpha Over", "Hue Saturation Value", "Invert", "Tonemap" and "ZCombine" nodes. The other nodes in "Color" submenu are implemented separately. No functional changes. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12092 --- .../operations/COM_AlphaOverKeyOperation.cc | 30 +++++ .../operations/COM_AlphaOverKeyOperation.h | 4 + .../operations/COM_AlphaOverMixedOperation.cc | 27 +++++ .../operations/COM_AlphaOverMixedOperation.h | 2 + .../COM_AlphaOverPremultiplyOperation.cc | 29 +++++ .../operations/COM_AlphaOverPremultiplyOperation.h | 4 + .../operations/COM_ChangeHSVOperation.cc | 23 ++++ .../compositor/operations/COM_ChangeHSVOperation.h | 8 +- .../compositor/operations/COM_InvertOperation.cc | 28 +++++ .../compositor/operations/COM_InvertOperation.h | 8 +- .../compositor/operations/COM_TonemapOperation.cc | 124 +++++++++++++++++++++ .../compositor/operations/COM_TonemapOperation.h | 16 ++- .../compositor/operations/COM_ZCombineOperation.cc | 70 ++++++++++++ .../compositor/operations/COM_ZCombineOperation.h | 20 +++- 14 files changed, 385 insertions(+), 8 deletions(-) diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc index 0c656753a51..30e7fab4027 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc @@ -20,6 +20,11 @@ namespace blender::compositor { +AlphaOverKeyOperation::AlphaOverKeyOperation() +{ + this->flags.can_be_constant = true; +} + void AlphaOverKeyOperation::executePixelSampled(float output[4], float x, float y, @@ -50,4 +55,29 @@ void AlphaOverKeyOperation::executePixelSampled(float output[4], } } +void AlphaOverKeyOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *color1 = p.color1; + const float *over_color = p.color2; + const float value = *p.value; + + if (over_color[3] <= 0.0f) { + copy_v4_v4(p.out, color1); + } + else if (value == 1.0f && over_color[3] >= 1.0f) { + copy_v4_v4(p.out, over_color); + } + else { + const float premul = value * over_color[3]; + const float mul = 1.0f - premul; + + p.out[0] = (mul * color1[0]) + premul * over_color[0]; + p.out[1] = (mul * color1[1]) + premul * over_color[1]; + p.out[2] = (mul * color1[2]) + premul * over_color[2]; + p.out[3] = (mul * color1[3]) + value * over_color[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h index 83713d18971..960fbc98fe9 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h @@ -28,10 +28,14 @@ namespace blender::compositor { */ class AlphaOverKeyOperation : public MixBaseOperation { public: + AlphaOverKeyOperation(); + /** * The inner loop of this operation. */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc index c68c79d2263..0cc179ea209 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc @@ -23,6 +23,7 @@ namespace blender::compositor { AlphaOverMixedOperation::AlphaOverMixedOperation() { this->m_x = 0.0f; + this->flags.can_be_constant = true; } void AlphaOverMixedOperation::executePixelSampled(float output[4], @@ -56,4 +57,30 @@ void AlphaOverMixedOperation::executePixelSampled(float output[4], } } +void AlphaOverMixedOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *color1 = p.color1; + const float *over_color = p.color2; + const float value = *p.value; + + if (over_color[3] <= 0.0f) { + copy_v4_v4(p.out, color1); + } + else if (value == 1.0f && over_color[3] >= 1.0f) { + copy_v4_v4(p.out, over_color); + } + else { + const float addfac = 1.0f - this->m_x + over_color[3] * this->m_x; + const float premul = value * addfac; + const float mul = 1.0f - value * over_color[3]; + + p.out[0] = (mul * color1[0]) + premul * over_color[0]; + p.out[1] = (mul * color1[1]) + premul * over_color[1]; + p.out[2] = (mul * color1[2]) + premul * over_color[2]; + p.out[3] = (mul * color1[3]) + value * over_color[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h index e2b3af84162..2b88cd5f421 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h @@ -45,6 +45,8 @@ class AlphaOverMixedOperation : public MixBaseOperation { { this->m_x = x; } + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc index 3dd4607e273..a57e8c7f8a3 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc @@ -20,6 +20,11 @@ namespace blender::compositor { +AlphaOverPremultiplyOperation::AlphaOverPremultiplyOperation() +{ + this->flags.can_be_constant = true; +} + void AlphaOverPremultiplyOperation::executePixelSampled(float output[4], float x, float y, @@ -50,4 +55,28 @@ void AlphaOverPremultiplyOperation::executePixelSampled(float output[4], } } +void AlphaOverPremultiplyOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *color1 = p.color1; + const float *over_color = p.color2; + const float value = *p.value; + + if (over_color[3] <= 0.0f) { + copy_v4_v4(p.out, color1); + } + else if (value == 1.0f && over_color[3] >= 1.0f) { + copy_v4_v4(p.out, over_color); + } + else { + const float mul = 1.0f - value * over_color[3]; + + p.out[0] = (mul * color1[0]) + value * over_color[0]; + p.out[1] = (mul * color1[1]) + value * over_color[1]; + p.out[2] = (mul * color1[2]) + value * over_color[2]; + p.out[3] = (mul * color1[3]) + value * over_color[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h index f1d4b668fce..701bc07cc27 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h @@ -28,10 +28,14 @@ namespace blender::compositor { */ class AlphaOverPremultiplyOperation : public MixBaseOperation { public: + AlphaOverPremultiplyOperation(); + /** * The inner loop of this operation. */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc index eee007ce9e6..1e3e7806968 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc @@ -28,6 +28,7 @@ ChangeHSVOperation::ChangeHSVOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->m_inputOperation = nullptr; + this->flags.can_be_constant = true; } void ChangeHSVOperation::initExecution() @@ -71,4 +72,26 @@ void ChangeHSVOperation::executePixelSampled(float output[4], output[3] = inputColor1[3]; } +void ChangeHSVOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *color = it.in(0); + const float hue = *it.in(1); + it.out[0] = color[0] + (hue - 0.5f); + if (it.out[0] > 1.0f) { + it.out[0] -= 1.0f; + } + else if (it.out[0] < 0.0f) { + it.out[0] += 1.0f; + } + const float saturation = *it.in(2); + const float value = *it.in(3); + it.out[1] = color[1] * saturation; + it.out[2] = color[2] * value; + it.out[3] = color[3]; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.h b/source/blender/compositor/operations/COM_ChangeHSVOperation.h index d38b4be3efe..e7bc3274f25 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.h +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_MixOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ChangeHSVOperation : public NodeOperation { +class ChangeHSVOperation : public MultiThreadedOperation { private: SocketReader *m_inputOperation; SocketReader *m_hueOperation; @@ -46,6 +46,10 @@ class ChangeHSVOperation : public NodeOperation { * The inner loop of this operation. */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc index 339e40a5d1f..4f71a1d0d1d 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.cc +++ b/source/blender/compositor/operations/COM_InvertOperation.cc @@ -30,6 +30,7 @@ InvertOperation::InvertOperation() this->m_color = true; this->m_alpha = false; setResolutionInputSocketIndex(1); + this->flags.can_be_constant = true; } void InvertOperation::initExecution() { @@ -70,4 +71,31 @@ void InvertOperation::deinitExecution() this->m_inputColorProgram = nullptr; } +void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float value = *it.in(0); + const float inverted_value = 1.0f - value; + const float *color = it.in(1); + + if (this->m_color) { + it.out[0] = (1.0f - color[0]) * value + color[0] * inverted_value; + it.out[1] = (1.0f - color[1]) * value + color[1] * inverted_value; + it.out[2] = (1.0f - color[2]) * value + color[2] * inverted_value; + } + else { + copy_v3_v3(it.out, color); + } + + if (this->m_alpha) { + it.out[3] = (1.0f - color[3]) * value + color[3] * inverted_value; + } + else { + it.out[3] = color[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_InvertOperation.h b/source/blender/compositor/operations/COM_InvertOperation.h index 17e5eb95f3e..a084bf5d559 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.h +++ b/source/blender/compositor/operations/COM_InvertOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class InvertOperation : public NodeOperation { +class InvertOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -59,6 +59,10 @@ class InvertOperation : public NodeOperation { { this->m_alpha = alpha; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc index 6bfacb0c75d..20da468eeb1 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.cc +++ b/source/blender/compositor/operations/COM_TonemapOperation.cc @@ -17,6 +17,8 @@ */ #include "COM_TonemapOperation.h" +#include "COM_ExecutionSystem.h" + #include "BLI_math.h" #include "BLI_utildefines.h" @@ -153,4 +155,126 @@ void TonemapOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) /* pass */ } +void TonemapOperation::get_area_of_interest(const int input_idx, + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + NodeOperation *operation = getInputOperation(input_idx); + r_input_area.xmin = 0; + r_input_area.ymin = 0; + r_input_area.xmax = operation->getWidth(); + r_input_area.ymax = operation->getHeight(); +} + +struct Luminance { + float sum; + float color_sum[3]; + float log_sum; + float min; + float max; + int num_pixels; +}; + +static Luminance calc_area_luminance(const MemoryBuffer *input, const rcti &area) +{ + Luminance lum = {0}; + for (const float *elem : input->get_buffer_area(area)) { + const float lu = IMB_colormanagement_get_luminance(elem); + lum.sum += lu; + add_v3_v3(lum.color_sum, elem); + lum.log_sum += logf(MAX2(lu, 0.0f) + 1e-5f); + lum.max = MAX2(lu, lum.max); + lum.min = MIN2(lu, lum.min); + lum.num_pixels++; + } + return lum; +} + +void TonemapOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span inputs) +{ + if (this->m_cachedInstance == nullptr) { + Luminance lum = {0}; + const MemoryBuffer *input = inputs[0]; + exec_system_->execute_work( + input->get_rect(), + [=](const rcti &split) { return calc_area_luminance(input, split); }, + lum, + [](Luminance &join, const Luminance &chunk) { + join.sum += chunk.sum; + add_v3_v3(join.color_sum, chunk.color_sum); + join.log_sum += chunk.log_sum; + join.max = MAX2(join.max, chunk.max); + join.min = MIN2(join.min, chunk.min); + join.num_pixels += chunk.num_pixels; + }); + + AvgLogLum *avg = new AvgLogLum(); + avg->lav = lum.sum / lum.num_pixels; + mul_v3_v3fl(avg->cav, lum.color_sum, 1.0f / lum.num_pixels); + const float max_log = log((double)lum.max + 1e-5); + const float min_log = log((double)lum.min + 1e-5); + const float avg_log = lum.log_sum / lum.num_pixels; + avg->auto_key = (max_log > min_log) ? ((max_log - avg_log) / (max_log - min_log)) : 1.0f; + const float al = exp((double)avg_log); + avg->al = (al == 0.0f) ? 0.0f : (this->m_data->key / al); + avg->igm = (this->m_data->gamma == 0.0f) ? 1 : (1.0f / this->m_data->gamma); + this->m_cachedInstance = avg; + } +} + +void TonemapOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + AvgLogLum *avg = m_cachedInstance; + const float igm = avg->igm; + const float offset = this->m_data->offset; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + copy_v4_v4(it.out, it.in(0)); + mul_v3_fl(it.out, avg->al); + float dr = it.out[0] + offset; + float dg = it.out[1] + offset; + float db = it.out[2] + offset; + it.out[0] /= ((dr == 0.0f) ? 1.0f : dr); + it.out[1] /= ((dg == 0.0f) ? 1.0f : dg); + it.out[2] /= ((db == 0.0f) ? 1.0f : db); + if (igm != 0.0f) { + it.out[0] = powf(MAX2(it.out[0], 0.0f), igm); + it.out[1] = powf(MAX2(it.out[1], 0.0f), igm); + it.out[2] = powf(MAX2(it.out[2], 0.0f), igm); + } + } +} + +void PhotoreceptorTonemapOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + AvgLogLum *avg = m_cachedInstance; + NodeTonemap *ntm = this->m_data; + const float f = expf(-this->m_data->f); + const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); + const float ic = 1.0f - ntm->c; + const float ia = 1.0f - ntm->a; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + copy_v4_v4(it.out, it.in(0)); + const float L = IMB_colormanagement_get_luminance(it.out); + float I_l = it.out[0] + ic * (L - it.out[0]); + float I_g = avg->cav[0] + ic * (avg->lav - avg->cav[0]); + float I_a = I_l + ia * (I_g - I_l); + it.out[0] /= (it.out[0] + powf(f * I_a, m)); + I_l = it.out[1] + ic * (L - it.out[1]); + I_g = avg->cav[1] + ic * (avg->lav - avg->cav[1]); + I_a = I_l + ia * (I_g - I_l); + it.out[1] /= (it.out[1] + powf(f * I_a, m)); + I_l = it.out[2] + ic * (L - it.out[2]); + I_g = avg->cav[2] + ic * (avg->lav - avg->cav[2]); + I_a = I_l + ia * (I_g - I_l); + it.out[2] /= (it.out[2] + powf(f * I_a, m)); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TonemapOperation.h b/source/blender/compositor/operations/COM_TonemapOperation.h index 7ecb179504d..56b57730ec1 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.h +++ b/source/blender/compositor/operations/COM_TonemapOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_node_types.h" namespace blender::compositor { @@ -39,7 +39,7 @@ typedef struct AvgLogLum { * \brief base class of tonemap, implementing the simple tonemap * \ingroup operation */ -class TonemapOperation : public NodeOperation { +class TonemapOperation : public MultiThreadedOperation { protected: /** * \brief Cached reference to the reader @@ -85,6 +85,14 @@ class TonemapOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span inputs) override; + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; /** @@ -99,6 +107,10 @@ class PhotoreceptorTonemapOperation : public TonemapOperation { * The inner loop of this operation. */ void executePixel(float output[4], int x, int y, void *data) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.cc b/source/blender/compositor/operations/COM_ZCombineOperation.cc index 9d3ca7e736e..7050c3b2d83 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.cc +++ b/source/blender/compositor/operations/COM_ZCombineOperation.cc @@ -33,6 +33,7 @@ ZCombineOperation::ZCombineOperation() this->m_depth1Reader = nullptr; this->m_image2Reader = nullptr; this->m_depth2Reader = nullptr; + this->flags.can_be_constant = true; } void ZCombineOperation::initExecution() @@ -60,6 +61,19 @@ void ZCombineOperation::executePixelSampled(float output[4], this->m_image2Reader->readSampled(output, x, y, sampler); } } + +void ZCombineOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float depth1 = *it.in(1); + const float depth2 = *it.in(3); + const float *color = (depth1 < depth2) ? it.in(0) : it.in(2); + copy_v4_v4(it.out, color); + } +} + void ZCombineAlphaOperation::executePixelSampled(float output[4], float x, float y, @@ -88,6 +102,32 @@ void ZCombineAlphaOperation::executePixelSampled(float output[4], output[3] = MAX2(color1[3], color2[3]); } +void ZCombineAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float depth1 = *it.in(1); + const float depth2 = *it.in(3); + const float *color1; + const float *color2; + if (depth1 <= depth2) { + color1 = it.in(0); + color2 = it.in(2); + } + else { + color1 = it.in(2); + color2 = it.in(0); + } + const float fac = color1[3]; + const float ifac = 1.0f - fac; + it.out[0] = fac * color1[0] + ifac * color2[0]; + it.out[1] = fac * color1[1] + ifac * color2[1]; + it.out[2] = fac * color1[2] + ifac * color2[2]; + it.out[3] = MAX2(color1[3], color2[3]); + } +} + void ZCombineOperation::deinitExecution() { this->m_image1Reader = nullptr; @@ -132,6 +172,18 @@ void ZCombineMaskOperation::executePixelSampled(float output[4], interp_v4_v4v4(output, color1, color2, 1.0f - mask[0]); } +void ZCombineMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float mask = *it.in(0); + const float *color1 = it.in(1); + const float *color2 = it.in(2); + interp_v4_v4v4(it.out, color1, color2, 1.0f - mask); + } +} + void ZCombineMaskAlphaOperation::executePixelSampled(float output[4], float x, float y, @@ -154,6 +206,24 @@ void ZCombineMaskAlphaOperation::executePixelSampled(float output[4], output[3] = MAX2(color1[3], color2[3]); } +void ZCombineMaskAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float mask = *it.in(0); + const float *color1 = it.in(1); + const float *color2 = it.in(2); + const float fac = (1.0f - mask) * (1.0f - color1[3]) + mask * color2[3]; + const float mfac = 1.0f - fac; + + it.out[0] = color1[0] * mfac + color2[0] * fac; + it.out[1] = color1[1] * mfac + color2[1] * fac; + it.out[2] = color1[2] * mfac + color2[2] * fac; + it.out[3] = MAX2(color1[3], color2[3]); + } +} + void ZCombineMaskOperation::deinitExecution() { this->m_image1Reader = nullptr; diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.h b/source/blender/compositor/operations/COM_ZCombineOperation.h index d0b1aee7310..acd60b6c866 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.h +++ b/source/blender/compositor/operations/COM_ZCombineOperation.h @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ZCombineOperation : public NodeOperation { +class ZCombineOperation : public MultiThreadedOperation { protected: SocketReader *m_image1Reader; SocketReader *m_depth1Reader; @@ -46,13 +46,21 @@ class ZCombineOperation : public NodeOperation { * The inner loop of this operation. */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; class ZCombineAlphaOperation : public ZCombineOperation { void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; -class ZCombineMaskOperation : public NodeOperation { +class ZCombineMaskOperation : public MultiThreadedOperation { protected: SocketReader *m_maskReader; SocketReader *m_image1Reader; @@ -64,9 +72,17 @@ class ZCombineMaskOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; class ZCombineMaskAlphaOperation : public ZCombineMaskOperation { void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor -- cgit v1.2.3 From eb03529ab950dc0868a22eb97d3a58a07980b827 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 10 Aug 2021 15:25:26 +0200 Subject: Compositor: Full frame output nodes Adds full frame implementation to "Composite", "File Output" and "Split Viewer" nodes. The other nodes in "Output" submenu are implemented separately. No functional changes. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12091 --- source/blender/compositor/COM_defines.h | 5 ++++ .../operations/COM_CompositorOperation.cc | 16 +++++++++++ .../operations/COM_CompositorOperation.h | 8 ++++-- .../operations/COM_OutputFileOperation.cc | 33 ++++++++++++++++++++++ .../operations/COM_OutputFileOperation.h | 14 +++++++-- .../compositor/operations/COM_SplitOperation.cc | 13 +++++++++ .../compositor/operations/COM_SplitOperation.h | 8 ++++-- 7 files changed, 90 insertions(+), 7 deletions(-) diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index 5b826d8fc08..40a1e0da2a8 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -62,6 +62,11 @@ constexpr int COM_data_type_num_channels(const DataType datatype) } } +constexpr int COM_data_type_bytes_len(DataType data_type) +{ + return COM_data_type_num_channels(data_type) * sizeof(float); +} + constexpr int COM_DATA_TYPE_VALUE_CHANNELS = COM_data_type_num_channels(DataType::Value); constexpr int COM_DATA_TYPE_VECTOR_CHANNELS = COM_data_type_num_channels(DataType::Vector); constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType::Color); diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index 94d41b28f5d..8752d764107 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -220,6 +220,22 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) } } +void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &area, + Span inputs) +{ + if (!m_outputBuffer) { + return; + } + MemoryBuffer output_buf(m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight()); + output_buf.copy_from(inputs[0], area); + if (this->m_useAlphaInput) { + output_buf.copy_from(inputs[1], area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3); + } + MemoryBuffer depth_buf(m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight()); + depth_buf.copy_from(inputs[2], area); +} + void CompositorOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h index 65988c86cc5..66367ec8bae 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.h +++ b/source/blender/compositor/operations/COM_CompositorOperation.h @@ -20,7 +20,7 @@ #include "BLI_rect.h" #include "BLI_string.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" struct Scene; @@ -29,7 +29,7 @@ namespace blender::compositor { /** * \brief Compositor output operation */ -class CompositorOperation : public NodeOperation { +class CompositorOperation : public MultiThreadedOperation { private: const struct Scene *m_scene; /** @@ -125,6 +125,10 @@ class CompositorOperation : public NodeOperation { { this->m_active = active; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 6c5984e3414..402d29893a4 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -294,6 +294,22 @@ void OutputSingleLayerOperation::deinitExecution() this->m_imageInput = nullptr; } +void OutputSingleLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &area, + Span inputs) +{ + if (!m_outputBuffer) { + return; + } + + MemoryBuffer output_buf(m_outputBuffer, + COM_data_type_num_channels(this->m_datatype), + this->getWidth(), + this->getHeight()); + const MemoryBuffer *input_image = inputs[0]; + output_buf.copy_from(input_image, area); +} + /******************************* MultiLayer *******************************/ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bool use_layer_) @@ -444,4 +460,21 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() } } +void OutputOpenExrMultiLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &area, + Span inputs) +{ + const MemoryBuffer *input_image = inputs[0]; + for (int i = 0; i < this->m_layers.size(); i++) { + OutputOpenExrLayer &layer = this->m_layers[i]; + if (layer.outputBuffer) { + MemoryBuffer output_buf(layer.outputBuffer, + COM_data_type_num_channels(layer.datatype), + this->getWidth(), + this->getHeight()); + output_buf.copy_from(input_image, area); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index 64ab4c06e7c..057cee0c43e 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "BLI_path_util.h" #include "BLI_rect.h" @@ -30,7 +30,7 @@ namespace blender::compositor { /* Writes the image to a single-layer file. */ -class OutputSingleLayerOperation : public NodeOperation { +class OutputSingleLayerOperation : public MultiThreadedOperation { protected: const RenderData *m_rd; const bNodeTree *m_tree; @@ -70,6 +70,10 @@ class OutputSingleLayerOperation : public NodeOperation { { return eCompositorPriority::Low; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; /* extra info for OpenEXR layers */ @@ -86,7 +90,7 @@ struct OutputOpenExrLayer { }; /* Writes inputs into OpenEXR multilayer channels. */ -class OutputOpenExrMultiLayerOperation : public NodeOperation { +class OutputOpenExrMultiLayerOperation : public MultiThreadedOperation { protected: const Scene *m_scene; const RenderData *m_rd; @@ -122,6 +126,10 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation { { return eCompositorPriority::Low; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; void add_exr_channels(void *exrhandle, diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc index a4754de370d..d18ed3b8e14 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.cc +++ b/source/blender/compositor/operations/COM_SplitOperation.cc @@ -79,4 +79,17 @@ void SplitOperation::determineResolution(unsigned int resolution[2], NodeOperation::determineResolution(resolution, preferredResolution); } +void SplitOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + const int percent = this->m_xSplit ? this->m_splitPercentage * this->getWidth() / 100.0f : + this->m_splitPercentage * this->getHeight() / 100.0f; + const size_t elem_bytes = COM_data_type_bytes_len(getOutputSocket()->getDataType()); + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const bool is_image1 = this->m_xSplit ? it.x > percent : it.y > percent; + memcpy(it.out, it.in(is_image1 ? 0 : 1), elem_bytes); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SplitOperation.h b/source/blender/compositor/operations/COM_SplitOperation.h index 09e48821dd0..2d09d2a07dc 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.h +++ b/source/blender/compositor/operations/COM_SplitOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class SplitOperation : public NodeOperation { +class SplitOperation : public MultiThreadedOperation { private: SocketReader *m_image1Input; SocketReader *m_image2Input; @@ -45,6 +45,10 @@ class SplitOperation : public NodeOperation { { this->m_xSplit = xsplit; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor -- cgit v1.2.3 From b6538e1492bfc03bf5e06c25002f4626a62590b5 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 10 Aug 2021 15:25:38 +0200 Subject: Fix T90572: "Render Region" is broken due to compositing It was using viewer instead of render border. A copy-paste error. --- source/blender/compositor/intern/COM_TiledExecutionModel.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc index d025ce53330..a081b80349d 100644 --- a/source/blender/compositor/intern/COM_TiledExecutionModel.cc +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc @@ -45,7 +45,7 @@ TiledExecutionModel::TiledExecutionModel(CompositorContext &context, group->determineResolution(resolution); if (border_.use_render_border) { - const rctf *render_border = border_.viewer_border; + const rctf *render_border = border_.render_border; group->setRenderBorder( render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax); } -- cgit v1.2.3 From 6806459246255440aade69bc0e00a40325dfd95c Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 21 May 2021 16:51:37 +0200 Subject: UI: hide Viewport Display Bounds for object types that dont have bounding boxes These are namely 'LIGHT', 'CAMERA', 'EMPTY', 'SPEAKER' and 'LIGHTPROBE'. Note that Empties are included here despite the fact that they have instancing capabilities ('Display As' can be 'Bounds' for example which then displays all instanced geometry with boundingboxes -- this however is not meant to work with the 'Bounds' checkbox and the display bounds type, these are only affective for the object itself, not its instances) Issue came up in T88443. Maniphest Tasks: T88443 Differential Revision: https://developer.blender.org/D11344 --- release/scripts/startup/bl_ui/properties_object.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py index e93b0b0d0a1..52af4fafd09 100644 --- a/release/scripts/startup/bl_ui/properties_object.py +++ b/release/scripts/startup/bl_ui/properties_object.py @@ -216,6 +216,7 @@ class OBJECT_PT_display(ObjectButtonsPanel, Panel): obj = context.object obj_type = obj.type is_geometry = (obj_type in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'VOLUME', 'HAIR', 'POINTCLOUD'}) + has_bounds = (is_geometry or obj_type in {'LATTICE', 'ARMATURE'}) is_wire = (obj_type in {'CAMERA', 'EMPTY'}) is_empty_image = (obj_type == 'EMPTY' and obj.empty_display_type == 'IMAGE') is_dupli = (obj.instance_type != 'NONE') @@ -247,15 +248,16 @@ class OBJECT_PT_display(ObjectButtonsPanel, Panel): # Only useful with object having faces/materials... col.prop(obj, "color") - col = layout.column(align=False, heading="Bounds") - col.use_property_decorate = False - row = col.row(align=True) - sub = row.row(align=True) - sub.prop(obj, "show_bounds", text="") - sub = sub.row(align=True) - sub.active = obj.show_bounds or (obj.display_type == 'BOUNDS') - sub.prop(obj, "display_bounds_type", text="") - row.prop_decorator(obj, "display_bounds_type") + if has_bounds: + col = layout.column(align=False, heading="Bounds") + col.use_property_decorate = False + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(obj, "show_bounds", text="") + sub = sub.row(align=True) + sub.active = obj.show_bounds or (obj.display_type == 'BOUNDS') + sub.prop(obj, "display_bounds_type", text="") + row.prop_decorator(obj, "display_bounds_type") class OBJECT_PT_instancing(ObjectButtonsPanel, Panel): -- cgit v1.2.3 From 946da86e026e16cc607797d3ccb94f39269ca4ca Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Tue, 10 Aug 2021 08:52:36 -0700 Subject: Win32 IME: Replace Usage of Language IDs This is a slight refactoring of the Win32 IME code to remove the use of Language IDs, which is now strongly deprecated. Instead this uses the new recommended Locale Names, ie ISO-639-1 2-letter abbreviated names like "en" for English rather than ID 0x09. See D12143 for more details. Differential Revision: https://developer.blender.org/D12143 Reviewed by Ray Molenkamp --- intern/ghost/intern/GHOST_ImeWin32.cpp | 199 +++++++++++++++------------------ intern/ghost/intern/GHOST_ImeWin32.h | 35 ++---- 2 files changed, 99 insertions(+), 135 deletions(-) diff --git a/intern/ghost/intern/GHOST_ImeWin32.cpp b/intern/ghost/intern/GHOST_ImeWin32.cpp index 343f4d68078..47b5f5688df 100644 --- a/intern/ghost/intern/GHOST_ImeWin32.cpp +++ b/intern/ghost/intern/GHOST_ImeWin32.cpp @@ -30,9 +30,15 @@ # include "GHOST_WindowWin32.h" # include "utfconv.h" +/* ISO_639-1 2-Letter Abbreviations. */ +# define IMELANG_ENGLISH "en" +# define IMELANG_CHINESE "zh" +# define IMELANG_JAPANESE "ja" +# define IMELANG_KOREAN "ko" + GHOST_ImeWin32::GHOST_ImeWin32() : is_composing_(false), - input_language_id_(LANG_USER_DEFAULT), + language_(IMELANG_ENGLISH), conversion_modes_(IME_CMODE_ALPHANUMERIC), sentence_mode_(IME_SMODE_NONE), system_caret_(false), @@ -48,16 +54,21 @@ GHOST_ImeWin32::~GHOST_ImeWin32() void GHOST_ImeWin32::UpdateInputLanguage() { - /** - * Store the current input language. - */ - HKL input_locale = ::GetKeyboardLayout(0); - input_language_id_ = LOWORD(input_locale); + /* Get the current input locale full name. */ + WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + LCIDToLocaleName( + MAKELCID(LOWORD(::GetKeyboardLayout(0)), SORT_DEFAULT), locale, LOCALE_NAME_MAX_LENGTH, 0); + /* Get the 2-letter ISO-63901 abbreviation of the input locale name. */ + WCHAR language_u16[W32_ISO639_LEN]; + GetLocaleInfoEx(locale, LOCALE_SISO639LANGNAME, language_u16, W32_ISO639_LEN); + /* Store this as a UTF-8 string. */ + WideCharToMultiByte( + CP_UTF8, 0, language_u16, W32_ISO639_LEN, language_, W32_ISO639_LEN, NULL, NULL); } -WORD GHOST_ImeWin32::GetInputLanguage() +BOOL GHOST_ImeWin32::IsLanguage(const char name[W32_ISO639_LEN]) { - return input_language_id_; + return (strcmp(name, language_) == 0); } void GHOST_ImeWin32::UpdateConversionStatus(HWND window_handle) @@ -92,21 +103,11 @@ bool GHOST_ImeWin32::IsImeKeyEvent(char ascii) if ((ascii >= 'A' && ascii <= 'Z') || (ascii >= 'a' && ascii <= 'z')) { return true; } - switch (PRIMARYLANGID(GetInputLanguage())) { - /* In Japanese, all symbolic characters are also processed by IME. */ - case LANG_JAPANESE: { - if (ascii >= ' ' && ascii <= '~') { - return true; - } - break; - } - /* In Chinese, some symbolic characters are also processed by IME. */ - case LANG_CHINESE: { - if (ascii && strchr("!\"$'(),.:;<>?[\\]^_`", ascii)) { - return true; - } - break; - } + if (IsLanguage(IMELANG_JAPANESE) && (ascii >= ' ' && ascii <= '~')) { + return true; + } + else if (IsLanguage(IMELANG_CHINESE) && ascii && strchr("!\"$'(),.:;<>?[\\]^_`", ascii)) { + return true; } } return false; @@ -126,13 +127,8 @@ void GHOST_ImeWin32::CreateImeWindow(HWND window_handle) * Since some third-party Japanese IME also uses ::GetCaretPos() to determine * their window position, we also create a caret for Japanese IMEs. */ - if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE || - PRIMARYLANGID(input_language_id_) == LANG_JAPANESE) { - if (!system_caret_) { - if (::CreateCaret(window_handle, NULL, 1, 1)) { - system_caret_ = true; - } - } + if (!system_caret_ && (IsLanguage(IMELANG_CHINESE) || IsLanguage(IMELANG_JAPANESE))) { + system_caret_ = ::CreateCaret(window_handle, NULL, 1, 1); } /* Restore the positions of the IME windows. */ UpdateImeWindow(window_handle); @@ -185,16 +181,9 @@ void GHOST_ImeWin32::MoveImeWindow(HWND window_handle, HIMC imm_context) CANDIDATEFORM candidate_position = {0, CFS_CANDIDATEPOS, {x, y}, {0, 0, 0, 0}}; ::ImmSetCandidateWindow(imm_context, &candidate_position); if (system_caret_) { - switch (PRIMARYLANGID(input_language_id_)) { - case LANG_JAPANESE: - ::SetCaretPos(x, y + caret_rect_.getHeight()); - break; - default: - ::SetCaretPos(x, y); - break; - } + ::SetCaretPos(x, y); } - if (PRIMARYLANGID(input_language_id_) == LANG_KOREAN) { + if (IsLanguage(IMELANG_KOREAN)) { /** * Chinese IMEs and Japanese IMEs require the upper-left corner of * the caret to move the position of their candidate windows. @@ -284,83 +273,79 @@ void GHOST_ImeWin32::GetCaret(HIMC imm_context, LPARAM lparam, ImeComposition *c */ int target_start = -1; int target_end = -1; - switch (PRIMARYLANGID(input_language_id_)) { - case LANG_KOREAN: - if (lparam & CS_NOMOVECARET) { - target_start = 0; - target_end = 1; + if (IsLanguage(IMELANG_KOREAN)) { + if (lparam & CS_NOMOVECARET) { + target_start = 0; + target_end = 1; + } + } + else if (IsLanguage(IMELANG_CHINESE)) { + int clause_size = ImmGetCompositionStringW(imm_context, GCS_COMPCLAUSE, NULL, 0); + if (clause_size) { + static std::vector clauses; + clause_size = clause_size / sizeof(clauses[0]); + clauses.resize(clause_size); + ImmGetCompositionStringW( + imm_context, GCS_COMPCLAUSE, &clauses[0], sizeof(clauses[0]) * clause_size); + if (composition->cursor_position == composition->ime_string.size()) { + target_start = clauses[clause_size - 2]; + target_end = clauses[clause_size - 1]; } - break; - case LANG_CHINESE: { - int clause_size = ImmGetCompositionStringW(imm_context, GCS_COMPCLAUSE, NULL, 0); - if (clause_size) { - static std::vector clauses; - clause_size = clause_size / sizeof(clauses[0]); - clauses.resize(clause_size); - ImmGetCompositionStringW( - imm_context, GCS_COMPCLAUSE, &clauses[0], sizeof(clauses[0]) * clause_size); - if (composition->cursor_position == composition->ime_string.size()) { - target_start = clauses[clause_size - 2]; - target_end = clauses[clause_size - 1]; - } - else { - for (int i = 0; i < clause_size - 1; i++) { - if (clauses[i] == composition->cursor_position) { - target_start = clauses[i]; - target_end = clauses[i + 1]; - break; - } + else { + for (int i = 0; i < clause_size - 1; i++) { + if (clauses[i] == composition->cursor_position) { + target_start = clauses[i]; + target_end = clauses[i + 1]; + break; } } } - else { - if (composition->cursor_position != -1) { - target_start = composition->cursor_position; - target_end = composition->ime_string.size(); - } + } + else { + if (composition->cursor_position != -1) { + target_start = composition->cursor_position; + target_end = composition->ime_string.size(); } - break; } - case LANG_JAPANESE: - - /** - * For Japanese IMEs, the robustest way to retrieve the caret - * is scanning the attribute of the latest composition string and - * retrieving the beginning and the end of the target clause, i.e. - * a clause being converted. - */ - if (lparam & GCS_COMPATTR) { - int attribute_size = ::ImmGetCompositionStringW(imm_context, GCS_COMPATTR, NULL, 0); - if (attribute_size > 0) { - char *attribute_data = new char[attribute_size]; - if (attribute_data) { - ::ImmGetCompositionStringW(imm_context, GCS_COMPATTR, attribute_data, attribute_size); - for (target_start = 0; target_start < attribute_size; ++target_start) { - if (IsTargetAttribute(attribute_data[target_start])) - break; - } - for (target_end = target_start; target_end < attribute_size; ++target_end) { - if (!IsTargetAttribute(attribute_data[target_end])) - break; - } - if (target_start == attribute_size) { - /** - * This composition clause does not contain any target clauses, - * i.e. this clauses is an input clause. - * We treat whole this clause as a target clause. - */ - target_end = target_start; - target_start = 0; - } - if (target_start != -1 && target_start < attribute_size && - attribute_data[target_start] == ATTR_TARGET_NOTCONVERTED) { - composition->cursor_position = target_start; - } + } + else if (IsLanguage(IMELANG_JAPANESE)) { + /** + * For Japanese IMEs, the robustest way to retrieve the caret + * is scanning the attribute of the latest composition string and + * retrieving the beginning and the end of the target clause, i.e. + * a clause being converted. + */ + if (lparam & GCS_COMPATTR) { + int attribute_size = ::ImmGetCompositionStringW(imm_context, GCS_COMPATTR, NULL, 0); + if (attribute_size > 0) { + char *attribute_data = new char[attribute_size]; + if (attribute_data) { + ::ImmGetCompositionStringW(imm_context, GCS_COMPATTR, attribute_data, attribute_size); + for (target_start = 0; target_start < attribute_size; ++target_start) { + if (IsTargetAttribute(attribute_data[target_start])) + break; + } + for (target_end = target_start; target_end < attribute_size; ++target_end) { + if (!IsTargetAttribute(attribute_data[target_end])) + break; + } + if (target_start == attribute_size) { + /** + * This composition clause does not contain any target clauses, + * i.e. this clauses is an input clause. + * We treat whole this clause as a target clause. + */ + target_end = target_start; + target_start = 0; + } + if (target_start != -1 && target_start < attribute_size && + attribute_data[target_start] == ATTR_TARGET_NOTCONVERTED) { + composition->cursor_position = target_start; } - delete[] attribute_data; } + delete[] attribute_data; } - break; + } } composition->target_start = target_start; composition->target_end = target_end; diff --git a/intern/ghost/intern/GHOST_ImeWin32.h b/intern/ghost/intern/GHOST_ImeWin32.h index d430a7d745d..ce0e4d64d53 100644 --- a/intern/ghost/intern/GHOST_ImeWin32.h +++ b/intern/ghost/intern/GHOST_ImeWin32.h @@ -36,6 +36,9 @@ # include "GHOST_Rect.h" # include +/* MSDN LOCALE_SISO639LANGNAME states maximum length of 9, including terminating null. */ +# define W32_ISO639_LEN 9 + class GHOST_EventIME : public GHOST_Event { public: /** @@ -146,13 +149,10 @@ class GHOST_ImeWin32 { return is_composing_; } - /** - * Retrieves the input language from Windows and update it. - */ + /* Retrieve the input language from Windows and store it. */ void UpdateInputLanguage(); - /* Returns the current input language id. */ - WORD GetInputLanguage(); + BOOL IsLanguage(const char name[W32_ISO639_LEN]); /* Saves the current conversion status. */ void UpdateConversionStatus(HWND window_handle); @@ -345,29 +345,8 @@ class GHOST_ImeWin32 { */ bool is_composing_; - /** - * The current input Language ID retrieved from Windows, which consists of: - * * Primary Language ID (bit 0 to bit 9), which shows a natural language - * (English, Korean, Chinese, Japanese, etc.) and; - * * Sub-Language ID (bit 10 to bit 15), which shows a geometrical region - * the language is spoken (For English, United States, United Kingdom, - * Australia, Canada, etc.) - * The following list enumerates some examples for the Language ID: - * * "en-US" (0x0409) - * MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); - * * "ko-KR" (0x0412) - * MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN); - * * "zh-TW" (0x0404) - * MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL); - * * "zh-CN" (0x0804) - * MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED); - * * "ja-JP" (0x0411) - * MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), etc. - * (See `winnt.h` for other available values.) - * This Language ID is used for processing language-specific operations in - * IME functions. - */ - LANGID input_language_id_; + /* Abbreviated ISO 639-1 name of the input language, such as "en" for English. */ + char language_[W32_ISO639_LEN]; /* Current Conversion Mode Values. Retrieved with ImmGetConversionStatus. */ DWORD conversion_modes_; -- cgit v1.2.3 From 32c687b5ec43463d7d419313c4eecf1ad629f61e Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Tue, 10 Aug 2021 11:27:18 -0600 Subject: Clean-up: Remove UTF8-BOM markers Done at the request of Sergey. --- extern/audaspace/bindings/python/setup.py.in | 2 +- source/blender/blenkernel/intern/icons.cc | 2 +- source/blender/blenkernel/intern/mesh.c | 2 +- source/blender/io/alembic/intern/abc_reader_object.cc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extern/audaspace/bindings/python/setup.py.in b/extern/audaspace/bindings/python/setup.py.in index add1a2d1475..5ad1a37db3a 100644 --- a/extern/audaspace/bindings/python/setup.py.in +++ b/extern/audaspace/bindings/python/setup.py.in @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import sys import os diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc index 12a0a1e3ae7..5a4b2448a73 100644 --- a/source/blender/blenkernel/intern/icons.cc +++ b/source/blender/blenkernel/intern/icons.cc @@ -1,4 +1,4 @@ -/* +/* * 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 diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index b04988a8035..18274d4023f 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -1,4 +1,4 @@ -/* +/* * 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 diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc index 00b73d29c5c..d136d8c3e91 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.cc +++ b/source/blender/io/alembic/intern/abc_reader_object.cc @@ -1,4 +1,4 @@ -/* +/* * 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 -- cgit v1.2.3 From 8652e69d8bd0ad3a6dcc6c1bbacfaf826df56744 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 10 Aug 2021 12:33:47 -0500 Subject: Fix T90447: 3D view transform locks do not use driver colors Caused by rB6942dd9f4900. --- source/blender/editors/space_view3d/view3d_buttons.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index 3428a738dde..b79303551a1 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -1456,7 +1456,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr) colsub = uiLayoutColumn(split, true); uiItemR(colsub, ptr, "location", 0, NULL, ICON_NONE); colsub = uiLayoutColumn(split, true); - uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE); + uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS); uiItemL(colsub, "", ICON_NONE); uiItemR(colsub, ptr, @@ -1472,7 +1472,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr) colsub = uiLayoutColumn(split, true); uiItemR(colsub, ptr, "rotation_quaternion", 0, IFACE_("Rotation"), ICON_NONE); colsub = uiLayoutColumn(split, true); - uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE); + uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS); uiItemR(colsub, ptr, "lock_rotations_4d", UI_ITEM_R_TOGGLE, IFACE_("4L"), ICON_NONE); if (RNA_boolean_get(ptr, "lock_rotations_4d")) { uiItemR(colsub, @@ -1496,7 +1496,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr) colsub = uiLayoutColumn(split, true); uiItemR(colsub, ptr, "rotation_axis_angle", 0, IFACE_("Rotation"), ICON_NONE); colsub = uiLayoutColumn(split, true); - uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE); + uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS); uiItemR(colsub, ptr, "lock_rotations_4d", UI_ITEM_R_TOGGLE, IFACE_("4L"), ICON_NONE); if (RNA_boolean_get(ptr, "lock_rotations_4d")) { uiItemR(colsub, @@ -1520,7 +1520,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr) colsub = uiLayoutColumn(split, true); uiItemR(colsub, ptr, "rotation_euler", 0, IFACE_("Rotation"), ICON_NONE); colsub = uiLayoutColumn(split, true); - uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE); + uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS); uiItemL(colsub, "", ICON_NONE); uiItemR(colsub, ptr, @@ -1536,7 +1536,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr) colsub = uiLayoutColumn(split, true); uiItemR(colsub, ptr, "scale", 0, NULL, ICON_NONE); colsub = uiLayoutColumn(split, true); - uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE); + uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS); uiItemL(colsub, "", ICON_NONE); uiItemR(colsub, ptr, -- cgit v1.2.3 From fcd2d63b644edc9ad6a3be4b0fdbd41428a7392a Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 10 Aug 2021 18:05:48 -0300 Subject: Fix 'GPU_matrix_unproject_3fv' not working with out-of-bounds points To solve this, the unproject code was redone in order to simplify and optimize. --- .../blender/editors/space_view3d/view3d_project.c | 2 +- source/blender/gpu/GPU_matrix.h | 6 +- source/blender/gpu/intern/gpu_matrix.cc | 122 +++++++-------------- .../windowmanager/gizmo/intern/wm_gizmo_map.c | 8 +- 4 files changed, 46 insertions(+), 92 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index d926ea84e0f..88efc530484 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -843,7 +843,7 @@ bool ED_view3d_unproject_v3( const int viewport[4] = {0, 0, region->winx, region->winy}; const float region_co[3] = {regionx, regiony, regionz}; - return GPU_matrix_unproject_3fv(region_co, rv3d->viewmat, rv3d->winmat, viewport, world); + return GPU_matrix_unproject_3fv(region_co, rv3d->viewinv, rv3d->winmat, viewport, world); } /** \} */ diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h index e073263f352..edf16f04349 100644 --- a/source/blender/gpu/GPU_matrix.h +++ b/source/blender/gpu/GPU_matrix.h @@ -131,15 +131,11 @@ void GPU_matrix_project_2fv(const float world[3], float r_win[2]); bool GPU_matrix_unproject_3fv(const float win[3], - const float model[4][4], + const float model_inverted[4][4], const float proj[4][4], const int view[4], float r_world[3]); -void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc, - const float win[3], - float r_world[3]); - /* 2D Projection Matrix */ void GPU_matrix_ortho_2d_set(float left, float right, float bottom, float top); diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index efa04568401..e277dda3812 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -513,93 +513,55 @@ void GPU_matrix_project_2fv(const float world[3], win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f; } -/** - * The same result could be obtained as follows: - * - * \code{.c} - * float projinv[4][4]; - * invert_m4_m4(projinv, projmat); - * co[0] = 2 * co[0] - 1; - * co[1] = 2 * co[1] - 1; - * co[2] = 2 * co[2] - 1; - * mul_project_m4_v3(projinv, co); - * \endcode - * - * But that solution loses much precision. - * Therefore, get the same result without inverting the matrix. - */ -static void gpu_mul_invert_projmat_m4_unmapped_v3_with_precalc( - const struct GPUMatrixUnproject_Precalc *precalc, float co[3]) -{ - /* 'precalc->dims' is the result of 'projmat_dimensions(proj, ...)'. */ - co[0] = (float)scalenormd(precalc->dims.xmin, precalc->dims.xmax, co[0]); - co[1] = (float)scalenormd(precalc->dims.ymin, precalc->dims.ymax, co[1]); - - if (precalc->is_persp) { - co[2] = (precalc->dims.zmax * precalc->dims.zmin) / - (precalc->dims.zmax + co[2] * (precalc->dims.zmin - precalc->dims.zmax)); - co[0] *= co[2] / precalc->dims.zmin; - co[1] *= co[2] / precalc->dims.zmin; - } - else { - co[2] = (float)scalenormd(precalc->dims.zmin, precalc->dims.zmax, co[2]); - } - co[2] *= -1; -} - -bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc, - const float model[4][4], - const float proj[4][4], - const int view[4]) -{ - precalc->is_persp = proj[3][3] == 0.0f; - projmat_dimensions_db(proj, - &precalc->dims.xmin, - &precalc->dims.xmax, - &precalc->dims.ymin, - &precalc->dims.ymax, - &precalc->dims.zmin, - &precalc->dims.zmax); - if (isinf(precalc->dims.zmax)) { - /* We cannot retrieve the actual value of the clip_end. - * Use `FLT_MAX` to avoid NAN's. */ - precalc->dims.zmax = FLT_MAX; - } - for (int i = 0; i < 4; i++) { - precalc->view[i] = (float)view[i]; - } - if (!invert_m4_m4(precalc->model_inverted, model)) { - unit_m4(precalc->model_inverted); - return false; - } - return true; -} - -void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc, - const float win[3], - float r_world[3]) -{ - float in[3] = { - (win[0] - precalc->view[0]) / precalc->view[2], - (win[1] - precalc->view[1]) / precalc->view[3], - win[2], - }; - gpu_mul_invert_projmat_m4_unmapped_v3_with_precalc(precalc, in); - mul_v3_m4v3(r_world, precalc->model_inverted, in); -} - bool GPU_matrix_unproject_3fv(const float win[3], - const float model[4][4], + const float model_inverted[4][4], const float proj[4][4], const int view[4], float r_world[3]) { - struct GPUMatrixUnproject_Precalc precalc; - if (!GPU_matrix_unproject_precalc(&precalc, model, proj, view)) { - zero_v3(r_world); + zero_v3(r_world); + float in[3] = { + 2 * ((win[0] - view[0]) / view[2]) - 1.0f, + 2 * ((win[1] - view[1]) / view[3]) - 1.0f, + 2 * win[2] - 1.0f, + }; + + /** + * The same result could be obtained as follows: + * + * \code{.c} + * float projinv[4][4]; + * invert_m4_m4(projinv, projview); + * copy_v3_v3(r_world, in); + * mul_project_m4_v3(projinv, r_world); + * \endcode + * + * But that solution loses much precision. + * Therefore, get the same result without inverting the project view matrix. + */ + + float out[3]; + const bool is_persp = proj[3][3] == 0.0f; + if (is_persp) { + out[2] = proj[3][2] / (proj[2][2] + in[2]); + if (isinf(out[2])) { + out[2] = FLT_MAX; + } + out[0] = out[2] * ((proj[2][0] + in[0]) / proj[0][0]); + out[1] = out[2] * ((proj[2][1] + in[1]) / proj[1][1]); + out[2] *= -1; + } + else { + out[0] = (-proj[3][0] + in[0]) / proj[0][0]; + out[1] = (-proj[3][1] + in[1]) / proj[1][1]; + out[2] = (-proj[3][2] + in[2]) / proj[2][2]; + } + + if (!is_finite_v3(out)) { return false; } - GPU_matrix_unproject_3fv_with_precalc(&precalc, win, r_world); + + mul_v3_m4v3(r_world, model_inverted, out); return true; } diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 5ec26a7b208..b90e288776e 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -616,11 +616,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, const int viewport[4] = {0, 0, region->winx, region->winy}; float co_3d_origin[3]; - /* Avoid multiple calculations. */ - struct GPUMatrixUnproject_Precalc unproj_precalc; - GPU_matrix_unproject_precalc(&unproj_precalc, rv3d->viewmat, rv3d->winmat, viewport); - - GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d_origin); + GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d_origin); uint *buf_iter = buffer; int hit_found = -1; @@ -631,7 +627,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8]; float co_3d[3]; co_screen[2] = int_as_float(buf_iter[1]); - GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d); + GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d_origin); float select_bias = gz->select_bias; if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) { select_bias *= gz->scale_final; -- cgit v1.2.3 From 55615e2600735d83c2332dbc18a8553d3d5e5cfd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 11 Aug 2021 10:11:11 +1000 Subject: Cleanup: trailing space, remove BOM --- make.bat | 2 +- source/blender/gpu/intern/gpu_matrix.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/make.bat b/make.bat index 0fda7594e25..e94f7637512 100644 --- a/make.bat +++ b/make.bat @@ -14,7 +14,7 @@ call "%BLENDER_DIR%\build_files\windows\parse_arguments.cmd" %* if errorlevel 1 goto EOF REM if it is one of the convenience targets and BLENDER_BIN is set -REM skip compiler detection +REM skip compiler detection if "%ICONS%%ICONS_GEOM%%DOC_PY%" == "1" ( if EXIST "%BLENDER_BIN%" ( goto convenience_targets diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index e277dda3812..bbcc241f5e3 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -560,7 +560,7 @@ bool GPU_matrix_unproject_3fv(const float win[3], if (!is_finite_v3(out)) { return false; } - + mul_v3_m4v3(r_world, model_inverted, out); return true; } -- cgit v1.2.3 From d480f03952a13355eb3f4ad2dbc0df332d6aff67 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 11 Aug 2021 10:11:12 +1000 Subject: Cleanup: clang-format --- source/blender/compositor/operations/COM_BilateralBlurOperation.h | 4 +--- source/blender/gpu/tests/gpu_shader_test.cc | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.h b/source/blender/compositor/operations/COM_BilateralBlurOperation.h index 4819715deb0..517c5292827 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.h +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.h @@ -58,9 +58,7 @@ class BilateralBlurOperation : public MultiThreadedOperation, public QualityStep this->m_space = data->sigma_space + data->iter; } - void get_area_of_interest(int input_idx, - const rcti &output_area, - rcti &r_input_area) override; + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/gpu/tests/gpu_shader_test.cc b/source/blender/gpu/tests/gpu_shader_test.cc index 561858f421e..dbe336af097 100644 --- a/source/blender/gpu/tests/gpu_shader_test.cc +++ b/source/blender/gpu/tests/gpu_shader_test.cc @@ -108,7 +108,8 @@ void main() { EXPECT_NE(shader, nullptr); /* Construct Texture. */ - GPUTexture *texture = GPU_texture_create_1d("gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, nullptr); + GPUTexture *texture = GPU_texture_create_1d( + "gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, nullptr); EXPECT_NE(texture, nullptr); GPU_shader_bind(shader); -- cgit v1.2.3 From 2f39f7f81549cff0285c2f1934de5d2c743785d4 Mon Sep 17 00:00:00 2001 From: Henrik Dick Date: Wed, 11 Aug 2021 10:22:49 +1000 Subject: Modifier: use high quality normals for vertex offset Using high quality normals for vertex offset when set for higher precision offsets. This was only used for calculating even-offset. Reviewed By: campbellbarton Ref D12176 --- .../modifiers/intern/MOD_solidify_extrude.c | 47 +++++++++++++--------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index e97190b1878..64e1eb92fda 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -507,8 +507,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex /* NOTE: copied vertex layers don't have flipped normals yet. do this after applying offset. */ if ((smd->flag & MOD_SOLIDIFY_EVEN) == 0) { /* no even thickness, very simple */ - float scalar_short; - float scalar_short_vgroup; + float ofs_new_vgroup; /* for clamping */ float *vert_lens = NULL; @@ -597,7 +596,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex uint i_orig, i_end; bool do_shell_align; - scalar_short = scalar_short_vgroup = ofs_new / 32767.0f; + ofs_new_vgroup = ofs_new; INIT_VERT_ARRAY_OFFSETS(false); @@ -606,36 +605,40 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex if (dvert) { MDeformVert *dv = &dvert[i]; if (defgrp_invert) { - scalar_short_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index); + ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index); } else { - scalar_short_vgroup = BKE_defvert_find_weight(dv, defgrp_index); + ofs_new_vgroup = BKE_defvert_find_weight(dv, defgrp_index); } - scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * - scalar_short; + ofs_new_vgroup = (offset_fac_vg + (ofs_new_vgroup * offset_fac_vg_inv)) * ofs_new; } if (do_clamp && offset > FLT_EPSILON) { /* always reset because we may have set before */ if (dvert == NULL) { - scalar_short_vgroup = scalar_short; + ofs_new_vgroup = ofs_new; } if (do_angle_clamp) { float cos_ang = cosf(((2 * M_PI) - vert_angs[i]) * 0.5f); if (cos_ang > 0) { float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang; if (max_off < offset * 0.5f) { - scalar_short_vgroup *= max_off / offset * 2; + ofs_new_vgroup *= max_off / offset * 2; } } } else { if (vert_lens[i] < offset_sq) { float scalar = sqrtf(vert_lens[i]) / offset; - scalar_short_vgroup *= scalar; + ofs_new_vgroup *= scalar; } } } - madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); + if (vert_nors) { + madd_v3_v3fl(mv->co, vert_nors[i], ofs_new_vgroup); + } + else { + madd_v3v3short_fl(mv->co, mv->no, ofs_new_vgroup / 32767.0f); + } } } @@ -643,7 +646,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex uint i_orig, i_end; bool do_shell_align; - scalar_short = scalar_short_vgroup = ofs_orig / 32767.0f; + ofs_new_vgroup = ofs_orig; /* as above but swapped */ INIT_VERT_ARRAY_OFFSETS(true); @@ -653,36 +656,40 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex if (dvert) { MDeformVert *dv = &dvert[i]; if (defgrp_invert) { - scalar_short_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index); + ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index); } else { - scalar_short_vgroup = BKE_defvert_find_weight(dv, defgrp_index); + ofs_new_vgroup = BKE_defvert_find_weight(dv, defgrp_index); } - scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * - scalar_short; + ofs_new_vgroup = (offset_fac_vg + (ofs_new_vgroup * offset_fac_vg_inv)) * ofs_orig; } if (do_clamp && offset > FLT_EPSILON) { /* always reset because we may have set before */ if (dvert == NULL) { - scalar_short_vgroup = scalar_short; + ofs_new_vgroup = ofs_orig; } if (do_angle_clamp) { float cos_ang = cosf(vert_angs[i_orig] * 0.5f); if (cos_ang > 0) { float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang; if (max_off < offset * 0.5f) { - scalar_short_vgroup *= max_off / offset * 2; + ofs_new_vgroup *= max_off / offset * 2; } } } else { if (vert_lens[i] < offset_sq) { float scalar = sqrtf(vert_lens[i]) / offset; - scalar_short_vgroup *= scalar; + ofs_new_vgroup *= scalar; } } } - madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); + if (vert_nors) { + madd_v3_v3fl(mv->co, vert_nors[i], ofs_new_vgroup); + } + else { + madd_v3v3short_fl(mv->co, mv->no, ofs_new_vgroup / 32767.0f); + } } } -- cgit v1.2.3 From 18fbcaf7b9273cc4a7e153fea60a53fec956d600 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 11 Aug 2021 16:09:23 +1000 Subject: Cleanup: rename BKE_collection_{free => free_data} This function doesn't free the collection, only it's memory. --- source/blender/blenkernel/BKE_collection.h | 2 +- source/blender/blenkernel/intern/collection.c | 2 +- source/blender/blenkernel/intern/scene.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index 8b8d0f7b107..2c7143be60e 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -65,7 +65,7 @@ void BKE_collection_add_from_collection(struct Main *bmain, struct Scene *scene, struct Collection *collection_src, struct Collection *collection_dst); -void BKE_collection_free(struct Collection *collection); +void BKE_collection_free_data(struct Collection *collection); bool BKE_collection_delete(struct Main *bmain, struct Collection *collection, bool hierarchy); struct Collection *BKE_collection_duplicate(struct Main *bmain, diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index dbcd80fe065..b62e33ff564 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -508,7 +508,7 @@ void BKE_collection_add_from_collection(Main *bmain, * \{ */ /** Free (or release) any data used by this collection (does not free the collection itself). */ -void BKE_collection_free(Collection *collection) +void BKE_collection_free_data(Collection *collection) { BKE_libblock_free_data(&collection->id, false); collection_free_data(&collection->id); diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 9dab276af95..c55185c0f2a 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -445,7 +445,7 @@ static void scene_free_data(ID *id) * for objects directly in the master collection? then other * collections in the scene need to do it too? */ if (scene->master_collection) { - BKE_collection_free(scene->master_collection); + BKE_collection_free_data(scene->master_collection); MEM_freeN(scene->master_collection); scene->master_collection = NULL; } -- cgit v1.2.3 From cbc671947a3baca3da7d6c5a2980a86b7f6a7055 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 11 Aug 2021 16:56:11 +1000 Subject: Fix T88033: Python reference memory leaks for non main data-blocks ID data-blocks that could be accessed from Python and weren't freed using BKE_id_free_ex did not release the Python reference count. Add BKE_libblock_free_data_py function to clear the Python reference in this case. Add asserts to ensure no Python reference is held in situations when ID's are copied for internal use (not exposed through the RNA API), to ensure these kinds of leaks don't go by unnoticed again. --- source/blender/blenkernel/BKE_lib_id.h | 2 ++ source/blender/blenkernel/intern/gpencil.c | 1 + source/blender/blenkernel/intern/lib_id_delete.c | 35 +++++++++++++++++----- source/blender/blenkernel/intern/material.c | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/blenkernel/intern/particle_system.c | 1 + source/blender/blenkernel/intern/scene.c | 1 + source/blender/editors/space_clip/clip_editor.c | 1 + source/blender/gpu/intern/gpu_material.c | 1 + source/blender/nodes/shader/node_shader_tree.c | 1 + source/blender/windowmanager/intern/wm.c | 1 + 11 files changed, 38 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index fac5dc8c010..5de669cb620 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -201,6 +201,8 @@ enum { void BKE_libblock_free_datablock(struct ID *id, const int flag) ATTR_NONNULL(); void BKE_libblock_free_data(struct ID *id, const bool do_id_user) ATTR_NONNULL(); +void BKE_libblock_free_data_py(struct ID *id); + void BKE_id_free_ex(struct Main *bmain, void *idv, int flag, const bool use_flag_from_idtag); void BKE_id_free(struct Main *bmain, void *idv); diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 38397f8f307..f566e18fb2f 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -521,6 +521,7 @@ void BKE_gpencil_eval_delete(bGPdata *gpd_eval) { BKE_gpencil_free(gpd_eval, true); BKE_libblock_free_data(&gpd_eval->id, false); + BLI_assert(!gpd_eval->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(gpd_eval); } diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index a9407860c06..43afac5a376 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -142,14 +142,7 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i DEG_id_type_tag(bmain, type); } -#ifdef WITH_PYTHON -# ifdef WITH_PYTHON_SAFETY - BPY_id_release(id); -# endif - if (id->py_instance) { - BPY_DECREF_RNA_INVALIDATE(id->py_instance); - } -#endif + BKE_libblock_free_data_py(id); Key *key = ((flag & LIB_ID_FREE_NO_MAIN) == 0) ? BKE_key_from_id(id) : NULL; @@ -406,3 +399,29 @@ size_t BKE_id_multi_tagged_delete(Main *bmain) { return id_delete(bmain, true); } + +/* -------------------------------------------------------------------- */ +/** \name Python Data Handling + * \{ */ + +/** + * In most cases #BKE_id_free_ex handles this, when lower level functions are called directly + * this function will need to be called too, if Python has access to the data. + * + * ID data-blocks such as #Material.nodetree are not stored in #Main. + */ +void BKE_libblock_free_data_py(ID *id) +{ +#ifdef WITH_PYTHON +# ifdef WITH_PYTHON_SAFETY + BPY_id_release(id); +# endif + if (id->py_instance) { + BPY_DECREF_RNA_INVALIDATE(id->py_instance); + } +#else + UNUSED_VARS(id); +#endif +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 4f0b2a718ed..ca57038f1c4 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -1802,6 +1802,7 @@ void BKE_material_copybuf_free(void) { if (matcopybuf.nodetree) { ntreeFreeLocalTree(matcopybuf.nodetree); + BLI_assert(!matcopybuf.nodetree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(matcopybuf.nodetree); matcopybuf.nodetree = NULL; } diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 1bf95369794..bd22f049a8b 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -3194,6 +3194,7 @@ void ntreeFreeEmbeddedTree(bNodeTree *ntree) { ntreeFreeTree(ntree); BKE_libblock_free_data(&ntree->id, true); + BKE_libblock_free_data_py(&ntree->id); } void ntreeFreeLocalTree(bNodeTree *ntree) diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index f592b0f97ea..60edb78f8ba 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -4761,6 +4761,7 @@ static void particle_settings_free_local(ParticleSettings *particle_settings) { BKE_libblock_free_datablock(&particle_settings->id, 0); BKE_libblock_free_data(&particle_settings->id, false); + BLI_assert(!particle_settings->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(particle_settings); } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index c55185c0f2a..5ecd9b7283e 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -446,6 +446,7 @@ static void scene_free_data(ID *id) * collections in the scene need to do it too? */ if (scene->master_collection) { BKE_collection_free_data(scene->master_collection); + BKE_libblock_free_data_py(&scene->master_collection->id); MEM_freeN(scene->master_collection); scene->master_collection = NULL; } diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index 67b4fd61d38..834ef847069 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -1037,6 +1037,7 @@ static void prefetch_freejob(void *pjv) if (clip_local != NULL) { BKE_libblock_free_datablock(&clip_local->id, 0); BKE_libblock_free_data(&clip_local->id, false); + BLI_assert(!clip_local->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(clip_local); } diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 37089785e0e..56e72fbeca9 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -758,6 +758,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, /* Only free after GPU_pass_shader_get where GPUUniformBuf * read data from the local tree. */ ntreeFreeLocalTree(localtree); + BLI_assert(!localtree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(localtree); /* note that even if building the shader fails in some way, we still keep diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index 7367f73d171..98edc133a1d 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -531,6 +531,7 @@ static void ntree_shader_groups_flatten(bNodeTree *localtree) bNodeTree *ngroup = (bNodeTree *)node->id; ntreeFreeLocalNode(localtree, node); ntreeFreeTree(ngroup); + BLI_assert(!ngroup->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(ngroup); } else { diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 9657f8aa03c..e11ef52eb84 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -628,6 +628,7 @@ void wm_close_and_free_all(bContext *C, ListBase *wmlist) wm_close_and_free(C, wm); BLI_remlink(wmlist, wm); BKE_libblock_free_data(&wm->id, true); + BKE_libblock_free_data_py(&wm->id); MEM_freeN(wm); } } -- cgit v1.2.3 From f3e26c847b6ba0924cfd02641345164c54234425 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 11 Aug 2021 17:29:26 +1000 Subject: PyAPI: report unreleased ID's with WITH_PYTHON_SAFETY enabled This would have made T88033 more straightforward to track down. --- source/blender/python/intern/bpy.c | 3 -- source/blender/python/intern/bpy_interface.c | 7 +++- source/blender/python/intern/bpy_rna.c | 53 ++++++++++++++++------------ source/blender/python/intern/bpy_rna.h | 1 + 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 30a61067c9e..0e0f431cb19 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -417,9 +417,6 @@ void BPy_init_modules(struct bContext *C) PyDict_SetItemString(PyImport_GetModuleDict(), "_bpy", mod); Py_DECREF(mod); - /* run first, initializes rna types */ - BPY_rna_init(); - /* needs to be first so bpy_types can run */ PyModule_AddObject(mod, "types", BPY_rna_types()); diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 35450e3eaad..945933dd8b7 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -502,7 +502,10 @@ void BPY_python_start(bContext *C, int argc, const char **argv) } #endif - /* bpy.* and lets us import it */ + /* Run first, initializes RNA types. */ + BPY_rna_init(); + + /* Defines `bpy.*` and lets us import it. */ BPy_init_modules(C); pyrna_alloc_types(); @@ -541,6 +544,8 @@ void BPY_python_end(void) /* free other python data. */ pyrna_free_types(); + BPY_rna_exit(); + /* clear all python data from structs */ bpy_intern_string_exit(); diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index b83c13e1974..17cbf8e4569 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -181,23 +181,13 @@ static PyMethodDef id_free_weakref_cb_def = { /* Adds a reference to the list, remember to decref. */ static GHash *id_weakref_pool_get(ID *id) { - GHash *weakinfo_hash = NULL; - - if (id_weakref_pool) { - weakinfo_hash = BLI_ghash_lookup(id_weakref_pool, (void *)id); - } - else { - /* First time, allocate pool. */ - id_weakref_pool = BLI_ghash_ptr_new("rna_global_pool"); - weakinfo_hash = NULL; - } - + GHash *weakinfo_hash = BLI_ghash_lookup(id_weakref_pool, (void *)id); if (weakinfo_hash == NULL) { - /* We use a ghash as a set, we could use libHX's HXMAP_SINGULAR, but would be an extra dep. */ + /* This could be a set, values are used to keep a reference back to the ID + * (all of them are the same). */ weakinfo_hash = BLI_ghash_ptr_new("rna_id"); BLI_ghash_insert(id_weakref_pool, id, weakinfo_hash); } - return weakinfo_hash; } @@ -283,14 +273,6 @@ static void id_release_weakref_list(struct ID *id, GHash *weakinfo_hash) BLI_ghash_remove(id_weakref_pool, (void *)id, NULL, NULL); BLI_ghash_free(weakinfo_hash, NULL, NULL); - - if (BLI_ghash_len(id_weakref_pool) == 0) { - BLI_ghash_free(id_weakref_pool, NULL, NULL); - id_weakref_pool = NULL; -# ifdef DEBUG_RNA_WEAKREF - printf("id_release_weakref freeing pool\n"); -# endif - } } static void id_release_weakref(struct ID *id) @@ -310,7 +292,8 @@ void BPY_id_release(struct ID *id) #endif #ifdef USE_PYRNA_INVALIDATE_WEAKREF - if (id_weakref_pool) { + /* Check for NULL since this may run before Python has been started. */ + if (id_weakref_pool != NULL) { PyGILState_STATE gilstate = PyGILState_Ensure(); id_release_weakref(id); @@ -7776,6 +7759,32 @@ void BPY_rna_init(void) return; } #endif + +#ifdef USE_PYRNA_INVALIDATE_WEAKREF + BLI_assert(id_weakref_pool == NULL); + id_weakref_pool = BLI_ghash_ptr_new("rna_global_pool"); +#endif +} + +void BPY_rna_exit(void) +{ +#ifdef USE_PYRNA_INVALIDATE_WEAKREF + /* This can help track down which kinds of data were not released. + * If they were in fact freed by Blender, printing their names + * will crash giving a useful error with address sanitizer. The likely cause + * for this list not being empty is a missing call to: #BKE_libblock_free_data_py. */ + const int id_weakref_pool_len = BLI_ghash_len(id_weakref_pool); + if (id_weakref_pool_len != id_weakref_pool_len) { + printf("Found %d unreleased ID's\n", id_weakref_pool_len); + GHashIterator gh_iter; + GHASH_ITER (gh_iter, id_weakref_pool) { + ID *id = BLI_ghashIterator_getKey(&gh_iter); + printf("ID: %s\n", id->name); + } + } + BLI_ghash_free(id_weakref_pool, NULL, NULL); + id_weakref_pool = NULL; +#endif } /* 'bpy.data' from Python. */ diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index 24dbad53eb3..fd468bed470 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -181,6 +181,7 @@ StructRNA *srna_from_self(PyObject *self, const char *error_prefix); StructRNA *pyrna_struct_as_srna(PyObject *self, const bool parent, const char *error_prefix); void BPY_rna_init(void); +void BPY_rna_exit(void); PyObject *BPY_rna_module(void); void BPY_update_rna_module(void); // PyObject *BPY_rna_doc(void); -- cgit v1.2.3 From 6aae14027828f8f75d5041a8fb23fce29c66f26d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 11 Aug 2021 11:22:57 +0200 Subject: Cleanup: Minor comment update on `LIB_TAG_NEW`. --- source/blender/makesdna/DNA_ID.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 3995e98f34d..8a577be2749 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -551,7 +551,7 @@ enum { /* tag data-block as having actually increased user-count for the extra virtual user. */ LIB_TAG_EXTRAUSER_SET = 1 << 7, - /* RESET_AFTER_USE tag newly duplicated/copied IDs. + /* RESET_AFTER_USE tag newly duplicated/copied IDs (see #ID_NEW_SET macro above). * Also used internally in readfile.c to mark data-blocks needing do_versions. */ LIB_TAG_NEW = 1 << 8, /* RESET_BEFORE_USE free test flag. -- cgit v1.2.3 From 48ba341d151206a5acfd3c945a9c9ef183c3d0a6 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 11 Aug 2021 13:34:11 +0200 Subject: Cleanup: Keylist Drawing - Split up in multiple functions. --- source/blender/editors/animation/keyframes_draw.c | 322 ++++++++++++++-------- 1 file changed, 203 insertions(+), 119 deletions(-) diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index deed79942ac..d858ef3ca5a 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -183,141 +183,210 @@ void draw_keyframe_shape(float x, immVertex2f(pos_id, x, y); } -static void draw_keylist(View2D *v2d, - const struct AnimKeylist *keylist, - float ypos, - float yscale_fac, - bool channelLocked, - int saction_flag) -{ - const float icon_sz = U.widget_unit * 0.5f * yscale_fac; - const float half_icon_sz = 0.5f * icon_sz; - const float smaller_sz = 0.35f * icon_sz; - const float ipo_sz = 0.1f * icon_sz; - const float gpencil_sz = smaller_sz * 0.8f; - const float screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d); +/* Common attributes shared between the draw calls. */ +typedef struct DrawKeylistUIData { + float alpha; + float icon_sz; + float half_icon_sz; + float smaller_sz; + float ipo_sz; + float gpencil_sz; + float screenspace_margin; + float sel_color[4]; + float unsel_color[4]; + float sel_mhcol[4]; + float unsel_mhcol[4]; + float ipo_color[4]; + float ipo_color_mix[4]; + /* Show interpolation and handle type? */ + bool show_ipo; +} DrawKeylistUIData; + +static void draw_keylist_ui_data_init(DrawKeylistUIData *ctx, + View2D *v2d, + float yscale_fac, + bool channel_locked, + eSAction_Flag saction_flag) +{ /* locked channels are less strongly shown, as feedback for locked channels in DopeSheet */ /* TODO: allow this opacity factor to be themed? */ - float alpha = channelLocked ? 0.25f : 1.0f; + ctx->alpha = channel_locked ? 0.25f : 1.0f; + + ctx->icon_sz = U.widget_unit * 0.5f * yscale_fac; + ctx->half_icon_sz = 0.5f * ctx->icon_sz; + ctx->smaller_sz = 0.35f * ctx->icon_sz; + ctx->ipo_sz = 0.1f * ctx->icon_sz; + ctx->gpencil_sz = ctx->smaller_sz * 0.8f; + ctx->screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d); + + ctx->show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0; + + UI_GetThemeColor4fv(TH_STRIP_SELECT, ctx->sel_color); + UI_GetThemeColor4fv(TH_STRIP, ctx->unsel_color); + UI_GetThemeColor4fv(TH_DOPESHEET_IPOLINE, ctx->ipo_color); + + ctx->sel_color[3] *= ctx->alpha; + ctx->unsel_color[3] *= ctx->alpha; + ctx->ipo_color[3] *= ctx->alpha; + + copy_v4_v4(ctx->sel_mhcol, ctx->sel_color); + ctx->sel_mhcol[3] *= 0.8f; + copy_v4_v4(ctx->unsel_mhcol, ctx->unsel_color); + ctx->unsel_mhcol[3] *= 0.8f; + copy_v4_v4(ctx->ipo_color_mix, ctx->ipo_color); + ctx->ipo_color_mix[3] *= 0.5f; +} - /* Show interpolation and handle type? */ - bool show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0; - /* draw keyblocks */ - float sel_color[4], unsel_color[4]; - float sel_mhcol[4], unsel_mhcol[4]; - float ipo_color[4], ipo_color_mix[4]; - - /* cache colors first */ - UI_GetThemeColor4fv(TH_STRIP_SELECT, sel_color); - UI_GetThemeColor4fv(TH_STRIP, unsel_color); - UI_GetThemeColor4fv(TH_DOPESHEET_IPOLINE, ipo_color); - - sel_color[3] *= alpha; - unsel_color[3] *= alpha; - ipo_color[3] *= alpha; - - copy_v4_v4(sel_mhcol, sel_color); - sel_mhcol[3] *= 0.8f; - copy_v4_v4(unsel_mhcol, unsel_color); - unsel_mhcol[3] *= 0.8f; - copy_v4_v4(ipo_color_mix, ipo_color); - ipo_color_mix[3] *= 0.5f; - - const ListBase *keys = ED_keylist_listbase(keylist); - - LISTBASE_FOREACH (ActKeyColumn *, ab, keys) { - /* Draw grease pencil bars between keyframes. */ - if ((ab->next != NULL) && (ab->block.flag & ACTKEYBLOCK_FLAG_GPENCIL)) { - UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT); - float size = 1.0f; - switch (ab->next->key_type) { - case BEZT_KEYTYPE_BREAKDOWN: - case BEZT_KEYTYPE_MOVEHOLD: - case BEZT_KEYTYPE_JITTER: - size *= 0.5f; - break; - case BEZT_KEYTYPE_KEYFRAME: - size *= 0.8f; - break; - default: - break; - } - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = ab->cfra, - .xmax = min_ff(ab->next->cfra - (screenspace_margin * size), ab->next->cfra), - .ymin = ypos - gpencil_sz, - .ymax = ypos + gpencil_sz, - }, - true, - 0.25f * (float)UI_UNIT_X, - (ab->block.sel) ? sel_mhcol : unsel_mhcol); - } - else { - /* Draw other types. */ - UI_draw_roundbox_corner_set(UI_CNR_NONE); - - int valid_hold = actkeyblock_get_valid_hold(ab); - if (valid_hold != 0) { - if ((valid_hold & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { - /* draw "moving hold" long-keyframe block - slightly smaller */ - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = ab->cfra, - .xmax = ab->next->cfra, - .ymin = ypos - smaller_sz, - .ymax = ypos + smaller_sz, - }, - true, - 3.0f, - (ab->block.sel) ? sel_mhcol : unsel_mhcol); - } - else { - /* draw standard long-keyframe block */ - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = ab->cfra, - .xmax = ab->next->cfra, - .ymin = ypos - half_icon_sz, - .ymax = ypos + half_icon_sz, - }, - true, - 3.0f, - (ab->block.sel) ? sel_color : unsel_color); - } +static void draw_keylist_block_gpencil(const DrawKeylistUIData *ctx, + const ActKeyColumn *ab, + float ypos) +{ + UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT); + float size = 1.0f; + switch (ab->next->key_type) { + case BEZT_KEYTYPE_BREAKDOWN: + case BEZT_KEYTYPE_MOVEHOLD: + case BEZT_KEYTYPE_JITTER: + size *= 0.5f; + break; + case BEZT_KEYTYPE_KEYFRAME: + size *= 0.8f; + break; + default: + break; + } + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = ab->cfra, + .xmax = min_ff(ab->next->cfra - (ctx->screenspace_margin * size), ab->next->cfra), + .ymin = ypos - ctx->gpencil_sz, + .ymax = ypos + ctx->gpencil_sz, + }, + true, + 0.25f * (float)UI_UNIT_X, + (ab->block.sel) ? ctx->sel_mhcol : ctx->unsel_mhcol); +} + +static void draw_keylist_block_moving_hold(const DrawKeylistUIData *ctx, + const ActKeyColumn *ab, + float ypos) +{ + + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = ab->cfra, + .xmax = ab->next->cfra, + .ymin = ypos - ctx->smaller_sz, + .ymax = ypos + ctx->smaller_sz, + }, + true, + 3.0f, + (ab->block.sel) ? ctx->sel_mhcol : ctx->unsel_mhcol); +} + +static void draw_keylist_block_standard(const DrawKeylistUIData *ctx, + const ActKeyColumn *ab, + float ypos) +{ + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = ab->cfra, + .xmax = ab->next->cfra, + .ymin = ypos - ctx->half_icon_sz, + .ymax = ypos + ctx->half_icon_sz, + }, + true, + 3.0f, + (ab->block.sel) ? ctx->sel_color : ctx->unsel_color); +} + +static void draw_keylist_block_interpolation_line(const DrawKeylistUIData *ctx, + const ActKeyColumn *ab, + float ypos) +{ + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = ab->cfra, + .xmax = ab->next->cfra, + .ymin = ypos - ctx->ipo_sz, + .ymax = ypos + ctx->ipo_sz, + }, + true, + 3.0f, + (ab->block.conflict & ACTKEYBLOCK_FLAG_NON_BEZIER) ? ctx->ipo_color_mix : ctx->ipo_color); +} + +static void draw_keylist_block(const DrawKeylistUIData *ctx, const ActKeyColumn *ab, float ypos) +{ + + /* Draw grease pencil bars between keyframes. */ + if ((ab->next != NULL) && (ab->block.flag & ACTKEYBLOCK_FLAG_GPENCIL)) { + draw_keylist_block_gpencil(ctx, ab, ypos); + } + else { + /* Draw other types. */ + UI_draw_roundbox_corner_set(UI_CNR_NONE); + + int valid_hold = actkeyblock_get_valid_hold(ab); + if (valid_hold != 0) { + if ((valid_hold & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { + /* draw "moving hold" long-keyframe block - slightly smaller */ + draw_keylist_block_moving_hold(ctx, ab, ypos); } - if (show_ipo && actkeyblock_is_valid(ab) && (ab->block.flag & ACTKEYBLOCK_FLAG_NON_BEZIER)) { - /* draw an interpolation line */ - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = ab->cfra, - .xmax = ab->next->cfra, - .ymin = ypos - ipo_sz, - .ymax = ypos + ipo_sz, - }, - true, - 3.0f, - (ab->block.conflict & ACTKEYBLOCK_FLAG_NON_BEZIER) ? ipo_color_mix : ipo_color); + else { + /* draw standard long-keyframe block */ + draw_keylist_block_standard(ctx, ab, ypos); } } + if (ctx->show_ipo && actkeyblock_is_valid(ab) && + (ab->block.flag & ACTKEYBLOCK_FLAG_NON_BEZIER)) { + /* draw an interpolation line */ + draw_keylist_block_interpolation_line(ctx, ab, ypos); + } } +} - GPU_blend(GPU_BLEND_ALPHA); +static void draw_keylist_blocks(const DrawKeylistUIData *ctx, + const ListBase * /*ActKeyColumn*/ columns, + float ypos) +{ + LISTBASE_FOREACH (ActKeyColumn *, ab, columns) { + draw_keylist_block(ctx, ab, ypos); + } +} + +static bool draw_keylist_is_visible_key(const View2D *v2d, const ActKeyColumn *ak) +{ + return IN_RANGE_INCL(ak->cfra, v2d->cur.xmin, v2d->cur.xmax); +} +static int draw_keylist_visible_key_len(View2D *v2d, const ListBase * /*ActKeyColumn*/ keys) +{ /* count keys */ - uint key_len = 0; + uint len = 0; + LISTBASE_FOREACH (ActKeyColumn *, ak, keys) { /* Optimization: if keyframe doesn't appear within 5 units (screenspace) * in visible area, don't draw. * This might give some improvements, * since we current have to flip between view/region matrices. */ - if (IN_RANGE_INCL(ak->cfra, v2d->cur.xmin, v2d->cur.xmax)) { - key_len++; + if (draw_keylist_is_visible_key(v2d, ak)) { + len++; } } + return len; +} +static void draw_keylist_keys(const DrawKeylistUIData *ctx, + View2D *v2d, + const ListBase * /*ActKeyColumn*/ keys, + float ypos, + eSAction_Flag saction_flag) +{ + GPU_blend(GPU_BLEND_ALPHA); + const int key_len = draw_keylist_visible_key_len(v2d, keys); if (key_len > 0) { /* draw keys */ GPUVertFormat *format = immVertexFormat(); @@ -338,8 +407,8 @@ static void draw_keylist(View2D *v2d, short handle_type = KEYFRAME_HANDLE_NONE, extreme_type = KEYFRAME_EXTREME_NONE; LISTBASE_FOREACH (ActKeyColumn *, ak, keys) { - if (IN_RANGE_INCL(ak->cfra, v2d->cur.xmin, v2d->cur.xmax)) { - if (show_ipo) { + if (draw_keylist_is_visible_key(v2d, ak)) { + if (ctx->show_ipo) { handle_type = ak->handle_type; } if (saction_flag & SACTION_SHOW_EXTREMES) { @@ -348,11 +417,11 @@ static void draw_keylist(View2D *v2d, draw_keyframe_shape(ak->cfra, ypos, - icon_sz, + ctx->icon_sz, (ak->sel & SELECT), ak->key_type, KEYFRAME_SHAPE_BOTH, - alpha, + ctx->alpha, pos_id, size_id, color_id, @@ -371,6 +440,21 @@ static void draw_keylist(View2D *v2d, GPU_blend(GPU_BLEND_NONE); } +static void draw_keylist(View2D *v2d, + const struct AnimKeylist *keylist, + float ypos, + float yscale_fac, + bool channelLocked, + eSAction_Flag saction_flag) +{ + DrawKeylistUIData ctx; + draw_keylist_ui_data_init(&ctx, v2d, yscale_fac, channelLocked, saction_flag); + + const ListBase *columns = ED_keylist_listbase(keylist); + draw_keylist_blocks(&ctx, columns, ypos); + draw_keylist_keys(&ctx, v2d, columns, ypos, saction_flag); +} + /* *************************** Channel Drawing Funcs *************************** */ void draw_summary_channel( -- cgit v1.2.3 From 62cb5c5c4aacc3f83fa5053e4fb5fc4524522fe7 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 11 Aug 2021 14:20:49 +0200 Subject: Enable Asset Browser by default for poses, rest stays experimental Idea for 3.0 is to disable all functionality that isn't well polished and focus on those parts first. Starting with poses. * Adds a new experimental option "Extended Asset Browser", replacing "Asset Browser". * Unlike the previous option, this isn't enabled by default anymore. This didn't work well in practice and caused plenty of confusion. * "Mark as Asset" and "Clear Asset" are hidden if the option is disabled. * Same for the category selection in the Asset Browser. * Always show display the "Only Assets" option in the File Browser while browing inside .blend files. That way you can hide data-blocks that are not pose assets. * The Asset Library setup UI in the Preferences is always visible now, it's needed for pose library access. Addresses T90181, T90180 and T90300. Differential Revision: https://developer.blender.org/D12120 --- release/scripts/startup/bl_ui/space_filebrowser.py | 6 ++++- release/scripts/startup/bl_ui/space_outliner.py | 2 +- release/scripts/startup/bl_ui/space_userpref.py | 7 +----- source/blender/blenkernel/intern/blendfile.c | 4 ---- .../blender/blenloader/intern/versioning_userdef.c | 7 ------ source/blender/editors/asset/intern/asset_ops.cc | 2 +- .../editors/interface/interface_context_menu.c | 2 +- source/blender/editors/space_file/space_file.c | 26 +++++++++++++++------- source/blender/makesdna/DNA_userdef_types.h | 2 +- source/blender/makesrna/RNA_access.h | 1 + source/blender/makesrna/intern/rna_userdef.c | 12 +++++----- 11 files changed, 35 insertions(+), 36 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 00ac69595c7..aea2b76e07b 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -195,7 +195,7 @@ class FILEBROWSER_PT_filter(FileBrowserPanel, Panel): sub = row.column(align=True) - if context.preferences.experimental.use_asset_browser: + if context.preferences.experimental.use_extended_asset_browser: sub.prop(params, "use_filter_asset_only") filter_id = params.filter_id @@ -653,6 +653,10 @@ class ASSETBROWSER_PT_navigation_bar(asset_utils.AssetBrowserPanel, Panel): bl_region_type = 'TOOLS' bl_options = {'HIDE_HEADER'} + @classmethod + def poll(cls, context): + return context.preferences.experimental.use_extended_asset_browser + def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index 0a4f419362d..febd064147f 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -315,7 +315,7 @@ class OUTLINER_MT_asset(Menu): @classmethod def poll(cls, context): - return context.preferences.experimental.use_asset_browser + return context.preferences.experimental.use_extended_asset_browser def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 91bd5f04b9d..003f2f223ea 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -1366,11 +1366,6 @@ class USERPREF_PT_saveload_autorun(FilePathsPanel, Panel): class USERPREF_PT_file_paths_asset_libraries(FilePathsPanel, Panel): bl_label = "Asset Libraries" - @classmethod - def poll(cls, context): - prefs = context.preferences - return prefs.experimental.use_asset_browser - def draw(self, context): layout = self.layout layout.use_property_split = False @@ -2258,7 +2253,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): context, ( ({"property": "use_sculpt_vertex_colors"}, "T71947"), ({"property": "use_sculpt_tools_tilt"}, "T82877"), - ({"property": "use_asset_browser"}, ("project/profile/124/", "Milestone 1")), + ({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")), ({"property": "use_override_templates"}, ("T73318", "Milestone 4")), ), ) diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 61827be08e5..1c5d8804280 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -660,10 +660,6 @@ UserDef *BKE_blendfile_userdef_from_defaults(void) BKE_studiolight_default(userdef->light_param, userdef->light_ambient); BKE_preferences_asset_library_default_add(userdef); - /* Enable asset browser features by default for alpha testing. - * BLO_sanitize_experimental_features_userpref_blend() will disable it again for non-alpha - * builds. */ - userdef->experimental.use_asset_browser = true; return userdef; } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index c409f0a71fc..0042ff29dc2 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -873,13 +873,6 @@ void blo_do_versions_userdef(UserDef *userdef) } } - if (!USER_VERSION_ATLEAST(293, 2)) { - /* Enable asset browser features by default for alpha testing. - * BLO_sanitize_experimental_features_userpref_blend() will disable it again for non-alpha - * builds. */ - userdef->experimental.use_asset_browser = true; - } - if (!USER_VERSION_ATLEAST(293, 12)) { if (userdef->gizmo_size_navigate_v3d == 0) { userdef->gizmo_size_navigate_v3d = 80; diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index dc418a1b3d5..d69a2cae94d 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -36,7 +36,7 @@ using PointerRNAVec = blender::Vector; static bool asset_operation_poll(bContext * /*C*/) { - return U.experimental.use_asset_browser; + return U.experimental.use_extended_asset_browser; } /** diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index d917534895d..8ace057891d 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -952,7 +952,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev } /* If the button represents an id, it can set the "id" context pointer. */ - if (U.experimental.use_asset_browser && ED_asset_can_mark_single_from_context(C)) { + if (U.experimental.use_extended_asset_browser && ED_asset_can_mark_single_from_context(C)) { ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data; /* Gray out items depending on if data-block is an asset. Preferably this could be done via diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 0b3349f5751..7deaa2fec60 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -1,4 +1,4 @@ -/* +/* * 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 @@ -334,6 +334,12 @@ static void file_refresh(const bContext *C, ScrArea *area) sfile->files = filelist_new(params->type); params->highlight_file = -1; /* added this so it opens nicer (ton) */ } + + if (!U.experimental.use_extended_asset_browser && ED_fileselect_is_asset_browser(sfile)) { + /* Only poses supported as non-experimental right now. */ + params->filter_id = FILTER_ID_AC; + } + filelist_settype(sfile->files, params->type); filelist_setdir(sfile->files, params->dir); filelist_setrecursion(sfile->files, params->recursion_level); @@ -575,6 +581,16 @@ static void file_main_region_message_subscribe(const wmRegionMessageSubscribePar /* All properties for this space type. */ WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_area_tag_refresh, __func__); } + + /* Experimental Asset Browser features option. */ + { + PointerRNA ptr; + RNA_pointer_create(NULL, &RNA_PreferencesExperimental, &U.experimental, &ptr); + PropertyRNA *prop = RNA_struct_find_property(&ptr, "use_extended_asset_browser"); + + /* All properties for this space type. */ + WM_msg_subscribe_rna(mbus, &ptr, prop, &msg_sub_value_area_tag_refresh, __func__); + } } static bool file_main_region_needs_refresh_before_draw(SpaceFile *sfile) @@ -852,13 +868,7 @@ static void file_space_subtype_item_extend(bContext *UNUSED(C), EnumPropertyItem **item, int *totitem) { - if (U.experimental.use_asset_browser) { - RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items); - } - else { - RNA_enum_items_add_value( - item, totitem, rna_enum_space_file_browse_mode_items, FILE_BROWSE_MODE_FILES); - } + RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items); } static const char *file_context_dir[] = { diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 5f8a8c6230a..28acf5413b8 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -645,7 +645,7 @@ typedef struct UserDef_Experimental { char use_full_frame_compositor; char use_sculpt_vertex_colors; char use_sculpt_tools_tilt; - char use_asset_browser; + char use_extended_asset_browser; char use_override_templates; char _pad[5]; /** `makesdna` does not allow empty structs. */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 97615016016..1eeb68c3a23 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -499,6 +499,7 @@ extern StructRNA RNA_Pose; extern StructRNA RNA_PoseBone; extern StructRNA RNA_Preferences; extern StructRNA RNA_PreferencesEdit; +extern StructRNA RNA_PreferencesExperimental; extern StructRNA RNA_PreferencesFilePaths; extern StructRNA RNA_PreferencesInput; extern StructRNA RNA_PreferencesKeymap; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 2d93715a438..483506d5733 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6297,12 +6297,12 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Sculpt Mode Tilt Support", "Support for pen tablet tilt events in Sculpt Mode"); - prop = RNA_def_property(srna, "use_asset_browser", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "use_asset_browser", 1); - RNA_def_property_ui_text( - prop, - "Asset Browser", - "Enable Asset Browser editor and operators to manage data-blocks as asset"); + prop = RNA_def_property(srna, "use_extended_asset_browser", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, + "Extended Asset Browser", + "Enable Asset Browser editor and operators to manage regular " + "data-blocks as assets, not just poses"); + RNA_def_property_update(prop, 0, "rna_userdef_ui_update"); prop = RNA_def_property(srna, "use_override_templates", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_override_templates", 1); -- cgit v1.2.3 From bbcb60fb22b375094836e4ab90569db1f73c42e3 Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Wed, 11 Aug 2021 09:35:38 -0300 Subject: Fix T90519: USD Exporter Error Fixes: `Error: metersPerUnit does not match retrieved type float` --- source/blender/io/usd/intern/usd_capi_export.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/io/usd/intern/usd_capi_export.cc b/source/blender/io/usd/intern/usd_capi_export.cc index 25f12e683cf..efa31df25c1 100644 --- a/source/blender/io/usd/intern/usd_capi_export.cc +++ b/source/blender/io/usd/intern/usd_capi_export.cc @@ -102,7 +102,7 @@ static void export_startjob(void *customdata, usd_stage->SetMetadata(pxr::UsdGeomTokens->upAxis, pxr::VtValue(pxr::UsdGeomTokens->z)); usd_stage->SetMetadata(pxr::UsdGeomTokens->metersPerUnit, - pxr::VtValue(scene->unit.scale_length)); + static_cast(scene->unit.scale_length)); usd_stage->GetRootLayer()->SetDocumentation(std::string("Blender v") + BKE_blender_version_string()); -- cgit v1.2.3 From 6a9d7139f7d05e0c51827a3a4b862c0547dc0513 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 11 Aug 2021 14:49:17 +0200 Subject: Cleanup: ID management: remove unused old `BKE_libblock_copy_for_localize` function. --- source/blender/blenkernel/BKE_lib_id.h | 2 - source/blender/blenkernel/intern/lib_id.c | 8 - .../blender/blenkernel/intern/mesh_normals.cc.orig | 2217 ++++++++++++++++++++ 3 files changed, 2217 insertions(+), 10 deletions(-) create mode 100644 source/blender/blenkernel/intern/mesh_normals.cc.orig diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 5de669cb620..bb875f8d1c9 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -152,8 +152,6 @@ void BKE_libblock_copy_ex(struct Main *bmain, const int orig_flag); void *BKE_libblock_copy(struct Main *bmain, const struct ID *id) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -/* Special version: used by data-block localization. */ -void *BKE_libblock_copy_for_localize(const struct ID *id); void BKE_libblock_rename(struct Main *bmain, struct ID *id, const char *name) ATTR_NONNULL(); void BLI_libblock_ensure_unique_name(struct Main *bmain, const char *name) ATTR_NONNULL(); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 5e1027c62af..0f880d16358 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1321,14 +1321,6 @@ void *BKE_libblock_copy(Main *bmain, const ID *id) return idn; } -/* XXX TODO: get rid of this useless wrapper at some point... */ -void *BKE_libblock_copy_for_localize(const ID *id) -{ - ID *idn; - BKE_libblock_copy_ex(NULL, id, &idn, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA); - return idn; -} - /* ***************** ID ************************ */ ID *BKE_libblock_find_name(struct Main *bmain, const short type, const char *name) { diff --git a/source/blender/blenkernel/intern/mesh_normals.cc.orig b/source/blender/blenkernel/intern/mesh_normals.cc.orig new file mode 100644 index 00000000000..18d384e8589 --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_normals.cc.orig @@ -0,0 +1,2217 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup bke + * + * Mesh normal calculation functions. + * + * \see bmesh_mesh_normals.c for the equivalent #BMesh functionality. + */ + +#include + +#include "CLG_log.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_alloca.h" +#include "BLI_bitmap.h" + +#include "BLI_linklist.h" +#include "BLI_linklist_stack.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_stack.h" +#include "BLI_task.h" +#include "BLI_utildefines.h" + +#include "BKE_customdata.h" +#include "BKE_editmesh_cache.h" +#include "BKE_global.h" +#include "BKE_mesh.h" + +<<<<<<< Updated upstream +#include "atomic_ops.h" + +// #define DEBUG_TIME +======= +#define DEBUG_TIME +>>>>>>> Stashed changes + +#ifdef DEBUG_TIME +# include "PIL_time.h" +# include "PIL_time_utildefines.h" +#endif + +static CLG_LogRef LOG = {"bke.mesh_normals"}; + +/* -------------------------------------------------------------------- */ +/** \name Private Utility Functions + * \{ */ + +/** + * A thread-safe version of #add_v3_v3 that uses a spin-lock. + * + * \note Avoid using this when the chance of contention is high. + */ +static void add_v3_v3_atomic(float r[3], const float a[3]) +{ +#define FLT_EQ_NONAN(_fa, _fb) (*((const uint32_t *)&_fa) == *((const uint32_t *)&_fb)) + + float virtual_lock = r[0]; + while (true) { + /* This loops until following conditions are met: + * - `r[0]` has same value as virtual_lock (i.e. it did not change since last try). + * - `r[0]` was not `FLT_MAX`, i.e. it was not locked by another thread. */ + const float test_lock = atomic_cas_float(&r[0], virtual_lock, FLT_MAX); + if (_ATOMIC_LIKELY(FLT_EQ_NONAN(test_lock, virtual_lock) && (test_lock != FLT_MAX))) { + break; + } + virtual_lock = test_lock; + } + virtual_lock += a[0]; + r[1] += a[1]; + r[2] += a[2]; + + /* Second atomic operation to 'release' + * our lock on that vector and set its first scalar value. */ + /* Note that we do not need to loop here, since we 'locked' `r[0]`, + * nobody should have changed it in the mean time. */ + virtual_lock = atomic_cas_float(&r[0], FLT_MAX, virtual_lock); + BLI_assert(virtual_lock == FLT_MAX); + +#undef FLT_EQ_NONAN +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Normal Calculation + * \{ */ + +void BKE_mesh_normals_tag_dirty(Mesh *mesh) +{ + mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; +} + +/** + * Call when there are no polygons. + */ +static void mesh_calc_normals_vert_fallback(MVert *mverts, int numVerts) +{ + for (int i = 0; i < numVerts; i++) { + MVert *mv = &mverts[i]; + float no[3]; + + normalize_v3_v3(no, mv->co); + normal_float_to_short_v3(mv->no, no); + } +} + +/* TODO(Sybren): we can probably rename this to BKE_mesh_calc_normals_mapping(), + * and remove the function of the same name below, as that one doesn't seem to be + * called anywhere. */ +void BKE_mesh_calc_normals_mapping_simple(struct Mesh *mesh) +{ + const bool only_face_normals = CustomData_is_referenced_layer(&mesh->vdata, CD_MVERT); + + BKE_mesh_calc_normals_mapping_ex(mesh->mvert, + mesh->totvert, + mesh->mloop, + mesh->mpoly, + mesh->totloop, + mesh->totpoly, + nullptr, + mesh->mface, + mesh->totface, + nullptr, + nullptr, + only_face_normals); +} + +/* Calculate vertex and face normals, face normals are returned in *r_faceNors if non-nullptr + * and vertex normals are stored in actual mverts. + */ +void BKE_mesh_calc_normals_mapping(MVert *mverts, + int numVerts, + const MLoop *mloop, + const MPoly *mpolys, + int numLoops, + int numPolys, + float (*r_polyNors)[3], + const MFace *mfaces, + int numFaces, + const int *origIndexFace, + float (*r_faceNors)[3]) +{ + BKE_mesh_calc_normals_mapping_ex(mverts, + numVerts, + mloop, + mpolys, + numLoops, + numPolys, + r_polyNors, + mfaces, + numFaces, + origIndexFace, + r_faceNors, + false); +} +/* extended version of 'BKE_mesh_calc_normals_poly' with option not to calc vertex normals */ +void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, + int numVerts, + const MLoop *mloop, + const MPoly *mpolys, + int numLoops, + int numPolys, + float (*r_polyNors)[3], + const MFace *mfaces, + int numFaces, + const int *origIndexFace, + float (*r_faceNors)[3], + const bool only_face_normals) +{ + float(*pnors)[3] = r_polyNors, (*fnors)[3] = r_faceNors; + + if (numPolys == 0) { + if (only_face_normals == false) { + mesh_calc_normals_vert_fallback(mverts, numVerts); + } + return; + } + + /* if we are not calculating verts and no verts were passes then we have nothing to do */ + if ((only_face_normals == true) && (r_polyNors == nullptr) && (r_faceNors == nullptr)) { + CLOG_WARN(&LOG, "called with nothing to do"); + return; + } + + if (!pnors) { + pnors = (float(*)[3])MEM_calloc_arrayN((size_t)numPolys, sizeof(float[3]), __func__); + } + /* NO NEED TO ALLOC YET */ + /* if (!fnors) fnors = MEM_calloc_arrayN(numFaces, sizeof(float[3]), "face nors mesh.c"); */ + + if (only_face_normals == false) { + /* vertex normals are optional, they require some extra calculations, + * so make them optional */ + BKE_mesh_calc_normals_poly( + mverts, nullptr, numVerts, mloop, mpolys, numLoops, numPolys, pnors, false); + } + else { + /* only calc poly normals */ + const MPoly *mp = mpolys; + for (int i = 0; i < numPolys; i++, mp++) { + BKE_mesh_calc_poly_normal(mp, mloop + mp->loopstart, mverts, pnors[i]); + } + } + + if (origIndexFace && + /* fnors == r_faceNors */ /* NO NEED TO ALLOC YET */ + fnors != nullptr && + numFaces) { + const MFace *mf = mfaces; + for (int i = 0; i < numFaces; i++, mf++, origIndexFace++) { + if (*origIndexFace < numPolys) { + copy_v3_v3(fnors[i], pnors[*origIndexFace]); + } + else { + /* eek, we're not corresponding to polys */ + CLOG_ERROR(&LOG, "tessellation face indices are incorrect. normals may look bad."); + } + } + } + + if (pnors != r_polyNors) { + MEM_freeN(pnors); + } + /* if (fnors != r_faceNors) MEM_freeN(fnors); */ /* NO NEED TO ALLOC YET */ + + fnors = pnors = nullptr; +} + +struct MeshCalcNormalsData { + const MPoly *mpolys; + const MLoop *mloop; + MVert *mverts; + float (*pnors)[3]; + float (*vnors)[3]; +}; + +static void mesh_calc_normals_poly_cb(void *__restrict userdata, + const int pidx, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; + const MPoly *mp = &data->mpolys[pidx]; + + BKE_mesh_calc_poly_normal(mp, data->mloop + mp->loopstart, data->mverts, data->pnors[pidx]); +} + +static void mesh_calc_normals_poly_and_accum_cb(void *__restrict userdata, + const int pidx, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + const MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; + const MPoly *mp = &data->mpolys[pidx]; + const MLoop *ml = &data->mloop[mp->loopstart]; + const MVert *mverts = data->mverts; + float(*vnors)[3] = data->vnors; + + float pnor_temp[3]; + float *pnor = data->pnors ? data->pnors[pidx] : pnor_temp; + + const int i_end = mp->totloop - 1; + + /* Polygon Normal and edge-vector */ + /* inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors */ + { + zero_v3(pnor); + /* Newell's Method */ + const float *v_curr = mverts[ml[i_end].v].co; + for (int i_next = 0; i_next <= i_end; i_next++) { + const float *v_next = mverts[ml[i_next].v].co; + add_newell_cross_v3_v3v3(pnor, v_curr, v_next); + v_curr = v_next; + } + if (UNLIKELY(normalize_v3(pnor) == 0.0f)) { + pnor[2] = 1.0f; /* other axes set to 0.0 */ + } + } + + /* Accumulate angle weighted face normal into the vertex normal. */ + /* inline version of #accumulate_vertex_normals_poly_v3. */ + { + float edvec_prev[3], edvec_next[3], edvec_end[3]; + const float *v_curr = mverts[ml[i_end].v].co; + sub_v3_v3v3(edvec_prev, mverts[ml[i_end - 1].v].co, v_curr); + normalize_v3(edvec_prev); + copy_v3_v3(edvec_end, edvec_prev); + + for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) { + const float *v_next = mverts[ml[i_next].v].co; + + /* Skip an extra normalization by reusing the first calculated edge. */ + if (i_next != i_end) { + sub_v3_v3v3(edvec_next, v_curr, v_next); + normalize_v3(edvec_next); + } + else { + copy_v3_v3(edvec_next, edvec_end); + } + + /* Calculate angle between the two poly edges incident on this vertex. */ + const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next)); + const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac}; + + add_v3_v3_atomic(vnors[ml[i_curr].v], vnor_add); + v_curr = v_next; + copy_v3_v3(edvec_prev, edvec_next); + } + } +} + +static void mesh_calc_normals_poly_finalize_cb(void *__restrict userdata, + const int vidx, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; + + MVert *mv = &data->mverts[vidx]; + float *no = data->vnors[vidx]; + + if (UNLIKELY(normalize_v3(no) == 0.0f)) { + /* following Mesh convention; we use vertex coordinate itself for normal in this case */ + normalize_v3_v3(no, mv->co); + } + + normal_float_to_short_v3(mv->no, no); +} + +void BKE_mesh_calc_normals_poly(MVert *mverts, + float (*r_vertnors)[3], + int numVerts, + const MLoop *mloop, + const MPoly *mpolys, + int UNUSED(numLoops), + int numPolys, + float (*r_polynors)[3], + const bool only_face_normals) +{ + float(*pnors)[3] = r_polynors; + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = 1024; + + if (only_face_normals) { + BLI_assert((pnors != nullptr) || (numPolys == 0)); + BLI_assert(r_vertnors == nullptr); + + MeshCalcNormalsData data; + data.mpolys = mpolys; + data.mloop = mloop; + data.mverts = mverts; + data.pnors = pnors; + + BLI_task_parallel_range(0, numPolys, &data, mesh_calc_normals_poly_cb, &settings); + return; + } + + float(*vnors)[3] = r_vertnors; + bool free_vnors = false; + + /* first go through and calculate normals for all the polys */ + if (vnors == nullptr) { + vnors = (float(*)[3])MEM_calloc_arrayN((size_t)numVerts, sizeof(*vnors), __func__); + free_vnors = true; + } + else { + memset(vnors, 0, sizeof(*vnors) * (size_t)numVerts); + } + + MeshCalcNormalsData data; + data.mpolys = mpolys; + data.mloop = mloop; + data.mverts = mverts; + data.pnors = pnors; + data.vnors = vnors; + + /* Compute poly normals (`pnors`), accumulating them into vertex normals (`vnors`). */ + BLI_task_parallel_range(0, numPolys, &data, mesh_calc_normals_poly_and_accum_cb, &settings); + + /* Normalize and validate computed vertex normals (`vnors`). */ + BLI_task_parallel_range(0, numVerts, &data, mesh_calc_normals_poly_finalize_cb, &settings); + + if (free_vnors) { + MEM_freeN(vnors); + } +} + +void BKE_mesh_ensure_normals(Mesh *mesh) +{ + if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) { + BKE_mesh_calc_normals(mesh); + } + BLI_assert((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) == 0); +} + +/** + * Called after calculating all modifiers. + */ +void BKE_mesh_ensure_normals_for_display(Mesh *mesh) +{ + switch ((eMeshWrapperType)mesh->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_MDATA: + /* Run code below. */ + break; + case ME_WRAPPER_TYPE_BMESH: { + struct BMEditMesh *em = mesh->edit_mesh; + EditMeshData *emd = mesh->runtime.edit_data; + if (emd->vertexCos) { + BKE_editmesh_cache_ensure_vert_normals(em, emd); + BKE_editmesh_cache_ensure_poly_normals(em, emd); + } + return; + } + } + + float(*poly_nors)[3] = (float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL); + const bool do_vert_normals = (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) != 0; + const bool do_poly_normals = (mesh->runtime.cd_dirty_poly & CD_MASK_NORMAL || + poly_nors == nullptr); + + if (do_vert_normals || do_poly_normals) { + const bool do_add_poly_nors_cddata = (poly_nors == nullptr); + if (do_add_poly_nors_cddata) { + poly_nors = (float(*)[3])MEM_malloc_arrayN( + (size_t)mesh->totpoly, sizeof(*poly_nors), __func__); + } + + /* calculate poly/vert normals */ + BKE_mesh_calc_normals_poly(mesh->mvert, + nullptr, + mesh->totvert, + mesh->mloop, + mesh->mpoly, + mesh->totloop, + mesh->totpoly, + poly_nors, + !do_vert_normals); + + if (do_add_poly_nors_cddata) { + CustomData_add_layer(&mesh->pdata, CD_NORMAL, CD_ASSIGN, poly_nors, mesh->totpoly); + } + + mesh->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL; + mesh->runtime.cd_dirty_poly &= ~CD_MASK_NORMAL; + } +} + +/* Note that this does not update the CD_NORMAL layer, + * but does update the normals in the CD_MVERT layer. */ +void BKE_mesh_calc_normals(Mesh *mesh) +{ +#ifdef DEBUG_TIME + TIMEIT_START_AVERAGED(BKE_mesh_calc_normals); +#endif + BKE_mesh_calc_normals_poly(mesh->mvert, + nullptr, + mesh->totvert, + mesh->mloop, + mesh->mpoly, + mesh->totloop, + mesh->totpoly, + nullptr, + false); +#ifdef DEBUG_TIME + TIMEIT_END_AVERAGED(BKE_mesh_calc_normals); +#endif + mesh->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL; +} + +void BKE_mesh_calc_normals_looptri(MVert *mverts, + int numVerts, + const MLoop *mloop, + const MLoopTri *looptri, + int looptri_num, + float (*r_tri_nors)[3]) +{ + float(*tnorms)[3] = (float(*)[3])MEM_calloc_arrayN((size_t)numVerts, sizeof(*tnorms), "tnorms"); + float(*fnors)[3] = (r_tri_nors) ? r_tri_nors : + (float(*)[3])MEM_calloc_arrayN( + (size_t)looptri_num, sizeof(*fnors), "meshnormals"); + + if (!tnorms || !fnors) { + goto cleanup; + } + + for (int i = 0; i < looptri_num; i++) { + const MLoopTri *lt = &looptri[i]; + float *f_no = fnors[i]; + const uint vtri[3] = { + mloop[lt->tri[0]].v, + mloop[lt->tri[1]].v, + mloop[lt->tri[2]].v, + }; + + normal_tri_v3(f_no, mverts[vtri[0]].co, mverts[vtri[1]].co, mverts[vtri[2]].co); + + accumulate_vertex_normals_tri_v3(tnorms[vtri[0]], + tnorms[vtri[1]], + tnorms[vtri[2]], + f_no, + mverts[vtri[0]].co, + mverts[vtri[1]].co, + mverts[vtri[2]].co); + } + + /* following Mesh convention; we use vertex coordinate itself for normal in this case */ + for (int i = 0; i < numVerts; i++) { + MVert *mv = &mverts[i]; + float *no = tnorms[i]; + + if (UNLIKELY(normalize_v3(no) == 0.0f)) { + normalize_v3_v3(no, mv->co); + } + + normal_float_to_short_v3(mv->no, no); + } + +cleanup: + MEM_freeN(tnorms); + + if (fnors != r_tri_nors) { + MEM_freeN(fnors); + } +} + +void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, + const int numLoops, + const char data_type) +{ + if (!(lnors_spacearr->lspacearr && lnors_spacearr->loops_pool)) { + MemArena *mem; + + if (!lnors_spacearr->mem) { + lnors_spacearr->mem = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + mem = lnors_spacearr->mem; + lnors_spacearr->lspacearr = (MLoopNorSpace **)BLI_memarena_calloc( + mem, sizeof(MLoopNorSpace *) * (size_t)numLoops); + lnors_spacearr->loops_pool = (LinkNode *)BLI_memarena_alloc( + mem, sizeof(LinkNode) * (size_t)numLoops); + + lnors_spacearr->num_spaces = 0; + } + BLI_assert(ELEM(data_type, MLNOR_SPACEARR_BMLOOP_PTR, MLNOR_SPACEARR_LOOP_INDEX)); + lnors_spacearr->data_type = data_type; +} + +/** + * Utility for multi-threaded calculation that ensures + * `lnors_spacearr_tls` doesn't share memory with `lnors_spacearr` + * that would cause it not to be thread safe. + * + * \note This works as long as threads never operate on the same loops at once. + */ +void BKE_lnor_spacearr_tls_init(MLoopNorSpaceArray *lnors_spacearr, + MLoopNorSpaceArray *lnors_spacearr_tls) +{ + *lnors_spacearr_tls = *lnors_spacearr; + lnors_spacearr_tls->mem = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); +} + +/** + * Utility for multi-threaded calculation + * that merges `lnors_spacearr_tls` into `lnors_spacearr`. + */ +void BKE_lnor_spacearr_tls_join(MLoopNorSpaceArray *lnors_spacearr, + MLoopNorSpaceArray *lnors_spacearr_tls) +{ + BLI_assert(lnors_spacearr->data_type == lnors_spacearr_tls->data_type); + BLI_assert(lnors_spacearr->mem != lnors_spacearr_tls->mem); + lnors_spacearr->num_spaces += lnors_spacearr_tls->num_spaces; + BLI_memarena_merge(lnors_spacearr->mem, lnors_spacearr_tls->mem); + BLI_memarena_free(lnors_spacearr_tls->mem); + lnors_spacearr_tls->mem = nullptr; + BKE_lnor_spacearr_clear(lnors_spacearr_tls); +} + +void BKE_lnor_spacearr_clear(MLoopNorSpaceArray *lnors_spacearr) +{ + lnors_spacearr->num_spaces = 0; + lnors_spacearr->lspacearr = nullptr; + lnors_spacearr->loops_pool = nullptr; + if (lnors_spacearr->mem != nullptr) { + BLI_memarena_clear(lnors_spacearr->mem); + } +} + +void BKE_lnor_spacearr_free(MLoopNorSpaceArray *lnors_spacearr) +{ + lnors_spacearr->num_spaces = 0; + lnors_spacearr->lspacearr = nullptr; + lnors_spacearr->loops_pool = nullptr; + BLI_memarena_free(lnors_spacearr->mem); + lnors_spacearr->mem = nullptr; +} + +MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr) +{ + lnors_spacearr->num_spaces++; + return (MLoopNorSpace *)BLI_memarena_calloc(lnors_spacearr->mem, sizeof(MLoopNorSpace)); +} + +/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */ +#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f) + +/* Should only be called once. + * Beware, this modifies ref_vec and other_vec in place! + * In case no valid space can be generated, ref_alpha and ref_beta are set to zero + * (which means 'use auto lnors'). + */ +void BKE_lnor_space_define(MLoopNorSpace *lnor_space, + const float lnor[3], + float vec_ref[3], + float vec_other[3], + BLI_Stack *edge_vectors) +{ + const float pi2 = (float)M_PI * 2.0f; + float tvec[3], dtp; + const float dtp_ref = dot_v3v3(vec_ref, lnor); + const float dtp_other = dot_v3v3(vec_other, lnor); + + if (UNLIKELY(fabsf(dtp_ref) >= LNOR_SPACE_TRIGO_THRESHOLD || + fabsf(dtp_other) >= LNOR_SPACE_TRIGO_THRESHOLD)) { + /* If vec_ref or vec_other are too much aligned with lnor, we can't build lnor space, + * tag it as invalid and abort. */ + lnor_space->ref_alpha = lnor_space->ref_beta = 0.0f; + + if (edge_vectors) { + BLI_stack_clear(edge_vectors); + } + return; + } + + copy_v3_v3(lnor_space->vec_lnor, lnor); + + /* Compute ref alpha, average angle of all available edge vectors to lnor. */ + if (edge_vectors) { + float alpha = 0.0f; + int nbr = 0; + while (!BLI_stack_is_empty(edge_vectors)) { + const float *vec = (const float *)BLI_stack_peek(edge_vectors); + alpha += saacosf(dot_v3v3(vec, lnor)); + BLI_stack_discard(edge_vectors); + nbr++; + } + /* NOTE: In theory, this could be 'nbr > 2', + * but there is one case where we only have two edges for two loops: + * a smooth vertex with only two edges and two faces (our Monkey's nose has that, e.g.). + */ + BLI_assert(nbr >= 2); /* This piece of code shall only be called for more than one loop... */ + lnor_space->ref_alpha = alpha / (float)nbr; + } + else { + lnor_space->ref_alpha = (saacosf(dot_v3v3(vec_ref, lnor)) + + saacosf(dot_v3v3(vec_other, lnor))) / + 2.0f; + } + + /* Project vec_ref on lnor's ortho plane. */ + mul_v3_v3fl(tvec, lnor, dtp_ref); + sub_v3_v3(vec_ref, tvec); + normalize_v3_v3(lnor_space->vec_ref, vec_ref); + + cross_v3_v3v3(tvec, lnor, lnor_space->vec_ref); + normalize_v3_v3(lnor_space->vec_ortho, tvec); + + /* Project vec_other on lnor's ortho plane. */ + mul_v3_v3fl(tvec, lnor, dtp_other); + sub_v3_v3(vec_other, tvec); + normalize_v3(vec_other); + + /* Beta is angle between ref_vec and other_vec, around lnor. */ + dtp = dot_v3v3(lnor_space->vec_ref, vec_other); + if (LIKELY(dtp < LNOR_SPACE_TRIGO_THRESHOLD)) { + const float beta = saacos(dtp); + lnor_space->ref_beta = (dot_v3v3(lnor_space->vec_ortho, vec_other) < 0.0f) ? pi2 - beta : beta; + } + else { + lnor_space->ref_beta = pi2; + } +} + +/** + * Add a new given loop to given lnor_space. + * Depending on \a lnor_space->data_type, we expect \a bm_loop to be a pointer to BMLoop struct + * (in case of BMLOOP_PTR), or nullptr (in case of LOOP_INDEX), loop index is then stored in + * pointer. If \a is_single is set, the BMLoop or loop index is directly stored in \a + * lnor_space->loops pointer (since there is only one loop in this fan), else it is added to the + * linked list of loops in the fan. + */ +void BKE_lnor_space_add_loop(MLoopNorSpaceArray *lnors_spacearr, + MLoopNorSpace *lnor_space, + const int ml_index, + void *bm_loop, + const bool is_single) +{ + BLI_assert((lnors_spacearr->data_type == MLNOR_SPACEARR_LOOP_INDEX && bm_loop == nullptr) || + (lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR && bm_loop != nullptr)); + + lnors_spacearr->lspacearr[ml_index] = lnor_space; + if (bm_loop == nullptr) { + bm_loop = POINTER_FROM_INT(ml_index); + } + if (is_single) { + BLI_assert(lnor_space->loops == nullptr); + lnor_space->flags |= MLNOR_SPACE_IS_SINGLE; + lnor_space->loops = (LinkNode *)bm_loop; + } + else { + BLI_assert((lnor_space->flags & MLNOR_SPACE_IS_SINGLE) == 0); + BLI_linklist_prepend_nlink(&lnor_space->loops, bm_loop, &lnors_spacearr->loops_pool[ml_index]); + } +} + +MINLINE float unit_short_to_float(const short val) +{ + return (float)val / (float)SHRT_MAX; +} + +MINLINE short unit_float_to_short(const float val) +{ + /* Rounding... */ + return (short)floorf(val * (float)SHRT_MAX + 0.5f); +} + +void BKE_lnor_space_custom_data_to_normal(MLoopNorSpace *lnor_space, + const short clnor_data[2], + float r_custom_lnor[3]) +{ + /* NOP custom normal data or invalid lnor space, return. */ + if (clnor_data[0] == 0 || lnor_space->ref_alpha == 0.0f || lnor_space->ref_beta == 0.0f) { + copy_v3_v3(r_custom_lnor, lnor_space->vec_lnor); + return; + } + + { + /* TODO: Check whether using #sincosf() gives any noticeable benefit + * (could not even get it working under linux though)! */ + const float pi2 = (float)(M_PI * 2.0); + const float alphafac = unit_short_to_float(clnor_data[0]); + const float alpha = (alphafac > 0.0f ? lnor_space->ref_alpha : pi2 - lnor_space->ref_alpha) * + alphafac; + const float betafac = unit_short_to_float(clnor_data[1]); + + mul_v3_v3fl(r_custom_lnor, lnor_space->vec_lnor, cosf(alpha)); + + if (betafac == 0.0f) { + madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ref, sinf(alpha)); + } + else { + const float sinalpha = sinf(alpha); + const float beta = (betafac > 0.0f ? lnor_space->ref_beta : pi2 - lnor_space->ref_beta) * + betafac; + madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ref, sinalpha * cosf(beta)); + madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ortho, sinalpha * sinf(beta)); + } + } +} + +void BKE_lnor_space_custom_normal_to_data(MLoopNorSpace *lnor_space, + const float custom_lnor[3], + short r_clnor_data[2]) +{ + /* We use nullptr vector as NOP custom normal (can be simpler than giving auto-computed `lnor`). + */ + if (is_zero_v3(custom_lnor) || compare_v3v3(lnor_space->vec_lnor, custom_lnor, 1e-4f)) { + r_clnor_data[0] = r_clnor_data[1] = 0; + return; + } + + { + const float pi2 = (float)(M_PI * 2.0); + const float cos_alpha = dot_v3v3(lnor_space->vec_lnor, custom_lnor); + float vec[3], cos_beta; + float alpha; + + alpha = saacosf(cos_alpha); + if (alpha > lnor_space->ref_alpha) { + /* Note we could stick to [0, pi] range here, + * but makes decoding more complex, not worth it. */ + r_clnor_data[0] = unit_float_to_short(-(pi2 - alpha) / (pi2 - lnor_space->ref_alpha)); + } + else { + r_clnor_data[0] = unit_float_to_short(alpha / lnor_space->ref_alpha); + } + + /* Project custom lnor on (vec_ref, vec_ortho) plane. */ + mul_v3_v3fl(vec, lnor_space->vec_lnor, -cos_alpha); + add_v3_v3(vec, custom_lnor); + normalize_v3(vec); + + cos_beta = dot_v3v3(lnor_space->vec_ref, vec); + + if (cos_beta < LNOR_SPACE_TRIGO_THRESHOLD) { + float beta = saacosf(cos_beta); + if (dot_v3v3(lnor_space->vec_ortho, vec) < 0.0f) { + beta = pi2 - beta; + } + + if (beta > lnor_space->ref_beta) { + r_clnor_data[1] = unit_float_to_short(-(pi2 - beta) / (pi2 - lnor_space->ref_beta)); + } + else { + r_clnor_data[1] = unit_float_to_short(beta / lnor_space->ref_beta); + } + } + else { + r_clnor_data[1] = 0; + } + } +} + +#define LOOP_SPLIT_TASK_BLOCK_SIZE 1024 + +struct LoopSplitTaskData { + /* Specific to each instance (each task). */ + + /** We have to create those outside of tasks, since #MemArena is not thread-safe. */ + MLoopNorSpace *lnor_space; + float (*lnor)[3]; + const MLoop *ml_curr; + const MLoop *ml_prev; + int ml_curr_index; + int ml_prev_index; + /** Also used a flag to switch between single or fan process! */ + const int *e2l_prev; + int mp_index; + + /** This one is special, it's owned and managed by worker tasks, + * avoid to have to create it for each fan! */ + BLI_Stack *edge_vectors; + + char pad_c; +}; + +struct LoopSplitTaskDataCommon { + /* Read/write. + * Note we do not need to protect it, though, since two different tasks will *always* affect + * different elements in the arrays. */ + MLoopNorSpaceArray *lnors_spacearr; + float (*loopnors)[3]; + short (*clnors_data)[2]; + + /* Read-only. */ + const MVert *mverts; + const MEdge *medges; + const MLoop *mloops; + const MPoly *mpolys; + int (*edge_to_loops)[2]; + int *loop_to_poly; + const float (*polynors)[3]; + + int numEdges; + int numLoops; + int numPolys; +}; + +#define INDEX_UNSET INT_MIN +#define INDEX_INVALID -1 +/* See comment about edge_to_loops below. */ +#define IS_EDGE_SHARP(_e2l) (ELEM((_e2l)[1], INDEX_UNSET, INDEX_INVALID)) + +static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data, + const bool check_angle, + const float split_angle, + const bool do_sharp_edges_tag) +{ + const MVert *mverts = data->mverts; + const MEdge *medges = data->medges; + const MLoop *mloops = data->mloops; + + const MPoly *mpolys = data->mpolys; + + const int numEdges = data->numEdges; + const int numPolys = data->numPolys; + + float(*loopnors)[3] = data->loopnors; /* NOTE: loopnors may be nullptr here. */ + const float(*polynors)[3] = data->polynors; + + int(*edge_to_loops)[2] = data->edge_to_loops; + int *loop_to_poly = data->loop_to_poly; + + BLI_bitmap *sharp_edges = do_sharp_edges_tag ? BLI_BITMAP_NEW(numEdges, __func__) : nullptr; + + const MPoly *mp; + int mp_index; + + const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f; + + for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) { + const MLoop *ml_curr; + int *e2l; + int ml_curr_index = mp->loopstart; + const int ml_last_index = (ml_curr_index + mp->totloop) - 1; + + ml_curr = &mloops[ml_curr_index]; + + for (; ml_curr_index <= ml_last_index; ml_curr++, ml_curr_index++) { + e2l = edge_to_loops[ml_curr->e]; + + loop_to_poly[ml_curr_index] = mp_index; + + /* Pre-populate all loop normals as if their verts were all-smooth, + * this way we don't have to compute those later! + */ + if (loopnors) { + normal_short_to_float_v3(loopnors[ml_curr_index], mverts[ml_curr->v].no); + } + + /* Check whether current edge might be smooth or sharp */ + if ((e2l[0] | e2l[1]) == 0) { + /* 'Empty' edge until now, set e2l[0] (and e2l[1] to INDEX_UNSET to tag it as unset). */ + e2l[0] = ml_curr_index; + /* We have to check this here too, else we might miss some flat faces!!! */ + e2l[1] = (mp->flag & ME_SMOOTH) ? INDEX_UNSET : INDEX_INVALID; + } + else if (e2l[1] == INDEX_UNSET) { + const bool is_angle_sharp = (check_angle && + dot_v3v3(polynors[loop_to_poly[e2l[0]]], polynors[mp_index]) < + split_angle_cos); + + /* Second loop using this edge, time to test its sharpness. + * An edge is sharp if it is tagged as such, or its face is not smooth, + * or both poly have opposed (flipped) normals, i.e. both loops on the same edge share the + * same vertex, or angle between both its polys' normals is above split_angle value. + */ + if (!(mp->flag & ME_SMOOTH) || (medges[ml_curr->e].flag & ME_SHARP) || + ml_curr->v == mloops[e2l[0]].v || is_angle_sharp) { + /* NOTE: we are sure that loop != 0 here ;). */ + e2l[1] = INDEX_INVALID; + + /* We want to avoid tagging edges as sharp when it is already defined as such by + * other causes than angle threshold... */ + if (do_sharp_edges_tag && is_angle_sharp) { + BLI_BITMAP_SET(sharp_edges, ml_curr->e, true); + } + } + else { + e2l[1] = ml_curr_index; + } + } + else if (!IS_EDGE_SHARP(e2l)) { + /* More than two loops using this edge, tag as sharp if not yet done. */ + e2l[1] = INDEX_INVALID; + + /* We want to avoid tagging edges as sharp when it is already defined as such by + * other causes than angle threshold... */ + if (do_sharp_edges_tag) { + BLI_BITMAP_SET(sharp_edges, ml_curr->e, false); + } + } + /* Else, edge is already 'disqualified' (i.e. sharp)! */ + } + } + + /* If requested, do actual tagging of edges as sharp in another loop. */ + if (do_sharp_edges_tag) { + MEdge *me; + int me_index; + for (me = (MEdge *)medges, me_index = 0; me_index < numEdges; me++, me_index++) { + if (BLI_BITMAP_TEST(sharp_edges, me_index)) { + me->flag |= ME_SHARP; + } + } + + MEM_freeN(sharp_edges); + } +} + +/** + * Define sharp edges as needed to mimic 'autosmooth' from angle threshold. + * + * Used when defining an empty custom loop normals data layer, + * to keep same shading as with auto-smooth! + */ +void BKE_edges_sharp_from_angle_set(const struct MVert *mverts, + const int UNUSED(numVerts), + struct MEdge *medges, + const int numEdges, + struct MLoop *mloops, + const int numLoops, + struct MPoly *mpolys, + const float (*polynors)[3], + const int numPolys, + const float split_angle) +{ + if (split_angle >= (float)M_PI) { + /* Nothing to do! */ + return; + } + + /* Mapping edge -> loops. See BKE_mesh_normals_loop_split() for details. */ + int(*edge_to_loops)[2] = (int(*)[2])MEM_calloc_arrayN( + (size_t)numEdges, sizeof(*edge_to_loops), __func__); + + /* Simple mapping from a loop to its polygon index. */ + int *loop_to_poly = (int *)MEM_malloc_arrayN((size_t)numLoops, sizeof(*loop_to_poly), __func__); + + LoopSplitTaskDataCommon common_data = {}; + common_data.mverts = mverts; + common_data.medges = medges; + common_data.mloops = mloops; + common_data.mpolys = mpolys; + common_data.edge_to_loops = edge_to_loops; + common_data.loop_to_poly = loop_to_poly; + common_data.polynors = polynors; + common_data.numEdges = numEdges; + common_data.numPolys = numPolys; + + mesh_edges_sharp_tag(&common_data, true, split_angle, true); + + MEM_freeN(edge_to_loops); + MEM_freeN(loop_to_poly); +} + +void BKE_mesh_loop_manifold_fan_around_vert_next(const MLoop *mloops, + const MPoly *mpolys, + const int *loop_to_poly, + const int *e2lfan_curr, + const uint mv_pivot_index, + const MLoop **r_mlfan_curr, + int *r_mlfan_curr_index, + int *r_mlfan_vert_index, + int *r_mpfan_curr_index) +{ + const MLoop *mlfan_next; + const MPoly *mpfan_next; + + /* Warning! This is rather complex! + * We have to find our next edge around the vertex (fan mode). + * First we find the next loop, which is either previous or next to mlfan_curr_index, depending + * whether both loops using current edge are in the same direction or not, and whether + * mlfan_curr_index actually uses the vertex we are fanning around! + * mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one + * (i.e. not the future mlfan_curr)... + */ + *r_mlfan_curr_index = (e2lfan_curr[0] == *r_mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0]; + *r_mpfan_curr_index = loop_to_poly[*r_mlfan_curr_index]; + + BLI_assert(*r_mlfan_curr_index >= 0); + BLI_assert(*r_mpfan_curr_index >= 0); + + mlfan_next = &mloops[*r_mlfan_curr_index]; + mpfan_next = &mpolys[*r_mpfan_curr_index]; + if (((*r_mlfan_curr)->v == mlfan_next->v && (*r_mlfan_curr)->v == mv_pivot_index) || + ((*r_mlfan_curr)->v != mlfan_next->v && (*r_mlfan_curr)->v != mv_pivot_index)) { + /* We need the previous loop, but current one is our vertex's loop. */ + *r_mlfan_vert_index = *r_mlfan_curr_index; + if (--(*r_mlfan_curr_index) < mpfan_next->loopstart) { + *r_mlfan_curr_index = mpfan_next->loopstart + mpfan_next->totloop - 1; + } + } + else { + /* We need the next loop, which is also our vertex's loop. */ + if (++(*r_mlfan_curr_index) >= mpfan_next->loopstart + mpfan_next->totloop) { + *r_mlfan_curr_index = mpfan_next->loopstart; + } + *r_mlfan_vert_index = *r_mlfan_curr_index; + } + *r_mlfan_curr = &mloops[*r_mlfan_curr_index]; + /* And now we are back in sync, mlfan_curr_index is the index of mlfan_curr! Pff! */ +} + +static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data) +{ + MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; + const short(*clnors_data)[2] = common_data->clnors_data; + + const MVert *mverts = common_data->mverts; + const MEdge *medges = common_data->medges; + const float(*polynors)[3] = common_data->polynors; + + MLoopNorSpace *lnor_space = data->lnor_space; + float(*lnor)[3] = data->lnor; + const MLoop *ml_curr = data->ml_curr; + const MLoop *ml_prev = data->ml_prev; + const int ml_curr_index = data->ml_curr_index; +#if 0 /* Not needed for 'single' loop. */ + const int ml_prev_index = data->ml_prev_index; + const int *e2l_prev = data->e2l_prev; +#endif + const int mp_index = data->mp_index; + + /* Simple case (both edges around that vertex are sharp in current polygon), + * this loop just takes its poly normal. + */ + copy_v3_v3(*lnor, polynors[mp_index]); + +#if 0 + printf("BASIC: handling loop %d / edge %d / vert %d / poly %d\n", + ml_curr_index, + ml_curr->e, + ml_curr->v, + mp_index); +#endif + + /* If needed, generate this (simple!) lnor space. */ + if (lnors_spacearr) { + float vec_curr[3], vec_prev[3]; + + const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ + const MVert *mv_pivot = &mverts[mv_pivot_index]; + const MEdge *me_curr = &medges[ml_curr->e]; + const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] : + &mverts[me_curr->v1]; + const MEdge *me_prev = &medges[ml_prev->e]; + const MVert *mv_3 = (me_prev->v1 == mv_pivot_index) ? &mverts[me_prev->v2] : + &mverts[me_prev->v1]; + + sub_v3_v3v3(vec_curr, mv_2->co, mv_pivot->co); + normalize_v3(vec_curr); + sub_v3_v3v3(vec_prev, mv_3->co, mv_pivot->co); + normalize_v3(vec_prev); + + BKE_lnor_space_define(lnor_space, *lnor, vec_curr, vec_prev, nullptr); + /* We know there is only one loop in this space, + * no need to create a linklist in this case... */ + BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, ml_curr_index, nullptr, true); + + if (clnors_data) { + BKE_lnor_space_custom_data_to_normal(lnor_space, clnors_data[ml_curr_index], *lnor); + } + } +} + +static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data) +{ + MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; + float(*loopnors)[3] = common_data->loopnors; + short(*clnors_data)[2] = common_data->clnors_data; + + const MVert *mverts = common_data->mverts; + const MEdge *medges = common_data->medges; + const MLoop *mloops = common_data->mloops; + const MPoly *mpolys = common_data->mpolys; + const int(*edge_to_loops)[2] = common_data->edge_to_loops; + const int *loop_to_poly = common_data->loop_to_poly; + const float(*polynors)[3] = common_data->polynors; + + MLoopNorSpace *lnor_space = data->lnor_space; +#if 0 /* Not needed for 'fan' loops. */ + float(*lnor)[3] = data->lnor; +#endif + const MLoop *ml_curr = data->ml_curr; + const MLoop *ml_prev = data->ml_prev; + const int ml_curr_index = data->ml_curr_index; + const int ml_prev_index = data->ml_prev_index; + const int mp_index = data->mp_index; + const int *e2l_prev = data->e2l_prev; + + BLI_Stack *edge_vectors = data->edge_vectors; + + /* Gah... We have to fan around current vertex, until we find the other non-smooth edge, + * and accumulate face normals into the vertex! + * Note in case this vertex has only one sharp edges, this is a waste because the normal is the + * same as the vertex normal, but I do not see any easy way to detect that (would need to count + * number of sharp edges per vertex, I doubt the additional memory usage would be worth it, + * especially as it should not be a common case in real-life meshes anyway). + */ + const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ + const MVert *mv_pivot = &mverts[mv_pivot_index]; + + /* ml_curr would be mlfan_prev if we needed that one. */ + const MEdge *me_org = &medges[ml_curr->e]; + + const int *e2lfan_curr; + float vec_curr[3], vec_prev[3], vec_org[3]; + const MLoop *mlfan_curr; + float lnor[3] = {0.0f, 0.0f, 0.0f}; + /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ + int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index; + + /* We validate clnors data on the fly - cheapest way to do! */ + int clnors_avg[2] = {0, 0}; + short(*clnor_ref)[2] = nullptr; + int clnors_nbr = 0; + bool clnors_invalid = false; + + /* Temp loop normal stack. */ + BLI_SMALLSTACK_DECLARE(normal, float *); + /* Temp clnors stack. */ + BLI_SMALLSTACK_DECLARE(clnors, short *); + + e2lfan_curr = e2l_prev; + mlfan_curr = ml_prev; + mlfan_curr_index = ml_prev_index; + mlfan_vert_index = ml_curr_index; + mpfan_curr_index = mp_index; + + BLI_assert(mlfan_curr_index >= 0); + BLI_assert(mlfan_vert_index >= 0); + BLI_assert(mpfan_curr_index >= 0); + + /* Only need to compute previous edge's vector once, then we can just reuse old current one! */ + { + const MVert *mv_2 = (me_org->v1 == mv_pivot_index) ? &mverts[me_org->v2] : &mverts[me_org->v1]; + + sub_v3_v3v3(vec_org, mv_2->co, mv_pivot->co); + normalize_v3(vec_org); + copy_v3_v3(vec_prev, vec_org); + + if (lnors_spacearr) { + BLI_stack_push(edge_vectors, vec_org); + } + } + + // printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); + + while (true) { + const MEdge *me_curr = &medges[mlfan_curr->e]; + /* Compute edge vectors. + * NOTE: We could pre-compute those into an array, in the first iteration, instead of computing + * them twice (or more) here. However, time gained is not worth memory and time lost, + * given the fact that this code should not be called that much in real-life meshes... + */ + { + const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] : + &mverts[me_curr->v1]; + + sub_v3_v3v3(vec_curr, mv_2->co, mv_pivot->co); + normalize_v3(vec_curr); + } + + // printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); + + { + /* Code similar to accumulate_vertex_normals_poly_v3. */ + /* Calculate angle between the two poly edges incident on this vertex. */ + const float fac = saacos(dot_v3v3(vec_curr, vec_prev)); + /* Accumulate */ + madd_v3_v3fl(lnor, polynors[mpfan_curr_index], fac); + + if (clnors_data) { + /* Accumulate all clnors, if they are not all equal we have to fix that! */ + short(*clnor)[2] = &clnors_data[mlfan_vert_index]; + if (clnors_nbr) { + clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] || (*clnor_ref)[1] != (*clnor)[1]); + } + else { + clnor_ref = clnor; + } + clnors_avg[0] += (*clnor)[0]; + clnors_avg[1] += (*clnor)[1]; + clnors_nbr++; + /* We store here a pointer to all custom lnors processed. */ + BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor); + } + } + + /* We store here a pointer to all loop-normals processed. */ + BLI_SMALLSTACK_PUSH(normal, (float *)(loopnors[mlfan_vert_index])); + + if (lnors_spacearr) { + /* Assign current lnor space to current 'vertex' loop. */ + BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, mlfan_vert_index, nullptr, false); + if (me_curr != me_org) { + /* We store here all edges-normalized vectors processed. */ + BLI_stack_push(edge_vectors, vec_curr); + } + } + + if (IS_EDGE_SHARP(e2lfan_curr) || (me_curr == me_org)) { + /* Current edge is sharp and we have finished with this fan of faces around this vert, + * or this vert is smooth, and we have completed a full turn around it. */ + // printf("FAN: Finished!\n"); + break; + } + + copy_v3_v3(vec_prev, vec_curr); + + /* Find next loop of the smooth fan. */ + BKE_mesh_loop_manifold_fan_around_vert_next(mloops, + mpolys, + loop_to_poly, + e2lfan_curr, + mv_pivot_index, + &mlfan_curr, + &mlfan_curr_index, + &mlfan_vert_index, + &mpfan_curr_index); + + e2lfan_curr = edge_to_loops[mlfan_curr->e]; + } + + { + float lnor_len = normalize_v3(lnor); + + /* If we are generating lnor spacearr, we can now define the one for this fan, + * and optionally compute final lnor from custom data too! + */ + if (lnors_spacearr) { + if (UNLIKELY(lnor_len == 0.0f)) { + /* Use vertex normal as fallback! */ + copy_v3_v3(lnor, loopnors[mlfan_vert_index]); + lnor_len = 1.0f; + } + + BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_curr, edge_vectors); + + if (clnors_data) { + if (clnors_invalid) { + short *clnor; + + clnors_avg[0] /= clnors_nbr; + clnors_avg[1] /= clnors_nbr; + /* Fix/update all clnors of this fan with computed average value. */ + if (G.debug & G_DEBUG) { + printf("Invalid clnors in this fan!\n"); + } + while ((clnor = (short *)BLI_SMALLSTACK_POP(clnors))) { + // print_v2("org clnor", clnor); + clnor[0] = (short)clnors_avg[0]; + clnor[1] = (short)clnors_avg[1]; + } + // print_v2("new clnors", clnors_avg); + } + /* Extra bonus: since small-stack is local to this function, + * no more need to empty it at all cost! */ + + BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor); + } + } + + /* In case we get a zero normal here, just use vertex normal already set! */ + if (LIKELY(lnor_len != 0.0f)) { + /* Copy back the final computed normal into all related loop-normals. */ + float *nor; + + while ((nor = (float *)BLI_SMALLSTACK_POP(normal))) { + copy_v3_v3(nor, lnor); + } + } + /* Extra bonus: since small-stack is local to this function, + * no more need to empty it at all cost! */ + } +} + +static void loop_split_worker_do(LoopSplitTaskDataCommon *common_data, + LoopSplitTaskData *data, + BLI_Stack *edge_vectors) +{ + BLI_assert(data->ml_curr); + if (data->e2l_prev) { + BLI_assert((edge_vectors == nullptr) || BLI_stack_is_empty(edge_vectors)); + data->edge_vectors = edge_vectors; + split_loop_nor_fan_do(common_data, data); + } + else { + /* No need for edge_vectors for 'single' case! */ + split_loop_nor_single_do(common_data, data); + } +} + +static void loop_split_worker(TaskPool *__restrict pool, void *taskdata) +{ + LoopSplitTaskDataCommon *common_data = (LoopSplitTaskDataCommon *)BLI_task_pool_user_data(pool); + LoopSplitTaskData *data = (LoopSplitTaskData *)taskdata; + + /* Temp edge vectors stack, only used when computing lnor spacearr. */ + BLI_Stack *edge_vectors = common_data->lnors_spacearr ? + BLI_stack_new(sizeof(float[3]), __func__) : + nullptr; + +#ifdef DEBUG_TIME + TIMEIT_START_AVERAGED(loop_split_worker); +#endif + + for (int i = 0; i < LOOP_SPLIT_TASK_BLOCK_SIZE; i++, data++) { + /* A nullptr ml_curr is used to tag ended data! */ + if (data->ml_curr == nullptr) { + break; + } + + loop_split_worker_do(common_data, data, edge_vectors); + } + + if (edge_vectors) { + BLI_stack_free(edge_vectors); + } + +#ifdef DEBUG_TIME + TIMEIT_END_AVERAGED(loop_split_worker); +#endif +} + +/** + * Check whether given loop is part of an unknown-so-far cyclic smooth fan, or not. + * Needed because cyclic smooth fans have no obvious 'entry point', + * and yet we need to walk them once, and only once. + */ +static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops, + const MPoly *mpolys, + const int (*edge_to_loops)[2], + const int *loop_to_poly, + const int *e2l_prev, + BLI_bitmap *skip_loops, + const MLoop *ml_curr, + const MLoop *ml_prev, + const int ml_curr_index, + const int ml_prev_index, + const int mp_curr_index) +{ + const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ + const int *e2lfan_curr; + const MLoop *mlfan_curr; + /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ + int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index; + + e2lfan_curr = e2l_prev; + if (IS_EDGE_SHARP(e2lfan_curr)) { + /* Sharp loop, so not a cyclic smooth fan... */ + return false; + } + + mlfan_curr = ml_prev; + mlfan_curr_index = ml_prev_index; + mlfan_vert_index = ml_curr_index; + mpfan_curr_index = mp_curr_index; + + BLI_assert(mlfan_curr_index >= 0); + BLI_assert(mlfan_vert_index >= 0); + BLI_assert(mpfan_curr_index >= 0); + + BLI_assert(!BLI_BITMAP_TEST(skip_loops, mlfan_vert_index)); + BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index); + + while (true) { + /* Find next loop of the smooth fan. */ + BKE_mesh_loop_manifold_fan_around_vert_next(mloops, + mpolys, + loop_to_poly, + e2lfan_curr, + mv_pivot_index, + &mlfan_curr, + &mlfan_curr_index, + &mlfan_vert_index, + &mpfan_curr_index); + + e2lfan_curr = edge_to_loops[mlfan_curr->e]; + + if (IS_EDGE_SHARP(e2lfan_curr)) { + /* Sharp loop/edge, so not a cyclic smooth fan... */ + return false; + } + /* Smooth loop/edge... */ + if (BLI_BITMAP_TEST(skip_loops, mlfan_vert_index)) { + if (mlfan_vert_index == ml_curr_index) { + /* We walked around a whole cyclic smooth fan without finding any already-processed loop, + * means we can use initial ml_curr/ml_prev edge as start for this smooth fan. */ + return true; + } + /* ... already checked in some previous looping, we can abort. */ + return false; + } + + /* ... we can skip it in future, and keep checking the smooth fan. */ + BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index); + } +} + +static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common_data) +{ + MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; + float(*loopnors)[3] = common_data->loopnors; + + const MLoop *mloops = common_data->mloops; + const MPoly *mpolys = common_data->mpolys; + const int *loop_to_poly = common_data->loop_to_poly; + const int(*edge_to_loops)[2] = common_data->edge_to_loops; + const int numLoops = common_data->numLoops; + const int numPolys = common_data->numPolys; + + const MPoly *mp; + int mp_index; + + const MLoop *ml_curr; + const MLoop *ml_prev; + int ml_curr_index; + int ml_prev_index; + + BLI_bitmap *skip_loops = BLI_BITMAP_NEW(numLoops, __func__); + + LoopSplitTaskData *data_buff = nullptr; + int data_idx = 0; + + /* Temp edge vectors stack, only used when computing lnor spacearr + * (and we are not multi-threading). */ + BLI_Stack *edge_vectors = nullptr; + +#ifdef DEBUG_TIME + TIMEIT_START_AVERAGED(loop_split_generator); +#endif + + if (!pool) { + if (lnors_spacearr) { + edge_vectors = BLI_stack_new(sizeof(float[3]), __func__); + } + } + + /* We now know edges that can be smoothed (with their vector, and their two loops), + * and edges that will be hard! Now, time to generate the normals. + */ + for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) { + float(*lnors)[3]; + const int ml_last_index = (mp->loopstart + mp->totloop) - 1; + ml_curr_index = mp->loopstart; + ml_prev_index = ml_last_index; + + ml_curr = &mloops[ml_curr_index]; + ml_prev = &mloops[ml_prev_index]; + lnors = &loopnors[ml_curr_index]; + + for (; ml_curr_index <= ml_last_index; ml_curr++, ml_curr_index++, lnors++) { + const int *e2l_curr = edge_to_loops[ml_curr->e]; + const int *e2l_prev = edge_to_loops[ml_prev->e]; + +#if 0 + printf("Checking loop %d / edge %u / vert %u (sharp edge: %d, skiploop: %d)...", + ml_curr_index, + ml_curr->e, + ml_curr->v, + IS_EDGE_SHARP(e2l_curr), + BLI_BITMAP_TEST_BOOL(skip_loops, ml_curr_index)); +#endif + + /* A smooth edge, we have to check for cyclic smooth fan case. + * If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge + * as 'entry point', otherwise we can skip it. */ + + /* NOTE: In theory, we could make #loop_split_generator_check_cyclic_smooth_fan() store + * mlfan_vert_index'es and edge indexes in two stacks, to avoid having to fan again around + * the vert during actual computation of `clnor` & `clnorspace`. + * However, this would complicate the code, add more memory usage, and despite its logical + * complexity, #loop_manifold_fan_around_vert_next() is quite cheap in term of CPU cycles, + * so really think it's not worth it. */ + if (!IS_EDGE_SHARP(e2l_curr) && (BLI_BITMAP_TEST(skip_loops, ml_curr_index) || + !loop_split_generator_check_cyclic_smooth_fan(mloops, + mpolys, + edge_to_loops, + loop_to_poly, + e2l_prev, + skip_loops, + ml_curr, + ml_prev, + ml_curr_index, + ml_prev_index, + mp_index))) { + // printf("SKIPPING!\n"); + } + else { + LoopSplitTaskData *data, data_local; + + // printf("PROCESSING!\n"); + + if (pool) { + if (data_idx == 0) { + data_buff = (LoopSplitTaskData *)MEM_calloc_arrayN( + LOOP_SPLIT_TASK_BLOCK_SIZE, sizeof(*data_buff), __func__); + } + data = &data_buff[data_idx]; + } + else { + data = &data_local; + memset(data, 0, sizeof(*data)); + } + + if (IS_EDGE_SHARP(e2l_curr) && IS_EDGE_SHARP(e2l_prev)) { + data->lnor = lnors; + data->ml_curr = ml_curr; + data->ml_prev = ml_prev; + data->ml_curr_index = ml_curr_index; +#if 0 /* Not needed for 'single' loop. */ + data->ml_prev_index = ml_prev_index; + data->e2l_prev = nullptr; /* Tag as 'single' task. */ +#endif + data->mp_index = mp_index; + if (lnors_spacearr) { + data->lnor_space = BKE_lnor_space_create(lnors_spacearr); + } + } + /* We *do not need* to check/tag loops as already computed! + * Due to the fact a loop only links to one of its two edges, + * a same fan *will never be walked more than once!* + * Since we consider edges having neighbor polys with inverted + * (flipped) normals as sharp, we are sure that no fan will be skipped, + * even only considering the case (sharp curr_edge, smooth prev_edge), + * and not the alternative (smooth curr_edge, sharp prev_edge). + * All this due/thanks to link between normals and loop ordering (i.e. winding). + */ + else { +#if 0 /* Not needed for 'fan' loops. */ + data->lnor = lnors; +#endif + data->ml_curr = ml_curr; + data->ml_prev = ml_prev; + data->ml_curr_index = ml_curr_index; + data->ml_prev_index = ml_prev_index; + data->e2l_prev = e2l_prev; /* Also tag as 'fan' task. */ + data->mp_index = mp_index; + if (lnors_spacearr) { + data->lnor_space = BKE_lnor_space_create(lnors_spacearr); + } + } + + if (pool) { + data_idx++; + if (data_idx == LOOP_SPLIT_TASK_BLOCK_SIZE) { + BLI_task_pool_push(pool, loop_split_worker, data_buff, true, nullptr); + data_idx = 0; + } + } + else { + loop_split_worker_do(common_data, data, edge_vectors); + } + } + + ml_prev = ml_curr; + ml_prev_index = ml_curr_index; + } + } + + /* Last block of data... Since it is calloc'ed and we use first nullptr item as stopper, + * everything is fine. */ + if (pool && data_idx) { + BLI_task_pool_push(pool, loop_split_worker, data_buff, true, nullptr); + } + + if (edge_vectors) { + BLI_stack_free(edge_vectors); + } + MEM_freeN(skip_loops); + +#ifdef DEBUG_TIME + TIMEIT_END_AVERAGED(loop_split_generator); +#endif +} + +/** + * Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals'). + * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry + * (splitting edges). + */ +void BKE_mesh_normals_loop_split(const MVert *mverts, + const int UNUSED(numVerts), + MEdge *medges, + const int numEdges, + MLoop *mloops, + float (*r_loopnors)[3], + const int numLoops, + MPoly *mpolys, + const float (*polynors)[3], + const int numPolys, + const bool use_split_normals, + const float split_angle, + MLoopNorSpaceArray *r_lnors_spacearr, + short (*clnors_data)[2], + int *r_loop_to_poly) +{ + /* For now this is not supported. + * If we do not use split normals, we do not generate anything fancy! */ + BLI_assert(use_split_normals || !(r_lnors_spacearr)); + + if (!use_split_normals) { + /* In this case, we simply fill lnors with vnors (or fnors for flat faces), quite simple! + * Note this is done here to keep some logic and consistency in this quite complex code, + * since we may want to use lnors even when mesh's 'autosmooth' is disabled + * (see e.g. mesh mapping code). + * As usual, we could handle that on case-by-case basis, + * but simpler to keep it well confined here. + */ + int mp_index; + + for (mp_index = 0; mp_index < numPolys; mp_index++) { + MPoly *mp = &mpolys[mp_index]; + int ml_index = mp->loopstart; + const int ml_index_end = ml_index + mp->totloop; + const bool is_poly_flat = ((mp->flag & ME_SMOOTH) == 0); + + for (; ml_index < ml_index_end; ml_index++) { + if (r_loop_to_poly) { + r_loop_to_poly[ml_index] = mp_index; + } + if (is_poly_flat) { + copy_v3_v3(r_loopnors[ml_index], polynors[mp_index]); + } + else { + normal_short_to_float_v3(r_loopnors[ml_index], mverts[mloops[ml_index].v].no); + } + } + } + return; + } + + /** + * Mapping edge -> loops. + * If that edge is used by more than two loops (polys), + * it is always sharp (and tagged as such, see below). + * We also use the second loop index as a kind of flag: + * + * - smooth edge: > 0. + * - sharp edge: < 0 (INDEX_INVALID || INDEX_UNSET). + * - unset: INDEX_UNSET. + * + * Note that currently we only have two values for second loop of sharp edges. + * However, if needed, we can store the negated value of loop index instead of INDEX_INVALID + * to retrieve the real value later in code). + * Note also that loose edges always have both values set to 0! */ + int(*edge_to_loops)[2] = (int(*)[2])MEM_calloc_arrayN( + (size_t)numEdges, sizeof(*edge_to_loops), __func__); + + /* Simple mapping from a loop to its polygon index. */ + int *loop_to_poly = r_loop_to_poly ? r_loop_to_poly : + (int *)MEM_malloc_arrayN( + (size_t)numLoops, sizeof(*loop_to_poly), __func__); + + /* When using custom loop normals, disable the angle feature! */ + const bool check_angle = (split_angle < (float)M_PI) && (clnors_data == nullptr); + + MLoopNorSpaceArray _lnors_spacearr = {nullptr}; + +#ifdef DEBUG_TIME + TIMEIT_START_AVERAGED(BKE_mesh_normals_loop_split); +#endif + + if (!r_lnors_spacearr && clnors_data) { + /* We need to compute lnor spacearr if some custom lnor data are given to us! */ + r_lnors_spacearr = &_lnors_spacearr; + } + if (r_lnors_spacearr) { + BKE_lnor_spacearr_init(r_lnors_spacearr, numLoops, MLNOR_SPACEARR_LOOP_INDEX); + } + + /* Init data common to all tasks. */ + LoopSplitTaskDataCommon common_data; + common_data.lnors_spacearr = r_lnors_spacearr; + common_data.loopnors = r_loopnors; + common_data.clnors_data = clnors_data; + common_data.mverts = mverts; + common_data.medges = medges; + common_data.mloops = mloops; + common_data.mpolys = mpolys; + common_data.edge_to_loops = edge_to_loops; + common_data.loop_to_poly = loop_to_poly; + common_data.polynors = polynors; + common_data.numEdges = numEdges; + common_data.numLoops = numLoops; + common_data.numPolys = numPolys; + + /* This first loop check which edges are actually smooth, and compute edge vectors. */ + mesh_edges_sharp_tag(&common_data, check_angle, split_angle, false); + + if (numLoops < LOOP_SPLIT_TASK_BLOCK_SIZE * 8) { + /* Not enough loops to be worth the whole threading overhead... */ + loop_split_generator(nullptr, &common_data); + } + else { + TaskPool *task_pool = BLI_task_pool_create(&common_data, TASK_PRIORITY_HIGH); + + loop_split_generator(task_pool, &common_data); + + BLI_task_pool_work_and_wait(task_pool); + + BLI_task_pool_free(task_pool); + } + + MEM_freeN(edge_to_loops); + if (!r_loop_to_poly) { + MEM_freeN(loop_to_poly); + } + + if (r_lnors_spacearr) { + if (r_lnors_spacearr == &_lnors_spacearr) { + BKE_lnor_spacearr_free(r_lnors_spacearr); + } + } + +#ifdef DEBUG_TIME + TIMEIT_END_AVERAGED(BKE_mesh_normals_loop_split); +#endif +} + +#undef INDEX_UNSET +#undef INDEX_INVALID +#undef IS_EDGE_SHARP + +/** + * Compute internal representation of given custom normals (as an array of float[2]). + * It also makes sure the mesh matches those custom normals, by setting sharp edges flag as needed + * to get a same custom lnor for all loops sharing a same smooth fan. + * If use_vertices if true, r_custom_loopnors is assumed to be per-vertex, not per-loop + * (this allows to set whole vert's normals at once, useful in some cases). + * r_custom_loopnors is expected to have normalized normals, or zero ones, + * in which case they will be replaced by default loop/vertex normal. + */ +static void mesh_normals_loop_custom_set(const MVert *mverts, + const int numVerts, + MEdge *medges, + const int numEdges, + MLoop *mloops, + float (*r_custom_loopnors)[3], + const int numLoops, + MPoly *mpolys, + const float (*polynors)[3], + const int numPolys, + short (*r_clnors_data)[2], + const bool use_vertices) +{ + /* We *may* make that poor BKE_mesh_normals_loop_split() even more complex by making it handling + * that feature too, would probably be more efficient in absolute. + * However, this function *is not* performance-critical, since it is mostly expected to be called + * by io addons when importing custom normals, and modifier + * (and perhaps from some editing tools later?). + * So better to keep some simplicity here, and just call BKE_mesh_normals_loop_split() twice! + */ + MLoopNorSpaceArray lnors_spacearr = {nullptr}; + BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)numLoops, __func__); + float(*lnors)[3] = (float(*)[3])MEM_calloc_arrayN((size_t)numLoops, sizeof(*lnors), __func__); + int *loop_to_poly = (int *)MEM_malloc_arrayN((size_t)numLoops, sizeof(int), __func__); + /* In this case we always consider split nors as ON, + * and do not want to use angle to define smooth fans! */ + const bool use_split_normals = true; + const float split_angle = (float)M_PI; + + BLI_SMALLSTACK_DECLARE(clnors_data, short *); + + /* Compute current lnor spacearr. */ + BKE_mesh_normals_loop_split(mverts, + numVerts, + medges, + numEdges, + mloops, + lnors, + numLoops, + mpolys, + polynors, + numPolys, + use_split_normals, + split_angle, + &lnors_spacearr, + nullptr, + loop_to_poly); + + /* Set all given zero vectors to their default value. */ + if (use_vertices) { + for (int i = 0; i < numVerts; i++) { + if (is_zero_v3(r_custom_loopnors[i])) { + normal_short_to_float_v3(r_custom_loopnors[i], mverts[i].no); + } + } + } + else { + for (int i = 0; i < numLoops; i++) { + if (is_zero_v3(r_custom_loopnors[i])) { + copy_v3_v3(r_custom_loopnors[i], lnors[i]); + } + } + } + + BLI_assert(lnors_spacearr.data_type == MLNOR_SPACEARR_LOOP_INDEX); + + /* Now, check each current smooth fan (one lnor space per smooth fan!), + * and if all its matching custom lnors are not (enough) equal, add sharp edges as needed. + * This way, next time we run BKE_mesh_normals_loop_split(), we'll get lnor spacearr/smooth fans + * matching given custom lnors. + * Note this code *will never* unsharp edges! And quite obviously, + * when we set custom normals per vertices, running this is absolutely useless. + */ + if (!use_vertices) { + for (int i = 0; i < numLoops; i++) { + if (!lnors_spacearr.lspacearr[i]) { + /* This should not happen in theory, but in some rare case (probably ugly geometry) + * we can get some nullptr loopspacearr at this point. :/ + * Maybe we should set those loops' edges as sharp? + */ + BLI_BITMAP_ENABLE(done_loops, i); + if (G.debug & G_DEBUG) { + printf("WARNING! Getting invalid nullptr loop space for loop %d!\n", i); + } + continue; + } + + if (!BLI_BITMAP_TEST(done_loops, i)) { + /* Notes: + * * In case of mono-loop smooth fan, we have nothing to do. + * * Loops in this linklist are ordered (in reversed order compared to how they were + * discovered by BKE_mesh_normals_loop_split(), but this is not a problem). + * Which means if we find a mismatching clnor, + * we know all remaining loops will have to be in a new, different smooth fan/lnor space. + * * In smooth fan case, we compare each clnor against a ref one, + * to avoid small differences adding up into a real big one in the end! + */ + if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { + BLI_BITMAP_ENABLE(done_loops, i); + continue; + } + + LinkNode *loops = lnors_spacearr.lspacearr[i]->loops; + MLoop *prev_ml = nullptr; + const float *org_nor = nullptr; + + while (loops) { + const int lidx = POINTER_AS_INT(loops->link); + MLoop *ml = &mloops[lidx]; + const int nidx = lidx; + float *nor = r_custom_loopnors[nidx]; + + if (!org_nor) { + org_nor = nor; + } + else if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) { + /* Current normal differs too much from org one, we have to tag the edge between + * previous loop's face and current's one as sharp. + * We know those two loops do not point to the same edge, + * since we do not allow reversed winding in a same smooth fan. + */ + const MPoly *mp = &mpolys[loop_to_poly[lidx]]; + const MLoop *mlp = + &mloops[(lidx == mp->loopstart) ? mp->loopstart + mp->totloop - 1 : lidx - 1]; + medges[(prev_ml->e == mlp->e) ? prev_ml->e : ml->e].flag |= ME_SHARP; + + org_nor = nor; + } + + prev_ml = ml; + loops = loops->next; + BLI_BITMAP_ENABLE(done_loops, lidx); + } + + /* We also have to check between last and first loops, + * otherwise we may miss some sharp edges here! + * This is just a simplified version of above while loop. + * See T45984. */ + loops = lnors_spacearr.lspacearr[i]->loops; + if (loops && org_nor) { + const int lidx = POINTER_AS_INT(loops->link); + MLoop *ml = &mloops[lidx]; + const int nidx = lidx; + float *nor = r_custom_loopnors[nidx]; + + if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) { + const MPoly *mp = &mpolys[loop_to_poly[lidx]]; + const MLoop *mlp = + &mloops[(lidx == mp->loopstart) ? mp->loopstart + mp->totloop - 1 : lidx - 1]; + medges[(prev_ml->e == mlp->e) ? prev_ml->e : ml->e].flag |= ME_SHARP; + } + } + } + } + + /* And now, recompute our new auto lnors and lnor spacearr! */ + BKE_lnor_spacearr_clear(&lnors_spacearr); + BKE_mesh_normals_loop_split(mverts, + numVerts, + medges, + numEdges, + mloops, + lnors, + numLoops, + mpolys, + polynors, + numPolys, + use_split_normals, + split_angle, + &lnors_spacearr, + nullptr, + loop_to_poly); + } + else { + BLI_bitmap_set_all(done_loops, true, (size_t)numLoops); + } + + /* And we just have to convert plain object-space custom normals to our + * lnor space-encoded ones. */ + for (int i = 0; i < numLoops; i++) { + if (!lnors_spacearr.lspacearr[i]) { + BLI_BITMAP_DISABLE(done_loops, i); + if (G.debug & G_DEBUG) { + printf("WARNING! Still getting invalid nullptr loop space in second loop for loop %d!\n", + i); + } + continue; + } + + if (BLI_BITMAP_TEST_BOOL(done_loops, i)) { + /* Note we accumulate and average all custom normals in current smooth fan, + * to avoid getting different clnors data (tiny differences in plain custom normals can + * give rather huge differences in computed 2D factors). + */ + LinkNode *loops = lnors_spacearr.lspacearr[i]->loops; + if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { + BLI_assert(POINTER_AS_INT(loops) == i); + const int nidx = use_vertices ? (int)mloops[i].v : i; + float *nor = r_custom_loopnors[nidx]; + + BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], nor, r_clnors_data[i]); + BLI_BITMAP_DISABLE(done_loops, i); + } + else { + int nbr_nors = 0; + float avg_nor[3]; + short clnor_data_tmp[2], *clnor_data; + + zero_v3(avg_nor); + while (loops) { + const int lidx = POINTER_AS_INT(loops->link); + const int nidx = use_vertices ? (int)mloops[lidx].v : lidx; + float *nor = r_custom_loopnors[nidx]; + + nbr_nors++; + add_v3_v3(avg_nor, nor); + BLI_SMALLSTACK_PUSH(clnors_data, (short *)r_clnors_data[lidx]); + + loops = loops->next; + BLI_BITMAP_DISABLE(done_loops, lidx); + } + + mul_v3_fl(avg_nor, 1.0f / (float)nbr_nors); + BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], avg_nor, clnor_data_tmp); + + while ((clnor_data = (short *)BLI_SMALLSTACK_POP(clnors_data))) { + clnor_data[0] = clnor_data_tmp[0]; + clnor_data[1] = clnor_data_tmp[1]; + } + } + } + } + + MEM_freeN(lnors); + MEM_freeN(loop_to_poly); + MEM_freeN(done_loops); + BKE_lnor_spacearr_free(&lnors_spacearr); +} + +void BKE_mesh_normals_loop_custom_set(const MVert *mverts, + const int numVerts, + MEdge *medges, + const int numEdges, + MLoop *mloops, + float (*r_custom_loopnors)[3], + const int numLoops, + MPoly *mpolys, + const float (*polynors)[3], + const int numPolys, + short (*r_clnors_data)[2]) +{ + mesh_normals_loop_custom_set(mverts, + numVerts, + medges, + numEdges, + mloops, + r_custom_loopnors, + numLoops, + mpolys, + polynors, + numPolys, + r_clnors_data, + false); +} + +void BKE_mesh_normals_loop_custom_from_vertices_set(const MVert *mverts, + float (*r_custom_vertnors)[3], + const int numVerts, + MEdge *medges, + const int numEdges, + MLoop *mloops, + const int numLoops, + MPoly *mpolys, + const float (*polynors)[3], + const int numPolys, + short (*r_clnors_data)[2]) +{ + mesh_normals_loop_custom_set(mverts, + numVerts, + medges, + numEdges, + mloops, + r_custom_vertnors, + numLoops, + mpolys, + polynors, + numPolys, + r_clnors_data, + true); +} + +static void mesh_set_custom_normals(Mesh *mesh, float (*r_custom_nors)[3], const bool use_vertices) +{ + short(*clnors)[2]; + const int numloops = mesh->totloop; + + clnors = (short(*)[2])CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); + if (clnors != nullptr) { + memset(clnors, 0, sizeof(*clnors) * (size_t)numloops); + } + else { + clnors = (short(*)[2])CustomData_add_layer( + &mesh->ldata, CD_CUSTOMLOOPNORMAL, CD_CALLOC, nullptr, numloops); + } + + float(*polynors)[3] = (float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL); + bool free_polynors = false; + if (polynors == nullptr) { + polynors = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (size_t)mesh->totpoly, __func__); + BKE_mesh_calc_normals_poly(mesh->mvert, + nullptr, + mesh->totvert, + mesh->mloop, + mesh->mpoly, + mesh->totloop, + mesh->totpoly, + polynors, + false); + free_polynors = true; + } + + mesh_normals_loop_custom_set(mesh->mvert, + mesh->totvert, + mesh->medge, + mesh->totedge, + mesh->mloop, + r_custom_nors, + mesh->totloop, + mesh->mpoly, + polynors, + mesh->totpoly, + clnors, + use_vertices); + + if (free_polynors) { + MEM_freeN(polynors); + } +} + +/** + * Higher level functions hiding most of the code needed around call to + * #BKE_mesh_normals_loop_custom_set(). + * + * \param r_custom_loopnors: is not const, since code will replace zero_v3 normals there + * with automatically computed vectors. + */ +void BKE_mesh_set_custom_normals(Mesh *mesh, float (*r_custom_loopnors)[3]) +{ + mesh_set_custom_normals(mesh, r_custom_loopnors, false); +} + +/** + * Higher level functions hiding most of the code needed around call to + * #BKE_mesh_normals_loop_custom_from_vertices_set(). + * + * \param r_custom_vertnors: is not const, since code will replace zero_v3 normals there + * with automatically computed vectors. + */ +void BKE_mesh_set_custom_normals_from_vertices(Mesh *mesh, float (*r_custom_vertnors)[3]) +{ + mesh_set_custom_normals(mesh, r_custom_vertnors, true); +} + +/** + * Computes average per-vertex normals from given custom loop normals. + * + * \param clnors: The computed custom loop normals. + * \param r_vert_clnors: The (already allocated) array where to store averaged per-vertex normals. + */ +void BKE_mesh_normals_loop_to_vertex(const int numVerts, + const MLoop *mloops, + const int numLoops, + const float (*clnors)[3], + float (*r_vert_clnors)[3]) +{ + int *vert_loops_nbr = (int *)MEM_calloc_arrayN( + (size_t)numVerts, sizeof(*vert_loops_nbr), __func__); + + copy_vn_fl((float *)r_vert_clnors, 3 * numVerts, 0.0f); + + int i; + const MLoop *ml; + for (i = 0, ml = mloops; i < numLoops; i++, ml++) { + const uint v = ml->v; + + add_v3_v3(r_vert_clnors[v], clnors[i]); + vert_loops_nbr[v]++; + } + + for (i = 0; i < numVerts; i++) { + mul_v3_fl(r_vert_clnors[i], 1.0f / (float)vert_loops_nbr[i]); + } + + MEM_freeN(vert_loops_nbr); +} + +#undef LNOR_SPACE_TRIGO_THRESHOLD + +/** \} */ -- cgit v1.2.3 From 6d24017529fd0f551fd60df1b2f140e7cdd0b68e Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 11 Aug 2021 15:42:36 +0200 Subject: Cleanup: use socket identifier instead of names in Cycles shader export Will be required when we support setting different names and identifiers for OSL. Ref D12074 --- intern/cycles/blender/blender_shader.cpp | 78 +++++++++++++------------------- 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 7f129310736..f3ad9c3639a 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -149,7 +149,7 @@ BlenderAttributeType blender_attribute_name_split_type(ustring name, string *r_r static BL::NodeSocket get_node_output(BL::Node &b_node, const string &name) { for (BL::NodeSocket &b_out : b_node.outputs) { - if (b_out.name() == name) { + if (b_out.identifier() == name) { return b_out; } } @@ -215,7 +215,12 @@ static void set_default_value(ShaderInput *input, break; } case SocketType::INT: { - node->set(socket, get_int(b_sock.ptr, "default_value")); + if (b_sock.type() == BL::NodeSocket::type_BOOLEAN) { + node->set(socket, get_boolean(b_sock.ptr, "default_value")); + } + else { + node->set(socket, get_int(b_sock.ptr, "default_value")); + } break; } case SocketType::COLOR: { @@ -1006,67 +1011,48 @@ static ShaderInput *node_find_input_by_name(ShaderNode *node, BL::Node &b_node, BL::NodeSocket &b_socket) { - string name = b_socket.name(); + string name = b_socket.identifier(); + ShaderInput *input = node->input(name.c_str()); - if (node_use_modified_socket_name(node)) { - bool found = false; - int counter = 0, total = 0; + if (!input && node_use_modified_socket_name(node)) { + /* Different internal name for shader. */ + if (string_startswith(name, "Shader")) { + string_replace(name, "Shader", "Closure"); + } + input = node->input(name.c_str()); - for (BL::NodeSocket &b_input : b_node.inputs) { - if (b_input.name() == name) { - if (!found) { - counter++; - } - total++; + if (!input) { + /* Different internal numbering of two sockets with same name. */ + if (string_endswith(name, "_001")) { + string_replace(name, "_001", "2"); + } + else { + name += "1"; } - if (b_input.ptr.data == b_socket.ptr.data) - found = true; + input = node->input(name.c_str()); } - - /* rename if needed */ - if (name == "Shader") - name = "Closure"; - - if (total > 1) - name = string_printf("%s%d", name.c_str(), counter); } - return node->input(name.c_str()); + return input; } static ShaderOutput *node_find_output_by_name(ShaderNode *node, BL::Node &b_node, BL::NodeSocket &b_socket) { - string name = b_socket.name(); + string name = b_socket.identifier(); + ShaderOutput *output = node->output(name.c_str()); - if (node_use_modified_socket_name(node)) { - bool found = false; - int counter = 0, total = 0; - - for (BL::NodeSocket &b_output : b_node.outputs) { - if (b_output.name() == name) { - if (!found) { - counter++; - } - total++; - } - - if (b_output.ptr.data == b_socket.ptr.data) { - found = true; - } - } - - /* rename if needed */ - if (name == "Shader") + if (!output && node_use_modified_socket_name(node)) { + /* Different internal name for shader. */ + if (name == "Shader") { name = "Closure"; - - if (total > 1) - name = string_printf("%s%d", name.c_str(), counter); + output = node->output(name.c_str()); + } } - return node->output(name.c_str()); + return output; } static void add_nodes(Scene *scene, -- cgit v1.2.3 From e6bbbd965a447249393a27f911a1b29595c439ab Mon Sep 17 00:00:00 2001 From: Pedro A Date: Wed, 11 Aug 2021 14:49:48 +0200 Subject: Cycles: OSL metadata support for UI labels and checkboxes To improve the presentation of nodes in the node editor. Recognize the following metadata from the OSL specification: * [[ string label = "UI Label" ]] * [[ string widget = "checkBox" ]] * [[ string widget = "boolean" ]] Ref T89741 Differential Revision: https://developer.blender.org/D12074 --- intern/cycles/blender/blender_python.cpp | 107 +++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 26 deletions(-) diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index fd145effde7..6e06b6a468f 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -487,6 +487,24 @@ static PyObject *osl_update_node_func(PyObject * /*self*/, PyObject *args) if (param->varlenarray || param->isstruct || param->type.arraylen > 1) continue; + /* Read metadata. */ + bool is_bool_param = false; + ustring param_label = param->name; + + for (const OSL::OSLQuery::Parameter &metadata : param->metadata) { + if (metadata.type == TypeDesc::STRING) { + if (metadata.name == "widget") { + /* Boolean socket. */ + if (metadata.sdefault[0] == "boolean" || metadata.sdefault[0] == "checkBox") { + is_bool_param = true; + } + } + else if (metadata.name == "label") { + /* Socket label. */ + param_label = metadata.sdefault[0]; + } + } + } /* determine socket type */ string socket_type; BL::NodeSocket::type_enum data_type = BL::NodeSocket::type_VALUE; @@ -494,6 +512,7 @@ static PyObject *osl_update_node_func(PyObject * /*self*/, PyObject *args) float default_float = 0.0f; int default_int = 0; string default_string = ""; + bool default_boolean = false; if (param->isclosure) { socket_type = "NodeSocketShader"; @@ -523,10 +542,19 @@ static PyObject *osl_update_node_func(PyObject * /*self*/, PyObject *args) } else if (param->type.aggregate == TypeDesc::SCALAR) { if (param->type.basetype == TypeDesc::INT) { - socket_type = "NodeSocketInt"; - data_type = BL::NodeSocket::type_INT; - if (param->validdefault) - default_int = param->idefault[0]; + if (is_bool_param) { + socket_type = "NodeSocketBool"; + data_type = BL::NodeSocket::type_BOOLEAN; + if (param->validdefault) { + default_boolean = (bool)param->idefault[0]; + } + } + else { + socket_type = "NodeSocketInt"; + data_type = BL::NodeSocket::type_INT; + if (param->validdefault) + default_int = param->idefault[0]; + } } else if (param->type.basetype == TypeDesc::FLOAT) { socket_type = "NodeSocketFloat"; @@ -546,33 +574,57 @@ static PyObject *osl_update_node_func(PyObject * /*self*/, PyObject *args) else continue; - /* find socket socket */ - BL::NodeSocket b_sock(PointerRNA_NULL); + /* Update existing socket. */ + bool found_existing = false; if (param->isoutput) { - b_sock = b_node.outputs[param->name.string()]; - /* remove if type no longer matches */ - if (b_sock && b_sock.bl_idname() != socket_type) { - b_node.outputs.remove(b_data, b_sock); - b_sock = BL::NodeSocket(PointerRNA_NULL); + for (BL::NodeSocket &b_sock : b_node.outputs) { + if (b_sock.identifier() == param->name) { + if (b_sock.bl_idname() != socket_type) { + /* Remove if type no longer matches. */ + b_node.outputs.remove(b_data, b_sock); + } + else { + /* Reuse and update label. */ + if (b_sock.name() != param_label) { + b_sock.name(param_label.string()); + } + used_sockets.insert(b_sock.ptr.data); + found_existing = true; + } + break; + } } } else { - b_sock = b_node.inputs[param->name.string()]; - /* remove if type no longer matches */ - if (b_sock && b_sock.bl_idname() != socket_type) { - b_node.inputs.remove(b_data, b_sock); - b_sock = BL::NodeSocket(PointerRNA_NULL); + for (BL::NodeSocket &b_sock : b_node.inputs) { + if (b_sock.identifier() == param->name) { + if (b_sock.bl_idname() != socket_type) { + /* Remove if type no longer matches. */ + b_node.inputs.remove(b_data, b_sock); + } + else { + /* Reuse and update label. */ + if (b_sock.name() != param_label) { + b_sock.name(param_label.string()); + } + used_sockets.insert(b_sock.ptr.data); + found_existing = true; + } + break; + } } } - if (!b_sock) { - /* create new socket */ - if (param->isoutput) - b_sock = b_node.outputs.create( - b_data, socket_type.c_str(), param->name.c_str(), param->name.c_str()); - else - b_sock = b_node.inputs.create( - b_data, socket_type.c_str(), param->name.c_str(), param->name.c_str()); + if (!found_existing) { + /* Create new socket. */ + BL::NodeSocket b_sock = (param->isoutput) ? b_node.outputs.create(b_data, + socket_type.c_str(), + param_label.c_str(), + param->name.c_str()) : + b_node.inputs.create(b_data, + socket_type.c_str(), + param_label.c_str(), + param->name.c_str()); /* set default value */ if (data_type == BL::NodeSocket::type_VALUE) { @@ -590,9 +642,12 @@ static PyObject *osl_update_node_func(PyObject * /*self*/, PyObject *args) else if (data_type == BL::NodeSocket::type_STRING) { set_string(b_sock.ptr, "default_value", default_string); } - } + else if (data_type == BL::NodeSocket::type_BOOLEAN) { + set_boolean(b_sock.ptr, "default_value", default_boolean); + } - used_sockets.insert(b_sock.ptr.data); + used_sockets.insert(b_sock.ptr.data); + } } /* remove unused parameters */ -- cgit v1.2.3 From c9a9d5332bbe31c5f1c8974eeee3064d02557061 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 11 Aug 2021 11:38:34 -0300 Subject: PyAPI: GPU: Expose builtin shaders Expose `3D_POLYLINE_SMOOTH_COLOR` and `3D_POLYLINE_FLAT_COLOR` builtins. Requested by addon developers. --- source/blender/python/gpu/gpu_py_shader.c | 40 ++++++++++++++++--------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index b3f1c186716..41c40fdeb96 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -44,14 +44,28 @@ /** \name Enum Conversion. * \{ */ +#define PYDOC_BUILTIN_SHADER_LIST \ + " - ``2D_FLAT_COLOR``\n" \ + " - ``2D_IMAGE``\n" \ + " - ``2D_SMOOTH_COLOR``\n" \ + " - ``2D_UNIFORM_COLOR``\n" \ + " - ``3D_FLAT_COLOR``\n" \ + " - ``3D_SMOOTH_COLOR``\n" \ + " - ``3D_UNIFORM_COLOR``\n" \ + " - ``3D_POLYLINE_FLAT_COLOR``\n" \ + " - ``3D_POLYLINE_SMOOTH_COLOR``\n" \ + " - ``3D_POLYLINE_UNIFORM_COLOR``\n" + static const struct PyC_StringEnumItems pygpu_shader_builtin_items[] = { - {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"}, {GPU_SHADER_2D_FLAT_COLOR, "2D_FLAT_COLOR"}, - {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"}, {GPU_SHADER_2D_IMAGE, "2D_IMAGE"}, - {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"}, + {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"}, + {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"}, {GPU_SHADER_3D_FLAT_COLOR, "3D_FLAT_COLOR"}, {GPU_SHADER_3D_SMOOTH_COLOR, "3D_SMOOTH_COLOR"}, + {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"}, + {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "3D_POLYLINE_FLAT_COLOR"}, + {GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR, "3D_POLYLINE_SMOOTH_COLOR"}, {GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, "3D_POLYLINE_UNIFORM_COLOR"}, {0, NULL}, }; @@ -704,14 +718,8 @@ PyDoc_STRVAR(pygpu_shader_from_builtin_doc, " For more details, you can check the shader code with the\n" " :func:`gpu.shader.code_from_builtin` function.\n" "\n" - " :param pygpu_shader_name: One of these builtin shader names:\n\n" - " - ``2D_UNIFORM_COLOR``\n" - " - ``2D_FLAT_COLOR``\n" - " - ``2D_SMOOTH_COLOR``\n" - " - ``2D_IMAGE``\n" - " - ``3D_UNIFORM_COLOR``\n" - " - ``3D_FLAT_COLOR``\n" - " - ``3D_SMOOTH_COLOR``\n" + " :param pygpu_shader_name: One of these builtin shader names:\n" + "\n" PYDOC_BUILTIN_SHADER_LIST " :type pygpu_shader_name: str\n" " :return: Shader object corresponding to the given name.\n" " :rtype: :class:`bpy.types.GPUShader`\n"); @@ -734,14 +742,8 @@ PyDoc_STRVAR(pygpu_shader_code_from_builtin_doc, "\n" " Exposes the internal shader code for query.\n" "\n" - " :param pygpu_shader_name: One of these builtin shader names:\n\n" - " - ``2D_UNIFORM_COLOR``\n" - " - ``2D_FLAT_COLOR``\n" - " - ``2D_SMOOTH_COLOR``\n" - " - ``2D_IMAGE``\n" - " - ``3D_UNIFORM_COLOR``\n" - " - ``3D_FLAT_COLOR``\n" - " - ``3D_SMOOTH_COLOR``\n" + " :param pygpu_shader_name: One of these builtin shader names:\n" + "\n" PYDOC_BUILTIN_SHADER_LIST " :type pygpu_shader_name: str\n" " :return: Vertex, fragment and geometry shader codes.\n" " :rtype: dict\n"); -- cgit v1.2.3 From e53afad2414969ac0a1affb11398feff0df00516 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 11 Aug 2021 16:18:52 +0200 Subject: Cleanup: moved keyframe drawing to a draw list. In preparation to do threaded drawing preparation. There should not be any functional changes. --- source/blender/editors/animation/keyframes_draw.c | 289 +++++++++++++++------ source/blender/editors/include/ED_keyframes_draw.h | 26 +- source/blender/editors/space_action/action_draw.c | 25 +- 3 files changed, 247 insertions(+), 93 deletions(-) diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index d858ef3ca5a..d25f81005c5 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -25,6 +25,8 @@ #include +#include "MEM_guardedalloc.h" + #include "BLI_dlrbTree.h" #include "BLI_listbase.h" #include "BLI_rect.h" @@ -455,95 +457,232 @@ static void draw_keylist(View2D *v2d, draw_keylist_keys(&ctx, v2d, columns, ypos, saction_flag); } -/* *************************** Channel Drawing Funcs *************************** */ +/* *************************** Drawing Stack *************************** */ +typedef enum eAnimKeylistDrawListElemType { + ANIM_KEYLIST_SUMMARY, + ANIM_KEYLIST_SCENE, + ANIM_KEYLIST_OBJECT, + ANIM_KEYLIST_FCURVE, + ANIM_KEYLIST_ACTION, + ANIM_KEYLIST_AGROUP, + ANIM_KEYLIST_GP_LAYER, + ANIM_KEYLIST_MASK_LAYER, +} eAnimKeylistDrawListElemType; + +typedef struct AnimKeylistDrawListElem { + struct AnimKeylistDrawListElem *next, *prev; + struct AnimKeylist *keylist; + eAnimKeylistDrawListElemType type; + + float yscale_fac; + float ypos; + eSAction_Flag saction_flag; + bool channel_locked; + + bAnimContext *ac; + bDopeSheet *ads; + Scene *sce; + Object *ob; + AnimData *adt; + FCurve *fcu; + bAction *act; + bActionGroup *agrp; + bGPDlayer *gpl; + MaskLayer *masklay; + +} AnimKeylistDrawListElem; + +static void ED_keylist_draw_list_elem_build_keylist(AnimKeylistDrawListElem *elem) +{ + switch (elem->type) { + case ANIM_KEYLIST_SUMMARY: { + summary_to_keylist(elem->ac, elem->keylist, elem->saction_flag); + break; + } + case ANIM_KEYLIST_SCENE: { + scene_to_keylist(elem->ads, elem->sce, elem->keylist, elem->saction_flag); + break; + } + case ANIM_KEYLIST_OBJECT: { + ob_to_keylist(elem->ads, elem->ob, elem->keylist, elem->saction_flag); + break; + } + case ANIM_KEYLIST_FCURVE: { + fcurve_to_keylist(elem->adt, elem->fcu, elem->keylist, elem->saction_flag); + break; + } + case ANIM_KEYLIST_ACTION: { + action_to_keylist(elem->adt, elem->act, elem->keylist, elem->saction_flag); + break; + } + case ANIM_KEYLIST_AGROUP: { + agroup_to_keylist(elem->adt, elem->agrp, elem->keylist, elem->saction_flag); + break; + } + case ANIM_KEYLIST_GP_LAYER: { + gpl_to_keylist(elem->ads, elem->gpl, elem->keylist); + break; + } + case ANIM_KEYLIST_MASK_LAYER: { + mask_to_keylist(elem->ads, elem->masklay, elem->keylist); + break; + } + } +} -void draw_summary_channel( - View2D *v2d, bAnimContext *ac, float ypos, float yscale_fac, int saction_flag) +static void ED_keylist_draw_list_elem_draw(AnimKeylistDrawListElem *elem, View2D *v2d) { - struct AnimKeylist *keylist = ED_keylist_create(); + draw_keylist( + v2d, elem->keylist, elem->ypos, elem->yscale_fac, elem->channel_locked, elem->saction_flag); +} - saction_flag &= ~SACTION_SHOW_EXTREMES; +typedef struct AnimKeylistDrawList { + ListBase /* AnimKeylistDrawListElem*/ channels; +} AnimKeylistDrawList; - summary_to_keylist(ac, keylist, saction_flag); +AnimKeylistDrawList *ED_keylist_draw_list_create(void) +{ + return MEM_callocN(sizeof(AnimKeylistDrawList), __func__); +} - draw_keylist(v2d, keylist, ypos, yscale_fac, false, saction_flag); +static void ED_keylist_draw_list_build_keylists(AnimKeylistDrawList *draw_list) +{ + LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { + ED_keylist_draw_list_elem_build_keylist(elem); + } +} - ED_keylist_free(keylist); +static void ED_keylist_draw_list_draw(AnimKeylistDrawList *draw_list, View2D *v2d) +{ + LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { + ED_keylist_draw_list_elem_draw(elem, v2d); + } } -void draw_scene_channel( - View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos, float yscale_fac, int saction_flag) +void ED_keylist_draw_list_flush(AnimKeylistDrawList *draw_list, View2D *v2d) { - struct AnimKeylist *keylist = ED_keylist_create(); + ED_keylist_draw_list_build_keylists(draw_list); + ED_keylist_draw_list_draw(draw_list, v2d); +} - saction_flag &= ~SACTION_SHOW_EXTREMES; +void ED_keylist_draw_list_free(AnimKeylistDrawList *draw_list) +{ + LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { + ED_keylist_free(elem->keylist); + } + BLI_freelistN(&draw_list->channels); + MEM_freeN(draw_list); +} - scene_to_keylist(ads, sce, keylist, saction_flag); +static AnimKeylistDrawListElem *ed_keylist_draw_list_add_elem( + AnimKeylistDrawList *draw_list, + eAnimKeylistDrawListElemType elem_type, + float ypos, + float yscale_fac, + eSAction_Flag saction_flag) +{ + AnimKeylistDrawListElem *draw_elem = MEM_callocN(sizeof(AnimKeylistDrawListElem), __func__); + BLI_addtail(&draw_list->channels, draw_elem); + draw_elem->type = elem_type; + draw_elem->keylist = ED_keylist_create(); + draw_elem->ypos = ypos; + draw_elem->yscale_fac = yscale_fac; + draw_elem->saction_flag = saction_flag; + return draw_elem; +} - draw_keylist(v2d, keylist, ypos, yscale_fac, false, saction_flag); +/* *************************** Channel Drawing Funcs *************************** */ - ED_keylist_free(keylist); +void draw_summary_channel(struct AnimKeylistDrawList *draw_list, + bAnimContext *ac, + float ypos, + float yscale_fac, + int saction_flag) +{ + saction_flag &= ~SACTION_SHOW_EXTREMES; + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_SUMMARY, ypos, yscale_fac, saction_flag); + draw_elem->ac = ac; } -void draw_object_channel( - View2D *v2d, bDopeSheet *ads, Object *ob, float ypos, float yscale_fac, int saction_flag) +void draw_scene_channel(AnimKeylistDrawList *draw_list, + bDopeSheet *ads, + Scene *sce, + float ypos, + float yscale_fac, + int saction_flag) { - struct AnimKeylist *keylist = ED_keylist_create(); - saction_flag &= ~SACTION_SHOW_EXTREMES; - - ob_to_keylist(ads, ob, keylist, saction_flag); - - draw_keylist(v2d, keylist, ypos, yscale_fac, false, saction_flag); - - ED_keylist_free(keylist); + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_SCENE, ypos, yscale_fac, saction_flag); + draw_elem->ads = ads; + draw_elem->sce = sce; } -void draw_fcurve_channel( - View2D *v2d, AnimData *adt, FCurve *fcu, float ypos, float yscale_fac, int saction_flag) +void draw_object_channel(AnimKeylistDrawList *draw_list, + bDopeSheet *ads, + Object *ob, + float ypos, + float yscale_fac, + int saction_flag) { - struct AnimKeylist *keylist = ED_keylist_create(); - - bool locked = (fcu->flag & FCURVE_PROTECTED) || - ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)) || - ((adt && adt->action) && ID_IS_LINKED(adt->action)); - - fcurve_to_keylist(adt, fcu, keylist, saction_flag); - - draw_keylist(v2d, keylist, ypos, yscale_fac, locked, saction_flag); - - ED_keylist_free(keylist); + saction_flag &= ~SACTION_SHOW_EXTREMES; + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_OBJECT, ypos, yscale_fac, saction_flag); + draw_elem->ads = ads; + draw_elem->ob = ob; } -void draw_agroup_channel( - View2D *v2d, AnimData *adt, bActionGroup *agrp, float ypos, float yscale_fac, int saction_flag) +void draw_fcurve_channel(AnimKeylistDrawList *draw_list, + AnimData *adt, + FCurve *fcu, + float ypos, + float yscale_fac, + int saction_flag) { - struct AnimKeylist *keylist = ED_keylist_create(); + const bool locked = (fcu->flag & FCURVE_PROTECTED) || + ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)) || + ((adt && adt->action) && ID_IS_LINKED(adt->action)); + + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_FCURVE, ypos, yscale_fac, saction_flag); + draw_elem->adt = adt; + draw_elem->fcu = fcu; + draw_elem->channel_locked = locked; +} +void draw_agroup_channel(AnimKeylistDrawList *draw_list, + AnimData *adt, + bActionGroup *agrp, + float ypos, + float yscale_fac, + int saction_flag) +{ bool locked = (agrp->flag & AGRP_PROTECTED) || ((adt && adt->action) && ID_IS_LINKED(adt->action)); - agroup_to_keylist(adt, agrp, keylist, saction_flag); - - draw_keylist(v2d, keylist, ypos, yscale_fac, locked, saction_flag); - - ED_keylist_free(keylist); + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_AGROUP, ypos, yscale_fac, saction_flag); + draw_elem->adt = adt; + draw_elem->agrp = agrp; + draw_elem->channel_locked = locked; } -void draw_action_channel( - View2D *v2d, AnimData *adt, bAction *act, float ypos, float yscale_fac, int saction_flag) +void draw_action_channel(AnimKeylistDrawList *draw_list, + AnimData *adt, + bAction *act, + float ypos, + float yscale_fac, + int saction_flag) { - struct AnimKeylist *keylist = ED_keylist_create(); - - bool locked = (act && ID_IS_LINKED(act)); - + const bool locked = (act && ID_IS_LINKED(act)); saction_flag &= ~SACTION_SHOW_EXTREMES; - action_to_keylist(adt, act, keylist, saction_flag); - - draw_keylist(v2d, keylist, ypos, yscale_fac, locked, saction_flag); - - ED_keylist_free(keylist); + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_ACTION, ypos, yscale_fac, saction_flag); + draw_elem->adt = adt; + draw_elem->act = act; + draw_elem->channel_locked = locked; } void draw_gpencil_channel( @@ -560,34 +699,32 @@ void draw_gpencil_channel( ED_keylist_free(keylist); } -void draw_gpl_channel( - View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos, float yscale_fac, int saction_flag) +void draw_gpl_channel(AnimKeylistDrawList *draw_list, + bDopeSheet *ads, + bGPDlayer *gpl, + float ypos, + float yscale_fac, + int saction_flag) { - struct AnimKeylist *keylist = ED_keylist_create(); - bool locked = (gpl->flag & GP_LAYER_LOCKED) != 0; - - gpl_to_keylist(ads, gpl, keylist); - - draw_keylist(v2d, keylist, ypos, yscale_fac, locked, saction_flag); - - ED_keylist_free(keylist); + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_GP_LAYER, ypos, yscale_fac, saction_flag); + draw_elem->ads = ads; + draw_elem->gpl = gpl; + draw_elem->channel_locked = locked; } -void draw_masklay_channel(View2D *v2d, +void draw_masklay_channel(AnimKeylistDrawList *draw_list, bDopeSheet *ads, MaskLayer *masklay, float ypos, float yscale_fac, int saction_flag) { - struct AnimKeylist *keylist = ED_keylist_create(); - bool locked = (masklay->flag & MASK_LAYERFLAG_LOCKED) != 0; - - mask_to_keylist(ads, masklay, keylist); - - draw_keylist(v2d, keylist, ypos, yscale_fac, locked, saction_flag); - - ED_keylist_free(keylist); + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_MASK_LAYER, ypos, yscale_fac, saction_flag); + draw_elem->ads = ads; + draw_elem->masklay = masklay; + draw_elem->channel_locked = locked; } diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index d2d22dd38dc..c9bbf58ff7a 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -28,6 +28,7 @@ extern "C" { #endif struct AnimData; +struct AnimKeylistDrawList; struct FCurve; struct MaskLayer; struct Object; @@ -61,43 +62,46 @@ void draw_keyframe_shape(float x, /* Channel Drawing ------------------ */ /* F-Curve */ -void draw_fcurve_channel(struct View2D *v2d, +void draw_fcurve_channel(struct AnimKeylistDrawList *draw_list, struct AnimData *adt, struct FCurve *fcu, float ypos, float yscale_fac, int saction_flag); /* Action Group Summary */ -void draw_agroup_channel(struct View2D *v2d, +void draw_agroup_channel(struct AnimKeylistDrawList *draw_list, struct AnimData *adt, struct bActionGroup *agrp, float ypos, float yscale_fac, int saction_flag); /* Action Summary */ -void draw_action_channel(struct View2D *v2d, +void draw_action_channel(struct AnimKeylistDrawList *draw_list, struct AnimData *adt, struct bAction *act, float ypos, float yscale_fac, int saction_flag); /* Object Summary */ -void draw_object_channel(struct View2D *v2d, +void draw_object_channel(struct AnimKeylistDrawList *draw_list, struct bDopeSheet *ads, struct Object *ob, float ypos, float yscale_fac, int saction_flag); /* Scene Summary */ -void draw_scene_channel(struct View2D *v2d, +void draw_scene_channel(struct AnimKeylistDrawList *draw_list, struct bDopeSheet *ads, struct Scene *sce, float ypos, float yscale_fac, int saction_flag); /* DopeSheet Summary */ -void draw_summary_channel( - struct View2D *v2d, struct bAnimContext *ac, float ypos, float yscale_fac, int saction_flag); +void draw_summary_channel(struct AnimKeylistDrawList *draw_list, + struct bAnimContext *ac, + float ypos, + float yscale_fac, + int saction_flag); /* Grease Pencil datablock summary */ void draw_gpencil_channel(struct View2D *v2d, struct bDopeSheet *ads, @@ -106,20 +110,24 @@ void draw_gpencil_channel(struct View2D *v2d, float yscale_fac, int saction_flag); /* Grease Pencil Layer */ -void draw_gpl_channel(struct View2D *v2d, +void draw_gpl_channel(struct AnimKeylistDrawList *draw_list, struct bDopeSheet *ads, struct bGPDlayer *gpl, float ypos, float yscale_fac, int saction_flag); /* Mask Layer */ -void draw_masklay_channel(struct View2D *v2d, +void draw_masklay_channel(struct AnimKeylistDrawList *draw_list, struct bDopeSheet *ads, struct MaskLayer *masklay, float ypos, float yscale_fac, int saction_flag); +struct AnimKeylistDrawList *ED_keylist_draw_list_create(void); +void ED_keylist_draw_list_flush(struct AnimKeylistDrawList *draw_list, struct View2D *v2d); +void ED_keylist_draw_list_free(struct AnimKeylistDrawList *draw_list); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index ce07b9c5fad..903754f4fb1 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -302,6 +302,8 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region ymax = ACHANNEL_FIRST_TOP(ac); + struct AnimKeylistDrawList *draw_list = ED_keylist_draw_list_create(); + for (ale = anim_data.first; ale; ale = ale->next, ymax -= ACHANNEL_STEP(ac)) { float ymin = ymax - ACHANNEL_HEIGHT(ac); float ycenter = (ymin + ymax) / 2.0f; @@ -316,34 +318,41 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region /* draw 'keyframes' for each specific datatype */ switch (ale->datatype) { case ALE_ALL: - draw_summary_channel(v2d, ale->data, ycenter, ac->yscale_fac, action_flag); + draw_summary_channel(draw_list, ale->data, ycenter, ac->yscale_fac, action_flag); break; case ALE_SCE: - draw_scene_channel(v2d, ads, ale->key_data, ycenter, ac->yscale_fac, action_flag); + draw_scene_channel( + draw_list, ads, ale->key_data, ycenter, ac->yscale_fac, action_flag); break; case ALE_OB: - draw_object_channel(v2d, ads, ale->key_data, ycenter, ac->yscale_fac, action_flag); + draw_object_channel( + draw_list, ads, ale->key_data, ycenter, ac->yscale_fac, action_flag); break; case ALE_ACT: - draw_action_channel(v2d, adt, ale->key_data, ycenter, ac->yscale_fac, action_flag); + draw_action_channel( + draw_list, adt, ale->key_data, ycenter, ac->yscale_fac, action_flag); break; case ALE_GROUP: - draw_agroup_channel(v2d, adt, ale->data, ycenter, ac->yscale_fac, action_flag); + draw_agroup_channel(draw_list, adt, ale->data, ycenter, ac->yscale_fac, action_flag); break; case ALE_FCURVE: - draw_fcurve_channel(v2d, adt, ale->key_data, ycenter, ac->yscale_fac, action_flag); + draw_fcurve_channel( + draw_list, adt, ale->key_data, ycenter, ac->yscale_fac, action_flag); break; case ALE_GPFRAME: - draw_gpl_channel(v2d, ads, ale->data, ycenter, ac->yscale_fac, action_flag); + draw_gpl_channel(draw_list, ads, ale->data, ycenter, ac->yscale_fac, action_flag); break; case ALE_MASKLAY: - draw_masklay_channel(v2d, ads, ale->data, ycenter, ac->yscale_fac, action_flag); + draw_masklay_channel(draw_list, ads, ale->data, ycenter, ac->yscale_fac, action_flag); break; } } } } + ED_keylist_draw_list_flush(draw_list, v2d); + ED_keylist_draw_list_free(draw_list); + /* free temporary channels used for drawing */ ANIM_animdata_freelist(&anim_data); } -- cgit v1.2.3 From bb487bc2bc82c02922722efb3ca1d2570d907ad9 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 11 Aug 2021 16:59:09 +0200 Subject: Fix T89984: Improve Icon previews reflective and transmissive materials. Before this commit rendering material icons the floor will is hidden. This reduces the readability of reflective/refractive materials. check patch for additional screenshots and notes. This patch will switch the floor material that uses ray visibility tricks to render a floor for reflective rays. Eevee uses screen space reflections that makes this a different problem. There is nothing else drawn in the scene in screen space so we need a different trick. Using math we convert a reflective ray to UV space and generate a world that projects a checker pattern to infinity. As now the floor is in the world it is being reflected via a cubemap. As the film is transparent the background (including the floor isn't rendered) In the future when Eevee supports vulkan raytracing we can re-evaluate and perhaps remove this approximation. We tried lightprobes but that wasn't able to do the trick. Using the compositor would lead to more memory usage (render layers and intermediate buffers) and slower performance. Solution has been validated with Simon Reviewed By: sybren, Severin Differential Revision: https://developer.blender.org/D11988 --- release/datafiles/preview.blend | Bin 1453009 -> 1471916 bytes source/blender/editors/render/render_preview.c | 92 +++++++++++++++++++++---- 2 files changed, 77 insertions(+), 15 deletions(-) diff --git a/release/datafiles/preview.blend b/release/datafiles/preview.blend index f92f68ad029..e342cb85158 100644 Binary files a/release/datafiles/preview.blend and b/release/datafiles/preview.blend differ diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index bd4c83c107e..95351de45f0 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -47,6 +47,7 @@ #include "DNA_collection_types.h" #include "DNA_light_types.h" #include "DNA_material_types.h" +#include "DNA_mesh_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -265,6 +266,11 @@ static const char *preview_collection_name(const ePreviewType pr_type) } } +static bool render_engine_supports_ray_visibility(const Scene *sce) +{ + return !STREQ(sce->r.engine, RE_engine_id_BLENDER_EEVEE); +} + static void switch_preview_collection_visibilty(ViewLayer *view_layer, const ePreviewType pr_type) { /* Set appropriate layer as visible. */ @@ -281,29 +287,60 @@ static void switch_preview_collection_visibilty(ViewLayer *view_layer, const ePr } } -static void switch_preview_floor_visibility(ViewLayer *view_layer, +static const char *preview_floor_material_name(const Scene *scene, + const ePreviewRenderMethod pr_method) +{ + if (pr_method == PR_ICON_RENDER && render_engine_supports_ray_visibility(scene)) { + return "FloorHidden"; + } + return "Floor"; +} + +static void switch_preview_floor_material(Main *pr_main, + Mesh *me, + const Scene *scene, + const ePreviewRenderMethod pr_method) +{ + if (me->totcol == 0) { + return; + } + + const char *material_name = preview_floor_material_name(scene, pr_method); + Material *mat = BLI_findstring(&pr_main->materials, material_name, offsetof(ID, name) + 2); + if (mat) { + me->mat[0] = mat; + } +} + +static void switch_preview_floor_visibility(Main *pr_main, + const Scene *scene, + ViewLayer *view_layer, const ePreviewRenderMethod pr_method) { /* Hide floor for icon renders. */ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (STREQ(base->object->id.name + 2, "Floor")) { + base->object->visibility_flag &= ~OB_HIDE_RENDER; if (pr_method == PR_ICON_RENDER) { - base->object->visibility_flag |= OB_HIDE_RENDER; + if (!render_engine_supports_ray_visibility(scene)) { + base->object->visibility_flag |= OB_HIDE_RENDER; + } } - else { - base->object->visibility_flag &= ~OB_HIDE_RENDER; + if (base->object->type == OB_MESH) { + switch_preview_floor_material(pr_main, base->object->data, scene, pr_method); } } } } -static void set_preview_visibility(Scene *scene, +static void set_preview_visibility(Main *pr_main, + Scene *scene, ViewLayer *view_layer, const ePreviewType pr_type, const ePreviewRenderMethod pr_method) { switch_preview_collection_visibilty(view_layer, pr_type); - switch_preview_floor_visibility(view_layer, pr_method); + switch_preview_floor_visibility(pr_main, scene, view_layer, pr_method); BKE_layer_collection_sync(scene, view_layer); } @@ -357,10 +394,31 @@ static ID *duplicate_ids(ID *id, const bool allow_failure) } } -static World *preview_get_world(Main *pr_main) +static const char *preview_world_name(const Scene *sce, + const ID_Type id_type, + const ePreviewRenderMethod pr_method) +{ + /* When rendering material icons the floor will not be shown in the output. Cycles will use a + * material trick to show the floor in the reflections, but hide the floor for camera rays. For + * Eevee we use a transparent world that has a projected grid. + * + * In the future when Eevee supports vulkan raytracing we can re-evaluate and perhaps remove this + * approximation. + */ + if (id_type == ID_MA && pr_method == PR_ICON_RENDER && + !render_engine_supports_ray_visibility(sce)) { + return "WorldFloor"; + } + return "World"; +} + +static World *preview_get_world(Main *pr_main, + const Scene *sce, + const ID_Type id_type, + const ePreviewRenderMethod pr_method) { World *result = NULL; - const char *world_name = "World"; + const char *world_name = preview_world_name(sce, id_type, pr_method); result = BLI_findstring(&pr_main->worlds, world_name, offsetof(ID, name) + 2); /* No world found return first world. */ @@ -380,9 +438,13 @@ static void preview_sync_exposure(World *dst, const World *src) dst->range = src->range; } -static World *preview_prepare_world(Main *pr_main, const World *world) +static World *preview_prepare_world(Main *pr_main, + const Scene *sce, + const World *world, + const ID_Type id_type, + const ePreviewRenderMethod pr_method) { - World *result = preview_get_world(pr_main); + World *result = preview_get_world(pr_main, sce, id_type, pr_method); if (world) { preview_sync_exposure(result, world); } @@ -436,7 +498,7 @@ static Scene *preview_prepare_scene( sce->r.cfra = scene->r.cfra; /* Setup the world. */ - sce->world = preview_prepare_world(pr_main, scene->world); + sce->world = preview_prepare_world(pr_main, sce, scene->world, id_type, sp->pr_method); if (id_type == ID_TE) { /* Texture is not actually rendered with engine, just set dummy value. */ @@ -458,7 +520,7 @@ static Scene *preview_prepare_scene( /* Use current scene world to light sphere. */ sce->world = preview_get_localized_world(sp, scene->world); } - else if (sce->world) { + else if (sce->world && sp->pr_method != PR_ICON_RENDER) { /* Use a default world color. Using the current * scene world can be slow if it has big textures. */ sce->world->use_nodes = false; @@ -472,7 +534,7 @@ static Scene *preview_prepare_scene( sp->pr_main == G_pr_main_grease_pencil) ? MA_SPHERE_A : mat->pr_type; - set_preview_visibility(sce, view_layer, preview_type, sp->pr_method); + set_preview_visibility(pr_main, sce, view_layer, preview_type, sp->pr_method); if (sp->pr_method != PR_ICON_RENDER) { if (mat->nodetree && sp->pr_method == PR_NODE_RENDER) { @@ -536,7 +598,7 @@ static Scene *preview_prepare_scene( BLI_addtail(&pr_main->lights, la); } - set_preview_visibility(sce, view_layer, MA_LAMP, sp->pr_method); + set_preview_visibility(pr_main, sce, view_layer, MA_LAMP, sp->pr_method); if (sce->world) { /* Only use lighting from the light. */ @@ -571,7 +633,7 @@ static Scene *preview_prepare_scene( BLI_addtail(&pr_main->worlds, wrld); } - set_preview_visibility(sce, view_layer, MA_SKY, sp->pr_method); + set_preview_visibility(pr_main, sce, view_layer, MA_SKY, sp->pr_method); sce->world = wrld; if (wrld && wrld->nodetree && sp->pr_method == PR_NODE_RENDER) { -- cgit v1.2.3 From cd1bb63159215396662ce49c8bc8d6d437114093 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Wed, 11 Aug 2021 13:55:58 -0700 Subject: BLF: Do Not Cache Unused Rendered Glyphs The loading of a font size or style renders bitmaps of the characters 0-255 and stores them in a cache. But glyphs 128-255 in this cache are not accessible. What used to be ansi high-bit characters are now multi- byte UTF-8 sequences. Therefore this patch reduces the glyph_ascii_table size to 128 and only caches characters 32-127, the visible portion of ASCII, which greatly reduces the time to load a font. See D12189 for more details. Differential Revision: https://developer.blender.org/D12189 Reviewed by Campbell Barton --- source/blender/blenfont/intern/blf_font.c | 3 ++- source/blender/blenfont/intern/blf_internal_types.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 6e2be4a8353..8e306730e3c 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -317,7 +317,8 @@ static GlyphBLF **blf_font_ensure_ascii_table(FontBLF *font, GlyphCacheBLF *gc) /* build ascii on demand */ if (glyph_ascii_table['0'] == NULL) { GlyphBLF *g; - for (uint i = 0; i < 256; i++) { + /* Skip control characters and just cache rendered glyphs for visible ASCII range. */ + for (uint i = 32; i < 128; i++) { g = blf_glyph_search(gc, i); if (!g) { FT_UInt glyph_index = FT_Get_Char_Index(font->face, i); diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 36bb8769306..6816c97321d 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -71,7 +71,7 @@ typedef struct GlyphCacheBLF { ListBase bucket[257]; /* fast ascii lookup */ - struct GlyphBLF *glyph_ascii_table[256]; + struct GlyphBLF *glyph_ascii_table[128]; /* texture array, to draw the glyphs. */ GPUTexture *texture; -- cgit v1.2.3 From 48c8f9fc9a34360b23e03aaae41cdbf0b4fa4424 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Wed, 11 Aug 2021 16:57:56 -0600 Subject: Fix: DNA struct alignment on 32 bit Some of the dna structs were not properly aligned for 32 bit builds causing issues for some of the 32 platforms Debian builds for. Reviewed By: sergey, brecht Differential Revision: https://developer.blender.org/D9389 --- source/blender/makesdna/DNA_gpencil_types.h | 1 + source/blender/makesdna/DNA_mesh_types.h | 1 + source/blender/makesdna/DNA_modifier_types.h | 25 +++++++++++++++++++++++++ source/blender/makesdna/DNA_object_types.h | 1 + source/blender/makesdna/DNA_sequence_types.h | 1 + 5 files changed, 29 insertions(+) diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 380d8ad1249..68bd2961f23 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -324,6 +324,7 @@ typedef struct bGPDstroke { struct bGPDcurve *editcurve; bGPDstroke_Runtime runtime; + void *_pad5; } bGPDstroke; /** #bGPDstroke.flag */ diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 932f4715298..97f14b2195d 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -231,6 +231,7 @@ typedef struct Mesh { * default and Face Sets can be used without affecting the color of the mesh. */ int face_sets_color_default; + void *_pad2; Mesh_Runtime runtime; } Mesh; diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index f66de378c35..1bebbc35747 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -136,6 +136,7 @@ typedef struct ModifierData { /* Runtime field which contains runtime data which is specific to a modifier type. */ void *runtime; + void *_pad1; } ModifierData; typedef enum { @@ -215,6 +216,7 @@ typedef struct LatticeModifierData { float strength; short flag; char _pad[2]; + void *_pad1; } LatticeModifierData; /* Lattice modifier flags. */ @@ -232,6 +234,7 @@ typedef struct CurveModifierData { short defaxis; short flag; char _pad[4]; + void *_pad1; } CurveModifierData; /* Curve modifier flags */ @@ -283,6 +286,7 @@ typedef struct MaskModifierData { /** Flags for various things. */ short flag; float threshold; + void *_pad1; } MaskModifierData; /* Mask Modifier -> mode */ @@ -373,6 +377,7 @@ typedef struct MirrorModifierData { float uv_offset[2]; float uv_offset_copy[2]; struct Object *mirror_ob; + void *_pad1; } MirrorModifierData; /* MirrorModifierData->flag */ @@ -451,6 +456,7 @@ typedef struct BevelModifierData { /** Curve info for the custom profile */ struct CurveProfile *custom_profile; + void *_pad2; } BevelModifierData; /* BevelModifierData->flags and BevelModifierData->lim_flags */ @@ -535,6 +541,7 @@ typedef struct FluidModifierData { float time; /** Domain, inflow, outflow, .... */ int type; + void *_pad1; } FluidModifierData; /* Fluid modifier flags */ @@ -680,6 +687,7 @@ typedef struct CastModifierData { /** MAX_VGROUP_NAME. */ char defgrp_name[64]; short flag, type; + void *_pad1; } CastModifierData; /* Cast modifier flags */ @@ -725,6 +733,7 @@ typedef struct WaveModifierData { float timeoffs, lifetime; char _pad1[4]; + void *_pad2; } WaveModifierData; /* WaveModifierData.flag */ @@ -797,6 +806,7 @@ typedef struct HookModifierData { float force; /** Optional vertexgroup name, MAX_VGROUP_NAME. */ char name[64]; + void *_pad1; } HookModifierData; typedef struct SoftbodyModifierData { @@ -1001,6 +1011,7 @@ typedef struct ParticleSystemModifierData { int totdmvert, totdmedge, totdmface; short flag; char _pad[2]; + void *_pad1; } ParticleSystemModifierData; typedef enum { @@ -1037,6 +1048,7 @@ typedef struct ParticleInstanceModifierData { char index_layer_name[64]; /** MAX_CUSTOMDATA_LAYER_NAME. */ char value_layer_name[64]; + void *_pad1; } ParticleInstanceModifierData; typedef enum { @@ -1057,6 +1069,7 @@ typedef struct ExplodeModifierData { float protect; /** MAX_CUSTOMDATA_LAYER_NAME. */ char uvname[64]; + void *_pad1; } ExplodeModifierData; typedef struct MultiresModifierData { @@ -1086,6 +1099,7 @@ typedef struct FluidsimModifierData { /** Definition is in DNA_object_fluidsim_types.h. */ struct FluidsimSettings *fss; + void *_pad1; } FluidsimModifierData; /* DEPRECATED, only used for versioning. */ @@ -1202,6 +1216,7 @@ typedef struct SimpleDeformModifierData { char deform_axis; char flag; + void *_pad1; } SimpleDeformModifierData; /* SimpleDeform->flag */ @@ -1310,6 +1325,7 @@ typedef struct ScrewModifierData { short flag; char axis; char _pad[5]; + void *_pad1; } ScrewModifierData; enum { @@ -1434,6 +1450,7 @@ typedef struct WarpModifierData { char flag; char falloff_type; char _pad[6]; + void *_pad1; } WarpModifierData; /* WarpModifierData->flag */ @@ -1497,6 +1514,7 @@ typedef struct WeightVGEditModifierData { /* Padding... */ char _pad0[4]; + void *_pad1; } WeightVGEditModifierData; /* WeightVGEdit flags. */ @@ -2064,6 +2082,7 @@ typedef struct DataTransferModifierData { char defgrp_name[64]; int flags; + void *_pad2; } DataTransferModifierData; /* DataTransferModifierData.flags */ @@ -2094,6 +2113,7 @@ typedef struct NormalEditModifierData { float mix_limit; float offset[3]; char _pad0[4]; + void *_pad1; } NormalEditModifierData; /* NormalEditModifierData.mode */ @@ -2154,6 +2174,7 @@ typedef struct MeshSeqCacheModifierData { float last_lookup_time; int _pad1; + void *_pad2; } MeshSeqCacheModifierData; /* MeshSeqCacheModifierData.read_flag */ @@ -2198,6 +2219,7 @@ typedef struct SurfaceDeformModifierData { float mat[4][4]; float strength; char defgrp_name[64]; + void *_pad1; } SurfaceDeformModifierData; /* Surface Deform modifier flags */ @@ -2259,6 +2281,7 @@ typedef struct NodesModifierData { /* Contains logged information from the last evaluation. This can be used to help the user to * debug a node tree. */ void *runtime_eval_log; + void *_pad1; } NodesModifierData; typedef struct MeshToVolumeModifierData { @@ -2286,6 +2309,7 @@ typedef struct MeshToVolumeModifierData { float density; char _pad2[4]; + void *_pad3; } MeshToVolumeModifierData; /* MeshToVolumeModifierData->resolution_mode */ @@ -2332,6 +2356,7 @@ typedef struct VolumeToMeshModifierData { /** MAX_NAME */ char grid_name[64]; + void *_pad1; } VolumeToMeshModifierData; /** VolumeToMeshModifierData->resolution_mode */ diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index e7091c78f71..c6d6334118f 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -433,6 +433,7 @@ typedef struct Object { ObjectLineArt lineart; /** Runtime evaluation data (keep last). */ + void *_pad9; Object_Runtime runtime; } Object; diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index af524ff4866..df18501d2ea 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -296,6 +296,7 @@ typedef struct Editing { int64_t disk_cache_timestamp; EditingRuntime runtime; + void *_pad1; } Editing; /* ************* Effect Variable Structs ********* */ -- cgit v1.2.3 From 45d100208e8fc07e65d4cad303fa353e85bf8747 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Wed, 11 Aug 2021 19:20:51 -0600 Subject: Makesdna: Fix detecting 32 bit padding issues. Makesdna fails to detect issues in 32 bit code that can only be resolved by adding a padding pointer. We never noticed since we ourselves no longer build for 32 bit, but debian's 32 bit builds got bitten by this A rather extensive explanation on why this is alignment requirement is there can be found in this comment: https://developer.blender.org/D9389#233034 Differential Revision: https://developer.blender.org/D12188 Reviewed by: sergey, campbellbarton --- source/blender/makesdna/intern/makesdna.c | 69 ++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index f2a75a60a44..8324db1a9c8 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -165,6 +165,10 @@ static char **names; static char **types; /** At `types_size[a]` is the size of type `a` on this systems bitness (32 or 64). */ static short *types_size_native; +/** Contains align requirements for a struct on 32 bit systems. */ +static short *types_align_32; +/** Contains align requirements for a struct on 64 bit systems. */ +static short *types_align_64; /** Contains sizes as they are calculated on 32 bit systems. */ static short *types_size_32; /** Contains sizes as they are calculated on 64 bit systems. */ @@ -406,6 +410,8 @@ static int add_type(const char *str, int size) types_size_native[index] = size; types_size_32[index] = size; types_size_64[index] = size; + types_align_32[index] = size; + types_align_64[index] = size; } return index; } @@ -419,7 +425,8 @@ static int add_type(const char *str, int size) types_size_native[types_len] = size; types_size_32[types_len] = size; types_size_64[types_len] = size; - + types_align_32[types_len] = size; + types_align_64[types_len] = size; if (types_len >= max_array_len) { printf("too many types\n"); return types_len - 1; @@ -966,7 +973,9 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char int size_native = 0; int size_32 = 0; int size_64 = 0; - bool has_pointer = false; + /* Sizes of the largest field in a struct. */ + int max_align_32 = 0; + int max_align_64 = 0; /* check all elements in struct */ for (int b = 0; b < structpoin[1]; b++, sp += 2) { @@ -995,7 +1004,6 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char /* is it a pointer or function pointer? */ if (cp[0] == '*' || cp[1] == '*') { - has_pointer = 1; /* has the name an extra length? (array) */ int mul = 1; if (cp[namelen - 1] == ']') { @@ -1042,6 +1050,8 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char size_native += sizeof(void *) * mul; size_32 += 4 * mul; size_64 += 8 * mul; + max_align_32 = MAX2(max_align_32, 4); + max_align_64 = MAX2(max_align_64, 8); } else if (cp[0] == '[') { /* parsing can cause names "var" and "[3]" @@ -1087,6 +1097,8 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char size_native += mul * types_size_native[type]; size_32 += mul * types_size_32[type]; size_64 += mul * types_size_64[type]; + max_align_32 = MAX2(max_align_32, types_align_32[type]); + max_align_64 = MAX2(max_align_64, types_align_64[type]); } else { size_native = 0; @@ -1103,16 +1115,42 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char types_size_native[structtype] = size_native; types_size_32[structtype] = size_32; types_size_64[structtype] = size_64; - /* Two ways to detect if a struct contains a pointer: - * has_pointer is set or size_native doesn't match any of 32/64bit lengths. */ - if (has_pointer || size_64 != size_native || size_32 != size_native) { - if (size_64 % 8) { + types_align_32[structtype] = max_align_32; + types_align_64[structtype] = max_align_64; + + /* Santiy check 1: alignment should never be 0. */ + BLI_assert(max_align_32); + BLI_assert(max_align_64); + + /* Santiy check 2: alignment should always be equal or smaller than the maximum + * size of a build in type which is 8 bytes (ie int64_t or double). */ + BLI_assert(max_align_32 <= 8); + BLI_assert(max_align_64 <= 8); + + if (size_32 % max_align_32) { + /* There is an one odd case where only the 32 bit struct has alignment issues + * and the 64 bit does not, that can only be fixed by adding a padding pointer + * to the struct to resolve the problem. */ + if ((size_64 % max_align_64 == 0) && (size_32 % max_align_32 == 4)) { fprintf(stderr, - "Sizeerror 8 in struct: %s (add %d bytes)\n", + "Sizeerror in 32 bit struct: %s (add paddding pointer)\n", + types[structtype]); + } + else { + fprintf(stderr, + "Sizeerror in 32 bit struct: %s (add %d bytes)\n", types[structtype], - size_64 % 8); - dna_error = 1; + max_align_32 - (size_32 % max_align_32)); } + dna_error = 1; + } + + if (size_64 % max_align_64) { + fprintf(stderr, + "Sizeerror in 64 bit struct: %s (add %d bytes)\n", + types[structtype], + max_align_64 - (size_64 % max_align_64)); + dna_error = 1; } if (size_native % 4 && !ELEM(size_native, 1, 2)) { @@ -1229,6 +1267,9 @@ static int make_structDNA(const char *base_directory, types_size_native = MEM_callocN(sizeof(short) * max_array_len, "types_size_native"); types_size_32 = MEM_callocN(sizeof(short) * max_array_len, "types_size_32"); types_size_64 = MEM_callocN(sizeof(short) * max_array_len, "types_size_64"); + types_align_32 = MEM_callocN(sizeof(short) * max_array_len, "types_size_32"); + types_align_64 = MEM_callocN(sizeof(short) * max_array_len, "types_size_64"); + structs = MEM_callocN(sizeof(short *) * max_array_len, "structs"); /* Build versioning data */ @@ -1317,7 +1358,11 @@ static int make_structDNA(const char *base_directory, sp += 2; /* ? num_types was elem? */ for (b = 0; b < num_types; b++, sp += 2) { - printf(" %s %s\n", types[sp[0]], names[sp[1]]); + printf(" %s %s allign32:%d, allign64:%d\n", + types[sp[0]], + names[sp[1]], + types_align_32[sp[0]], + types_align_64[sp[0]]); } } } @@ -1439,6 +1484,8 @@ static int make_structDNA(const char *base_directory, MEM_freeN(types_size_native); MEM_freeN(types_size_32); MEM_freeN(types_size_64); + MEM_freeN(types_align_32); + MEM_freeN(types_align_64); MEM_freeN(structs); BLI_memarena_free(mem_arena); -- cgit v1.2.3 From 4f61843a7e2fc7d92a630379c14cc87a6e892d6f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 12 Aug 2021 14:34:39 +1000 Subject: Cleanup: remove *.orig file from 6a9d7139f7d05e0c51827a3a4b862c0547dc0513 --- .../blender/blenkernel/intern/mesh_normals.cc.orig | 2217 -------------------- 1 file changed, 2217 deletions(-) delete mode 100644 source/blender/blenkernel/intern/mesh_normals.cc.orig diff --git a/source/blender/blenkernel/intern/mesh_normals.cc.orig b/source/blender/blenkernel/intern/mesh_normals.cc.orig deleted file mode 100644 index 18d384e8589..00000000000 --- a/source/blender/blenkernel/intern/mesh_normals.cc.orig +++ /dev/null @@ -1,2217 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup bke - * - * Mesh normal calculation functions. - * - * \see bmesh_mesh_normals.c for the equivalent #BMesh functionality. - */ - -#include - -#include "CLG_log.h" - -#include "MEM_guardedalloc.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BLI_alloca.h" -#include "BLI_bitmap.h" - -#include "BLI_linklist.h" -#include "BLI_linklist_stack.h" -#include "BLI_math.h" -#include "BLI_memarena.h" -#include "BLI_stack.h" -#include "BLI_task.h" -#include "BLI_utildefines.h" - -#include "BKE_customdata.h" -#include "BKE_editmesh_cache.h" -#include "BKE_global.h" -#include "BKE_mesh.h" - -<<<<<<< Updated upstream -#include "atomic_ops.h" - -// #define DEBUG_TIME -======= -#define DEBUG_TIME ->>>>>>> Stashed changes - -#ifdef DEBUG_TIME -# include "PIL_time.h" -# include "PIL_time_utildefines.h" -#endif - -static CLG_LogRef LOG = {"bke.mesh_normals"}; - -/* -------------------------------------------------------------------- */ -/** \name Private Utility Functions - * \{ */ - -/** - * A thread-safe version of #add_v3_v3 that uses a spin-lock. - * - * \note Avoid using this when the chance of contention is high. - */ -static void add_v3_v3_atomic(float r[3], const float a[3]) -{ -#define FLT_EQ_NONAN(_fa, _fb) (*((const uint32_t *)&_fa) == *((const uint32_t *)&_fb)) - - float virtual_lock = r[0]; - while (true) { - /* This loops until following conditions are met: - * - `r[0]` has same value as virtual_lock (i.e. it did not change since last try). - * - `r[0]` was not `FLT_MAX`, i.e. it was not locked by another thread. */ - const float test_lock = atomic_cas_float(&r[0], virtual_lock, FLT_MAX); - if (_ATOMIC_LIKELY(FLT_EQ_NONAN(test_lock, virtual_lock) && (test_lock != FLT_MAX))) { - break; - } - virtual_lock = test_lock; - } - virtual_lock += a[0]; - r[1] += a[1]; - r[2] += a[2]; - - /* Second atomic operation to 'release' - * our lock on that vector and set its first scalar value. */ - /* Note that we do not need to loop here, since we 'locked' `r[0]`, - * nobody should have changed it in the mean time. */ - virtual_lock = atomic_cas_float(&r[0], FLT_MAX, virtual_lock); - BLI_assert(virtual_lock == FLT_MAX); - -#undef FLT_EQ_NONAN -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Mesh Normal Calculation - * \{ */ - -void BKE_mesh_normals_tag_dirty(Mesh *mesh) -{ - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; -} - -/** - * Call when there are no polygons. - */ -static void mesh_calc_normals_vert_fallback(MVert *mverts, int numVerts) -{ - for (int i = 0; i < numVerts; i++) { - MVert *mv = &mverts[i]; - float no[3]; - - normalize_v3_v3(no, mv->co); - normal_float_to_short_v3(mv->no, no); - } -} - -/* TODO(Sybren): we can probably rename this to BKE_mesh_calc_normals_mapping(), - * and remove the function of the same name below, as that one doesn't seem to be - * called anywhere. */ -void BKE_mesh_calc_normals_mapping_simple(struct Mesh *mesh) -{ - const bool only_face_normals = CustomData_is_referenced_layer(&mesh->vdata, CD_MVERT); - - BKE_mesh_calc_normals_mapping_ex(mesh->mvert, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - nullptr, - mesh->mface, - mesh->totface, - nullptr, - nullptr, - only_face_normals); -} - -/* Calculate vertex and face normals, face normals are returned in *r_faceNors if non-nullptr - * and vertex normals are stored in actual mverts. - */ -void BKE_mesh_calc_normals_mapping(MVert *mverts, - int numVerts, - const MLoop *mloop, - const MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polyNors)[3], - const MFace *mfaces, - int numFaces, - const int *origIndexFace, - float (*r_faceNors)[3]) -{ - BKE_mesh_calc_normals_mapping_ex(mverts, - numVerts, - mloop, - mpolys, - numLoops, - numPolys, - r_polyNors, - mfaces, - numFaces, - origIndexFace, - r_faceNors, - false); -} -/* extended version of 'BKE_mesh_calc_normals_poly' with option not to calc vertex normals */ -void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, - int numVerts, - const MLoop *mloop, - const MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polyNors)[3], - const MFace *mfaces, - int numFaces, - const int *origIndexFace, - float (*r_faceNors)[3], - const bool only_face_normals) -{ - float(*pnors)[3] = r_polyNors, (*fnors)[3] = r_faceNors; - - if (numPolys == 0) { - if (only_face_normals == false) { - mesh_calc_normals_vert_fallback(mverts, numVerts); - } - return; - } - - /* if we are not calculating verts and no verts were passes then we have nothing to do */ - if ((only_face_normals == true) && (r_polyNors == nullptr) && (r_faceNors == nullptr)) { - CLOG_WARN(&LOG, "called with nothing to do"); - return; - } - - if (!pnors) { - pnors = (float(*)[3])MEM_calloc_arrayN((size_t)numPolys, sizeof(float[3]), __func__); - } - /* NO NEED TO ALLOC YET */ - /* if (!fnors) fnors = MEM_calloc_arrayN(numFaces, sizeof(float[3]), "face nors mesh.c"); */ - - if (only_face_normals == false) { - /* vertex normals are optional, they require some extra calculations, - * so make them optional */ - BKE_mesh_calc_normals_poly( - mverts, nullptr, numVerts, mloop, mpolys, numLoops, numPolys, pnors, false); - } - else { - /* only calc poly normals */ - const MPoly *mp = mpolys; - for (int i = 0; i < numPolys; i++, mp++) { - BKE_mesh_calc_poly_normal(mp, mloop + mp->loopstart, mverts, pnors[i]); - } - } - - if (origIndexFace && - /* fnors == r_faceNors */ /* NO NEED TO ALLOC YET */ - fnors != nullptr && - numFaces) { - const MFace *mf = mfaces; - for (int i = 0; i < numFaces; i++, mf++, origIndexFace++) { - if (*origIndexFace < numPolys) { - copy_v3_v3(fnors[i], pnors[*origIndexFace]); - } - else { - /* eek, we're not corresponding to polys */ - CLOG_ERROR(&LOG, "tessellation face indices are incorrect. normals may look bad."); - } - } - } - - if (pnors != r_polyNors) { - MEM_freeN(pnors); - } - /* if (fnors != r_faceNors) MEM_freeN(fnors); */ /* NO NEED TO ALLOC YET */ - - fnors = pnors = nullptr; -} - -struct MeshCalcNormalsData { - const MPoly *mpolys; - const MLoop *mloop; - MVert *mverts; - float (*pnors)[3]; - float (*vnors)[3]; -}; - -static void mesh_calc_normals_poly_cb(void *__restrict userdata, - const int pidx, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; - const MPoly *mp = &data->mpolys[pidx]; - - BKE_mesh_calc_poly_normal(mp, data->mloop + mp->loopstart, data->mverts, data->pnors[pidx]); -} - -static void mesh_calc_normals_poly_and_accum_cb(void *__restrict userdata, - const int pidx, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - const MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; - const MPoly *mp = &data->mpolys[pidx]; - const MLoop *ml = &data->mloop[mp->loopstart]; - const MVert *mverts = data->mverts; - float(*vnors)[3] = data->vnors; - - float pnor_temp[3]; - float *pnor = data->pnors ? data->pnors[pidx] : pnor_temp; - - const int i_end = mp->totloop - 1; - - /* Polygon Normal and edge-vector */ - /* inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors */ - { - zero_v3(pnor); - /* Newell's Method */ - const float *v_curr = mverts[ml[i_end].v].co; - for (int i_next = 0; i_next <= i_end; i_next++) { - const float *v_next = mverts[ml[i_next].v].co; - add_newell_cross_v3_v3v3(pnor, v_curr, v_next); - v_curr = v_next; - } - if (UNLIKELY(normalize_v3(pnor) == 0.0f)) { - pnor[2] = 1.0f; /* other axes set to 0.0 */ - } - } - - /* Accumulate angle weighted face normal into the vertex normal. */ - /* inline version of #accumulate_vertex_normals_poly_v3. */ - { - float edvec_prev[3], edvec_next[3], edvec_end[3]; - const float *v_curr = mverts[ml[i_end].v].co; - sub_v3_v3v3(edvec_prev, mverts[ml[i_end - 1].v].co, v_curr); - normalize_v3(edvec_prev); - copy_v3_v3(edvec_end, edvec_prev); - - for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) { - const float *v_next = mverts[ml[i_next].v].co; - - /* Skip an extra normalization by reusing the first calculated edge. */ - if (i_next != i_end) { - sub_v3_v3v3(edvec_next, v_curr, v_next); - normalize_v3(edvec_next); - } - else { - copy_v3_v3(edvec_next, edvec_end); - } - - /* Calculate angle between the two poly edges incident on this vertex. */ - const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next)); - const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac}; - - add_v3_v3_atomic(vnors[ml[i_curr].v], vnor_add); - v_curr = v_next; - copy_v3_v3(edvec_prev, edvec_next); - } - } -} - -static void mesh_calc_normals_poly_finalize_cb(void *__restrict userdata, - const int vidx, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; - - MVert *mv = &data->mverts[vidx]; - float *no = data->vnors[vidx]; - - if (UNLIKELY(normalize_v3(no) == 0.0f)) { - /* following Mesh convention; we use vertex coordinate itself for normal in this case */ - normalize_v3_v3(no, mv->co); - } - - normal_float_to_short_v3(mv->no, no); -} - -void BKE_mesh_calc_normals_poly(MVert *mverts, - float (*r_vertnors)[3], - int numVerts, - const MLoop *mloop, - const MPoly *mpolys, - int UNUSED(numLoops), - int numPolys, - float (*r_polynors)[3], - const bool only_face_normals) -{ - float(*pnors)[3] = r_polynors; - - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.min_iter_per_thread = 1024; - - if (only_face_normals) { - BLI_assert((pnors != nullptr) || (numPolys == 0)); - BLI_assert(r_vertnors == nullptr); - - MeshCalcNormalsData data; - data.mpolys = mpolys; - data.mloop = mloop; - data.mverts = mverts; - data.pnors = pnors; - - BLI_task_parallel_range(0, numPolys, &data, mesh_calc_normals_poly_cb, &settings); - return; - } - - float(*vnors)[3] = r_vertnors; - bool free_vnors = false; - - /* first go through and calculate normals for all the polys */ - if (vnors == nullptr) { - vnors = (float(*)[3])MEM_calloc_arrayN((size_t)numVerts, sizeof(*vnors), __func__); - free_vnors = true; - } - else { - memset(vnors, 0, sizeof(*vnors) * (size_t)numVerts); - } - - MeshCalcNormalsData data; - data.mpolys = mpolys; - data.mloop = mloop; - data.mverts = mverts; - data.pnors = pnors; - data.vnors = vnors; - - /* Compute poly normals (`pnors`), accumulating them into vertex normals (`vnors`). */ - BLI_task_parallel_range(0, numPolys, &data, mesh_calc_normals_poly_and_accum_cb, &settings); - - /* Normalize and validate computed vertex normals (`vnors`). */ - BLI_task_parallel_range(0, numVerts, &data, mesh_calc_normals_poly_finalize_cb, &settings); - - if (free_vnors) { - MEM_freeN(vnors); - } -} - -void BKE_mesh_ensure_normals(Mesh *mesh) -{ - if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) { - BKE_mesh_calc_normals(mesh); - } - BLI_assert((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) == 0); -} - -/** - * Called after calculating all modifiers. - */ -void BKE_mesh_ensure_normals_for_display(Mesh *mesh) -{ - switch ((eMeshWrapperType)mesh->runtime.wrapper_type) { - case ME_WRAPPER_TYPE_MDATA: - /* Run code below. */ - break; - case ME_WRAPPER_TYPE_BMESH: { - struct BMEditMesh *em = mesh->edit_mesh; - EditMeshData *emd = mesh->runtime.edit_data; - if (emd->vertexCos) { - BKE_editmesh_cache_ensure_vert_normals(em, emd); - BKE_editmesh_cache_ensure_poly_normals(em, emd); - } - return; - } - } - - float(*poly_nors)[3] = (float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL); - const bool do_vert_normals = (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) != 0; - const bool do_poly_normals = (mesh->runtime.cd_dirty_poly & CD_MASK_NORMAL || - poly_nors == nullptr); - - if (do_vert_normals || do_poly_normals) { - const bool do_add_poly_nors_cddata = (poly_nors == nullptr); - if (do_add_poly_nors_cddata) { - poly_nors = (float(*)[3])MEM_malloc_arrayN( - (size_t)mesh->totpoly, sizeof(*poly_nors), __func__); - } - - /* calculate poly/vert normals */ - BKE_mesh_calc_normals_poly(mesh->mvert, - nullptr, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - poly_nors, - !do_vert_normals); - - if (do_add_poly_nors_cddata) { - CustomData_add_layer(&mesh->pdata, CD_NORMAL, CD_ASSIGN, poly_nors, mesh->totpoly); - } - - mesh->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL; - mesh->runtime.cd_dirty_poly &= ~CD_MASK_NORMAL; - } -} - -/* Note that this does not update the CD_NORMAL layer, - * but does update the normals in the CD_MVERT layer. */ -void BKE_mesh_calc_normals(Mesh *mesh) -{ -#ifdef DEBUG_TIME - TIMEIT_START_AVERAGED(BKE_mesh_calc_normals); -#endif - BKE_mesh_calc_normals_poly(mesh->mvert, - nullptr, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - nullptr, - false); -#ifdef DEBUG_TIME - TIMEIT_END_AVERAGED(BKE_mesh_calc_normals); -#endif - mesh->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL; -} - -void BKE_mesh_calc_normals_looptri(MVert *mverts, - int numVerts, - const MLoop *mloop, - const MLoopTri *looptri, - int looptri_num, - float (*r_tri_nors)[3]) -{ - float(*tnorms)[3] = (float(*)[3])MEM_calloc_arrayN((size_t)numVerts, sizeof(*tnorms), "tnorms"); - float(*fnors)[3] = (r_tri_nors) ? r_tri_nors : - (float(*)[3])MEM_calloc_arrayN( - (size_t)looptri_num, sizeof(*fnors), "meshnormals"); - - if (!tnorms || !fnors) { - goto cleanup; - } - - for (int i = 0; i < looptri_num; i++) { - const MLoopTri *lt = &looptri[i]; - float *f_no = fnors[i]; - const uint vtri[3] = { - mloop[lt->tri[0]].v, - mloop[lt->tri[1]].v, - mloop[lt->tri[2]].v, - }; - - normal_tri_v3(f_no, mverts[vtri[0]].co, mverts[vtri[1]].co, mverts[vtri[2]].co); - - accumulate_vertex_normals_tri_v3(tnorms[vtri[0]], - tnorms[vtri[1]], - tnorms[vtri[2]], - f_no, - mverts[vtri[0]].co, - mverts[vtri[1]].co, - mverts[vtri[2]].co); - } - - /* following Mesh convention; we use vertex coordinate itself for normal in this case */ - for (int i = 0; i < numVerts; i++) { - MVert *mv = &mverts[i]; - float *no = tnorms[i]; - - if (UNLIKELY(normalize_v3(no) == 0.0f)) { - normalize_v3_v3(no, mv->co); - } - - normal_float_to_short_v3(mv->no, no); - } - -cleanup: - MEM_freeN(tnorms); - - if (fnors != r_tri_nors) { - MEM_freeN(fnors); - } -} - -void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, - const int numLoops, - const char data_type) -{ - if (!(lnors_spacearr->lspacearr && lnors_spacearr->loops_pool)) { - MemArena *mem; - - if (!lnors_spacearr->mem) { - lnors_spacearr->mem = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - } - mem = lnors_spacearr->mem; - lnors_spacearr->lspacearr = (MLoopNorSpace **)BLI_memarena_calloc( - mem, sizeof(MLoopNorSpace *) * (size_t)numLoops); - lnors_spacearr->loops_pool = (LinkNode *)BLI_memarena_alloc( - mem, sizeof(LinkNode) * (size_t)numLoops); - - lnors_spacearr->num_spaces = 0; - } - BLI_assert(ELEM(data_type, MLNOR_SPACEARR_BMLOOP_PTR, MLNOR_SPACEARR_LOOP_INDEX)); - lnors_spacearr->data_type = data_type; -} - -/** - * Utility for multi-threaded calculation that ensures - * `lnors_spacearr_tls` doesn't share memory with `lnors_spacearr` - * that would cause it not to be thread safe. - * - * \note This works as long as threads never operate on the same loops at once. - */ -void BKE_lnor_spacearr_tls_init(MLoopNorSpaceArray *lnors_spacearr, - MLoopNorSpaceArray *lnors_spacearr_tls) -{ - *lnors_spacearr_tls = *lnors_spacearr; - lnors_spacearr_tls->mem = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); -} - -/** - * Utility for multi-threaded calculation - * that merges `lnors_spacearr_tls` into `lnors_spacearr`. - */ -void BKE_lnor_spacearr_tls_join(MLoopNorSpaceArray *lnors_spacearr, - MLoopNorSpaceArray *lnors_spacearr_tls) -{ - BLI_assert(lnors_spacearr->data_type == lnors_spacearr_tls->data_type); - BLI_assert(lnors_spacearr->mem != lnors_spacearr_tls->mem); - lnors_spacearr->num_spaces += lnors_spacearr_tls->num_spaces; - BLI_memarena_merge(lnors_spacearr->mem, lnors_spacearr_tls->mem); - BLI_memarena_free(lnors_spacearr_tls->mem); - lnors_spacearr_tls->mem = nullptr; - BKE_lnor_spacearr_clear(lnors_spacearr_tls); -} - -void BKE_lnor_spacearr_clear(MLoopNorSpaceArray *lnors_spacearr) -{ - lnors_spacearr->num_spaces = 0; - lnors_spacearr->lspacearr = nullptr; - lnors_spacearr->loops_pool = nullptr; - if (lnors_spacearr->mem != nullptr) { - BLI_memarena_clear(lnors_spacearr->mem); - } -} - -void BKE_lnor_spacearr_free(MLoopNorSpaceArray *lnors_spacearr) -{ - lnors_spacearr->num_spaces = 0; - lnors_spacearr->lspacearr = nullptr; - lnors_spacearr->loops_pool = nullptr; - BLI_memarena_free(lnors_spacearr->mem); - lnors_spacearr->mem = nullptr; -} - -MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr) -{ - lnors_spacearr->num_spaces++; - return (MLoopNorSpace *)BLI_memarena_calloc(lnors_spacearr->mem, sizeof(MLoopNorSpace)); -} - -/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */ -#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f) - -/* Should only be called once. - * Beware, this modifies ref_vec and other_vec in place! - * In case no valid space can be generated, ref_alpha and ref_beta are set to zero - * (which means 'use auto lnors'). - */ -void BKE_lnor_space_define(MLoopNorSpace *lnor_space, - const float lnor[3], - float vec_ref[3], - float vec_other[3], - BLI_Stack *edge_vectors) -{ - const float pi2 = (float)M_PI * 2.0f; - float tvec[3], dtp; - const float dtp_ref = dot_v3v3(vec_ref, lnor); - const float dtp_other = dot_v3v3(vec_other, lnor); - - if (UNLIKELY(fabsf(dtp_ref) >= LNOR_SPACE_TRIGO_THRESHOLD || - fabsf(dtp_other) >= LNOR_SPACE_TRIGO_THRESHOLD)) { - /* If vec_ref or vec_other are too much aligned with lnor, we can't build lnor space, - * tag it as invalid and abort. */ - lnor_space->ref_alpha = lnor_space->ref_beta = 0.0f; - - if (edge_vectors) { - BLI_stack_clear(edge_vectors); - } - return; - } - - copy_v3_v3(lnor_space->vec_lnor, lnor); - - /* Compute ref alpha, average angle of all available edge vectors to lnor. */ - if (edge_vectors) { - float alpha = 0.0f; - int nbr = 0; - while (!BLI_stack_is_empty(edge_vectors)) { - const float *vec = (const float *)BLI_stack_peek(edge_vectors); - alpha += saacosf(dot_v3v3(vec, lnor)); - BLI_stack_discard(edge_vectors); - nbr++; - } - /* NOTE: In theory, this could be 'nbr > 2', - * but there is one case where we only have two edges for two loops: - * a smooth vertex with only two edges and two faces (our Monkey's nose has that, e.g.). - */ - BLI_assert(nbr >= 2); /* This piece of code shall only be called for more than one loop... */ - lnor_space->ref_alpha = alpha / (float)nbr; - } - else { - lnor_space->ref_alpha = (saacosf(dot_v3v3(vec_ref, lnor)) + - saacosf(dot_v3v3(vec_other, lnor))) / - 2.0f; - } - - /* Project vec_ref on lnor's ortho plane. */ - mul_v3_v3fl(tvec, lnor, dtp_ref); - sub_v3_v3(vec_ref, tvec); - normalize_v3_v3(lnor_space->vec_ref, vec_ref); - - cross_v3_v3v3(tvec, lnor, lnor_space->vec_ref); - normalize_v3_v3(lnor_space->vec_ortho, tvec); - - /* Project vec_other on lnor's ortho plane. */ - mul_v3_v3fl(tvec, lnor, dtp_other); - sub_v3_v3(vec_other, tvec); - normalize_v3(vec_other); - - /* Beta is angle between ref_vec and other_vec, around lnor. */ - dtp = dot_v3v3(lnor_space->vec_ref, vec_other); - if (LIKELY(dtp < LNOR_SPACE_TRIGO_THRESHOLD)) { - const float beta = saacos(dtp); - lnor_space->ref_beta = (dot_v3v3(lnor_space->vec_ortho, vec_other) < 0.0f) ? pi2 - beta : beta; - } - else { - lnor_space->ref_beta = pi2; - } -} - -/** - * Add a new given loop to given lnor_space. - * Depending on \a lnor_space->data_type, we expect \a bm_loop to be a pointer to BMLoop struct - * (in case of BMLOOP_PTR), or nullptr (in case of LOOP_INDEX), loop index is then stored in - * pointer. If \a is_single is set, the BMLoop or loop index is directly stored in \a - * lnor_space->loops pointer (since there is only one loop in this fan), else it is added to the - * linked list of loops in the fan. - */ -void BKE_lnor_space_add_loop(MLoopNorSpaceArray *lnors_spacearr, - MLoopNorSpace *lnor_space, - const int ml_index, - void *bm_loop, - const bool is_single) -{ - BLI_assert((lnors_spacearr->data_type == MLNOR_SPACEARR_LOOP_INDEX && bm_loop == nullptr) || - (lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR && bm_loop != nullptr)); - - lnors_spacearr->lspacearr[ml_index] = lnor_space; - if (bm_loop == nullptr) { - bm_loop = POINTER_FROM_INT(ml_index); - } - if (is_single) { - BLI_assert(lnor_space->loops == nullptr); - lnor_space->flags |= MLNOR_SPACE_IS_SINGLE; - lnor_space->loops = (LinkNode *)bm_loop; - } - else { - BLI_assert((lnor_space->flags & MLNOR_SPACE_IS_SINGLE) == 0); - BLI_linklist_prepend_nlink(&lnor_space->loops, bm_loop, &lnors_spacearr->loops_pool[ml_index]); - } -} - -MINLINE float unit_short_to_float(const short val) -{ - return (float)val / (float)SHRT_MAX; -} - -MINLINE short unit_float_to_short(const float val) -{ - /* Rounding... */ - return (short)floorf(val * (float)SHRT_MAX + 0.5f); -} - -void BKE_lnor_space_custom_data_to_normal(MLoopNorSpace *lnor_space, - const short clnor_data[2], - float r_custom_lnor[3]) -{ - /* NOP custom normal data or invalid lnor space, return. */ - if (clnor_data[0] == 0 || lnor_space->ref_alpha == 0.0f || lnor_space->ref_beta == 0.0f) { - copy_v3_v3(r_custom_lnor, lnor_space->vec_lnor); - return; - } - - { - /* TODO: Check whether using #sincosf() gives any noticeable benefit - * (could not even get it working under linux though)! */ - const float pi2 = (float)(M_PI * 2.0); - const float alphafac = unit_short_to_float(clnor_data[0]); - const float alpha = (alphafac > 0.0f ? lnor_space->ref_alpha : pi2 - lnor_space->ref_alpha) * - alphafac; - const float betafac = unit_short_to_float(clnor_data[1]); - - mul_v3_v3fl(r_custom_lnor, lnor_space->vec_lnor, cosf(alpha)); - - if (betafac == 0.0f) { - madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ref, sinf(alpha)); - } - else { - const float sinalpha = sinf(alpha); - const float beta = (betafac > 0.0f ? lnor_space->ref_beta : pi2 - lnor_space->ref_beta) * - betafac; - madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ref, sinalpha * cosf(beta)); - madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ortho, sinalpha * sinf(beta)); - } - } -} - -void BKE_lnor_space_custom_normal_to_data(MLoopNorSpace *lnor_space, - const float custom_lnor[3], - short r_clnor_data[2]) -{ - /* We use nullptr vector as NOP custom normal (can be simpler than giving auto-computed `lnor`). - */ - if (is_zero_v3(custom_lnor) || compare_v3v3(lnor_space->vec_lnor, custom_lnor, 1e-4f)) { - r_clnor_data[0] = r_clnor_data[1] = 0; - return; - } - - { - const float pi2 = (float)(M_PI * 2.0); - const float cos_alpha = dot_v3v3(lnor_space->vec_lnor, custom_lnor); - float vec[3], cos_beta; - float alpha; - - alpha = saacosf(cos_alpha); - if (alpha > lnor_space->ref_alpha) { - /* Note we could stick to [0, pi] range here, - * but makes decoding more complex, not worth it. */ - r_clnor_data[0] = unit_float_to_short(-(pi2 - alpha) / (pi2 - lnor_space->ref_alpha)); - } - else { - r_clnor_data[0] = unit_float_to_short(alpha / lnor_space->ref_alpha); - } - - /* Project custom lnor on (vec_ref, vec_ortho) plane. */ - mul_v3_v3fl(vec, lnor_space->vec_lnor, -cos_alpha); - add_v3_v3(vec, custom_lnor); - normalize_v3(vec); - - cos_beta = dot_v3v3(lnor_space->vec_ref, vec); - - if (cos_beta < LNOR_SPACE_TRIGO_THRESHOLD) { - float beta = saacosf(cos_beta); - if (dot_v3v3(lnor_space->vec_ortho, vec) < 0.0f) { - beta = pi2 - beta; - } - - if (beta > lnor_space->ref_beta) { - r_clnor_data[1] = unit_float_to_short(-(pi2 - beta) / (pi2 - lnor_space->ref_beta)); - } - else { - r_clnor_data[1] = unit_float_to_short(beta / lnor_space->ref_beta); - } - } - else { - r_clnor_data[1] = 0; - } - } -} - -#define LOOP_SPLIT_TASK_BLOCK_SIZE 1024 - -struct LoopSplitTaskData { - /* Specific to each instance (each task). */ - - /** We have to create those outside of tasks, since #MemArena is not thread-safe. */ - MLoopNorSpace *lnor_space; - float (*lnor)[3]; - const MLoop *ml_curr; - const MLoop *ml_prev; - int ml_curr_index; - int ml_prev_index; - /** Also used a flag to switch between single or fan process! */ - const int *e2l_prev; - int mp_index; - - /** This one is special, it's owned and managed by worker tasks, - * avoid to have to create it for each fan! */ - BLI_Stack *edge_vectors; - - char pad_c; -}; - -struct LoopSplitTaskDataCommon { - /* Read/write. - * Note we do not need to protect it, though, since two different tasks will *always* affect - * different elements in the arrays. */ - MLoopNorSpaceArray *lnors_spacearr; - float (*loopnors)[3]; - short (*clnors_data)[2]; - - /* Read-only. */ - const MVert *mverts; - const MEdge *medges; - const MLoop *mloops; - const MPoly *mpolys; - int (*edge_to_loops)[2]; - int *loop_to_poly; - const float (*polynors)[3]; - - int numEdges; - int numLoops; - int numPolys; -}; - -#define INDEX_UNSET INT_MIN -#define INDEX_INVALID -1 -/* See comment about edge_to_loops below. */ -#define IS_EDGE_SHARP(_e2l) (ELEM((_e2l)[1], INDEX_UNSET, INDEX_INVALID)) - -static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data, - const bool check_angle, - const float split_angle, - const bool do_sharp_edges_tag) -{ - const MVert *mverts = data->mverts; - const MEdge *medges = data->medges; - const MLoop *mloops = data->mloops; - - const MPoly *mpolys = data->mpolys; - - const int numEdges = data->numEdges; - const int numPolys = data->numPolys; - - float(*loopnors)[3] = data->loopnors; /* NOTE: loopnors may be nullptr here. */ - const float(*polynors)[3] = data->polynors; - - int(*edge_to_loops)[2] = data->edge_to_loops; - int *loop_to_poly = data->loop_to_poly; - - BLI_bitmap *sharp_edges = do_sharp_edges_tag ? BLI_BITMAP_NEW(numEdges, __func__) : nullptr; - - const MPoly *mp; - int mp_index; - - const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f; - - for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) { - const MLoop *ml_curr; - int *e2l; - int ml_curr_index = mp->loopstart; - const int ml_last_index = (ml_curr_index + mp->totloop) - 1; - - ml_curr = &mloops[ml_curr_index]; - - for (; ml_curr_index <= ml_last_index; ml_curr++, ml_curr_index++) { - e2l = edge_to_loops[ml_curr->e]; - - loop_to_poly[ml_curr_index] = mp_index; - - /* Pre-populate all loop normals as if their verts were all-smooth, - * this way we don't have to compute those later! - */ - if (loopnors) { - normal_short_to_float_v3(loopnors[ml_curr_index], mverts[ml_curr->v].no); - } - - /* Check whether current edge might be smooth or sharp */ - if ((e2l[0] | e2l[1]) == 0) { - /* 'Empty' edge until now, set e2l[0] (and e2l[1] to INDEX_UNSET to tag it as unset). */ - e2l[0] = ml_curr_index; - /* We have to check this here too, else we might miss some flat faces!!! */ - e2l[1] = (mp->flag & ME_SMOOTH) ? INDEX_UNSET : INDEX_INVALID; - } - else if (e2l[1] == INDEX_UNSET) { - const bool is_angle_sharp = (check_angle && - dot_v3v3(polynors[loop_to_poly[e2l[0]]], polynors[mp_index]) < - split_angle_cos); - - /* Second loop using this edge, time to test its sharpness. - * An edge is sharp if it is tagged as such, or its face is not smooth, - * or both poly have opposed (flipped) normals, i.e. both loops on the same edge share the - * same vertex, or angle between both its polys' normals is above split_angle value. - */ - if (!(mp->flag & ME_SMOOTH) || (medges[ml_curr->e].flag & ME_SHARP) || - ml_curr->v == mloops[e2l[0]].v || is_angle_sharp) { - /* NOTE: we are sure that loop != 0 here ;). */ - e2l[1] = INDEX_INVALID; - - /* We want to avoid tagging edges as sharp when it is already defined as such by - * other causes than angle threshold... */ - if (do_sharp_edges_tag && is_angle_sharp) { - BLI_BITMAP_SET(sharp_edges, ml_curr->e, true); - } - } - else { - e2l[1] = ml_curr_index; - } - } - else if (!IS_EDGE_SHARP(e2l)) { - /* More than two loops using this edge, tag as sharp if not yet done. */ - e2l[1] = INDEX_INVALID; - - /* We want to avoid tagging edges as sharp when it is already defined as such by - * other causes than angle threshold... */ - if (do_sharp_edges_tag) { - BLI_BITMAP_SET(sharp_edges, ml_curr->e, false); - } - } - /* Else, edge is already 'disqualified' (i.e. sharp)! */ - } - } - - /* If requested, do actual tagging of edges as sharp in another loop. */ - if (do_sharp_edges_tag) { - MEdge *me; - int me_index; - for (me = (MEdge *)medges, me_index = 0; me_index < numEdges; me++, me_index++) { - if (BLI_BITMAP_TEST(sharp_edges, me_index)) { - me->flag |= ME_SHARP; - } - } - - MEM_freeN(sharp_edges); - } -} - -/** - * Define sharp edges as needed to mimic 'autosmooth' from angle threshold. - * - * Used when defining an empty custom loop normals data layer, - * to keep same shading as with auto-smooth! - */ -void BKE_edges_sharp_from_angle_set(const struct MVert *mverts, - const int UNUSED(numVerts), - struct MEdge *medges, - const int numEdges, - struct MLoop *mloops, - const int numLoops, - struct MPoly *mpolys, - const float (*polynors)[3], - const int numPolys, - const float split_angle) -{ - if (split_angle >= (float)M_PI) { - /* Nothing to do! */ - return; - } - - /* Mapping edge -> loops. See BKE_mesh_normals_loop_split() for details. */ - int(*edge_to_loops)[2] = (int(*)[2])MEM_calloc_arrayN( - (size_t)numEdges, sizeof(*edge_to_loops), __func__); - - /* Simple mapping from a loop to its polygon index. */ - int *loop_to_poly = (int *)MEM_malloc_arrayN((size_t)numLoops, sizeof(*loop_to_poly), __func__); - - LoopSplitTaskDataCommon common_data = {}; - common_data.mverts = mverts; - common_data.medges = medges; - common_data.mloops = mloops; - common_data.mpolys = mpolys; - common_data.edge_to_loops = edge_to_loops; - common_data.loop_to_poly = loop_to_poly; - common_data.polynors = polynors; - common_data.numEdges = numEdges; - common_data.numPolys = numPolys; - - mesh_edges_sharp_tag(&common_data, true, split_angle, true); - - MEM_freeN(edge_to_loops); - MEM_freeN(loop_to_poly); -} - -void BKE_mesh_loop_manifold_fan_around_vert_next(const MLoop *mloops, - const MPoly *mpolys, - const int *loop_to_poly, - const int *e2lfan_curr, - const uint mv_pivot_index, - const MLoop **r_mlfan_curr, - int *r_mlfan_curr_index, - int *r_mlfan_vert_index, - int *r_mpfan_curr_index) -{ - const MLoop *mlfan_next; - const MPoly *mpfan_next; - - /* Warning! This is rather complex! - * We have to find our next edge around the vertex (fan mode). - * First we find the next loop, which is either previous or next to mlfan_curr_index, depending - * whether both loops using current edge are in the same direction or not, and whether - * mlfan_curr_index actually uses the vertex we are fanning around! - * mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one - * (i.e. not the future mlfan_curr)... - */ - *r_mlfan_curr_index = (e2lfan_curr[0] == *r_mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0]; - *r_mpfan_curr_index = loop_to_poly[*r_mlfan_curr_index]; - - BLI_assert(*r_mlfan_curr_index >= 0); - BLI_assert(*r_mpfan_curr_index >= 0); - - mlfan_next = &mloops[*r_mlfan_curr_index]; - mpfan_next = &mpolys[*r_mpfan_curr_index]; - if (((*r_mlfan_curr)->v == mlfan_next->v && (*r_mlfan_curr)->v == mv_pivot_index) || - ((*r_mlfan_curr)->v != mlfan_next->v && (*r_mlfan_curr)->v != mv_pivot_index)) { - /* We need the previous loop, but current one is our vertex's loop. */ - *r_mlfan_vert_index = *r_mlfan_curr_index; - if (--(*r_mlfan_curr_index) < mpfan_next->loopstart) { - *r_mlfan_curr_index = mpfan_next->loopstart + mpfan_next->totloop - 1; - } - } - else { - /* We need the next loop, which is also our vertex's loop. */ - if (++(*r_mlfan_curr_index) >= mpfan_next->loopstart + mpfan_next->totloop) { - *r_mlfan_curr_index = mpfan_next->loopstart; - } - *r_mlfan_vert_index = *r_mlfan_curr_index; - } - *r_mlfan_curr = &mloops[*r_mlfan_curr_index]; - /* And now we are back in sync, mlfan_curr_index is the index of mlfan_curr! Pff! */ -} - -static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data) -{ - MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; - const short(*clnors_data)[2] = common_data->clnors_data; - - const MVert *mverts = common_data->mverts; - const MEdge *medges = common_data->medges; - const float(*polynors)[3] = common_data->polynors; - - MLoopNorSpace *lnor_space = data->lnor_space; - float(*lnor)[3] = data->lnor; - const MLoop *ml_curr = data->ml_curr; - const MLoop *ml_prev = data->ml_prev; - const int ml_curr_index = data->ml_curr_index; -#if 0 /* Not needed for 'single' loop. */ - const int ml_prev_index = data->ml_prev_index; - const int *e2l_prev = data->e2l_prev; -#endif - const int mp_index = data->mp_index; - - /* Simple case (both edges around that vertex are sharp in current polygon), - * this loop just takes its poly normal. - */ - copy_v3_v3(*lnor, polynors[mp_index]); - -#if 0 - printf("BASIC: handling loop %d / edge %d / vert %d / poly %d\n", - ml_curr_index, - ml_curr->e, - ml_curr->v, - mp_index); -#endif - - /* If needed, generate this (simple!) lnor space. */ - if (lnors_spacearr) { - float vec_curr[3], vec_prev[3]; - - const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ - const MVert *mv_pivot = &mverts[mv_pivot_index]; - const MEdge *me_curr = &medges[ml_curr->e]; - const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] : - &mverts[me_curr->v1]; - const MEdge *me_prev = &medges[ml_prev->e]; - const MVert *mv_3 = (me_prev->v1 == mv_pivot_index) ? &mverts[me_prev->v2] : - &mverts[me_prev->v1]; - - sub_v3_v3v3(vec_curr, mv_2->co, mv_pivot->co); - normalize_v3(vec_curr); - sub_v3_v3v3(vec_prev, mv_3->co, mv_pivot->co); - normalize_v3(vec_prev); - - BKE_lnor_space_define(lnor_space, *lnor, vec_curr, vec_prev, nullptr); - /* We know there is only one loop in this space, - * no need to create a linklist in this case... */ - BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, ml_curr_index, nullptr, true); - - if (clnors_data) { - BKE_lnor_space_custom_data_to_normal(lnor_space, clnors_data[ml_curr_index], *lnor); - } - } -} - -static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data) -{ - MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; - float(*loopnors)[3] = common_data->loopnors; - short(*clnors_data)[2] = common_data->clnors_data; - - const MVert *mverts = common_data->mverts; - const MEdge *medges = common_data->medges; - const MLoop *mloops = common_data->mloops; - const MPoly *mpolys = common_data->mpolys; - const int(*edge_to_loops)[2] = common_data->edge_to_loops; - const int *loop_to_poly = common_data->loop_to_poly; - const float(*polynors)[3] = common_data->polynors; - - MLoopNorSpace *lnor_space = data->lnor_space; -#if 0 /* Not needed for 'fan' loops. */ - float(*lnor)[3] = data->lnor; -#endif - const MLoop *ml_curr = data->ml_curr; - const MLoop *ml_prev = data->ml_prev; - const int ml_curr_index = data->ml_curr_index; - const int ml_prev_index = data->ml_prev_index; - const int mp_index = data->mp_index; - const int *e2l_prev = data->e2l_prev; - - BLI_Stack *edge_vectors = data->edge_vectors; - - /* Gah... We have to fan around current vertex, until we find the other non-smooth edge, - * and accumulate face normals into the vertex! - * Note in case this vertex has only one sharp edges, this is a waste because the normal is the - * same as the vertex normal, but I do not see any easy way to detect that (would need to count - * number of sharp edges per vertex, I doubt the additional memory usage would be worth it, - * especially as it should not be a common case in real-life meshes anyway). - */ - const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ - const MVert *mv_pivot = &mverts[mv_pivot_index]; - - /* ml_curr would be mlfan_prev if we needed that one. */ - const MEdge *me_org = &medges[ml_curr->e]; - - const int *e2lfan_curr; - float vec_curr[3], vec_prev[3], vec_org[3]; - const MLoop *mlfan_curr; - float lnor[3] = {0.0f, 0.0f, 0.0f}; - /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ - int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index; - - /* We validate clnors data on the fly - cheapest way to do! */ - int clnors_avg[2] = {0, 0}; - short(*clnor_ref)[2] = nullptr; - int clnors_nbr = 0; - bool clnors_invalid = false; - - /* Temp loop normal stack. */ - BLI_SMALLSTACK_DECLARE(normal, float *); - /* Temp clnors stack. */ - BLI_SMALLSTACK_DECLARE(clnors, short *); - - e2lfan_curr = e2l_prev; - mlfan_curr = ml_prev; - mlfan_curr_index = ml_prev_index; - mlfan_vert_index = ml_curr_index; - mpfan_curr_index = mp_index; - - BLI_assert(mlfan_curr_index >= 0); - BLI_assert(mlfan_vert_index >= 0); - BLI_assert(mpfan_curr_index >= 0); - - /* Only need to compute previous edge's vector once, then we can just reuse old current one! */ - { - const MVert *mv_2 = (me_org->v1 == mv_pivot_index) ? &mverts[me_org->v2] : &mverts[me_org->v1]; - - sub_v3_v3v3(vec_org, mv_2->co, mv_pivot->co); - normalize_v3(vec_org); - copy_v3_v3(vec_prev, vec_org); - - if (lnors_spacearr) { - BLI_stack_push(edge_vectors, vec_org); - } - } - - // printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); - - while (true) { - const MEdge *me_curr = &medges[mlfan_curr->e]; - /* Compute edge vectors. - * NOTE: We could pre-compute those into an array, in the first iteration, instead of computing - * them twice (or more) here. However, time gained is not worth memory and time lost, - * given the fact that this code should not be called that much in real-life meshes... - */ - { - const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] : - &mverts[me_curr->v1]; - - sub_v3_v3v3(vec_curr, mv_2->co, mv_pivot->co); - normalize_v3(vec_curr); - } - - // printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); - - { - /* Code similar to accumulate_vertex_normals_poly_v3. */ - /* Calculate angle between the two poly edges incident on this vertex. */ - const float fac = saacos(dot_v3v3(vec_curr, vec_prev)); - /* Accumulate */ - madd_v3_v3fl(lnor, polynors[mpfan_curr_index], fac); - - if (clnors_data) { - /* Accumulate all clnors, if they are not all equal we have to fix that! */ - short(*clnor)[2] = &clnors_data[mlfan_vert_index]; - if (clnors_nbr) { - clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] || (*clnor_ref)[1] != (*clnor)[1]); - } - else { - clnor_ref = clnor; - } - clnors_avg[0] += (*clnor)[0]; - clnors_avg[1] += (*clnor)[1]; - clnors_nbr++; - /* We store here a pointer to all custom lnors processed. */ - BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor); - } - } - - /* We store here a pointer to all loop-normals processed. */ - BLI_SMALLSTACK_PUSH(normal, (float *)(loopnors[mlfan_vert_index])); - - if (lnors_spacearr) { - /* Assign current lnor space to current 'vertex' loop. */ - BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, mlfan_vert_index, nullptr, false); - if (me_curr != me_org) { - /* We store here all edges-normalized vectors processed. */ - BLI_stack_push(edge_vectors, vec_curr); - } - } - - if (IS_EDGE_SHARP(e2lfan_curr) || (me_curr == me_org)) { - /* Current edge is sharp and we have finished with this fan of faces around this vert, - * or this vert is smooth, and we have completed a full turn around it. */ - // printf("FAN: Finished!\n"); - break; - } - - copy_v3_v3(vec_prev, vec_curr); - - /* Find next loop of the smooth fan. */ - BKE_mesh_loop_manifold_fan_around_vert_next(mloops, - mpolys, - loop_to_poly, - e2lfan_curr, - mv_pivot_index, - &mlfan_curr, - &mlfan_curr_index, - &mlfan_vert_index, - &mpfan_curr_index); - - e2lfan_curr = edge_to_loops[mlfan_curr->e]; - } - - { - float lnor_len = normalize_v3(lnor); - - /* If we are generating lnor spacearr, we can now define the one for this fan, - * and optionally compute final lnor from custom data too! - */ - if (lnors_spacearr) { - if (UNLIKELY(lnor_len == 0.0f)) { - /* Use vertex normal as fallback! */ - copy_v3_v3(lnor, loopnors[mlfan_vert_index]); - lnor_len = 1.0f; - } - - BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_curr, edge_vectors); - - if (clnors_data) { - if (clnors_invalid) { - short *clnor; - - clnors_avg[0] /= clnors_nbr; - clnors_avg[1] /= clnors_nbr; - /* Fix/update all clnors of this fan with computed average value. */ - if (G.debug & G_DEBUG) { - printf("Invalid clnors in this fan!\n"); - } - while ((clnor = (short *)BLI_SMALLSTACK_POP(clnors))) { - // print_v2("org clnor", clnor); - clnor[0] = (short)clnors_avg[0]; - clnor[1] = (short)clnors_avg[1]; - } - // print_v2("new clnors", clnors_avg); - } - /* Extra bonus: since small-stack is local to this function, - * no more need to empty it at all cost! */ - - BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor); - } - } - - /* In case we get a zero normal here, just use vertex normal already set! */ - if (LIKELY(lnor_len != 0.0f)) { - /* Copy back the final computed normal into all related loop-normals. */ - float *nor; - - while ((nor = (float *)BLI_SMALLSTACK_POP(normal))) { - copy_v3_v3(nor, lnor); - } - } - /* Extra bonus: since small-stack is local to this function, - * no more need to empty it at all cost! */ - } -} - -static void loop_split_worker_do(LoopSplitTaskDataCommon *common_data, - LoopSplitTaskData *data, - BLI_Stack *edge_vectors) -{ - BLI_assert(data->ml_curr); - if (data->e2l_prev) { - BLI_assert((edge_vectors == nullptr) || BLI_stack_is_empty(edge_vectors)); - data->edge_vectors = edge_vectors; - split_loop_nor_fan_do(common_data, data); - } - else { - /* No need for edge_vectors for 'single' case! */ - split_loop_nor_single_do(common_data, data); - } -} - -static void loop_split_worker(TaskPool *__restrict pool, void *taskdata) -{ - LoopSplitTaskDataCommon *common_data = (LoopSplitTaskDataCommon *)BLI_task_pool_user_data(pool); - LoopSplitTaskData *data = (LoopSplitTaskData *)taskdata; - - /* Temp edge vectors stack, only used when computing lnor spacearr. */ - BLI_Stack *edge_vectors = common_data->lnors_spacearr ? - BLI_stack_new(sizeof(float[3]), __func__) : - nullptr; - -#ifdef DEBUG_TIME - TIMEIT_START_AVERAGED(loop_split_worker); -#endif - - for (int i = 0; i < LOOP_SPLIT_TASK_BLOCK_SIZE; i++, data++) { - /* A nullptr ml_curr is used to tag ended data! */ - if (data->ml_curr == nullptr) { - break; - } - - loop_split_worker_do(common_data, data, edge_vectors); - } - - if (edge_vectors) { - BLI_stack_free(edge_vectors); - } - -#ifdef DEBUG_TIME - TIMEIT_END_AVERAGED(loop_split_worker); -#endif -} - -/** - * Check whether given loop is part of an unknown-so-far cyclic smooth fan, or not. - * Needed because cyclic smooth fans have no obvious 'entry point', - * and yet we need to walk them once, and only once. - */ -static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops, - const MPoly *mpolys, - const int (*edge_to_loops)[2], - const int *loop_to_poly, - const int *e2l_prev, - BLI_bitmap *skip_loops, - const MLoop *ml_curr, - const MLoop *ml_prev, - const int ml_curr_index, - const int ml_prev_index, - const int mp_curr_index) -{ - const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ - const int *e2lfan_curr; - const MLoop *mlfan_curr; - /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ - int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index; - - e2lfan_curr = e2l_prev; - if (IS_EDGE_SHARP(e2lfan_curr)) { - /* Sharp loop, so not a cyclic smooth fan... */ - return false; - } - - mlfan_curr = ml_prev; - mlfan_curr_index = ml_prev_index; - mlfan_vert_index = ml_curr_index; - mpfan_curr_index = mp_curr_index; - - BLI_assert(mlfan_curr_index >= 0); - BLI_assert(mlfan_vert_index >= 0); - BLI_assert(mpfan_curr_index >= 0); - - BLI_assert(!BLI_BITMAP_TEST(skip_loops, mlfan_vert_index)); - BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index); - - while (true) { - /* Find next loop of the smooth fan. */ - BKE_mesh_loop_manifold_fan_around_vert_next(mloops, - mpolys, - loop_to_poly, - e2lfan_curr, - mv_pivot_index, - &mlfan_curr, - &mlfan_curr_index, - &mlfan_vert_index, - &mpfan_curr_index); - - e2lfan_curr = edge_to_loops[mlfan_curr->e]; - - if (IS_EDGE_SHARP(e2lfan_curr)) { - /* Sharp loop/edge, so not a cyclic smooth fan... */ - return false; - } - /* Smooth loop/edge... */ - if (BLI_BITMAP_TEST(skip_loops, mlfan_vert_index)) { - if (mlfan_vert_index == ml_curr_index) { - /* We walked around a whole cyclic smooth fan without finding any already-processed loop, - * means we can use initial ml_curr/ml_prev edge as start for this smooth fan. */ - return true; - } - /* ... already checked in some previous looping, we can abort. */ - return false; - } - - /* ... we can skip it in future, and keep checking the smooth fan. */ - BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index); - } -} - -static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common_data) -{ - MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; - float(*loopnors)[3] = common_data->loopnors; - - const MLoop *mloops = common_data->mloops; - const MPoly *mpolys = common_data->mpolys; - const int *loop_to_poly = common_data->loop_to_poly; - const int(*edge_to_loops)[2] = common_data->edge_to_loops; - const int numLoops = common_data->numLoops; - const int numPolys = common_data->numPolys; - - const MPoly *mp; - int mp_index; - - const MLoop *ml_curr; - const MLoop *ml_prev; - int ml_curr_index; - int ml_prev_index; - - BLI_bitmap *skip_loops = BLI_BITMAP_NEW(numLoops, __func__); - - LoopSplitTaskData *data_buff = nullptr; - int data_idx = 0; - - /* Temp edge vectors stack, only used when computing lnor spacearr - * (and we are not multi-threading). */ - BLI_Stack *edge_vectors = nullptr; - -#ifdef DEBUG_TIME - TIMEIT_START_AVERAGED(loop_split_generator); -#endif - - if (!pool) { - if (lnors_spacearr) { - edge_vectors = BLI_stack_new(sizeof(float[3]), __func__); - } - } - - /* We now know edges that can be smoothed (with their vector, and their two loops), - * and edges that will be hard! Now, time to generate the normals. - */ - for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) { - float(*lnors)[3]; - const int ml_last_index = (mp->loopstart + mp->totloop) - 1; - ml_curr_index = mp->loopstart; - ml_prev_index = ml_last_index; - - ml_curr = &mloops[ml_curr_index]; - ml_prev = &mloops[ml_prev_index]; - lnors = &loopnors[ml_curr_index]; - - for (; ml_curr_index <= ml_last_index; ml_curr++, ml_curr_index++, lnors++) { - const int *e2l_curr = edge_to_loops[ml_curr->e]; - const int *e2l_prev = edge_to_loops[ml_prev->e]; - -#if 0 - printf("Checking loop %d / edge %u / vert %u (sharp edge: %d, skiploop: %d)...", - ml_curr_index, - ml_curr->e, - ml_curr->v, - IS_EDGE_SHARP(e2l_curr), - BLI_BITMAP_TEST_BOOL(skip_loops, ml_curr_index)); -#endif - - /* A smooth edge, we have to check for cyclic smooth fan case. - * If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge - * as 'entry point', otherwise we can skip it. */ - - /* NOTE: In theory, we could make #loop_split_generator_check_cyclic_smooth_fan() store - * mlfan_vert_index'es and edge indexes in two stacks, to avoid having to fan again around - * the vert during actual computation of `clnor` & `clnorspace`. - * However, this would complicate the code, add more memory usage, and despite its logical - * complexity, #loop_manifold_fan_around_vert_next() is quite cheap in term of CPU cycles, - * so really think it's not worth it. */ - if (!IS_EDGE_SHARP(e2l_curr) && (BLI_BITMAP_TEST(skip_loops, ml_curr_index) || - !loop_split_generator_check_cyclic_smooth_fan(mloops, - mpolys, - edge_to_loops, - loop_to_poly, - e2l_prev, - skip_loops, - ml_curr, - ml_prev, - ml_curr_index, - ml_prev_index, - mp_index))) { - // printf("SKIPPING!\n"); - } - else { - LoopSplitTaskData *data, data_local; - - // printf("PROCESSING!\n"); - - if (pool) { - if (data_idx == 0) { - data_buff = (LoopSplitTaskData *)MEM_calloc_arrayN( - LOOP_SPLIT_TASK_BLOCK_SIZE, sizeof(*data_buff), __func__); - } - data = &data_buff[data_idx]; - } - else { - data = &data_local; - memset(data, 0, sizeof(*data)); - } - - if (IS_EDGE_SHARP(e2l_curr) && IS_EDGE_SHARP(e2l_prev)) { - data->lnor = lnors; - data->ml_curr = ml_curr; - data->ml_prev = ml_prev; - data->ml_curr_index = ml_curr_index; -#if 0 /* Not needed for 'single' loop. */ - data->ml_prev_index = ml_prev_index; - data->e2l_prev = nullptr; /* Tag as 'single' task. */ -#endif - data->mp_index = mp_index; - if (lnors_spacearr) { - data->lnor_space = BKE_lnor_space_create(lnors_spacearr); - } - } - /* We *do not need* to check/tag loops as already computed! - * Due to the fact a loop only links to one of its two edges, - * a same fan *will never be walked more than once!* - * Since we consider edges having neighbor polys with inverted - * (flipped) normals as sharp, we are sure that no fan will be skipped, - * even only considering the case (sharp curr_edge, smooth prev_edge), - * and not the alternative (smooth curr_edge, sharp prev_edge). - * All this due/thanks to link between normals and loop ordering (i.e. winding). - */ - else { -#if 0 /* Not needed for 'fan' loops. */ - data->lnor = lnors; -#endif - data->ml_curr = ml_curr; - data->ml_prev = ml_prev; - data->ml_curr_index = ml_curr_index; - data->ml_prev_index = ml_prev_index; - data->e2l_prev = e2l_prev; /* Also tag as 'fan' task. */ - data->mp_index = mp_index; - if (lnors_spacearr) { - data->lnor_space = BKE_lnor_space_create(lnors_spacearr); - } - } - - if (pool) { - data_idx++; - if (data_idx == LOOP_SPLIT_TASK_BLOCK_SIZE) { - BLI_task_pool_push(pool, loop_split_worker, data_buff, true, nullptr); - data_idx = 0; - } - } - else { - loop_split_worker_do(common_data, data, edge_vectors); - } - } - - ml_prev = ml_curr; - ml_prev_index = ml_curr_index; - } - } - - /* Last block of data... Since it is calloc'ed and we use first nullptr item as stopper, - * everything is fine. */ - if (pool && data_idx) { - BLI_task_pool_push(pool, loop_split_worker, data_buff, true, nullptr); - } - - if (edge_vectors) { - BLI_stack_free(edge_vectors); - } - MEM_freeN(skip_loops); - -#ifdef DEBUG_TIME - TIMEIT_END_AVERAGED(loop_split_generator); -#endif -} - -/** - * Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals'). - * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry - * (splitting edges). - */ -void BKE_mesh_normals_loop_split(const MVert *mverts, - const int UNUSED(numVerts), - MEdge *medges, - const int numEdges, - MLoop *mloops, - float (*r_loopnors)[3], - const int numLoops, - MPoly *mpolys, - const float (*polynors)[3], - const int numPolys, - const bool use_split_normals, - const float split_angle, - MLoopNorSpaceArray *r_lnors_spacearr, - short (*clnors_data)[2], - int *r_loop_to_poly) -{ - /* For now this is not supported. - * If we do not use split normals, we do not generate anything fancy! */ - BLI_assert(use_split_normals || !(r_lnors_spacearr)); - - if (!use_split_normals) { - /* In this case, we simply fill lnors with vnors (or fnors for flat faces), quite simple! - * Note this is done here to keep some logic and consistency in this quite complex code, - * since we may want to use lnors even when mesh's 'autosmooth' is disabled - * (see e.g. mesh mapping code). - * As usual, we could handle that on case-by-case basis, - * but simpler to keep it well confined here. - */ - int mp_index; - - for (mp_index = 0; mp_index < numPolys; mp_index++) { - MPoly *mp = &mpolys[mp_index]; - int ml_index = mp->loopstart; - const int ml_index_end = ml_index + mp->totloop; - const bool is_poly_flat = ((mp->flag & ME_SMOOTH) == 0); - - for (; ml_index < ml_index_end; ml_index++) { - if (r_loop_to_poly) { - r_loop_to_poly[ml_index] = mp_index; - } - if (is_poly_flat) { - copy_v3_v3(r_loopnors[ml_index], polynors[mp_index]); - } - else { - normal_short_to_float_v3(r_loopnors[ml_index], mverts[mloops[ml_index].v].no); - } - } - } - return; - } - - /** - * Mapping edge -> loops. - * If that edge is used by more than two loops (polys), - * it is always sharp (and tagged as such, see below). - * We also use the second loop index as a kind of flag: - * - * - smooth edge: > 0. - * - sharp edge: < 0 (INDEX_INVALID || INDEX_UNSET). - * - unset: INDEX_UNSET. - * - * Note that currently we only have two values for second loop of sharp edges. - * However, if needed, we can store the negated value of loop index instead of INDEX_INVALID - * to retrieve the real value later in code). - * Note also that loose edges always have both values set to 0! */ - int(*edge_to_loops)[2] = (int(*)[2])MEM_calloc_arrayN( - (size_t)numEdges, sizeof(*edge_to_loops), __func__); - - /* Simple mapping from a loop to its polygon index. */ - int *loop_to_poly = r_loop_to_poly ? r_loop_to_poly : - (int *)MEM_malloc_arrayN( - (size_t)numLoops, sizeof(*loop_to_poly), __func__); - - /* When using custom loop normals, disable the angle feature! */ - const bool check_angle = (split_angle < (float)M_PI) && (clnors_data == nullptr); - - MLoopNorSpaceArray _lnors_spacearr = {nullptr}; - -#ifdef DEBUG_TIME - TIMEIT_START_AVERAGED(BKE_mesh_normals_loop_split); -#endif - - if (!r_lnors_spacearr && clnors_data) { - /* We need to compute lnor spacearr if some custom lnor data are given to us! */ - r_lnors_spacearr = &_lnors_spacearr; - } - if (r_lnors_spacearr) { - BKE_lnor_spacearr_init(r_lnors_spacearr, numLoops, MLNOR_SPACEARR_LOOP_INDEX); - } - - /* Init data common to all tasks. */ - LoopSplitTaskDataCommon common_data; - common_data.lnors_spacearr = r_lnors_spacearr; - common_data.loopnors = r_loopnors; - common_data.clnors_data = clnors_data; - common_data.mverts = mverts; - common_data.medges = medges; - common_data.mloops = mloops; - common_data.mpolys = mpolys; - common_data.edge_to_loops = edge_to_loops; - common_data.loop_to_poly = loop_to_poly; - common_data.polynors = polynors; - common_data.numEdges = numEdges; - common_data.numLoops = numLoops; - common_data.numPolys = numPolys; - - /* This first loop check which edges are actually smooth, and compute edge vectors. */ - mesh_edges_sharp_tag(&common_data, check_angle, split_angle, false); - - if (numLoops < LOOP_SPLIT_TASK_BLOCK_SIZE * 8) { - /* Not enough loops to be worth the whole threading overhead... */ - loop_split_generator(nullptr, &common_data); - } - else { - TaskPool *task_pool = BLI_task_pool_create(&common_data, TASK_PRIORITY_HIGH); - - loop_split_generator(task_pool, &common_data); - - BLI_task_pool_work_and_wait(task_pool); - - BLI_task_pool_free(task_pool); - } - - MEM_freeN(edge_to_loops); - if (!r_loop_to_poly) { - MEM_freeN(loop_to_poly); - } - - if (r_lnors_spacearr) { - if (r_lnors_spacearr == &_lnors_spacearr) { - BKE_lnor_spacearr_free(r_lnors_spacearr); - } - } - -#ifdef DEBUG_TIME - TIMEIT_END_AVERAGED(BKE_mesh_normals_loop_split); -#endif -} - -#undef INDEX_UNSET -#undef INDEX_INVALID -#undef IS_EDGE_SHARP - -/** - * Compute internal representation of given custom normals (as an array of float[2]). - * It also makes sure the mesh matches those custom normals, by setting sharp edges flag as needed - * to get a same custom lnor for all loops sharing a same smooth fan. - * If use_vertices if true, r_custom_loopnors is assumed to be per-vertex, not per-loop - * (this allows to set whole vert's normals at once, useful in some cases). - * r_custom_loopnors is expected to have normalized normals, or zero ones, - * in which case they will be replaced by default loop/vertex normal. - */ -static void mesh_normals_loop_custom_set(const MVert *mverts, - const int numVerts, - MEdge *medges, - const int numEdges, - MLoop *mloops, - float (*r_custom_loopnors)[3], - const int numLoops, - MPoly *mpolys, - const float (*polynors)[3], - const int numPolys, - short (*r_clnors_data)[2], - const bool use_vertices) -{ - /* We *may* make that poor BKE_mesh_normals_loop_split() even more complex by making it handling - * that feature too, would probably be more efficient in absolute. - * However, this function *is not* performance-critical, since it is mostly expected to be called - * by io addons when importing custom normals, and modifier - * (and perhaps from some editing tools later?). - * So better to keep some simplicity here, and just call BKE_mesh_normals_loop_split() twice! - */ - MLoopNorSpaceArray lnors_spacearr = {nullptr}; - BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)numLoops, __func__); - float(*lnors)[3] = (float(*)[3])MEM_calloc_arrayN((size_t)numLoops, sizeof(*lnors), __func__); - int *loop_to_poly = (int *)MEM_malloc_arrayN((size_t)numLoops, sizeof(int), __func__); - /* In this case we always consider split nors as ON, - * and do not want to use angle to define smooth fans! */ - const bool use_split_normals = true; - const float split_angle = (float)M_PI; - - BLI_SMALLSTACK_DECLARE(clnors_data, short *); - - /* Compute current lnor spacearr. */ - BKE_mesh_normals_loop_split(mverts, - numVerts, - medges, - numEdges, - mloops, - lnors, - numLoops, - mpolys, - polynors, - numPolys, - use_split_normals, - split_angle, - &lnors_spacearr, - nullptr, - loop_to_poly); - - /* Set all given zero vectors to their default value. */ - if (use_vertices) { - for (int i = 0; i < numVerts; i++) { - if (is_zero_v3(r_custom_loopnors[i])) { - normal_short_to_float_v3(r_custom_loopnors[i], mverts[i].no); - } - } - } - else { - for (int i = 0; i < numLoops; i++) { - if (is_zero_v3(r_custom_loopnors[i])) { - copy_v3_v3(r_custom_loopnors[i], lnors[i]); - } - } - } - - BLI_assert(lnors_spacearr.data_type == MLNOR_SPACEARR_LOOP_INDEX); - - /* Now, check each current smooth fan (one lnor space per smooth fan!), - * and if all its matching custom lnors are not (enough) equal, add sharp edges as needed. - * This way, next time we run BKE_mesh_normals_loop_split(), we'll get lnor spacearr/smooth fans - * matching given custom lnors. - * Note this code *will never* unsharp edges! And quite obviously, - * when we set custom normals per vertices, running this is absolutely useless. - */ - if (!use_vertices) { - for (int i = 0; i < numLoops; i++) { - if (!lnors_spacearr.lspacearr[i]) { - /* This should not happen in theory, but in some rare case (probably ugly geometry) - * we can get some nullptr loopspacearr at this point. :/ - * Maybe we should set those loops' edges as sharp? - */ - BLI_BITMAP_ENABLE(done_loops, i); - if (G.debug & G_DEBUG) { - printf("WARNING! Getting invalid nullptr loop space for loop %d!\n", i); - } - continue; - } - - if (!BLI_BITMAP_TEST(done_loops, i)) { - /* Notes: - * * In case of mono-loop smooth fan, we have nothing to do. - * * Loops in this linklist are ordered (in reversed order compared to how they were - * discovered by BKE_mesh_normals_loop_split(), but this is not a problem). - * Which means if we find a mismatching clnor, - * we know all remaining loops will have to be in a new, different smooth fan/lnor space. - * * In smooth fan case, we compare each clnor against a ref one, - * to avoid small differences adding up into a real big one in the end! - */ - if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { - BLI_BITMAP_ENABLE(done_loops, i); - continue; - } - - LinkNode *loops = lnors_spacearr.lspacearr[i]->loops; - MLoop *prev_ml = nullptr; - const float *org_nor = nullptr; - - while (loops) { - const int lidx = POINTER_AS_INT(loops->link); - MLoop *ml = &mloops[lidx]; - const int nidx = lidx; - float *nor = r_custom_loopnors[nidx]; - - if (!org_nor) { - org_nor = nor; - } - else if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) { - /* Current normal differs too much from org one, we have to tag the edge between - * previous loop's face and current's one as sharp. - * We know those two loops do not point to the same edge, - * since we do not allow reversed winding in a same smooth fan. - */ - const MPoly *mp = &mpolys[loop_to_poly[lidx]]; - const MLoop *mlp = - &mloops[(lidx == mp->loopstart) ? mp->loopstart + mp->totloop - 1 : lidx - 1]; - medges[(prev_ml->e == mlp->e) ? prev_ml->e : ml->e].flag |= ME_SHARP; - - org_nor = nor; - } - - prev_ml = ml; - loops = loops->next; - BLI_BITMAP_ENABLE(done_loops, lidx); - } - - /* We also have to check between last and first loops, - * otherwise we may miss some sharp edges here! - * This is just a simplified version of above while loop. - * See T45984. */ - loops = lnors_spacearr.lspacearr[i]->loops; - if (loops && org_nor) { - const int lidx = POINTER_AS_INT(loops->link); - MLoop *ml = &mloops[lidx]; - const int nidx = lidx; - float *nor = r_custom_loopnors[nidx]; - - if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) { - const MPoly *mp = &mpolys[loop_to_poly[lidx]]; - const MLoop *mlp = - &mloops[(lidx == mp->loopstart) ? mp->loopstart + mp->totloop - 1 : lidx - 1]; - medges[(prev_ml->e == mlp->e) ? prev_ml->e : ml->e].flag |= ME_SHARP; - } - } - } - } - - /* And now, recompute our new auto lnors and lnor spacearr! */ - BKE_lnor_spacearr_clear(&lnors_spacearr); - BKE_mesh_normals_loop_split(mverts, - numVerts, - medges, - numEdges, - mloops, - lnors, - numLoops, - mpolys, - polynors, - numPolys, - use_split_normals, - split_angle, - &lnors_spacearr, - nullptr, - loop_to_poly); - } - else { - BLI_bitmap_set_all(done_loops, true, (size_t)numLoops); - } - - /* And we just have to convert plain object-space custom normals to our - * lnor space-encoded ones. */ - for (int i = 0; i < numLoops; i++) { - if (!lnors_spacearr.lspacearr[i]) { - BLI_BITMAP_DISABLE(done_loops, i); - if (G.debug & G_DEBUG) { - printf("WARNING! Still getting invalid nullptr loop space in second loop for loop %d!\n", - i); - } - continue; - } - - if (BLI_BITMAP_TEST_BOOL(done_loops, i)) { - /* Note we accumulate and average all custom normals in current smooth fan, - * to avoid getting different clnors data (tiny differences in plain custom normals can - * give rather huge differences in computed 2D factors). - */ - LinkNode *loops = lnors_spacearr.lspacearr[i]->loops; - if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { - BLI_assert(POINTER_AS_INT(loops) == i); - const int nidx = use_vertices ? (int)mloops[i].v : i; - float *nor = r_custom_loopnors[nidx]; - - BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], nor, r_clnors_data[i]); - BLI_BITMAP_DISABLE(done_loops, i); - } - else { - int nbr_nors = 0; - float avg_nor[3]; - short clnor_data_tmp[2], *clnor_data; - - zero_v3(avg_nor); - while (loops) { - const int lidx = POINTER_AS_INT(loops->link); - const int nidx = use_vertices ? (int)mloops[lidx].v : lidx; - float *nor = r_custom_loopnors[nidx]; - - nbr_nors++; - add_v3_v3(avg_nor, nor); - BLI_SMALLSTACK_PUSH(clnors_data, (short *)r_clnors_data[lidx]); - - loops = loops->next; - BLI_BITMAP_DISABLE(done_loops, lidx); - } - - mul_v3_fl(avg_nor, 1.0f / (float)nbr_nors); - BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], avg_nor, clnor_data_tmp); - - while ((clnor_data = (short *)BLI_SMALLSTACK_POP(clnors_data))) { - clnor_data[0] = clnor_data_tmp[0]; - clnor_data[1] = clnor_data_tmp[1]; - } - } - } - } - - MEM_freeN(lnors); - MEM_freeN(loop_to_poly); - MEM_freeN(done_loops); - BKE_lnor_spacearr_free(&lnors_spacearr); -} - -void BKE_mesh_normals_loop_custom_set(const MVert *mverts, - const int numVerts, - MEdge *medges, - const int numEdges, - MLoop *mloops, - float (*r_custom_loopnors)[3], - const int numLoops, - MPoly *mpolys, - const float (*polynors)[3], - const int numPolys, - short (*r_clnors_data)[2]) -{ - mesh_normals_loop_custom_set(mverts, - numVerts, - medges, - numEdges, - mloops, - r_custom_loopnors, - numLoops, - mpolys, - polynors, - numPolys, - r_clnors_data, - false); -} - -void BKE_mesh_normals_loop_custom_from_vertices_set(const MVert *mverts, - float (*r_custom_vertnors)[3], - const int numVerts, - MEdge *medges, - const int numEdges, - MLoop *mloops, - const int numLoops, - MPoly *mpolys, - const float (*polynors)[3], - const int numPolys, - short (*r_clnors_data)[2]) -{ - mesh_normals_loop_custom_set(mverts, - numVerts, - medges, - numEdges, - mloops, - r_custom_vertnors, - numLoops, - mpolys, - polynors, - numPolys, - r_clnors_data, - true); -} - -static void mesh_set_custom_normals(Mesh *mesh, float (*r_custom_nors)[3], const bool use_vertices) -{ - short(*clnors)[2]; - const int numloops = mesh->totloop; - - clnors = (short(*)[2])CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); - if (clnors != nullptr) { - memset(clnors, 0, sizeof(*clnors) * (size_t)numloops); - } - else { - clnors = (short(*)[2])CustomData_add_layer( - &mesh->ldata, CD_CUSTOMLOOPNORMAL, CD_CALLOC, nullptr, numloops); - } - - float(*polynors)[3] = (float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL); - bool free_polynors = false; - if (polynors == nullptr) { - polynors = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (size_t)mesh->totpoly, __func__); - BKE_mesh_calc_normals_poly(mesh->mvert, - nullptr, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - polynors, - false); - free_polynors = true; - } - - mesh_normals_loop_custom_set(mesh->mvert, - mesh->totvert, - mesh->medge, - mesh->totedge, - mesh->mloop, - r_custom_nors, - mesh->totloop, - mesh->mpoly, - polynors, - mesh->totpoly, - clnors, - use_vertices); - - if (free_polynors) { - MEM_freeN(polynors); - } -} - -/** - * Higher level functions hiding most of the code needed around call to - * #BKE_mesh_normals_loop_custom_set(). - * - * \param r_custom_loopnors: is not const, since code will replace zero_v3 normals there - * with automatically computed vectors. - */ -void BKE_mesh_set_custom_normals(Mesh *mesh, float (*r_custom_loopnors)[3]) -{ - mesh_set_custom_normals(mesh, r_custom_loopnors, false); -} - -/** - * Higher level functions hiding most of the code needed around call to - * #BKE_mesh_normals_loop_custom_from_vertices_set(). - * - * \param r_custom_vertnors: is not const, since code will replace zero_v3 normals there - * with automatically computed vectors. - */ -void BKE_mesh_set_custom_normals_from_vertices(Mesh *mesh, float (*r_custom_vertnors)[3]) -{ - mesh_set_custom_normals(mesh, r_custom_vertnors, true); -} - -/** - * Computes average per-vertex normals from given custom loop normals. - * - * \param clnors: The computed custom loop normals. - * \param r_vert_clnors: The (already allocated) array where to store averaged per-vertex normals. - */ -void BKE_mesh_normals_loop_to_vertex(const int numVerts, - const MLoop *mloops, - const int numLoops, - const float (*clnors)[3], - float (*r_vert_clnors)[3]) -{ - int *vert_loops_nbr = (int *)MEM_calloc_arrayN( - (size_t)numVerts, sizeof(*vert_loops_nbr), __func__); - - copy_vn_fl((float *)r_vert_clnors, 3 * numVerts, 0.0f); - - int i; - const MLoop *ml; - for (i = 0, ml = mloops; i < numLoops; i++, ml++) { - const uint v = ml->v; - - add_v3_v3(r_vert_clnors[v], clnors[i]); - vert_loops_nbr[v]++; - } - - for (i = 0; i < numVerts; i++) { - mul_v3_fl(r_vert_clnors[i], 1.0f / (float)vert_loops_nbr[i]); - } - - MEM_freeN(vert_loops_nbr); -} - -#undef LNOR_SPACE_TRIGO_THRESHOLD - -/** \} */ -- cgit v1.2.3 From 1ef275963d1cfa257de184f38a2abb04a5df3ac7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 12 Aug 2021 14:34:41 +1000 Subject: Cleanup: use C++ style comments for disabled code --- intern/cycles/blender/blender_util.h | 2 +- intern/guardedalloc/intern/mallocn_guarded_impl.c | 2 +- source/blender/blenkernel/intern/DerivedMesh.cc | 2 +- source/blender/blenkernel/intern/curve.c | 2 +- source/blender/blenkernel/intern/customdata.c | 2 +- source/blender/blenkernel/intern/image.c | 2 +- source/blender/blenkernel/intern/mask_rasterize.c | 4 +- source/blender/blenkernel/intern/mball.c | 2 +- source/blender/blenkernel/intern/mesh.c | 4 +- source/blender/blenkernel/intern/mesh_convert.c | 4 +- source/blender/blenkernel/intern/mesh_normals.cc | 4 +- source/blender/blenkernel/intern/ocean.c | 10 ++-- source/blender/blenkernel/intern/ocean_spectrum.c | 2 +- source/blender/blenkernel/intern/softbody.c | 4 +- source/blender/datatoc/datatoc.c | 2 +- source/blender/editors/gpencil/gpencil_utils.c | 2 +- source/blender/editors/interface/interface_align.c | 2 +- source/blender/editors/interface/interface_draw.c | 4 +- source/blender/editors/object/object_vgroup.c | 2 +- source/blender/editors/space_file/file_ops.c | 2 +- source/blender/editors/space_graph/graph_buttons.c | 2 +- .../blender/editors/space_outliner/outliner_edit.c | 2 +- source/blender/editors/transform/transform_mode.c | 4 +- .../editors/transform/transform_mode_edge_slide.c | 4 +- .../blender/imbuf/intern/openexr/openexr_api.cpp | 2 +- source/blender/io/collada/AnimationImporter.cpp | 2 +- source/blender/makesrna/intern/rna_access.c | 2 +- source/blender/makesrna/intern/rna_armature.c | 8 +-- source/blender/makesrna/intern/rna_mesh.c | 2 +- source/blender/makesrna/intern/rna_nodetree.c | 8 +-- source/blender/makesrna/intern/rna_particle.c | 10 ++-- source/blender/makesrna/intern/rna_speaker.c | 70 +++++++++++++++------- source/blender/makesrna/intern/rna_ui.c | 4 +- source/blender/makesrna/intern/rna_wm.c | 28 +++++---- source/blender/makesrna/intern/rna_wm_api.c | 2 +- source/blender/makesrna/intern/rna_wm_gizmo.c | 22 +++---- source/blender/makesrna/intern/rna_world.c | 2 +- source/blender/python/intern/bpy_rna.c | 2 +- source/blender/python/intern/bpy_rna_array.c | 2 +- .../blender/windowmanager/intern/wm_files_link.c | 4 +- 40 files changed, 134 insertions(+), 108 deletions(-) diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 2b2188b023d..82da3512269 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -145,7 +145,7 @@ static inline void curvemapping_minmax(/*const*/ BL::CurveMapping &cumap, float *min_x, float *max_x) { - /* const int num_curves = cumap.curves.length(); */ /* Gives linking error so far. */ + // const int num_curves = cumap.curves.length(); /* Gives linking error so far. */ const int num_curves = rgb_curve ? 4 : 3; *min_x = FLT_MAX; *max_x = -FLT_MAX; diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c index a7c3dc0951e..98a8553a3eb 100644 --- a/intern/guardedalloc/intern/mallocn_guarded_impl.c +++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c @@ -871,7 +871,7 @@ void MEM_guarded_freeN(void *vmemh) if (memh == NULL) { MemorY_ErroR("free", "attempt to free NULL pointer"); - /* print_error(err_stream, "%d\n", (memh+4000)->tag1); */ + // print_error(err_stream, "%d\n", (memh+4000)->tag1); return; } diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index ba8cf8debe9..4480b0d34fc 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -360,7 +360,7 @@ void DM_init(DerivedMesh *dm, dm->needsFree = 1; dm->dirty = (DMDirtyFlag)0; - /* Don't use CustomData_reset(...); because we don't want to touch custom-data. */ + /* Don't use #CustomData_reset because we don't want to touch custom-data. */ copy_vn_i(dm->vertData.typemap, CD_NUMTYPES, -1); copy_vn_i(dm->edgeData.typemap, CD_NUMTYPES, -1); copy_vn_i(dm->faceData.typemap, CD_NUMTYPES, -1); diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 49c81d793c3..db0ea71e233 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -638,7 +638,7 @@ void BKE_nurb_free(Nurb *nu) MEM_freeN(nu->knotsv); } nu->knotsv = NULL; - /* if (nu->trim.first) freeNurblist(&(nu->trim)); */ + // if (nu->trim.first) freeNurblist(&(nu->trim)); MEM_freeN(nu); } diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index a9a8fd7410a..1a3200a9b6c 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -4246,7 +4246,7 @@ void CustomData_blend_write_prepare(CustomData *data, CustomDataLayer *layer = &data->layers[i]; if (layer->flag & CD_FLAG_NOCOPY) { /* Layers with this flag set are not written to file. */ data->totlayer--; - /* CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name); */ + // CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name); } else { if (UNLIKELY((size_t)j >= write_layers_size)) { diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index f4ba1ff8b92..ba54858ba84 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -1020,7 +1020,7 @@ Image *BKE_image_add_generated(Main *bmain, int view_id; const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; - /* STRNCPY(ima->filepath, name); */ /* don't do this, this writes in ain invalid filepath! */ + // STRNCPY(ima->filepath, name); /* don't do this, this writes in ain invalid filepath! */ ima->gen_x = width; ima->gen_y = height; ima->gen_type = gen_type; diff --git a/source/blender/blenkernel/intern/mask_rasterize.c b/source/blender/blenkernel/intern/mask_rasterize.c index 8acc929a089..e04e5fceec6 100644 --- a/source/blender/blenkernel/intern/mask_rasterize.c +++ b/source/blender/blenkernel/intern/mask_rasterize.c @@ -292,10 +292,10 @@ static void maskrasterize_spline_differentiate_point_outset(float (*diff_feather co_curr = diff_points[k_curr]; co_next = diff_points[k_next]; - /* sub_v2_v2v2(d_prev, co_prev, co_curr); */ /* precalc */ + // sub_v2_v2v2(d_prev, co_prev, co_curr); /* precalc */ sub_v2_v2v2(d_next, co_curr, co_next); - /* normalize_v2(d_prev); */ /* precalc */ + // normalize_v2(d_prev); /* precalc */ normalize_v2(d_next); if ((do_test == false) || diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 6a2b56306d6..d6b189d484b 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -289,7 +289,7 @@ void BKE_mball_texspace_calc(Object *ob) bb = ob->runtime.bb; /* Weird one, this. */ - /* INIT_MINMAX(min, max); */ + // INIT_MINMAX(min, max); (min)[0] = (min)[1] = (min)[2] = 1.0e30f; (max)[0] = (max)[1] = (max)[2] = -1.0e30f; diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 18274d4023f..4aef0f346c3 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -262,7 +262,7 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address CD_LAYERS_FREE(vlayers); CD_LAYERS_FREE(elayers); - /* CD_LAYER_FREE(flayers); */ /* Never allocated. */ + // CD_LAYER_FREE(flayers); /* Never allocated. */ CD_LAYERS_FREE(llayers); CD_LAYERS_FREE(players); @@ -942,7 +942,7 @@ Mesh *BKE_mesh_new_nomain( NULL, ID_ME, BKE_idtype_idcode_to_name(ID_ME), LIB_ID_CREATE_LOCALIZE); BKE_libblock_init_empty(&mesh->id); - /* Don't use CustomData_reset(...); because we don't want to touch custom-data. */ + /* Don't use #CustomData_reset because we don't want to touch custom-data. */ copy_vn_i(mesh->vdata.typemap, CD_NUMTYPES, -1); copy_vn_i(mesh->edata.typemap, CD_NUMTYPES, -1); copy_vn_i(mesh->fdata.typemap, CD_NUMTYPES, -1); diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index ca594470cba..4b1eb5b39ce 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -272,8 +272,8 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, } if (totvert == 0) { - /* error("can't convert"); */ - /* Make Sure you check ob->data is a curve */ + /* Make Sure you check ob->data is a curve. */ + // error("can't convert"); return -1; } diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 87b11904f90..6bd08b3dbe0 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -165,7 +165,7 @@ void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, pnors = (float(*)[3])MEM_calloc_arrayN((size_t)numPolys, sizeof(float[3]), __func__); } /* NO NEED TO ALLOC YET */ - /* if (!fnors) fnors = MEM_calloc_arrayN(numFaces, sizeof(float[3]), "face nors mesh.c"); */ + // if (!fnors) {fnors = MEM_calloc_arrayN(numFaces, sizeof(float[3]), "face nors mesh.c"); } if (only_face_normals == false) { /* vertex normals are optional, they require some extra calculations, @@ -200,7 +200,7 @@ void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, if (pnors != r_polyNors) { MEM_freeN(pnors); } - /* if (fnors != r_faceNors) MEM_freeN(fnors); */ /* NO NEED TO ALLOC YET */ + // if (fnors != r_faceNors) { MEM_freeN(fnors); } /* NO NEED TO ALLOC YET */ fnors = pnors = nullptr; } diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 4d003ddc900..e9683d3b52c 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -530,7 +530,7 @@ static void ocean_compute_jacobian_jxx(TaskPool *__restrict pool, void *UNUSED(t for (j = 0; j <= o->_N / 2; j++) { fftw_complex mul_param; - /* init_complex(mul_param, -scale, 0); */ + // init_complex(mul_param, -scale, 0); init_complex(mul_param, -1, 0); mul_complex_f(mul_param, mul_param, chop_amount); @@ -563,7 +563,7 @@ static void ocean_compute_jacobian_jzz(TaskPool *__restrict pool, void *UNUSED(t for (j = 0; j <= o->_N / 2; j++) { fftw_complex mul_param; - /* init_complex(mul_param, -scale, 0); */ + // init_complex(mul_param, -scale, 0); init_complex(mul_param, -1, 0); mul_complex_f(mul_param, mul_param, chop_amount); @@ -596,7 +596,7 @@ static void ocean_compute_jacobian_jxz(TaskPool *__restrict pool, void *UNUSED(t for (j = 0; j <= o->_N / 2; j++) { fftw_complex mul_param; - /* init_complex(mul_param, -scale, 0); */ + // init_complex(mul_param, -scale, 0); init_complex(mul_param, -1, 0); mul_complex_f(mul_param, mul_param, chop_amount); @@ -1015,7 +1015,7 @@ bool BKE_ocean_init(struct Ocean *o, "ocean_fft_in_nz"); o->_N_x = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_N_x"); - /* o->_N_y = (float *) fftwf_malloc(o->_M * o->_N * sizeof(float)); (MEM01) */ + // o->_N_y = (float *) fftwf_malloc(o->_M * o->_N * sizeof(float)); /* (MEM01) */ o->_N_z = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_N_z"); o->_N_x_plan = fftw_plan_dft_c2r_2d(o->_M, o->_N, o->_fft_in_nx, o->_N_x, FFTW_ESTIMATE); @@ -1083,7 +1083,7 @@ void BKE_ocean_free_data(struct Ocean *oc) fftw_destroy_plan(oc->_N_x_plan); fftw_destroy_plan(oc->_N_z_plan); MEM_freeN(oc->_N_x); - /* fftwf_free(oc->_N_y); (MEM01) */ + // fftwf_free(oc->_N_y); /* (MEM01) */ MEM_freeN(oc->_N_z); } diff --git a/source/blender/blenkernel/intern/ocean_spectrum.c b/source/blender/blenkernel/intern/ocean_spectrum.c index 7ed70234baf..c5504b22b43 100644 --- a/source/blender/blenkernel/intern/ocean_spectrum.c +++ b/source/blender/blenkernel/intern/ocean_spectrum.c @@ -77,7 +77,7 @@ static float ocean_spectrum_wind_and_damp(const Ocean *oc, float newval = val * pow(fabs(k_dot_w), oc->_wind_alignment); /* Eliminate wavelengths smaller than cutoff. */ - /* val *= exp(-k2 * m_cutoff); */ + // val *= exp(-k2 * m_cutoff); /* Reduce reflected waves. */ if (k_dot_w < 0.0f) { diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 5e92be76197..fbc781f5eb9 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -2277,7 +2277,7 @@ static void softbody_calc_forces( float fieldfactor = -1.0f, windfactor = 0.25; int do_deflector /*, do_selfcollision */, do_springcollision, do_aero; - /* gravity = sb->grav * sb_grav_force_scale(ob); */ /* UNUSED */ + // gravity = sb->grav * sb_grav_force_scale(ob); /* UNUSED */ /* check conditions for various options */ do_deflector = query_external_colliders(depsgraph, sb->collision_group); @@ -2749,7 +2749,7 @@ static void mesh_to_softbody(Object *ob) build_bps_springlist(ob); /* scan for springs attached to bodypoints ONCE */ /* insert *other second order* springs if desired */ if (sb->secondspring > 0.0000001f) { - /* exploits the first run of build_bps_springlist(ob); */ + /* Exploits the first run of `build_bps_springlist(ob)`. */ add_2nd_order_springs(ob, sb->secondspring); /* yes we need to do it again. */ build_bps_springlist(ob); diff --git a/source/blender/datatoc/datatoc.c b/source/blender/datatoc/datatoc.c index 62b4cee4af0..2ea7fbd9fbe 100644 --- a/source/blender/datatoc/datatoc.c +++ b/source/blender/datatoc/datatoc.c @@ -108,7 +108,7 @@ int main(int argc, char **argv) } #endif - /* fprintf (fpout, "\\x%02x", getc(fpin)); */ + // fprintf(fpout, "\\x%02x", getc(fpin)); fprintf(fpout, "%3d,", getc(fpin)); } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index ba3d3b584d7..5cc52303cd6 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -341,7 +341,7 @@ bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra) return (gpl->actframe->framenum == cfra); } /* XXX: disabled as could be too much of a penalty */ - /* return BKE_gpencil_layer_frame_find(gpl, cfra); */ + // return BKE_gpencil_layer_frame_find(gpl, cfra); } } diff --git a/source/blender/editors/interface/interface_align.c b/source/blender/editors/interface/interface_align.c index dbfdfbf7950..3149675ac04 100644 --- a/source/blender/editors/interface/interface_align.c +++ b/source/blender/editors/interface/interface_align.c @@ -343,7 +343,7 @@ static int ui_block_align_butal_cmp(const void *a, const void *b) * stupid UI code produces widgets which have the same TOP and LEFT positions... * We do not care really, * because this happens when UI is way too small to be usable anyway. */ - /* BLI_assert(0); */ + // BLI_assert(0); return 0; } diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 65104885d98..ebebf69bc11 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -2281,7 +2281,7 @@ static void ui_shadowbox(const rctf *rect, uint pos, uint color, float shadsize, immVertex2fv(pos, v3); /* corner shape */ - /* immAttr4ub(color, 0, 0, 0, alpha); */ /* Not needed, done above in previous tri */ + // immAttr4ub(color, 0, 0, 0, alpha); /* Not needed, done above in previous tri. */ immVertex2fv(pos, v3); immAttr4ub(color, 0, 0, 0, 0); immVertex2fv(pos, v4); @@ -2293,7 +2293,7 @@ static void ui_shadowbox(const rctf *rect, uint pos, uint color, float shadsize, immVertex2fv(pos, v3); /* bottom quad */ - /* immAttr4ub(color, 0, 0, 0, alpha); */ /* Not needed, done above in previous tri */ + // immAttr4ub(color, 0, 0, 0, alpha); /* Not needed, done above in previous tri. */ immVertex2fv(pos, v3); immAttr4ub(color, 0, 0, 0, 0); immVertex2fv(pos, v6); diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index f64f95c5322..7a42c9d5d8b 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -1582,7 +1582,7 @@ static void vgroup_fix( mag = normalize_v3(norm); if (mag) { /* zeros fix */ d = -dot_v3v3(norm, coord); - /* dist = (dot_v3v3(norm, m.co) + d); */ /* UNUSED */ + // dist = (dot_v3v3(norm, m.co) + d); /* UNUSED */ moveCloserToDistanceFromPlane( depsgraph, scene_eval, object_eval, me, i, norm, coord, d, distToBe, strength, cp); } diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index d1ef1b33023..08d741545a8 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -2540,7 +2540,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN /* don't do for now because it selects entire text instead of * placing cursor at the end */ - /* UI_textbutton_activate_but(C, but); */ + // UI_textbutton_activate_but(C, but); } #if defined(WIN32) else if (!can_create_dir(params->dir)) { diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index ec5f443e2dc..f4c4b6cafcd 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -364,7 +364,7 @@ static void graph_panel_key_properties(const bContext *C, Panel *panel) } block = uiLayoutGetBlock(layout); - /* UI_block_func_handle_set(block, do_graph_region_buttons, NULL); */ + // UI_block_func_handle_set(block, do_graph_region_buttons, NULL); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 5be6c69363e..738db28a2b6 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -1773,7 +1773,7 @@ static void tree_element_to_path(TreeElement *te, char buf[128], *name; temnext = (TreeElement *)(ld->next->data); - /* tsenext = TREESTORE(temnext); */ /* UNUSED */ + // tsenext = TREESTORE(temnext); /* UNUSED */ nextptr = &temnext->rnaptr; name = RNA_struct_name_get_alloc(nextptr, buf, sizeof(buf), NULL); diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 65a673940f8..362ee179e7d 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -968,9 +968,9 @@ void ElementResize(const TransInfo *t, float obsizemat[3][3]; /* Reorient the size mat to fit the oriented object. */ mul_m3_m3m3(obsizemat, tmat, td->axismtx); - /* print_m3("obsizemat", obsizemat); */ + // print_m3("obsizemat", obsizemat); TransMat3ToSize(obsizemat, td->axismtx, fsize); - /* print_v3("fsize", fsize); */ + // print_v3("fsize", fsize); } else { mat3_to_size(fsize, tmat); diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c index 066a2853dc7..cfcb17b8da0 100644 --- a/source/blender/editors/transform/transform_mode_edge_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_slide.c @@ -852,7 +852,7 @@ static EdgeSlideData *createEdgeSlideVerts_double_side(TransInfo *t, TransDataCo #undef EDGESLIDE_VERT_IS_INNER } - /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */ + // EDBM_flag_disable_all(em, BM_ELEM_SELECT); BLI_assert(STACK_SIZE(sv_array) == (uint)sv_tot); @@ -1037,7 +1037,7 @@ static EdgeSlideData *createEdgeSlideVerts_single_side(TransInfo *t, TransDataCo } } - /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */ + // EDBM_flag_disable_all(em, BM_ELEM_SELECT); sld->sv = sv_array; sld->totsv = sv_tot; diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index a465c6b92bc..cd323e72003 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -1711,7 +1711,7 @@ static const char *exr_rgba_channelname(MultiPartInputFile &file, const char *ch const ChannelList &channels = file.header(0).channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { - /* const Channel &channel = i.channel(); */ /* Not used yet */ + // const Channel &channel = i.channel(); /* Not used yet. */ const char *str = i.name(); int len = strlen(str); if (len) { diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp index e52bdca0d87..e54192abc54 100644 --- a/source/blender/io/collada/AnimationImporter.cpp +++ b/source/blender/io/collada/AnimationImporter.cpp @@ -1363,7 +1363,7 @@ void AnimationImporter::add_bone_animation_sampled(Object *ob, calc_joint_parent_mat_rest(par, nullptr, root, node); mul_m4_m4m4(temp, par, matfra); - /* evaluate_joint_world_transform_at_frame(temp, NULL, node, fra); */ + // evaluate_joint_world_transform_at_frame(temp, NULL, node, fra); /* calc special matrix */ mul_m4_series(mat, irest, temp, irest_dae, rest); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 7bc09e3cd67..ac734efed8b 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -3003,7 +3003,7 @@ void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value) BLI_assert(RNA_property_type(prop) == PROP_FLOAT); BLI_assert(RNA_property_array_check(prop) == false); /* useful to check on bad values but set function should clamp */ - /* BLI_assert(RNA_property_float_clamp(ptr, prop, &value) == 0); */ + // BLI_assert(RNA_property_float_clamp(ptr, prop, &value) == 0); if ((idprop = rna_idproperty_check(&prop, ptr))) { RNA_property_float_clamp(ptr, prop, &value); diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index 49d02524e43..690506fa517 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -56,7 +56,7 @@ static void rna_Armature_update_data(Main *UNUSED(bmain), Scene *UNUSED(scene), DEG_id_tag_update(id, 0); WM_main_add_notifier(NC_GEOM | ND_DATA, id); - /*WM_main_add_notifier(NC_OBJECT|ND_POSE, NULL); */ + // WM_main_add_notifier(NC_OBJECT|ND_POSE, NULL); } static void rna_Armature_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) @@ -994,7 +994,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) } RNA_def_property_float_sdna(prop, NULL, "rad_head"); /* XXX range is 0 to lim, where lim = 10000.0f * MAX2(1.0, view3d->grid); */ - /*RNA_def_property_range(prop, 0, 1000); */ + // RNA_def_property_range(prop, 0, 1000); RNA_def_property_ui_range(prop, 0.01, 100, 0.1, 3); RNA_def_property_ui_text( prop, "Envelope Head Radius", "Radius of head of bone (for Envelope deform only)"); @@ -1008,7 +1008,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) } RNA_def_property_float_sdna(prop, NULL, "rad_tail"); /* XXX range is 0 to lim, where lim = 10000.0f * MAX2(1.0, view3d->grid); */ - /*RNA_def_property_range(prop, 0, 1000); */ + // RNA_def_property_range(prop, 0, 1000); RNA_def_property_ui_range(prop, 0.01, 100, 0.1, 3); RNA_def_property_ui_text( prop, "Envelope Tail Radius", "Radius of tail of bone (for Envelope deform only)"); @@ -1346,7 +1346,7 @@ static void rna_def_edit_bone(BlenderRNA *brna) /* calculated and read only, not actual data access */ prop = RNA_def_property(srna, "matrix", PROP_FLOAT, PROP_MATRIX); - /* RNA_def_property_float_sdna(prop, NULL, ""); */ /* Doesn't access any real data. */ + // RNA_def_property_float_sdna(prop, NULL, ""); /* Doesn't access any real data. */ RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4); // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_THICK_WRAP); /* no reference to original data */ diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 9caff88a3a5..fbc578acb8e 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1687,7 +1687,7 @@ static void rna_def_mvert(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "normal", PROP_FLOAT, PROP_DIRECTION); - /* RNA_def_property_float_sdna(prop, NULL, "no"); */ + // RNA_def_property_float_sdna(prop, NULL, "no"); RNA_def_property_array(prop, 3); RNA_def_property_range(prop, -1.0f, 1.0f); RNA_def_property_float_funcs( diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 5de7aa9a18b..b360d3b6672 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -1036,7 +1036,7 @@ static void rna_NodeTree_get_from_context( void *ret1, *ret2, *ret3; RNA_pointer_create(NULL, ntreetype->rna_ext.srna, NULL, &ptr); /* dummy */ - /* RNA_struct_find_function(&ptr, "get_from_context"); */ + // RNA_struct_find_function(&ptr, "get_from_context"); func = &rna_NodeTree_get_from_context_func; RNA_parameter_list_create(&list, &ptr, func); @@ -2987,7 +2987,7 @@ static void rna_NodeSocketInterface_register_properties(bNodeTree *ntree, } RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, stemp, &ptr); - /* RNA_struct_find_function(&ptr, "register_properties"); */ + // RNA_struct_find_function(&ptr, "register_properties"); func = &rna_NodeSocketInterface_register_properties_func; RNA_parameter_list_create(&list, &ptr, func); @@ -3013,7 +3013,7 @@ static void rna_NodeSocketInterface_init_socket( RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, stemp, &ptr); RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr); RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &sock_ptr); - /* RNA_struct_find_function(&ptr, "init_socket"); */ + // RNA_struct_find_function(&ptr, "init_socket"); func = &rna_NodeSocketInterface_init_socket_func; RNA_parameter_list_create(&list, &ptr, func); @@ -3043,7 +3043,7 @@ static void rna_NodeSocketInterface_from_socket(bNodeTree *ntree, RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, stemp, &ptr); RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr); RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &sock_ptr); - /* RNA_struct_find_function(&ptr, "from_socket"); */ + // RNA_struct_find_function(&ptr, "from_socket"); func = &rna_NodeSocketInterface_from_socket_func; RNA_parameter_list_create(&list, &ptr, func); diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index de4cfb2b61a..f732e14d905 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -1851,20 +1851,20 @@ static void rna_def_particle(BlenderRNA *brna) prop = RNA_def_property(srna, "birth_time", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "time"); - /* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */ + // RNA_def_property_range(prop, lowerLimitf, upperLimitf); RNA_def_property_ui_text(prop, "Birth Time", ""); prop = RNA_def_property(srna, "lifetime", PROP_FLOAT, PROP_TIME); - /* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */ + // RNA_def_property_range(prop, lowerLimitf, upperLimitf); RNA_def_property_ui_text(prop, "Lifetime", ""); prop = RNA_def_property(srna, "die_time", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "dietime"); - /* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */ + // RNA_def_property_range(prop, lowerLimitf, upperLimitf); RNA_def_property_ui_text(prop, "Die Time", ""); prop = RNA_def_property(srna, "size", PROP_FLOAT, PROP_NONE); - /* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */ + // RNA_def_property_range(prop, lowerLimitf, upperLimitf); RNA_def_property_ui_text(prop, "Size", ""); /* */ @@ -3658,7 +3658,7 @@ static void rna_def_particle_system(BlenderRNA *brna) /* access to particle settings is redirected through functions */ /* to allow proper id-buttons functionality */ prop = RNA_def_property(srna, "settings", PROP_POINTER, PROP_NONE); - /*RNA_def_property_pointer_sdna(prop, NULL, "part"); */ + // RNA_def_property_pointer_sdna(prop, NULL, "part"); RNA_def_property_struct_type(prop, "ParticleSettings"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL); RNA_def_property_pointer_funcs( diff --git a/source/blender/makesrna/intern/rna_speaker.c b/source/blender/makesrna/intern/rna_speaker.c index 43f0d27f514..2dce9d32006 100644 --- a/source/blender/makesrna/intern/rna_speaker.c +++ b/source/blender/makesrna/intern/rna_speaker.c @@ -55,7 +55,9 @@ static void rna_def_speaker(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Mute", "Mute the speaker"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SOUND); - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "sound", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Sound"); @@ -63,24 +65,30 @@ static void rna_def_speaker(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Sound", "Sound data-block used by this speaker"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_sound_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_sound_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "volume_max", PROP_FLOAT, PROP_FACTOR); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text( prop, "Maximum Volume", "Maximum volume, no matter how near the object is"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_max_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_max_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "volume_min", PROP_FLOAT, PROP_FACTOR); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text( prop, "Minimum Volume", "Minimum volume, no matter how far away the object is"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_min_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_min_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "distance_max", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -89,24 +97,30 @@ static void rna_def_speaker(BlenderRNA *brna) prop, "Maximum Distance", "Maximum distance for volume calculation, no matter how far away the object is"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_distance_max_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_distance_max_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "distance_reference", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text( prop, "Reference Distance", "Reference distance at which volume is 100%"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_distance_reference_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_distance_reference_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "attenuation", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text( prop, "Attenuation", "How strong the distance affects volume, depending on distance model"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_attenuation_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_attenuation_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "cone_angle_outer", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -116,8 +130,10 @@ static void rna_def_speaker(BlenderRNA *brna) "Outer Cone Angle", "Angle of the outer cone, in degrees, outside this cone the volume is " "the outer cone volume, between inner and outer cone the volume is interpolated"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_angle_outer_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_angle_outer_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "cone_angle_inner", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -126,29 +142,37 @@ static void rna_def_speaker(BlenderRNA *brna) prop, "Inner Cone Angle", "Angle of the inner cone, in degrees, inside the cone the volume is 100%"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_angle_inner_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_angle_inner_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "cone_volume_outer", PROP_FLOAT, PROP_FACTOR); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Outer Cone Volume", "Volume outside the outer cone"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_volume_outer_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_volume_outer_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "volume", PROP_FLOAT, PROP_FACTOR); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Volume", "How loud the sound is"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SOUND); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "pitch", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.1f, 10.0f); RNA_def_property_ui_text(prop, "Pitch", "Playback pitch of the sound"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SOUND); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_pitch_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_pitch_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif /* common */ rna_def_animdata_common(srna); diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index a88b100435a..c506a533032 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -1458,7 +1458,7 @@ static void rna_def_panel(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->description"); RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Panel_bl_description_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */ @@ -1820,7 +1820,7 @@ static void rna_def_menu(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->description"); RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Menu_bl_description_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */ diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 667f3822935..b910648495b 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -1915,7 +1915,7 @@ static void rna_def_operator(BlenderRNA *brna) /* Without setting the length the pointer size would be used. -3 because `.` -> `_OT_`. */ RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME - 3); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_idname_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); RNA_def_struct_name_property(srna, prop); @@ -1923,7 +1923,7 @@ static void rna_def_operator(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->name"); RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_label_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE); @@ -1943,7 +1943,7 @@ static void rna_def_operator(BlenderRNA *brna) "rna_Operator_bl_description_get", "rna_Operator_bl_description_length", "rna_Operator_bl_description_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE); @@ -1953,7 +1953,7 @@ static void rna_def_operator(BlenderRNA *brna) "rna_Operator_bl_undo_group_get", "rna_Operator_bl_undo_group_length", "rna_Operator_bl_undo_group_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE); @@ -2013,7 +2013,7 @@ static void rna_def_macro_operator(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->idname"); RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_idname_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); RNA_def_struct_name_property(srna, prop); @@ -2021,7 +2021,7 @@ static void rna_def_macro_operator(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->name"); RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_label_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE); @@ -2041,7 +2041,7 @@ static void rna_def_macro_operator(BlenderRNA *brna) "rna_Operator_bl_description_get", "rna_Operator_bl_description_length", "rna_Operator_bl_description_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE); @@ -2051,7 +2051,7 @@ static void rna_def_macro_operator(BlenderRNA *brna) "rna_Operator_bl_undo_group_get", "rna_Operator_bl_undo_group_length", "rna_Operator_bl_undo_group_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE); @@ -2073,11 +2073,13 @@ static void rna_def_operator_type_macro(BlenderRNA *brna) srna, "Operator Macro", "Storage of a sub operator in a macro after it has been added"); RNA_def_struct_sdna(srna, "wmOperatorTypeMacro"); - /* prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); */ - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ - /* RNA_def_property_string_sdna(prop, NULL, "idname"); */ - /* RNA_def_property_ui_text(prop, "Name", "Name of the sub operator"); */ - /* RNA_def_struct_name_property(srna, prop); */ +# if 0 + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_sdna(prop, NULL, "idname"); + RNA_def_property_ui_text(prop, "Name", "Name of the sub operator"); + RNA_def_struct_name_property(srna, prop); +# endif prop = RNA_def_property(srna, "properties", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index bde15daa682..e123604cbe9 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -230,7 +230,7 @@ static wmKeyMapItem *rna_KeyMap_item_new(wmKeyMap *km, bool repeat, bool head) { - /* wmWindowManager *wm = CTX_wm_manager(C); */ + // wmWindowManager *wm = CTX_wm_manager(C); wmKeyMapItem *kmi = NULL; char idname_bl[OP_MAX_TYPENAME]; int modifier = 0; diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c index 6a1574f3dbe..febb0e14e07 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo.c @@ -80,7 +80,7 @@ static void rna_gizmo_draw_cb(const struct bContext *C, struct wmGizmo *gz) ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "draw"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "draw")` directly. */ func = &rna_Gizmo_draw_func; RNA_parameter_list_create(&list, &gz_ptr, func); RNA_parameter_set_lookup(&list, "context", &C); @@ -98,7 +98,7 @@ static void rna_gizmo_draw_select_cb(const struct bContext *C, struct wmGizmo *g ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "draw_select"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "draw_select")` directly. */ func = &rna_Gizmo_draw_select_func; RNA_parameter_list_create(&list, &gz_ptr, func); RNA_parameter_set_lookup(&list, "context", &C); @@ -117,7 +117,7 @@ static int rna_gizmo_test_select_cb(struct bContext *C, struct wmGizmo *gz, cons ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "test_select"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "test_select")` directly. */ func = &rna_Gizmo_test_select_func; RNA_parameter_list_create(&list, &gz_ptr, func); RNA_parameter_set_lookup(&list, "context", &C); @@ -144,7 +144,7 @@ static int rna_gizmo_modal_cb(struct bContext *C, FunctionRNA *func; const int tweak_flag_int = tweak_flag; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "modal"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "modal")` directly. */ func = &rna_Gizmo_modal_func; RNA_parameter_list_create(&list, &gz_ptr, func); RNA_parameter_set_lookup(&list, "context", &C); @@ -168,7 +168,7 @@ static void rna_gizmo_setup_cb(struct wmGizmo *gz) ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "setup"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "setup")` directly. */ func = &rna_Gizmo_setup_func; RNA_parameter_list_create(&list, &gz_ptr, func); gzgroup->type->rna_ext.call((bContext *)NULL, &gz_ptr, func, &list); @@ -183,7 +183,7 @@ static int rna_gizmo_invoke_cb(struct bContext *C, struct wmGizmo *gz, const str ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "invoke"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "invoke")` directly. */ func = &rna_Gizmo_invoke_func; RNA_parameter_list_create(&list, &gz_ptr, func); RNA_parameter_set_lookup(&list, "context", &C); @@ -206,7 +206,7 @@ static void rna_gizmo_exit_cb(struct bContext *C, struct wmGizmo *gz, bool cance ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "exit"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "exit")` directly. */ func = &rna_Gizmo_exit_func; RNA_parameter_list_create(&list, &gz_ptr, func); RNA_parameter_set_lookup(&list, "context", &C); @@ -226,7 +226,7 @@ static void rna_gizmo_select_refresh_cb(struct wmGizmo *gz) ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "select_refresh"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "select_refresh")` directly. */ func = &rna_Gizmo_select_refresh_func; RNA_parameter_list_create(&list, &gz_ptr, func); gzgroup->type->rna_ext.call((bContext *)NULL, &gz_ptr, func, &list); @@ -785,7 +785,7 @@ static void rna_gizmogroup_invoke_prepare_cb(const bContext *C, FunctionRNA *func; RNA_pointer_create(NULL, gzgroup->type->rna_ext.srna, gzgroup, &gzgroup_ptr); - /* RNA_struct_find_function(&wgroupr, "invoke_prepare"); */ + /* Reference `RNA_struct_find_function(&wgroupr, "invoke_prepare")` directly. */ func = &rna_GizmoGroup_invoke_prepare_func; RNA_parameter_list_create(&list, &gzgroup_ptr, func); @@ -1033,7 +1033,7 @@ static void rna_def_gizmo(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_string_sdna(prop, NULL, "type->idname"); RNA_def_property_string_maxlength(prop, MAX_NAME); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Gizmo_bl_idname_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); RNA_define_verify_sdna(1); /* not in sdna */ @@ -1362,7 +1362,7 @@ static void rna_def_gizmogroup(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->name"); RNA_def_property_string_maxlength(prop, MAX_NAME); /* else it uses the pointer size! */ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GizmoGroup_bl_label_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); prop = RNA_def_property(srna, "bl_space_type", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_world.c b/source/blender/makesrna/intern/rna_world.c index 1ca0eb74cf5..a66e3090548 100644 --- a/source/blender/makesrna/intern/rna_world.c +++ b/source/blender/makesrna/intern/rna_world.c @@ -216,7 +216,7 @@ void RNA_def_world(BlenderRNA *brna) RNA_def_property_array(prop, 3); RNA_def_property_float_array_default(prop, default_world_color); RNA_def_property_ui_text(prop, "Color", "Color of the background"); - /* RNA_def_property_update(prop, 0, "rna_World_update"); */ + // RNA_def_property_update(prop, 0, "rna_World_update"); /* render-only uses this */ RNA_def_property_update(prop, 0, "rna_World_draw_update"); diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 17cbf8e4569..6dd96f66cec 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -7505,7 +7505,7 @@ static PyObject *pyrna_srna_Subtype(StructRNA *srna) /* Newclass will now have 2 ref's, ???, * probably 1 is internal since #Py_DECREF here segfaults. */ - /* PyC_ObSpit("new class ref", newclass); */ + // PyC_ObSpit("new class ref", newclass); if (newclass) { /* srna owns one, and the other is owned by the caller. */ diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c index abbc332d89d..fcc796d4545 100644 --- a/source/blender/python/intern/bpy_rna_array.c +++ b/source/blender/python/intern/bpy_rna_array.c @@ -334,7 +334,7 @@ static int validate_array_length(PyObject *rvalue, } if (tot != len) { - /* BLI_snprintf(error_str, error_str_size, "sequence must have length of %d", len); */ + // BLI_snprintf(error_str, error_str_size, "sequence must have length of %d", len); PyErr_Format(PyExc_ValueError, "%s %.200s.%.200s, sequence must have %d items total, not %d", error_prefix, diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index fec5a516688..7c3fce7fcb2 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -485,11 +485,11 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) } /* XXX We'd need re-entrant locking on Main for this to work... */ - /* BKE_main_lock(bmain); */ + // BKE_main_lock(bmain); wm_link_do(lapp_data, op->reports, bmain, scene, view_layer, CTX_wm_view3d(C)); - /* BKE_main_unlock(bmain); */ + // BKE_main_unlock(bmain); /* mark all library linked objects to be updated */ BKE_main_lib_objects_recalc_all(bmain); -- cgit v1.2.3 From c741558509a35317209bbfd3ff77c413f791a47c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 12 Aug 2021 14:34:43 +1000 Subject: Cleanup: spelling in comments --- source/blender/compositor/operations/COM_DilateErodeOperation.cc | 2 +- source/blender/draw/engines/select/select_debug_engine.c | 2 +- source/blender/editors/space_file/file_ops.c | 4 ++-- source/blender/makesdna/DNA_ID.h | 2 +- source/blender/makesdna/intern/makesdna.c | 4 ++-- source/blender/python/mathutils/mathutils_geometry.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index c67a35b686c..a27148f967d 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -500,7 +500,7 @@ void *ErodeStepOperation::initializeTileData(rcti *rect) int bwidth = rect->xmax - rect->xmin; int bheight = rect->ymax - rect->ymin; - /* NOTE: Cache buffer has original tilesize width, but new height. + /* NOTE: Cache buffer has original tile-size width, but new height. * We have to calculate the additional rows in the first pass, * to have valid data available for the second pass. */ tile_info *result = create_cache(rect->xmin, rect->xmax, ymin, ymax); diff --git a/source/blender/draw/engines/select/select_debug_engine.c b/source/blender/draw/engines/select/select_debug_engine.c index ded96be23f3..e9437c5ab92 100644 --- a/source/blender/draw/engines/select/select_debug_engine.c +++ b/source/blender/draw/engines/select/select_debug_engine.c @@ -19,7 +19,7 @@ /** \file * \ingroup draw_engine * - * Engine for debuging the selection map drawing. + * Engine for debugging the selection map drawing. */ #include "DNA_ID.h" diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 08d741545a8..2f1acd2ca4d 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1388,8 +1388,8 @@ int file_highlight_set(SpaceFile *sfile, ARegion *region, int mx, int my) FileSelectParams *params; int numfiles, origfile; - /* In case blender starts where the mouse is over a File broser, this operator can be invoked - * when the sfile or sfile->layout isn't initialized yet. */ + /* In case blender starts where the mouse is over a File browser, + * this operator can be invoked when the `sfile` or `sfile->layout` isn't initialized yet. */ if (sfile == NULL || sfile->files == NULL || sfile->layout == NULL) { return 0; } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 8a577be2749..10a5a0f1c47 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -587,7 +587,7 @@ enum { * * RESET_NEVER * - * NOTE: Only used by nodegroups currently. + * NOTE: Only used by node-groups currently. */ LIB_TAG_LOCALIZED = 1 << 14, diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 8324db1a9c8..061c3462a69 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -1118,11 +1118,11 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char types_align_32[structtype] = max_align_32; types_align_64[structtype] = max_align_64; - /* Santiy check 1: alignment should never be 0. */ + /* Sanity check 1: alignment should never be 0. */ BLI_assert(max_align_32); BLI_assert(max_align_64); - /* Santiy check 2: alignment should always be equal or smaller than the maximum + /* Sanity check 2: alignment should always be equal or smaller than the maximum * size of a build in type which is 8 bytes (ie int64_t or double). */ BLI_assert(max_align_32 <= 8); BLI_assert(max_align_64 <= 8); diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index 3c0ea9a9566..5868c76b28f 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -201,7 +201,7 @@ static PyObject *M_Geometry_intersect_line_line(PyObject *UNUSED(self), PyObject } if (result == 0) { - /* Co-linear. */ + /* Collinear. */ Py_RETURN_NONE; } -- cgit v1.2.3 From 216414c65a6b3690aba75caf74d1dbf6dd8247a1 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 12 Aug 2021 14:24:27 +1000 Subject: Cleanup: use parameters struct for wm_homefile_read Also add wm_homefile_read_ex which is only needed for the first execution at startup. --- source/blender/windowmanager/intern/wm_files.c | 80 +++++++++++----------- source/blender/windowmanager/intern/wm_init_exit.c | 23 +++---- source/blender/windowmanager/wm_files.h | 43 +++++++++--- 3 files changed, 85 insertions(+), 61 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index abf957a6396..65659dd7ab7 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -993,31 +993,12 @@ const char *WM_init_state_app_template_get(void) /** * Called on startup, (context entirely filled with NULLs) - * or called for 'New File' both startup.blend and userpref.blend are checked. - * - * \param use_factory_settings: - * Ignore on-disk startup file, use bundled `datatoc_startup_blend` instead. - * Used for "Restore Factory Settings". - * - * \param use_userdef: Load factory settings as well as startup file. - * Disabled for "File New" we don't want to reload preferences. - * - * \param filepath_startup_override: - * Optional path pointing to an alternative blend file (may be NULL). - * - * \param app_template_override: - * Template to use instead of the template defined in user-preferences. - * When not-null, this is written into the user preferences. + * or called for 'New File' both `startup.blend` and `userpref.blend` are checked. */ -void wm_homefile_read(bContext *C, - ReportList *reports, - bool use_factory_settings, - bool use_empty_data, - bool use_data, - bool use_userdef, - const char *filepath_startup_override, - const char *app_template_override, - bool *r_is_factory_startup) +void wm_homefile_read_ex(bContext *C, + const struct wmHomeFileRead_Params *params_homefile, + ReportList *reports, + bool *r_is_factory_startup) { #if 0 /* UNUSED, keep as this may be needed later & the comment below isn't self evident. */ /* Context does not always have valid main pointer here. */ @@ -1026,6 +1007,14 @@ void wm_homefile_read(bContext *C, ListBase wmbase; bool success = false; + /* May be enabled, when the user configuration doesn't exist. */ + const bool use_data = params_homefile->use_data; + const bool use_userdef = params_homefile->use_userdef; + bool use_factory_settings = params_homefile->use_factory_settings; + const bool use_empty_data = params_homefile->use_empty_data; + const char *filepath_startup_override = params_homefile->filepath_startup_override; + const char *app_template_override = params_homefile->app_template_override; + bool filepath_startup_is_factory = true; char filepath_startup[FILE_MAX]; char filepath_userdef[FILE_MAX]; @@ -1320,6 +1309,13 @@ void wm_homefile_read(bContext *C, } } +void wm_homefile_read(bContext *C, + const struct wmHomeFileRead_Params *params_homefile, + ReportList *reports) +{ + wm_homefile_read_ex(C, params_homefile, reports, NULL); +} + /* -------------------------------------------------------------------- */ /** \name Blend-File History API * \{ */ @@ -2087,14 +2083,15 @@ static int wm_userpref_read_exec(bContext *C, wmOperator *op) UserDef U_backup = U; wm_homefile_read(C, - op->reports, - use_factory_settings, - false, - use_data, - use_userdef, - NULL, - WM_init_state_app_template_get(), - NULL); + &(const struct wmHomeFileRead_Params){ + .use_data = use_data, + .use_userdef = use_userdef, + .use_factory_settings = use_factory_settings, + .use_empty_data = false, + .filepath_startup_override = NULL, + .app_template_override = WM_init_state_app_template_get(), + }, + op->reports); wm_userpref_read_exceptions(&U, &U_backup); SET_FLAG_FROM_TEST(G.f, use_factory_settings, G_FLAG_USERPREF_NO_SAVE_ON_EXIT); @@ -2233,16 +2230,17 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op) app_template = WM_init_state_app_template_get(); } - bool use_data = true; wm_homefile_read(C, - op->reports, - use_factory_settings, - use_empty_data, - use_data, - use_userdef, - filepath, - app_template, - NULL); + &(const struct wmHomeFileRead_Params){ + .use_data = true, + .use_userdef = use_userdef, + .use_factory_settings = use_factory_settings, + .use_empty_data = use_empty_data, + .filepath_startup_override = filepath, + .app_template_override = app_template, + }, + op->reports); + if (use_splash) { WM_init_splash(C); } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index d7ea47fc625..953aa683441 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -279,10 +279,7 @@ void WM_init(bContext *C, int argc, const char **argv) WM_msgbus_types_init(); - /* get the default database, plus a wm */ bool is_factory_startup = true; - const bool use_data = true; - const bool use_userdef = true; /* Studio-lights needs to be init before we read the home-file, * otherwise the versioning cannot find the default studio-light. */ @@ -290,15 +287,17 @@ void WM_init(bContext *C, int argc, const char **argv) BLI_assert((G.fileflags & G_FILE_NO_UI) == 0); - wm_homefile_read(C, - NULL, - G.factory_startup, - false, - use_data, - use_userdef, - NULL, - WM_init_state_app_template_get(), - &is_factory_startup); + wm_homefile_read_ex(C, + &(const struct wmHomeFileRead_Params){ + .use_data = true, + .use_userdef = true, + .use_factory_settings = G.factory_startup, + .use_empty_data = false, + .filepath_startup_override = NULL, + .app_template_override = WM_init_state_app_template_get(), + }, + NULL, + &is_factory_startup); /* Call again to set from userpreferences... */ BLT_lang_set(NULL); diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index c7fe07cad7f..7009885495b 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -33,15 +33,42 @@ extern "C" { /* wm_files.c */ void wm_history_file_read(void); + +struct wmHomeFileRead_Params { + /** Load data, disable when only loading user preferences. */ + unsigned int use_data : 1; + /** Load factory settings as well as startup file (disabled for "File New"). */ + unsigned int use_userdef : 1; + + /** + * Ignore on-disk startup file, use bundled `datatoc_startup_blend` instead. + * Used for "Restore Factory Settings". + */ + unsigned int use_factory_settings : 1; + /** + * Load the startup file without any data-blocks. + * Useful for automated content generation, so the file starts without data. + */ + unsigned int use_empty_data : 1; + /** + * Optional path pointing to an alternative blend file (may be NULL). + */ + const char *filepath_startup_override; + /** + * Template to use instead of the template defined in user-preferences. + * When not-null, this is written into the user preferences. + */ + const char *app_template_override; +}; + +void wm_homefile_read_ex(struct bContext *C, + const struct wmHomeFileRead_Params *params_homefile, + struct ReportList *reports, + bool *r_is_factory_startup); void wm_homefile_read(struct bContext *C, - struct ReportList *reports, - bool use_factory_settings, - bool use_empty_data, - bool use_data, - bool use_userdef, - const char *filepath_startup_override, - const char *app_template_override, - bool *r_is_factory_startup); + const struct wmHomeFileRead_Params *params_homefile, + struct ReportList *reports); + void wm_file_read_report(bContext *C, struct Main *bmain); void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action); -- cgit v1.2.3 From 497bc4d19977abc7b9e2c0f5024a23057e680954 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 12 Aug 2021 14:50:40 +1000 Subject: Fix T89046: Startup file with Python drivers crashes on load Resolve order of initialization error reading startup file, support postponing running wm_file_read_post until Blender has been initialized. Deferring updates allows duplicate initialization to be removed from WM_init. Reviewed By: mont29 Ref D12184 --- source/blender/windowmanager/intern/wm_files.c | 75 ++++++++++++++++++---- source/blender/windowmanager/intern/wm_init_exit.c | 56 ++++++---------- source/blender/windowmanager/wm_files.h | 6 +- 3 files changed, 88 insertions(+), 49 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 65659dd7ab7..21c16e94097 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -595,20 +595,34 @@ static void wm_file_read_pre(bContext *C, bool use_data, bool UNUSED(use_userdef UI_view2d_zoom_cache_reset(); } +/** + * Parameters for #wm_file_read_post, also used for deferred initialization. + */ +struct wmFileReadPost_Params { + uint use_data : 1; + uint use_userdef : 1; + + uint is_startup_file : 1; + uint is_factory_startup : 1; + uint reset_app_template : 1; +}; + /** * Logic shared between #WM_file_read & #wm_homefile_read, * updates to make after reading a file. */ -static void wm_file_read_post(bContext *C, - const bool is_startup_file, - const bool is_factory_startup, - const bool use_data, - const bool use_userdef, - const bool reset_app_template) +static void wm_file_read_post(bContext *C, const struct wmFileReadPost_Params *params) { - bool addons_loaded = false; wmWindowManager *wm = CTX_wm_manager(C); + const bool use_data = params->use_data; + const bool use_userdef = params->use_userdef; + const bool is_startup_file = params->is_startup_file; + const bool is_factory_startup = params->is_factory_startup; + const bool reset_app_template = params->reset_app_template; + + bool addons_loaded = false; + if (use_data) { if (!G.background) { /* remove windows which failed to be added via WM_check */ @@ -910,7 +924,14 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) wm_history_file_update(); } - wm_file_read_post(C, false, false, use_data, use_userdef, false); + wm_file_read_post(C, + &(const struct wmFileReadPost_Params){ + .use_data = use_data, + .use_userdef = use_userdef, + .is_startup_file = false, + .is_factory_startup = false, + .reset_app_template = false, + }); bf_reports.duration.whole = PIL_check_seconds_timer() - bf_reports.duration.whole; file_read_reports_finalize(&bf_reports); @@ -994,11 +1015,17 @@ const char *WM_init_state_app_template_get(void) /** * Called on startup, (context entirely filled with NULLs) * or called for 'New File' both `startup.blend` and `userpref.blend` are checked. + * + * \param r_params_file_read_post: Support postponed initialization, + * needed for initial startup when only some sub-systems have been initialized. + * When non-null, #wm_file_read_post doesn't run, instead it's arguments are stored + * in this return argument. + * The caller is responsible for calling #wm_homefile_read_post with this return argument. */ void wm_homefile_read_ex(bContext *C, const struct wmHomeFileRead_Params *params_homefile, ReportList *reports, - bool *r_is_factory_startup) + struct wmFileReadPost_Params **r_params_file_read_post) { #if 0 /* UNUSED, keep as this may be needed later & the comment below isn't self evident. */ /* Context does not always have valid main pointer here. */ @@ -1302,10 +1329,21 @@ void wm_homefile_read_ex(bContext *C, G.save_over = 0; } - wm_file_read_post(C, true, is_factory_startup, use_data, use_userdef, reset_app_template); - - if (r_is_factory_startup) { - *r_is_factory_startup = is_factory_startup; + { + const struct wmFileReadPost_Params params_file_read_post = { + .use_data = use_data, + .use_userdef = use_userdef, + .is_startup_file = true, + .is_factory_startup = is_factory_startup, + .reset_app_template = reset_app_template, + }; + if (r_params_file_read_post == NULL) { + wm_file_read_post(C, ¶ms_file_read_post); + } + else { + *r_params_file_read_post = MEM_mallocN(sizeof(struct wmFileReadPost_Params), __func__); + **r_params_file_read_post = params_file_read_post; + } } } @@ -1316,6 +1354,17 @@ void wm_homefile_read(bContext *C, wm_homefile_read_ex(C, params_homefile, reports, NULL); } +/** + * Special case, support deferred execution of #wm_file_read_post, + * Needed when loading for the first time to workaround order of initialization bug, see T89046. + */ +void wm_homefile_read_post(struct bContext *C, + const struct wmFileReadPost_Params *params_file_read_post) +{ + wm_file_read_post(C, params_file_read_post); + MEM_freeN((void *)params_file_read_post); +} + /* -------------------------------------------------------------------- */ /** \name Blend-File History API * \{ */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 953aa683441..4b839384853 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -279,14 +279,31 @@ void WM_init(bContext *C, int argc, const char **argv) WM_msgbus_types_init(); - bool is_factory_startup = true; - /* Studio-lights needs to be init before we read the home-file, * otherwise the versioning cannot find the default studio-light. */ BKE_studiolight_init(); BLI_assert((G.fileflags & G_FILE_NO_UI) == 0); + /** + * NOTE(@campbellbarton): Startup file and order of initialization. + * + * Loading #BLENDER_STARTUP_FILE, #BLENDER_USERPREF_FILE, starting Python and other sub-systems, + * have inter-dependencies, for example. + * + * - Some sub-systems depend on the preferences (initializing icons depend on the theme). + * - Add-ons depends on the preferences to know what has been enabled. + * - Add-ons depends on the window-manger to register their key-maps. + * - Evaluating the startup file depends on Python for animation-drivers (see T89046). + * - Starting Python depends on the startup file so key-maps can be added in the window-manger. + * + * Loading preferences early, then application subsystems and finally the startup data would + * simplify things if it weren't for key-maps being part of the window-manager + * which is blend file data. + * Creating a dummy window-manager early, or moving the key-maps into the preferences + * would resolve this and may be worth looking into long-term, see: D12184 for details. + */ + struct wmFileReadPost_Params *params_file_read_post = NULL; wm_homefile_read_ex(C, &(const struct wmHomeFileRead_Params){ .use_data = true, @@ -297,7 +314,7 @@ void WM_init(bContext *C, int argc, const char **argv) .app_template_override = WM_init_state_app_template_get(), }, NULL, - &is_factory_startup); + ¶ms_file_read_post); /* Call again to set from userpreferences... */ BLT_lang_set(NULL); @@ -327,14 +344,6 @@ void WM_init(bContext *C, int argc, const char **argv) ED_spacemacros_init(); - /* NOTE(campbell): there is a bug where python needs initializing before loading the - * startup.blend because it may contain PyDrivers. It also needs to be after - * initializing space types and other internal data. - * - * However can't redo this at the moment. Solution is to load python - * before wm_homefile_read() or make py-drivers check if python is running. - * Will try fix when the crash can be repeated. */ - #ifdef WITH_PYTHON BPY_python_start(C, argc, argv); BPY_python_reset(C); @@ -374,30 +383,7 @@ void WM_init(bContext *C, int argc, const char **argv) } #endif - { - Main *bmain = CTX_data_main(C); - /* NOTE: logic here is from wm_file_read_post, - * call functions that depend on Python being initialized. */ - - /* normally 'wm_homefile_read' will do this, - * however python is not initialized when called from this function. - * - * unlikely any handlers are set but its possible, - * note that recovering the last session does its own callbacks. */ - CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first); - - BKE_callback_exec_null(bmain, BKE_CB_EVT_VERSION_UPDATE); - BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_POST); - if (is_factory_startup) { - BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST); - } - - wm_file_read_report(C, bmain); - - if (!G.background) { - CTX_wm_window_set(C, NULL); - } - } + wm_homefile_read_post(C, params_file_read_post); } void WM_init_splash(bContext *C) diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index 7009885495b..2fa5a68829e 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -24,6 +24,7 @@ #pragma once struct Main; +struct wmFileReadPost_Params; struct wmGenericCallback; struct wmOperatorType; @@ -64,11 +65,14 @@ struct wmHomeFileRead_Params { void wm_homefile_read_ex(struct bContext *C, const struct wmHomeFileRead_Params *params_homefile, struct ReportList *reports, - bool *r_is_factory_startup); + struct wmFileReadPost_Params **r_params_file_read_post); void wm_homefile_read(struct bContext *C, const struct wmHomeFileRead_Params *params_homefile, struct ReportList *reports); +void wm_homefile_read_post(struct bContext *C, + const struct wmFileReadPost_Params *params_file_read_post); + void wm_file_read_report(bContext *C, struct Main *bmain); void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action); -- cgit v1.2.3 From 83603ba26a506a9b666bb7b0b1b6856ac69e834c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 12 Aug 2021 16:10:10 +1000 Subject: Cleanup: comments/disabled code - Remove old comment for editors with weak syntax highlighting. - Remove disabled code to initialize Blender with a file path. - Remove file name references to function names since these were outdated, modern development environments can look up this info. --- source/blender/makesrna/intern/rna_access.c | 3 +- source/blender/windowmanager/intern/wm_init_exit.c | 45 ++++++++++------------ 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index ac734efed8b..a795d78a058 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -883,8 +883,7 @@ bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna) PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier) { - if (identifier[0] == '[' && identifier[1] == '"') { /* " (dummy comment to avoid confusing some - * function lists in text editors) */ + if (identifier[0] == '[' && identifier[1] == '"') { /* id prop lookup, not so common */ PropertyRNA *r_prop = NULL; PointerRNA r_ptr; /* only support single level props */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 4b839384853..a8d2e000108 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -222,7 +222,10 @@ static void sound_jack_sync_callback(Main *bmain, int mode, double time) } } -/* only called once, for startup */ +/** + * Initialize Blender and load the startup file & preferences + * (only called once). + */ void WM_init(bContext *C, int argc, const char **argv) { @@ -248,24 +251,22 @@ void WM_init(bContext *C, int argc, const char **argv) ED_undosys_type_init(); - BKE_library_callback_free_notifier_reference_set( - WM_main_remove_notifier_reference); /* lib_id.c */ - BKE_region_callback_free_gizmomap_set(wm_gizmomap_remove); /* screen.c */ + BKE_library_callback_free_notifier_reference_set(WM_main_remove_notifier_reference); + BKE_region_callback_free_gizmomap_set(wm_gizmomap_remove); BKE_region_callback_refresh_tag_gizmomap_set(WM_gizmomap_tag_refresh); - BKE_library_callback_remap_editor_id_reference_set( - WM_main_remap_editor_id_reference); /* lib_id.c */ - BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap); /* screen.c */ + BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference); + BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap); DEG_editors_set_update_cb(ED_render_id_flush_update, ED_render_scene_update); - ED_spacetypes_init(); /* editors/space_api/spacetype.c */ + ED_spacetypes_init(); ED_node_init_butfuncs(); BLF_init(); BLT_lang_init(); - /* Must call first before doing any '.blend' file reading, - * since versioning code may create new IDs... See T57066. */ + /* Must call first before doing any `.blend` file reading, + * since versioning code may create new IDs. See T57066. */ BLT_lang_set(NULL); /* Init icons before reading .blend files for preview icons, which can @@ -273,7 +274,7 @@ void WM_init(bContext *C, int argc, const char **argv) * for scripts that do background processing with preview icons. */ BKE_icons_init(BIFICONID_LAST); - /* reports can't be initialized before the wm, + /* Reports can't be initialized before the window-manager, * but keep before file reading, since that may report errors */ wm_init_reports(C); @@ -316,10 +317,14 @@ void WM_init(bContext *C, int argc, const char **argv) NULL, ¶ms_file_read_post); - /* Call again to set from userpreferences... */ + /* NOTE: leave `G_MAIN->name` set to an empty string since this + * matches behavior after loading a new file. */ + BLI_assert(G_MAIN->name[0] == '\0'); + + /* Call again to set from preferences. */ BLT_lang_set(NULL); - /* For fsMenu. Called here so can include user preference paths if needed. */ + /* For file-system. Called here so can include user preference paths if needed. */ ED_file_init(); /* That one is generated on demand, we need to be sure it's clear on init. */ @@ -328,12 +333,13 @@ void WM_init(bContext *C, int argc, const char **argv) if (!G.background) { #ifdef WITH_INPUT_NDOF - /* sets 3D mouse deadzone */ + /* Sets 3D mouse dead-zone. */ WM_ndof_deadzone_set(U.ndof_deadzone); #endif WM_init_opengl(); if (!WM_platform_support_perform_checks()) { + /* No attempt to avoid memory leaks here. */ exit(-1); } @@ -348,8 +354,7 @@ void WM_init(bContext *C, int argc, const char **argv) BPY_python_start(C, argc, argv); BPY_python_reset(C); #else - (void)argc; /* unused */ - (void)argv; /* unused */ + UNUSED_VARS(argc, argv); #endif if (!G.background) { @@ -366,14 +371,6 @@ void WM_init(bContext *C, int argc, const char **argv) wm_history_file_read(); - /* allow a path of "", this is what happens when making a new file */ -#if 0 - if (BKE_main_blendfile_path_from_global()[0] == '\0') { - BLI_join_dirfile( - G_MAIN->name, sizeof(G_MAIN->name), BKE_appdir_folder_default(), "untitled.blend"); - } -#endif - BLI_strncpy(G.lib, BKE_main_blendfile_path_from_global(), sizeof(G.lib)); #ifdef WITH_COMPOSITOR -- cgit v1.2.3 From 806bf3f4527cf35b4510a3934bc73604d3f2b253 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 12 Aug 2021 16:22:10 +1000 Subject: datadoc: add newlines to generated source files --- source/blender/datatoc/datatoc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/blender/datatoc/datatoc.c b/source/blender/datatoc/datatoc.c index 2ea7fbd9fbe..816353be9de 100644 --- a/source/blender/datatoc/datatoc.c +++ b/source/blender/datatoc/datatoc.c @@ -100,13 +100,12 @@ int main(int argc, char **argv) fprintf(fpout, "const int datatoc_%s_size = %d;\n", argv[1], (int)size); fprintf(fpout, "const char datatoc_%s[] = {\n", argv[1]); while (size--) { - /* if we want to open in an editor - * this is nicer to avoid very long lines */ -#ifdef VERBOSE + /* Even though this file is generated and doesn't need new-lines, + * these files may be loaded by developers when looking up symbols. + * Avoid a very long single line that may lock-up some editors. */ if (size % 32 == 31) { fprintf(fpout, "\n"); } -#endif // fprintf(fpout, "\\x%02x", getc(fpin)); fprintf(fpout, "%3d,", getc(fpin)); -- cgit v1.2.3 From 04ef718226e800c376e370c2d219444b44817a29 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 12 Aug 2021 16:44:39 +1000 Subject: RNA: include base types in RNA_struct_type_find_property search Add RNA_struct_type_find_property_no_base for use in the rare situations when this isn't desired. Resolves T90617, where sequence strip sub-types weren't detecting properties that exist in the base "Sequence" types. --- source/blender/makesrna/RNA_access.h | 1 + source/blender/makesrna/intern/rna_access.c | 17 ++++++++++++++++- source/blender/python/intern/bpy_rna.c | 4 ++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 1eeb68c3a23..f206bde4705 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -828,6 +828,7 @@ unsigned int RNA_struct_count_properties(StructRNA *srna); /* lower level functions for access to type properties */ const struct ListBase *RNA_struct_type_properties(StructRNA *srna); +PropertyRNA *RNA_struct_type_find_property_no_base(StructRNA *srna, const char *identifier); PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifier); FunctionRNA *RNA_struct_find_function(StructRNA *srna, const char *identifier); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index a795d78a058..216e58a9c52 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -967,11 +967,26 @@ const struct ListBase *RNA_struct_type_properties(StructRNA *srna) return &srna->cont.properties; } -PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifier) +PropertyRNA *RNA_struct_type_find_property_no_base(StructRNA *srna, const char *identifier) { return BLI_findstring_ptr(&srna->cont.properties, identifier, offsetof(PropertyRNA, identifier)); } +/** + * \note #RNA_struct_find_property is a higher level alternative to this function + * which takes a #PointerRNA instead of a #StructRNA. + */ +PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifier) +{ + for (; srna; srna = srna->base) { + PropertyRNA *prop = RNA_struct_type_find_property_no_base(srna, identifier); + if (prop != NULL) { + return prop; + } + } + return NULL; +} + FunctionRNA *RNA_struct_find_function(StructRNA *srna, const char *identifier) { #if 1 diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 6dd96f66cec..ac1a7f68885 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -4475,7 +4475,7 @@ static PyObject *pyrna_struct_meta_idprop_getattro(PyObject *cls, PyObject *attr if ((ret == NULL) /* || BPy_PropDeferred_CheckTypeExact(ret) */ ) { StructRNA *srna = srna_from_self(cls, "StructRNA.__getattr__"); if (srna) { - PropertyRNA *prop = RNA_struct_type_find_property(srna, PyUnicode_AsUTF8(attr)); + PropertyRNA *prop = RNA_struct_type_find_property_no_base(srna, PyUnicode_AsUTF8(attr)); if (prop) { PointerRNA tptr; PyErr_Clear(); /* Clear error from tp_getattro. */ @@ -4497,7 +4497,7 @@ static int pyrna_struct_meta_idprop_setattro(PyObject *cls, PyObject *attr, PyOb const char *attr_str = PyUnicode_AsUTF8(attr); if (srna && !pyrna_write_check() && - (is_deferred_prop || RNA_struct_type_find_property(srna, attr_str))) { + (is_deferred_prop || RNA_struct_type_find_property_no_base(srna, attr_str))) { PyErr_Format(PyExc_AttributeError, "pyrna_struct_meta_idprop_setattro() " "can't set in readonly state '%.200s.%S'", -- cgit v1.2.3 From ad2fb92e9c8ff302d3bddbe77d7504bd8d307aca Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 12 Aug 2021 17:42:04 +1000 Subject: Cleanup: remove redundant variable --- source/blender/makesrna/intern/rna_access.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 216e58a9c52..41c31dfebcb 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -991,10 +991,9 @@ FunctionRNA *RNA_struct_find_function(StructRNA *srna, const char *identifier) { #if 1 FunctionRNA *func; - StructRNA *type; - for (type = srna; type; type = type->base) { + for (; srna; srna = srna->base) { func = (FunctionRNA *)BLI_findstring_ptr( - &type->functions, identifier, offsetof(FunctionRNA, identifier)); + &srna->functions, identifier, offsetof(FunctionRNA, identifier)); if (func) { return func; } -- cgit v1.2.3 From 6293cf61312763152b00cb4f588061f7b281caf7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 12 Aug 2021 20:32:37 +1000 Subject: Fix T90630: Crash loading certain user preferences Clearing the window was done in wm_file_read_post which was deferred. This was needed as it left the context in an invalid state where the window was set but the screen wasn't. Crashing when setting up keymaps that attempted to access the scene from the window in the property update function. Regression in 497bc4d19977abc7b9e2c0f5024a23057e680954 --- source/blender/windowmanager/intern/wm_files.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 21c16e94097..b53ad0ee927 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1343,6 +1343,9 @@ void wm_homefile_read_ex(bContext *C, else { *r_params_file_read_post = MEM_mallocN(sizeof(struct wmFileReadPost_Params), __func__); **r_params_file_read_post = params_file_read_post; + + /* Match #wm_file_read_post which leaves the window cleared too. */ + CTX_wm_window_set(C, NULL); } } } -- cgit v1.2.3 From 215734bc522a8db532a626f460b85b66b0aa0a4c Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 11 Aug 2021 23:28:34 -0300 Subject: Fix T88386: Continuous Grab occasionally jumping on Arm64 MacOS During the processing of a continuous drag event, other mouse move events may be in the queue waiting to be processed. But when a mouse wrapping happens, these waiting mouse move events become out of date as they report a mouse position prior to wrapping. The current code ignores these events by comparing their `timestamp` to the time recorded in the last mouse wrapping. The bug happens because the computed value in `mach_absolute_time() * 1e-9` for some reason is incompatible with the value of `[event timestamp]`. Since macOS 10.6, we have a new way to get the amount of time the system has been awake. `[[NSProcessInfo processInfo] systemUptime]`. Using this updated method fixed the problem. Differential Revision: https://developer.blender.org/D12202 --- intern/ghost/intern/GHOST_SystemCocoa.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 2b4c3237c73..933e0c70cc8 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -1629,7 +1629,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr) y_accum + (y_mouse - warped_y_mouse)); /* This is the current time that matches NSEvent timestamp. */ - m_last_warp_timestamp = mach_absolute_time() * 1e-9; + m_last_warp_timestamp = [[NSProcessInfo processInfo] systemUptime]; } // Generate event -- cgit v1.2.3 From d6891d9bee2bd2073cb45e1ac9a04b2f03f05a9a Mon Sep 17 00:00:00 2001 From: Henrik Dick Date: Thu, 12 Aug 2021 14:22:19 +0200 Subject: Add Extras Dropdown Menu to Constraints Add Apply Constraint, Duplicate Constraint, and Copy To Selected operators, and include them in a menu similar to the menu for modifiers. The shortcuts in the extras menu are also matched to modifiers. All the here added operators are intended to work exactly like the analogous ones for modifiers. That means the apply operator should apply a constraint as if it was first in the list, just like modifiers do. I have added the same warning message as for modifiers when that happens. The decision to use this approach of appling the constraint as if it was first, was made for consistency with modifiers. People are already used to how it works there. Is also provides more intricate control over the applied transforms, then just applying all constraints up to that one. Apply all constraints is already kinda implemented in Bake Animation. Reviewed By: HooglyBoogly, sybren, #user_interface Differential Revision: https://developer.blender.org/D10914 --- .../keyconfig/keymap_data/blender_default.py | 2 + .../keymap_data/industry_compatible_data.py | 1 + source/blender/blenkernel/BKE_constraint.h | 22 ++ source/blender/blenkernel/intern/constraint.c | 105 +++++++ .../blender/draw/engines/overlay/overlay_extra.c | 3 +- .../editors/interface/interface_templates.c | 94 +++++- source/blender/editors/object/object_constraint.c | 324 ++++++++++++++++++++- source/blender/editors/object/object_intern.h | 3 + source/blender/editors/object/object_ops.c | 3 + source/blender/makesrna/intern/rna_constraint.c | 6 + 10 files changed, 543 insertions(+), 20 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 7abbbaddb5c..d9538930f33 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -775,6 +775,8 @@ def km_property_editor(_params): # Constraint panels ("constraint.delete", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}), ("constraint.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("constraint.copy", {"type": 'D', "value": 'PRESS', "shift": True}, None), + ("constraint.apply", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("report", True)]}), ]) return keymap diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index cb9970f600b..dba94d71a43 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -465,6 +465,7 @@ def km_property_editor(params): # Constraint panels ("constraint.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("report", True)]}), ("constraint.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("constraint.copy", {"type": 'D', "value": 'PRESS', "ctrl": True}, None), ]) return keymap diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h index 575df93a9fc..784b395dfa5 100644 --- a/source/blender/blenkernel/BKE_constraint.h +++ b/source/blender/blenkernel/BKE_constraint.h @@ -192,6 +192,28 @@ bool BKE_constraint_remove_ex(ListBase *list, bool clear_dep); bool BKE_constraint_remove(ListBase *list, struct bConstraint *con); +bool BKE_constraint_apply_for_object(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + struct bConstraint *con); +bool BKE_constraint_apply_and_remove_for_object(struct Depsgraph *depsgraph, + struct Scene *scene, + ListBase /*bConstraint*/ *constraints, + struct Object *ob, + struct bConstraint *con); + +bool BKE_constraint_apply_for_pose(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + struct bPoseChannel *pchan, + struct bConstraint *con); +bool BKE_constraint_apply_and_remove_for_pose(struct Depsgraph *depsgraph, + struct Scene *scene, + ListBase /*bConstraint*/ *constraints, + struct Object *ob, + struct bConstraint *con, + struct bPoseChannel *pchan); + void BKE_constraint_panel_expand(struct bConstraint *con); /* Constraints + Proxies function prototypes */ diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 0da29ded13d..4b26022039e 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -5677,6 +5677,111 @@ bool BKE_constraint_remove_ex(ListBase *list, Object *ob, bConstraint *con, bool return false; } +/* Apply the specified constraint in the given constraint stack */ +bool BKE_constraint_apply_for_object(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + bConstraint *con) +{ + if (!con) { + return false; + } + + const float ctime = BKE_scene_frame_get(scene); + + bConstraint *new_con = BKE_constraint_duplicate_ex(con, 0, !ID_IS_LINKED(ob)); + ListBase single_con = {new_con, new_con}; + + bConstraintOb *cob = BKE_constraints_make_evalob( + depsgraph, scene, ob, NULL, CONSTRAINT_OBTYPE_OBJECT); + /* Undo the effect of the current constraint stack evaluation. */ + mul_m4_m4m4(cob->matrix, ob->constinv, cob->matrix); + + /* Evaluate single constraint. */ + BKE_constraints_solve(depsgraph, &single_con, cob, ctime); + /* Copy transforms back. This will leave the object in a bad state + * as ob->constinv will be wrong until next evaluation. */ + BKE_constraints_clear_evalob(cob); + + /* Free the copied constraint. */ + BKE_constraint_free_data(new_con); + BLI_freelinkN(&single_con, new_con); + + /* Apply transform from matrix. */ + BKE_object_apply_mat4(ob, ob->obmat, true, true); + + return true; +} + +bool BKE_constraint_apply_and_remove_for_object(Depsgraph *depsgraph, + Scene *scene, + ListBase /*bConstraint*/ *constraints, + Object *ob, + bConstraint *con) +{ + if (!BKE_constraint_apply_for_object(depsgraph, scene, ob, con)) { + return false; + } + + return BKE_constraint_remove_ex(constraints, ob, con, true); +} + +bool BKE_constraint_apply_for_pose( + Depsgraph *depsgraph, Scene *scene, Object *ob, bPoseChannel *pchan, bConstraint *con) +{ + if (!con) { + return false; + } + + const float ctime = BKE_scene_frame_get(scene); + + bConstraint *new_con = BKE_constraint_duplicate_ex(con, 0, !ID_IS_LINKED(ob)); + ListBase single_con; + single_con.first = new_con; + single_con.last = new_con; + + float vec[3]; + copy_v3_v3(vec, pchan->pose_mat[3]); + + bConstraintOb *cob = BKE_constraints_make_evalob( + depsgraph, scene, ob, pchan, CONSTRAINT_OBTYPE_BONE); + /* Undo the effects of currently applied constraints. */ + mul_m4_m4m4(cob->matrix, pchan->constinv, cob->matrix); + /* Evaluate single constraint. */ + BKE_constraints_solve(depsgraph, &single_con, cob, ctime); + BKE_constraints_clear_evalob(cob); + + /* Free the copied constraint. */ + BKE_constraint_free_data(new_con); + BLI_freelinkN(&single_con, new_con); + + /* Prevent constraints breaking a chain. */ + if (pchan->bone->flag & BONE_CONNECTED) { + copy_v3_v3(pchan->pose_mat[3], vec); + } + + /* Apply transform from matrix. */ + float mat[4][4]; + BKE_armature_mat_pose_to_bone(pchan, pchan->pose_mat, mat); + BKE_pchan_apply_mat4(pchan, mat, true); + + return true; +} + +bool BKE_constraint_apply_and_remove_for_pose(Depsgraph *depsgraph, + Scene *scene, + ListBase /*bConstraint*/ *constraints, + Object *ob, + bConstraint *con, + bPoseChannel *pchan) +{ + if (!BKE_constraint_apply_for_pose(depsgraph, scene, ob, pchan, con)) { + return false; + } + + return BKE_constraint_remove_ex(constraints, ob, con, true); +} + void BKE_constraint_panel_expand(bConstraint *con) { con->ui_expand_flag |= UI_PANEL_DATA_EXPAND_ROOT; diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index f5be9c846d1..2a9080eb217 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -1357,7 +1357,8 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb, } } } - BKE_constraints_clear_evalob(cob); + /* NOTE: Don't use BKE_constraints_clear_evalob here as that will reset ob->constinv. */ + MEM_freeN(cob); } } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 766840909cc..47ff0c9fd3c 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2621,6 +2621,72 @@ static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v) ED_object_constraint_active_set(ob_v, con_v); } +static void constraint_ops_extra_draw(bContext *C, uiLayout *layout, void *con_v) +{ + PointerRNA op_ptr; + uiLayout *row; + bConstraint *con = (bConstraint *)con_v; + + PointerRNA ptr; + Object *ob = ED_object_active_context(C); + + RNA_pointer_create(&ob->id, &RNA_Constraint, con, &ptr); + uiLayoutSetContextPointer(layout, "constraint", &ptr); + uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + + uiLayoutSetUnitsX(layout, 4.0f); + + /* Apply. */ + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"), + ICON_CHECKMARK, + "CONSTRAINT_OT_apply"); + + /* Duplicate. */ + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Duplicate"), + ICON_DUPLICATE, + "CONSTRAINT_OT_copy"); + + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy to Selected"), + 0, + "CONSTRAINT_OT_copy_to_selected"); + + uiItemS(layout); + + /* Move to first. */ + row = uiLayoutColumn(layout, false); + uiItemFullO(row, + "CONSTRAINT_OT_move_to_index", + IFACE_("Move to First"), + ICON_TRIA_UP, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &op_ptr); + RNA_int_set(&op_ptr, "index", 0); + if (!con->prev) { + uiLayoutSetEnabled(row, false); + } + + /* Move to last. */ + row = uiLayoutColumn(layout, false); + uiItemFullO(row, + "CONSTRAINT_OT_move_to_index", + IFACE_("Move to Last"), + ICON_TRIA_DOWN, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &op_ptr); + ListBase *constraint_list = ED_object_constraint_list_from_constraint(ob, con, NULL); + RNA_int_set(&op_ptr, "index", BLI_listbase_count(constraint_list) - 1); + if (!con->next) { + uiLayoutSetEnabled(row, false); + } +} + static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con) { bPoseChannel *pchan = BKE_pose_channel_active(ob); @@ -2652,11 +2718,13 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co UI_block_emboss_set(block, UI_EMBOSS); + uiLayout *row = uiLayoutRow(layout, true); + if (proxy_protected == 0) { - uiItemR(layout, &ptr, "name", 0, "", ICON_NONE); + uiItemR(row, &ptr, "name", 0, "", ICON_NONE); } else { - uiItemL(layout, con->name, ICON_NONE); + uiItemL(row, con->name, ICON_NONE); } /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */ @@ -2697,22 +2765,22 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co UI_block_emboss_set(block, UI_EMBOSS); } else { - /* enabled */ - UI_block_emboss_set(block, UI_EMBOSS_NONE_OR_STATUS); - uiItemR(layout, &ptr, "mute", 0, "", 0); - UI_block_emboss_set(block, UI_EMBOSS); + /* Enabled eye icon. */ + uiItemR(row, &ptr, "enabled", 0, "", ICON_NONE); - uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + /* Extra operators menu. */ + uiItemMenuF(row, "", ICON_DOWNARROW_HLT, constraint_ops_extra_draw, con); /* Close 'button' - emboss calls here disable drawing of 'button' behind X */ - UI_block_emboss_set(block, UI_EMBOSS_NONE); - uiItemO(layout, "", ICON_X, "CONSTRAINT_OT_delete"); - UI_block_emboss_set(block, UI_EMBOSS); - - /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */ - uiItemS(layout); + sub = uiLayoutRow(row, false); + uiLayoutSetEmboss(sub, UI_EMBOSS_NONE); + uiLayoutSetOperatorContext(sub, WM_OP_INVOKE_DEFAULT); + uiItemO(sub, "", ICON_X, "CONSTRAINT_OT_delete"); } + /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */ + uiItemS(layout); + /* Set but-locks for protected settings (magic numbers are used here!) */ if (proxy_protected) { UI_block_lock_set(block, true, TIP_("Cannot edit Proxy-Protected Constraint")); diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 3d0213f1830..e0419e0a4cc 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -1483,13 +1483,11 @@ static int constraint_delete_exec(bContext *C, wmOperator *op) /* free the constraint */ if (BKE_constraint_remove_ex(lb, ob, con, true)) { - /* there's no active constraint now, so make sure this is the case */ - BKE_constraints_active_set(&ob->constraints, NULL); /* needed to set the flags on posebones correctly */ ED_object_constraint_update(bmain, ob); /* relations */ - DEG_relations_tag_update(CTX_data_main(C)); + DEG_relations_tag_update(bmain); /* notifiers */ WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob); @@ -1507,10 +1505,10 @@ static int constraint_delete_exec(bContext *C, wmOperator *op) static int constraint_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - if (edit_constraint_invoke_properties(C, op, event, &retval)) { - return constraint_delete_exec(C, op); + if (!edit_constraint_invoke_properties(C, op, event, &retval)) { + return OPERATOR_CANCELLED; } - return OPERATOR_CANCELLED; + return constraint_delete_exec(C, op); } void CONSTRAINT_OT_delete(wmOperatorType *ot) @@ -1533,6 +1531,320 @@ void CONSTRAINT_OT_delete(wmOperatorType *ot) /** \} */ +/* ------------------------------------------------------------------- */ +/** \name Apply Constraint Operator + * \{ */ + +static int constraint_apply_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Main *bmain = CTX_data_main(C); + Object *ob = ED_object_active_context(C); + bConstraint *con = edit_constraint_property_get(C, op, ob, 0); + bPoseChannel *pchan; + ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan); + + /* Store name temporarily for report. */ + char name[MAX_NAME]; + strcpy(name, con->name); + const bool is_first_constraint = con != constraints->first; + + /* Copy the constraint. */ + bool success; + if (pchan) { + success = BKE_constraint_apply_and_remove_for_pose( + depsgraph, scene, constraints, ob, con, pchan); + } + else { + success = BKE_constraint_apply_and_remove_for_object(depsgraph, scene, constraints, ob, con); + } + + if (!success) { + /* Couldn't remove due to some invalid data. */ + return OPERATOR_CANCELLED; + } + + /* Update for any children that may get moved. */ + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + + /* Needed to set the flags on posebones correctly. */ + ED_object_constraint_update(bmain, ob); + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob); + if (pchan) { + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + } + else { + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); + } + + if (RNA_boolean_get(op->ptr, "report")) { + if (is_first_constraint) { + BKE_report(op->reports, + RPT_INFO, + "Applied constraint was not first, result may not be as expected"); + } + else { + /* Only add this report if the operator didn't cause another one. The purpose here is + * to alert that something happened, and the previous report will do that anyway. */ + BKE_reportf(op->reports, RPT_INFO, "Applied constraint: %s", name); + } + } + + return OPERATOR_FINISHED; +} + +static int constraint_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + if (!edit_constraint_invoke_properties(C, op, event, &retval)) { + return OPERATOR_CANCELLED; + } + return constraint_apply_exec(C, op); +} + +void CONSTRAINT_OT_apply(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Apply Constraint"; + ot->idname = "CONSTRAINT_OT_apply"; + ot->description = "Apply constraint and remove from the stack"; + + /* callbacks */ + ot->invoke = constraint_apply_invoke; + ot->exec = constraint_apply_exec; + ot->poll = edit_constraint_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + edit_constraint_properties(ot); + edit_constraint_report_property(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ +/** \name Copy Constraint Operator + * \{ */ + +static int constraint_copy_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = ED_object_active_context(C); + bConstraint *con = edit_constraint_property_get(C, op, ob, 0); + bPoseChannel *pchan; + ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan); + + /* Store name temporarily for report. */ + char name[MAX_NAME]; + strcpy(name, con->name); + + /* Copy the constraint. */ + bConstraint *copy_con; + if (pchan) { + copy_con = BKE_constraint_copy_for_pose(ob, pchan, con); + } + else { + copy_con = BKE_constraint_copy_for_object(ob, con); + } + + if (!copy_con) { + /* Couldn't remove due to some invalid data. */ + return OPERATOR_CANCELLED; + } + /* Move constraint to correct position. */ + const int new_index = BLI_findindex(constraints, con) + 1; + const int current_index = BLI_findindex(constraints, copy_con); + BLI_assert(new_index >= 0); + BLI_assert(current_index >= 0); + BLI_listbase_link_move(constraints, copy_con, new_index - current_index); + + /* Needed to set the flags on posebones correctly. */ + ED_object_constraint_update(bmain, ob); + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_ADDED, ob); + + if (RNA_boolean_get(op->ptr, "report")) { + BKE_reportf(op->reports, RPT_INFO, "Copied constraint: %s", name); + } + + return OPERATOR_FINISHED; +} + +static int constraint_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + if (!edit_constraint_invoke_properties(C, op, event, &retval)) { + return OPERATOR_CANCELLED; + } + return constraint_copy_exec(C, op); +} + +void CONSTRAINT_OT_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Duplicate Constraint"; + ot->idname = "CONSTRAINT_OT_copy"; + ot->description = "Duplicate constraint at the same position in the stack"; + + /* callbacks */ + ot->invoke = constraint_copy_invoke; + ot->exec = constraint_copy_exec; + ot->poll = edit_constraint_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + edit_constraint_properties(ot); + edit_constraint_report_property(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ +/** \name Copy Constraint To Selected Operator + * \{ */ + +static int constraint_copy_to_selected_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *obact = ED_object_active_context(C); + bConstraint *con = edit_constraint_property_get(C, op, obact, 0); + bPoseChannel *pchan; + ED_object_constraint_list_from_constraint(obact, con, &pchan); + + if (pchan) { + /* Don't do anything if bone doesn't exist or doesn't have any constraints. */ + if (pchan->constraints.first == NULL) { + BKE_report(op->reports, RPT_ERROR, "No constraints for copying"); + return OPERATOR_CANCELLED; + } + + Object *prev_ob = NULL; + + /* Copy all constraints from active posebone to all selected posebones. */ + CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) { + /* If we're not handling the object we're copying from, copy all constraints over. */ + if (pchan == chan) { + continue; + } + + BKE_constraint_copy_for_pose(ob, chan, con); + /* Update flags (need to add here, not just copy). */ + chan->constflag |= pchan->constflag; + + if (prev_ob == ob) { + continue; + } + + BKE_pose_tag_recalc(bmain, ob->pose); + DEG_id_tag_update((ID *)ob, ID_RECALC_GEOMETRY); + prev_ob = ob; + } + CTX_DATA_END; + } + else { + /* Copy all constraints from active object to all selected objects. */ + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { + /* If we're not handling the object we're copying from, copy all constraints over. */ + if (obact == ob) { + continue; + } + + BKE_constraint_copy_for_object(ob, con); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM); + } + CTX_DATA_END; + } + + /* Force depsgraph to get recalculated since new relationships added. */ + DEG_relations_tag_update(bmain); + + WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, NULL); + + return OPERATOR_FINISHED; +} + +static int constraint_copy_to_selected_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + if (!edit_constraint_invoke_properties(C, op, event, &retval)) { + return retval; + } + return constraint_copy_to_selected_exec(C, op); +} + +static bool constraint_copy_to_selected_poll(bContext *C) +{ + PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint); + Object *obact = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); + bConstraint *con = ptr.data; + bPoseChannel *pchan; + ED_object_constraint_list_from_constraint(obact, con, &pchan); + + if (pchan) { + bool found = false; + CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, UNUSED(ob)) { + if (pchan != chan) { + /** NOTE: Can not return here, because CTX_DATA_BEGIN_WITH_ID allocated + * a list that needs to be freed by CTX_DATA_END. */ + found = true; + break; + } + } + CTX_DATA_END; + if (found) { + return true; + } + + CTX_wm_operator_poll_msg_set(C, "No other bones are selected"); + return false; + } + + if (!obact) { + CTX_wm_operator_poll_msg_set(C, "No selected object to copy from"); + return false; + } + + bool found = false; + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if (ob != obact) { + /** NOTE: Can not return here, because CTX_DATA_BEGIN allocated + * a list that needs to be freed by CTX_DATA_END. */ + found = true; + break; + } + } + CTX_DATA_END; + if (found) { + return true; + } + + CTX_wm_operator_poll_msg_set(C, "No other objects are selected"); + return false; +} + +void CONSTRAINT_OT_copy_to_selected(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Constraint To Selected"; + ot->idname = "CONSTRAINT_OT_copy_to_selected"; + ot->description = "Copy constraint to other selected objects/bones"; + + /* api callbacks */ + ot->exec = constraint_copy_to_selected_exec; + ot->invoke = constraint_copy_to_selected_invoke; + ot->poll = constraint_copy_to_selected_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + edit_constraint_properties(ot); +} + +/** \} */ + /* ------------------------------------------------------------------- */ /** \name Move Down Constraint Operator * \{ */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 6299fdcc7f7..10e016738d0 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -226,6 +226,9 @@ void POSE_OT_ik_add(struct wmOperatorType *ot); void POSE_OT_ik_clear(struct wmOperatorType *ot); void CONSTRAINT_OT_delete(struct wmOperatorType *ot); +void CONSTRAINT_OT_apply(struct wmOperatorType *ot); +void CONSTRAINT_OT_copy(struct wmOperatorType *ot); +void CONSTRAINT_OT_copy_to_selected(struct wmOperatorType *ot); void CONSTRAINT_OT_move_up(struct wmOperatorType *ot); void CONSTRAINT_OT_move_to_index(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index a438c760d3b..c1928cf7f8a 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -183,6 +183,9 @@ void ED_operatortypes_object(void) WM_operatortype_append(POSE_OT_ik_add); WM_operatortype_append(POSE_OT_ik_clear); WM_operatortype_append(CONSTRAINT_OT_delete); + WM_operatortype_append(CONSTRAINT_OT_apply); + WM_operatortype_append(CONSTRAINT_OT_copy); + WM_operatortype_append(CONSTRAINT_OT_copy_to_selected); WM_operatortype_append(CONSTRAINT_OT_move_up); WM_operatortype_append(CONSTRAINT_OT_move_down); WM_operatortype_append(CONSTRAINT_OT_move_to_index); diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index e36d052d27c..5968c8bac8f 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -3509,6 +3509,12 @@ void RNA_def_constraint(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); RNA_def_property_ui_icon(prop, ICON_HIDE_OFF, -1); + prop = RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", CONSTRAINT_OFF); + RNA_def_property_ui_text(prop, "Enabled", "Use the results of this constraint"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + RNA_def_property_ui_icon(prop, ICON_HIDE_ON, 1); + prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0); -- cgit v1.2.3 From dc8844f8ef6a5cfa2be1035162d39e6d83c83d7c Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 4 Jun 2021 16:39:12 +0200 Subject: Fix T88498: 'Clear Parent' does not clear parent_bone Clearing the parent from the UI using the X (or from python) clears the `parsubstr` and set `partype` back to `PAROBJECT`. Using the Clear Parent operator would leave the `parsubstr` (and thus `parent_bone`) untouched even though this operator claims to "clear parenting relationship completely" (it also removes parent deform modifiers for example). So now, also clear `parsubstr` and set back to `PAROBJECT` [which is default]. Maniphest Tasks: T88498 Differential Revision: https://developer.blender.org/D11503 --- source/blender/editors/object/object_relations.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index c0b954f3cff..ec72ff11683 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -570,6 +570,8 @@ void ED_object_parent_clear(Object *ob, const int type) /* clear parenting relationship completely */ ob->parent = NULL; + ob->partype = PAROBJECT; + ob->parsubstr[0] = 0; break; } case CLEAR_PARENT_KEEP_TRANSFORM: { -- cgit v1.2.3 From 333c3c92ab83c1b18e05a0e765d72f66fc5a5466 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 30 Jul 2021 11:58:02 +0200 Subject: Fix T89805: NLA crash without active track Was reported for a file which does not have an active track set in AnimData even though it was in strip twek mode (but this was accessed in is_nlatrack_evaluatable()). Root cause for this is not totally clear, but I assume the situation is described as part T87681 (and is fixed in D11052). This patch here just prevents the crash for files that are already in the borked state. Reviewers: sybren Maniphest Tasks: T89805 Differential Revision: https://developer.blender.org/D12085 --- source/blender/blenkernel/intern/anim_sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 10a865880f2..92b0db5b214 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -2671,7 +2671,7 @@ static void animsys_create_action_track_strip(const AnimData *adt, static bool is_nlatrack_evaluatable(const AnimData *adt, const NlaTrack *nlt) { /* Skip disabled tracks unless it contains the tweaked strip. */ - const bool contains_tweak_strip = (adt->flag & ADT_NLA_EDIT_ON) && + const bool contains_tweak_strip = (adt->flag & ADT_NLA_EDIT_ON) && adt->act_track && (nlt->index == adt->act_track->index); if ((nlt->flag & NLATRACK_DISABLED) && !contains_tweak_strip) { return false; -- cgit v1.2.3 From 3930b8c69e274df4aab91f7469510c8636a96cbf Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 21 Apr 2021 18:08:50 +0200 Subject: Fix NLA action cannot be unlinked in certain cases The poll for unlinking calls `nla_panel_context` without providing an adt pointer, and there is a check for this pointer in `nla_panel_context` leading to never returning true if it is not provided. (this is fine if there are tracks already, poll would succeed in this case, `nla_panel_context` goes a different code path then) Same call to `nla_panel_context` is also done in the beginning of the corresponding unlink exec function (but this time providing the pointer because it is used later), so it makes sense to do the same thing in the poll function. Equal check is also done in the panel poll function, so now these are all in sync. Part of T87681. Maniphest Tasks: T87681 Differential Revision: https://developer.blender.org/D11041 --- source/blender/editors/space_nla/nla_channels.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 0498964c549..1b87a8c6b9d 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -562,7 +562,8 @@ void NLA_OT_action_pushdown(wmOperatorType *ot) static bool nla_action_unlink_poll(bContext *C) { if (ED_operator_nla_active(C)) { - return nla_panel_context(C, NULL, NULL, NULL); + PointerRNA adt_ptr; + return (nla_panel_context(C, &adt_ptr, NULL, NULL) && (adt_ptr.data != NULL)); } /* something failed... */ -- cgit v1.2.3 From f801d40dafc06be2c0827e1b858c84800f4ddad8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 13 Aug 2021 01:00:50 +1000 Subject: Fix T89241: 3D Text "Scale to Fit" wraps onto the second line Disable wrapping when "scale to fit" is used, assert the error is small so an invalid scale-to-fit value wont go by unnoticed. --- source/blender/blenkernel/intern/font.c | 95 +++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 37fc14911fe..de838f1b3cd 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -715,6 +715,11 @@ typedef struct VFontToCurveIter { float max; } bisect; bool ok; + /** + * Disables checking if word wrapping is needed to fit the text-box width. + * Currently only used when scale-to-fit is enabled. + */ + bool word_wrap; int status; } VFontToCurveIter; @@ -777,6 +782,7 @@ static bool vfont_to_curve(Object *ob, char32_t ascii; bool ok = false; const float font_size = cu->fsize * iter_data->scale_to_fit; + const bool word_wrap = iter_data->word_wrap; const float xof_scale = cu->xof / font_size; const float yof_scale = cu->yof / font_size; int last_line = -1; @@ -947,43 +953,59 @@ static bool vfont_to_curve(Object *ob, twidth = char_width(cu, che, info); - /* Calculate positions */ - if ((tb_scale.w != 0.0f) && (ct->dobreak == 0) && - (((xof - tb_scale.x) + twidth) > xof_scale + tb_scale.w)) { - // CLOG_WARN(&LOG, "linewidth exceeded: %c%c%c...", mem[i], mem[i+1], mem[i+2]); - for (j = i; j && (mem[j] != '\n') && (chartransdata[j].dobreak == 0); j--) { - bool dobreak = false; - if (ELEM(mem[j], ' ', '-')) { - ct -= (i - (j - 1)); - cnr -= (i - (j - 1)); - if (mem[j] == ' ') { - wsnr--; + /* Calculate positions. */ + + if ((tb_scale.w != 0.0f) && (ct->dobreak == 0)) { /* May need wrapping. */ + const float x_available = xof_scale + tb_scale.w; + const float x_used = (xof - tb_scale.x) + twidth; + + if (word_wrap == false) { + /* When scale to fit is used, don't do any wrapping. + * + * Floating precision error can cause the text to be slightly larger. + * Assert this is a small value as large values indicate incorrect + * calculations with scale-to-fit which shouldn't be ignored. See T89241. */ + if (x_used > x_available) { + BLI_assert_msg(compare_ff_relative(x_used, x_available, FLT_EPSILON, 64), + "VFontToCurveIter.scale_to_fit not set correctly!"); + } + } + else if (x_used > x_available) { + // CLOG_WARN(&LOG, "linewidth exceeded: %c%c%c...", mem[i], mem[i+1], mem[i+2]); + for (j = i; j && (mem[j] != '\n') && (chartransdata[j].dobreak == 0); j--) { + bool dobreak = false; + if (ELEM(mem[j], ' ', '-')) { + ct -= (i - (j - 1)); + cnr -= (i - (j - 1)); + if (mem[j] == ' ') { + wsnr--; + } + if (mem[j] == '-') { + wsnr++; + } + i = j - 1; + xof = ct->xof; + ct[1].dobreak = 1; + custrinfo[i + 1].flag |= CU_CHINFO_WRAP; + dobreak = true; } - if (mem[j] == '-') { - wsnr++; + else if (chartransdata[j].dobreak) { + // CLOG_WARN(&LOG, "word too long: %c%c%c...", mem[j], mem[j+1], mem[j+2]); + ct->dobreak = 1; + custrinfo[i + 1].flag |= CU_CHINFO_WRAP; + ct -= 1; + cnr -= 1; + i--; + xof = ct->xof; + dobreak = true; } - i = j - 1; - xof = ct->xof; - ct[1].dobreak = 1; - custrinfo[i + 1].flag |= CU_CHINFO_WRAP; - dobreak = true; - } - else if (chartransdata[j].dobreak) { - // CLOG_WARN(&LOG, "word too long: %c%c%c...", mem[j], mem[j+1], mem[j+2]); - ct->dobreak = 1; - custrinfo[i + 1].flag |= CU_CHINFO_WRAP; - ct -= 1; - cnr -= 1; - i--; - xof = ct->xof; - dobreak = true; - } - if (dobreak) { - if (tb_scale.h == 0.0f) { - /* NOTE: If underlined text is truncated away, the extra space is also truncated. */ - custrinfo[i + 1].flag |= CU_CHINFO_OVERFLOW; + if (dobreak) { + if (tb_scale.h == 0.0f) { + /* NOTE: If underlined text is truncated away, the extra space is also truncated. */ + custrinfo[i + 1].flag |= CU_CHINFO_OVERFLOW; + } + goto makebreak; } - goto makebreak; } } } @@ -1545,6 +1567,7 @@ static bool vfont_to_curve(Object *ob, const float total_text_height = lnr * linedist; iter_data->scale_to_fit = tb_scale.h / total_text_height; iter_data->status = VFONT_TO_CURVE_SCALE_ONCE; + iter_data->word_wrap = false; } } else if (tb_scale.h == 0.0f) { @@ -1552,10 +1575,10 @@ static bool vfont_to_curve(Object *ob, if (longest_line_length > tb_scale.w) { /* We make sure longest line before it broke can fit here. */ float scale_to_fit = tb_scale.w / longest_line_length; - scale_to_fit -= FLT_EPSILON; iter_data->scale_to_fit = scale_to_fit; iter_data->status = VFONT_TO_CURVE_SCALE_ONCE; + iter_data->word_wrap = false; } } } @@ -1616,6 +1639,7 @@ static bool vfont_to_curve(Object *ob, else { iter_data->scale_to_fit = iter_data->bisect.min; iter_data->status = VFONT_TO_CURVE_SCALE_ONCE; + iter_data->word_wrap = false; } } } @@ -1685,6 +1709,7 @@ bool BKE_vfont_to_curve_ex(Object *ob, VFontToCurveIter data = { .iteraction = cu->totbox * FONT_TO_CURVE_SCALE_ITERATIONS, .scale_to_fit = 1.0f, + .word_wrap = true, .ok = true, .status = VFONT_TO_CURVE_INIT, }; -- cgit v1.2.3 From e0fd5fef1236eff14d1c6dc1c7f6ba27a9ff5787 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 12 Aug 2021 17:36:37 +0200 Subject: Geometry Nodes: tag normals dirty after join Under some circumstances the normals were not tagged dirty even though they are. --- source/blender/blenkernel/intern/geometry_set_instances.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 90a97264c8f..32a65ab47bf 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -491,6 +491,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span Date: Thu, 12 Aug 2021 14:12:14 -0500 Subject: Cleanup: Remove unused includes I noticed this file was recompiling when adding a node. --- source/blender/makesrna/intern/rna_curveprofile.c | 27 ----------------------- 1 file changed, 27 deletions(-) diff --git a/source/blender/makesrna/intern/rna_curveprofile.c b/source/blender/makesrna/intern/rna_curveprofile.c index b3ab8cc15a2..66c0fc72c7a 100644 --- a/source/blender/makesrna/intern/rna_curveprofile.c +++ b/source/blender/makesrna/intern/rna_curveprofile.c @@ -23,9 +23,6 @@ #include "DNA_curve_types.h" #include "DNA_curveprofile_types.h" -#include "DNA_texture_types.h" - -#include "BLI_utildefines.h" #include "RNA_define.h" #include "rna_internal.h" @@ -37,31 +34,7 @@ # include "RNA_access.h" -# include "DNA_image_types.h" -# include "DNA_material_types.h" -# include "DNA_movieclip_types.h" -# include "DNA_node_types.h" -# include "DNA_object_types.h" -# include "DNA_particle_types.h" -# include "DNA_sequence_types.h" - -# include "MEM_guardedalloc.h" - -# include "BKE_colorband.h" # include "BKE_curveprofile.h" -# include "BKE_image.h" -# include "BKE_linestyle.h" -# include "BKE_movieclip.h" -# include "BKE_node.h" - -# include "DEG_depsgraph.h" - -# include "ED_node.h" - -# include "IMB_colormanagement.h" -# include "IMB_imbuf.h" - -# include "SEQ_sequencer.h" /** * Set both handle types for all selected points in the profile-- faster than changing types -- cgit v1.2.3 From 399b6ec76c80a8f343747a0459bb3d3df514555f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 13 Aug 2021 09:27:29 +1000 Subject: Mesh: optimize normal calculation Optimize mesh normal calculation. - Remove the intermediate `lnors_weighted` array, accumulate directly into the normal array using a spin-lock for thread safety. - Remove single threaded iteration over loops (normal calculation is now fully multi-threaded). - Remove stack array (alloca) for pre-calculating edge-directions. Summary of Performance Characteristics: - The largest gains are for single high poly meshes, with isolated normal-calculation benchmarks of meshes over ~1.5 million showing 2x+ speedup, ~25 million polygons are ~2.85x faster. - Single lower poly meshes (250k polys) can be ~2x slower. Since these meshes aren't normally a bottleneck, and this problem isn't noticeable on large scenes, we considered the performance trade-off reasonable. - The performance difference reduces with larger scenes, tests with production files from "Sprite Fight" showing the same or slightly better overall performance. NOTE: tested on a AMD Ryzen TR 3970X 32-Core. For more details & benchmarking scripts, see the patch description. Reviewed By: mont29 Ref D11993 --- source/blender/blenkernel/intern/mesh_normals.cc | 135 ++++++++++++++--------- 1 file changed, 82 insertions(+), 53 deletions(-) diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 6bd08b3dbe0..b86332097fa 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -50,6 +50,8 @@ #include "BKE_global.h" #include "BKE_mesh.h" +#include "atomic_ops.h" + // #define DEBUG_TIME #ifdef DEBUG_TIME @@ -59,6 +61,46 @@ static CLG_LogRef LOG = {"bke.mesh_normals"}; +/* -------------------------------------------------------------------- */ +/** \name Private Utility Functions + * \{ */ + +/** + * A thread-safe version of #add_v3_v3 that uses a spin-lock. + * + * \note Avoid using this when the chance of contention is high. + */ +static void add_v3_v3_atomic(float r[3], const float a[3]) +{ +#define FLT_EQ_NONAN(_fa, _fb) (*((const uint32_t *)&_fa) == *((const uint32_t *)&_fb)) + + float virtual_lock = r[0]; + while (true) { + /* This loops until following conditions are met: + * - `r[0]` has same value as virtual_lock (i.e. it did not change since last try). + * - `r[0]` was not `FLT_MAX`, i.e. it was not locked by another thread. */ + const float test_lock = atomic_cas_float(&r[0], virtual_lock, FLT_MAX); + if (_ATOMIC_LIKELY(FLT_EQ_NONAN(test_lock, virtual_lock) && (test_lock != FLT_MAX))) { + break; + } + virtual_lock = test_lock; + } + virtual_lock += a[0]; + r[1] += a[1]; + r[2] += a[2]; + + /* Second atomic operation to 'release' + * our lock on that vector and set its first scalar value. */ + /* Note that we do not need to loop here, since we 'locked' `r[0]`, + * nobody should have changed it in the mean time. */ + virtual_lock = atomic_cas_float(&r[0], FLT_MAX, virtual_lock); + BLI_assert(virtual_lock == FLT_MAX); + +#undef FLT_EQ_NONAN +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Mesh Normal Calculation * \{ */ @@ -210,7 +252,6 @@ struct MeshCalcNormalsData { const MLoop *mloop; MVert *mverts; float (*pnors)[3]; - float (*lnors_weighted)[3]; float (*vnors)[3]; }; @@ -224,65 +265,65 @@ static void mesh_calc_normals_poly_cb(void *__restrict userdata, BKE_mesh_calc_poly_normal(mp, data->mloop + mp->loopstart, data->mverts, data->pnors[pidx]); } -static void mesh_calc_normals_poly_prepare_cb(void *__restrict userdata, - const int pidx, - const TaskParallelTLS *__restrict UNUSED(tls)) +static void mesh_calc_normals_poly_and_accum_cb(void *__restrict userdata, + const int pidx, + const TaskParallelTLS *__restrict UNUSED(tls)) { - MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; + const MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; const MPoly *mp = &data->mpolys[pidx]; const MLoop *ml = &data->mloop[mp->loopstart]; const MVert *mverts = data->mverts; + float(*vnors)[3] = data->vnors; float pnor_temp[3]; float *pnor = data->pnors ? data->pnors[pidx] : pnor_temp; - float(*lnors_weighted)[3] = data->lnors_weighted; - const int nverts = mp->totloop; - float(*edgevecbuf)[3] = (float(*)[3])BLI_array_alloca(edgevecbuf, (size_t)nverts); + const int i_end = mp->totloop - 1; /* Polygon Normal and edge-vector */ /* inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors */ { - int i_prev = nverts - 1; - const float *v_prev = mverts[ml[i_prev].v].co; - const float *v_curr; - zero_v3(pnor); /* Newell's Method */ - for (int i = 0; i < nverts; i++) { - v_curr = mverts[ml[i].v].co; - add_newell_cross_v3_v3v3(pnor, v_prev, v_curr); - - /* Unrelated to normalize, calculate edge-vector */ - sub_v3_v3v3(edgevecbuf[i_prev], v_prev, v_curr); - normalize_v3(edgevecbuf[i_prev]); - i_prev = i; - - v_prev = v_curr; + const float *v_curr = mverts[ml[i_end].v].co; + for (int i_next = 0; i_next <= i_end; i_next++) { + const float *v_next = mverts[ml[i_next].v].co; + add_newell_cross_v3_v3v3(pnor, v_curr, v_next); + v_curr = v_next; } if (UNLIKELY(normalize_v3(pnor) == 0.0f)) { pnor[2] = 1.0f; /* other axes set to 0.0 */ } } - /* accumulate angle weighted face normal */ - /* inline version of #accumulate_vertex_normals_poly_v3, - * split between this threaded callback and #mesh_calc_normals_poly_accum_cb. */ + /* Accumulate angle weighted face normal into the vertex normal. */ + /* inline version of #accumulate_vertex_normals_poly_v3. */ { - const float *prev_edge = edgevecbuf[nverts - 1]; - - for (int i = 0; i < nverts; i++) { - const int lidx = mp->loopstart + i; - const float *cur_edge = edgevecbuf[i]; - - /* calculate angle between the two poly edges incident on - * this vertex */ - const float fac = saacos(-dot_v3v3(cur_edge, prev_edge)); + float edvec_prev[3], edvec_next[3], edvec_end[3]; + const float *v_curr = mverts[ml[i_end].v].co; + sub_v3_v3v3(edvec_prev, mverts[ml[i_end - 1].v].co, v_curr); + normalize_v3(edvec_prev); + copy_v3_v3(edvec_end, edvec_prev); + + for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) { + const float *v_next = mverts[ml[i_next].v].co; + + /* Skip an extra normalization by reusing the first calculated edge. */ + if (i_next != i_end) { + sub_v3_v3v3(edvec_next, v_curr, v_next); + normalize_v3(edvec_next); + } + else { + copy_v3_v3(edvec_next, edvec_end); + } - /* Store for later accumulation */ - mul_v3_v3fl(lnors_weighted[lidx], pnor, fac); + /* Calculate angle between the two poly edges incident on this vertex. */ + const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next)); + const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac}; - prev_edge = cur_edge; + add_v3_v3_atomic(vnors[ml[i_curr].v], vnor_add); + v_curr = v_next; + copy_v3_v3(edvec_prev, edvec_next); } } } @@ -309,7 +350,7 @@ void BKE_mesh_calc_normals_poly(MVert *mverts, int numVerts, const MLoop *mloop, const MPoly *mpolys, - int numLoops, + int UNUSED(numLoops), int numPolys, float (*r_polynors)[3], const bool only_face_normals) @@ -335,8 +376,6 @@ void BKE_mesh_calc_normals_poly(MVert *mverts, } float(*vnors)[3] = r_vertnors; - float(*lnors_weighted)[3] = (float(*)[3])MEM_malloc_arrayN( - (size_t)numLoops, sizeof(*lnors_weighted), __func__); bool free_vnors = false; /* first go through and calculate normals for all the polys */ @@ -353,27 +392,17 @@ void BKE_mesh_calc_normals_poly(MVert *mverts, data.mloop = mloop; data.mverts = mverts; data.pnors = pnors; - data.lnors_weighted = lnors_weighted; data.vnors = vnors; - /* Compute poly normals, and prepare weighted loop normals. */ - BLI_task_parallel_range(0, numPolys, &data, mesh_calc_normals_poly_prepare_cb, &settings); - - /* Actually accumulate weighted loop normals into vertex ones. */ - /* Unfortunately, not possible to thread that - * (not in a reasonable, totally lock- and barrier-free fashion), - * since several loops will point to the same vertex... */ - for (int lidx = 0; lidx < numLoops; lidx++) { - add_v3_v3(vnors[mloop[lidx].v], data.lnors_weighted[lidx]); - } + /* Compute poly normals (`pnors`), accumulating them into vertex normals (`vnors`). */ + BLI_task_parallel_range(0, numPolys, &data, mesh_calc_normals_poly_and_accum_cb, &settings); - /* Normalize and validate computed vertex normals. */ + /* Normalize and validate computed vertex normals (`vnors`). */ BLI_task_parallel_range(0, numVerts, &data, mesh_calc_normals_poly_finalize_cb, &settings); if (free_vnors) { MEM_freeN(vnors); } - MEM_freeN(lnors_weighted); } void BKE_mesh_ensure_normals(Mesh *mesh) -- cgit v1.2.3 From ed38d0c25da27c8fe6b3db76dbe024f33f82e338 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 13 Aug 2021 13:23:45 +1000 Subject: Cleanup: split BKE_mesh_calc_normals_poly function in two Remove the 'only_face_normals' argument. - BKE_mesh_calc_normals_poly for polygon normals. - BKE_mesh_calc_normals_poly_and_vertex for poly and vertex normals. Order arguments logically: - Pair array and length arguments. - Position normal array arguments (to be filled) last. --- source/blender/blenkernel/BKE_mesh.h | 22 +- source/blender/blenkernel/intern/DerivedMesh.cc | 34 +- source/blender/blenkernel/intern/data_transfer.c | 6 +- source/blender/blenkernel/intern/key.c | 11 +- source/blender/blenkernel/intern/mesh.c | 17 +- source/blender/blenkernel/intern/mesh_mirror.c | 17 +- source/blender/blenkernel/intern/mesh_normals.cc | 427 ++++++++++++--------- source/blender/blenkernel/intern/mesh_remap.c | 12 +- source/blender/editors/mesh/mesh_data.c | 11 +- source/blender/modifiers/intern/MOD_normal_edit.c | 16 +- .../modifiers/intern/MOD_solidify_extrude.c | 6 +- .../modifiers/intern/MOD_solidify_nonmanifold.c | 11 +- .../blender/modifiers/intern/MOD_weighted_normal.c | 4 +- 13 files changed, 308 insertions(+), 286 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index ef1384c804b..4eb3e6a3136 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -305,15 +305,21 @@ void BKE_mesh_calc_normals_mapping_ex(struct MVert *mverts, const int *origIndexFace, float (*r_faceNors)[3], const bool only_face_normals); -void BKE_mesh_calc_normals_poly(struct MVert *mverts, - float (*r_vertnors)[3], - int numVerts, +void BKE_mesh_calc_normals_poly(const struct MVert *mvert, + int mvert_len, const struct MLoop *mloop, - const struct MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polyNors)[3], - const bool only_face_normals); + int mloop_len, + const struct MPoly *mpoly, + int mpoly_len, + float (*r_poly_normals)[3]); +void BKE_mesh_calc_normals_poly_and_vertex(struct MVert *mvert, + int mvert_len, + const struct MLoop *mloop, + int mloop_len, + const struct MPoly *mpolys, + int mpoly_len, + float (*r_poly_normals)[3], + float (*r_vert_normals)[3]); void BKE_mesh_calc_normals(struct Mesh *me); void BKE_mesh_ensure_normals(struct Mesh *me); void BKE_mesh_ensure_normals_for_display(struct Mesh *mesh); diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 4480b0d34fc..59e81938e79 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -816,15 +816,14 @@ static void mesh_calc_modifier_final_normals(const Mesh *mesh_input, if (!CustomData_has_layer(&mesh_final->pdata, CD_NORMAL)) { float(*polynors)[3] = (float(*)[3])CustomData_add_layer( &mesh_final->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh_final->totpoly); - BKE_mesh_calc_normals_poly(mesh_final->mvert, - nullptr, - mesh_final->totvert, - mesh_final->mloop, - mesh_final->mpoly, - mesh_final->totloop, - mesh_final->totpoly, - polynors, - false); + BKE_mesh_calc_normals_poly_and_vertex(mesh_final->mvert, + mesh_final->totvert, + mesh_final->mloop, + mesh_final->totloop, + mesh_final->mpoly, + mesh_final->totpoly, + polynors, + nullptr); } } @@ -1536,15 +1535,14 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, if (!CustomData_has_layer(&mesh_final->pdata, CD_NORMAL)) { float(*polynors)[3] = (float(*)[3])CustomData_add_layer( &mesh_final->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh_final->totpoly); - BKE_mesh_calc_normals_poly(mesh_final->mvert, - nullptr, - mesh_final->totvert, - mesh_final->mloop, - mesh_final->mpoly, - mesh_final->totloop, - mesh_final->totpoly, - polynors, - false); + BKE_mesh_calc_normals_poly_and_vertex(mesh_final->mvert, + mesh_final->totvert, + mesh_final->mloop, + mesh_final->totloop, + mesh_final->mpoly, + mesh_final->totpoly, + polynors, + nullptr); } } diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 605061570b8..b83621e8b79 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -301,14 +301,12 @@ static void data_transfer_dtdata_type_preprocess(Mesh *me_src, } if (dirty_nors_dst || do_poly_nors_dst) { BKE_mesh_calc_normals_poly(verts_dst, - NULL, num_verts_dst, loops_dst, - polys_dst, num_loops_dst, + polys_dst, num_polys_dst, - poly_nors_dst, - true); + poly_nors_dst); } /* Cache loop nors into a temp CDLayer. */ loop_nors_dst = CustomData_get_layer(ldata_dst, CD_NORMAL); diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 0f8c9bad798..724216bee6c 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -2281,15 +2281,8 @@ void BKE_keyblock_mesh_calc_normals(struct KeyBlock *kb, r_polynors = MEM_mallocN(sizeof(float[3]) * me.totpoly, __func__); free_polynors = true; } - BKE_mesh_calc_normals_poly(me.mvert, - r_vertnors, - me.totvert, - me.mloop, - me.mpoly, - me.totloop, - me.totpoly, - r_polynors, - false); + BKE_mesh_calc_normals_poly_and_vertex( + me.mvert, me.totvert, me.mloop, me.totloop, me.mpoly, me.totpoly, r_polynors, r_vertnors); if (r_loopnors) { short(*clnors)[2] = CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); /* May be NULL. */ diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 4aef0f346c3..b32d58de1e2 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -1890,15 +1890,14 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac } else { polynors = MEM_malloc_arrayN(mesh->totpoly, sizeof(float[3]), __func__); - BKE_mesh_calc_normals_poly(mesh->mvert, - NULL, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - polynors, - false); + BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert, + mesh->totvert, + mesh->mloop, + mesh->totloop, + mesh->mpoly, + mesh->totpoly, + polynors, + NULL); free_polynors = true; } diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c index 9aeaa1ada52..b20d81e7b9c 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.c +++ b/source/blender/blenkernel/intern/mesh_mirror.c @@ -393,15 +393,14 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, /* calculate custom normals into loop_normals, then mirror first half into second half */ - BKE_mesh_calc_normals_poly(result->mvert, - NULL, - result->totvert, - result->mloop, - result->mpoly, - totloop, - totpoly, - poly_normals, - false); + BKE_mesh_calc_normals_poly_and_vertex(result->mvert, + result->totvert, + result->mloop, + totloop, + result->mpoly, + totpoly, + poly_normals, + NULL); BKE_mesh_normals_loop_split(result->mvert, result->totvert, diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index b86332097fa..fe28f10d2db 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -102,7 +102,9 @@ static void add_v3_v3_atomic(float r[3], const float a[3]) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Mesh Normal Calculation +/** \name Public Utility Functions + * + * Related to managing normals but not directly related to calculating normals. * \{ */ void BKE_mesh_normals_tag_dirty(Mesh *mesh) @@ -111,6 +113,205 @@ void BKE_mesh_normals_tag_dirty(Mesh *mesh) mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Normal Calculation (Polygons) + * \{ */ + +struct MeshCalcNormalsData_Poly { + const MVert *mvert; + const MLoop *mloop; + const MPoly *mpoly; + + /** Polygon normal output. */ + float (*pnors)[3]; +}; + +static void mesh_calc_normals_poly_fn(void *__restrict userdata, + const int pidx, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + const MeshCalcNormalsData_Poly *data = (MeshCalcNormalsData_Poly *)userdata; + const MPoly *mp = &data->mpoly[pidx]; + BKE_mesh_calc_poly_normal(mp, data->mloop + mp->loopstart, data->mvert, data->pnors[pidx]); +} + +void BKE_mesh_calc_normals_poly(const MVert *mvert, + int UNUSED(mvert_len), + const MLoop *mloop, + int UNUSED(mloop_len), + const MPoly *mpoly, + int mpoly_len, + float (*r_poly_normals)[3]) +{ + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = 1024; + + BLI_assert((r_poly_normals != nullptr) || (mpoly_len == 0)); + + MeshCalcNormalsData_Poly data = {}; + data.mpoly = mpoly; + data.mloop = mloop; + data.mvert = mvert; + data.pnors = r_poly_normals; + + BLI_task_parallel_range(0, mpoly_len, &data, mesh_calc_normals_poly_fn, &settings); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Normal Calculation (Polygons & Vertices) + * + * Implement #BKE_mesh_calc_normals_poly_and_vertex, + * + * Take care making optimizations to this function as improvements to low-poly + * meshes can slow down high-poly meshes. For details on performance, see D11993. + * \{ */ + +struct MeshCalcNormalsData_PolyAndVertex { + /** Write into vertex normals #MVert.no. */ + MVert *mvert; + const MLoop *mloop; + const MPoly *mpoly; + + /** Polygon normal output. */ + float (*pnors)[3]; + /** Vertex normal output (may be freed, copied into #MVert.no). */ + float (*vnors)[3]; +}; + +static void mesh_calc_normals_poly_and_vertex_accum_fn( + void *__restrict userdata, const int pidx, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + const MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata; + const MPoly *mp = &data->mpoly[pidx]; + const MLoop *ml = &data->mloop[mp->loopstart]; + const MVert *mverts = data->mvert; + float(*vnors)[3] = data->vnors; + + float pnor_temp[3]; + float *pnor = data->pnors ? data->pnors[pidx] : pnor_temp; + + const int i_end = mp->totloop - 1; + + /* Polygon Normal and edge-vector */ + /* inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors */ + { + zero_v3(pnor); + /* Newell's Method */ + const float *v_curr = mverts[ml[i_end].v].co; + for (int i_next = 0; i_next <= i_end; i_next++) { + const float *v_next = mverts[ml[i_next].v].co; + add_newell_cross_v3_v3v3(pnor, v_curr, v_next); + v_curr = v_next; + } + if (UNLIKELY(normalize_v3(pnor) == 0.0f)) { + pnor[2] = 1.0f; /* other axes set to 0.0 */ + } + } + + /* Accumulate angle weighted face normal into the vertex normal. */ + /* inline version of #accumulate_vertex_normals_poly_v3. */ + { + float edvec_prev[3], edvec_next[3], edvec_end[3]; + const float *v_curr = mverts[ml[i_end].v].co; + sub_v3_v3v3(edvec_prev, mverts[ml[i_end - 1].v].co, v_curr); + normalize_v3(edvec_prev); + copy_v3_v3(edvec_end, edvec_prev); + + for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) { + const float *v_next = mverts[ml[i_next].v].co; + + /* Skip an extra normalization by reusing the first calculated edge. */ + if (i_next != i_end) { + sub_v3_v3v3(edvec_next, v_curr, v_next); + normalize_v3(edvec_next); + } + else { + copy_v3_v3(edvec_next, edvec_end); + } + + /* Calculate angle between the two poly edges incident on this vertex. */ + const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next)); + const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac}; + + add_v3_v3_atomic(vnors[ml[i_curr].v], vnor_add); + v_curr = v_next; + copy_v3_v3(edvec_prev, edvec_next); + } + } +} + +static void mesh_calc_normals_poly_and_vertex_finalize_fn( + void *__restrict userdata, const int vidx, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata; + + MVert *mv = &data->mvert[vidx]; + float *no = data->vnors[vidx]; + + if (UNLIKELY(normalize_v3(no) == 0.0f)) { + /* following Mesh convention; we use vertex coordinate itself for normal in this case */ + normalize_v3_v3(no, mv->co); + } + + normal_float_to_short_v3(mv->no, no); +} + +void BKE_mesh_calc_normals_poly_and_vertex(MVert *mvert, + const int mvert_len, + const MLoop *mloop, + const int UNUSED(mloop_len), + const MPoly *mpoly, + const int mpoly_len, + float (*r_poly_normals)[3], + float (*r_vert_normals)[3]) +{ + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = 1024; + + float(*vnors)[3] = r_vert_normals; + bool free_vnors = false; + + /* first go through and calculate normals for all the polys */ + if (vnors == nullptr) { + vnors = (float(*)[3])MEM_calloc_arrayN((size_t)mvert_len, sizeof(*vnors), __func__); + free_vnors = true; + } + else { + memset(vnors, 0, sizeof(*vnors) * (size_t)mvert_len); + } + + MeshCalcNormalsData_PolyAndVertex data = {}; + data.mpoly = mpoly; + data.mloop = mloop; + data.mvert = mvert; + data.pnors = r_poly_normals; + data.vnors = vnors; + + /* Compute poly normals (`pnors`), accumulating them into vertex normals (`vnors`). */ + BLI_task_parallel_range( + 0, mpoly_len, &data, mesh_calc_normals_poly_and_vertex_accum_fn, &settings); + + /* Normalize and validate computed vertex normals (`vnors`). */ + BLI_task_parallel_range( + 0, mvert_len, &data, mesh_calc_normals_poly_and_vertex_finalize_fn, &settings); + + if (free_vnors) { + MEM_freeN(vnors); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Normal Calculation + * \{ */ + /** * Call when there are no polygons. */ @@ -212,8 +413,8 @@ void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, if (only_face_normals == false) { /* vertex normals are optional, they require some extra calculations, * so make them optional */ - BKE_mesh_calc_normals_poly( - mverts, nullptr, numVerts, mloop, mpolys, numLoops, numPolys, pnors, false); + BKE_mesh_calc_normals_poly_and_vertex( + mverts, numVerts, mloop, numLoops, mpolys, numPolys, pnors, nullptr); } else { /* only calc poly normals */ @@ -247,164 +448,6 @@ void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, fnors = pnors = nullptr; } -struct MeshCalcNormalsData { - const MPoly *mpolys; - const MLoop *mloop; - MVert *mverts; - float (*pnors)[3]; - float (*vnors)[3]; -}; - -static void mesh_calc_normals_poly_cb(void *__restrict userdata, - const int pidx, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; - const MPoly *mp = &data->mpolys[pidx]; - - BKE_mesh_calc_poly_normal(mp, data->mloop + mp->loopstart, data->mverts, data->pnors[pidx]); -} - -static void mesh_calc_normals_poly_and_accum_cb(void *__restrict userdata, - const int pidx, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - const MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; - const MPoly *mp = &data->mpolys[pidx]; - const MLoop *ml = &data->mloop[mp->loopstart]; - const MVert *mverts = data->mverts; - float(*vnors)[3] = data->vnors; - - float pnor_temp[3]; - float *pnor = data->pnors ? data->pnors[pidx] : pnor_temp; - - const int i_end = mp->totloop - 1; - - /* Polygon Normal and edge-vector */ - /* inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors */ - { - zero_v3(pnor); - /* Newell's Method */ - const float *v_curr = mverts[ml[i_end].v].co; - for (int i_next = 0; i_next <= i_end; i_next++) { - const float *v_next = mverts[ml[i_next].v].co; - add_newell_cross_v3_v3v3(pnor, v_curr, v_next); - v_curr = v_next; - } - if (UNLIKELY(normalize_v3(pnor) == 0.0f)) { - pnor[2] = 1.0f; /* other axes set to 0.0 */ - } - } - - /* Accumulate angle weighted face normal into the vertex normal. */ - /* inline version of #accumulate_vertex_normals_poly_v3. */ - { - float edvec_prev[3], edvec_next[3], edvec_end[3]; - const float *v_curr = mverts[ml[i_end].v].co; - sub_v3_v3v3(edvec_prev, mverts[ml[i_end - 1].v].co, v_curr); - normalize_v3(edvec_prev); - copy_v3_v3(edvec_end, edvec_prev); - - for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) { - const float *v_next = mverts[ml[i_next].v].co; - - /* Skip an extra normalization by reusing the first calculated edge. */ - if (i_next != i_end) { - sub_v3_v3v3(edvec_next, v_curr, v_next); - normalize_v3(edvec_next); - } - else { - copy_v3_v3(edvec_next, edvec_end); - } - - /* Calculate angle between the two poly edges incident on this vertex. */ - const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next)); - const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac}; - - add_v3_v3_atomic(vnors[ml[i_curr].v], vnor_add); - v_curr = v_next; - copy_v3_v3(edvec_prev, edvec_next); - } - } -} - -static void mesh_calc_normals_poly_finalize_cb(void *__restrict userdata, - const int vidx, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; - - MVert *mv = &data->mverts[vidx]; - float *no = data->vnors[vidx]; - - if (UNLIKELY(normalize_v3(no) == 0.0f)) { - /* following Mesh convention; we use vertex coordinate itself for normal in this case */ - normalize_v3_v3(no, mv->co); - } - - normal_float_to_short_v3(mv->no, no); -} - -void BKE_mesh_calc_normals_poly(MVert *mverts, - float (*r_vertnors)[3], - int numVerts, - const MLoop *mloop, - const MPoly *mpolys, - int UNUSED(numLoops), - int numPolys, - float (*r_polynors)[3], - const bool only_face_normals) -{ - float(*pnors)[3] = r_polynors; - - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.min_iter_per_thread = 1024; - - if (only_face_normals) { - BLI_assert((pnors != nullptr) || (numPolys == 0)); - BLI_assert(r_vertnors == nullptr); - - MeshCalcNormalsData data; - data.mpolys = mpolys; - data.mloop = mloop; - data.mverts = mverts; - data.pnors = pnors; - - BLI_task_parallel_range(0, numPolys, &data, mesh_calc_normals_poly_cb, &settings); - return; - } - - float(*vnors)[3] = r_vertnors; - bool free_vnors = false; - - /* first go through and calculate normals for all the polys */ - if (vnors == nullptr) { - vnors = (float(*)[3])MEM_calloc_arrayN((size_t)numVerts, sizeof(*vnors), __func__); - free_vnors = true; - } - else { - memset(vnors, 0, sizeof(*vnors) * (size_t)numVerts); - } - - MeshCalcNormalsData data; - data.mpolys = mpolys; - data.mloop = mloop; - data.mverts = mverts; - data.pnors = pnors; - data.vnors = vnors; - - /* Compute poly normals (`pnors`), accumulating them into vertex normals (`vnors`). */ - BLI_task_parallel_range(0, numPolys, &data, mesh_calc_normals_poly_and_accum_cb, &settings); - - /* Normalize and validate computed vertex normals (`vnors`). */ - BLI_task_parallel_range(0, numVerts, &data, mesh_calc_normals_poly_finalize_cb, &settings); - - if (free_vnors) { - MEM_freeN(vnors); - } -} - void BKE_mesh_ensure_normals(Mesh *mesh) { if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) { @@ -446,15 +489,25 @@ void BKE_mesh_ensure_normals_for_display(Mesh *mesh) } /* calculate poly/vert normals */ - BKE_mesh_calc_normals_poly(mesh->mvert, - nullptr, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - poly_nors, - !do_vert_normals); + if (do_vert_normals) { + BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert, + mesh->totvert, + mesh->mloop, + mesh->totloop, + mesh->mpoly, + mesh->totpoly, + poly_nors, + nullptr); + } + else { + BKE_mesh_calc_normals_poly(mesh->mvert, + mesh->totvert, + mesh->mloop, + mesh->totloop, + mesh->mpoly, + mesh->totpoly, + poly_nors); + } if (do_add_poly_nors_cddata) { CustomData_add_layer(&mesh->pdata, CD_NORMAL, CD_ASSIGN, poly_nors, mesh->totpoly); @@ -472,15 +525,14 @@ void BKE_mesh_calc_normals(Mesh *mesh) #ifdef DEBUG_TIME TIMEIT_START_AVERAGED(BKE_mesh_calc_normals); #endif - BKE_mesh_calc_normals_poly(mesh->mvert, - nullptr, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - nullptr, - false); + BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert, + mesh->totvert, + mesh->mloop, + mesh->totloop, + mesh->mpoly, + mesh->totpoly, + nullptr, + nullptr); #ifdef DEBUG_TIME TIMEIT_END_AVERAGED(BKE_mesh_calc_normals); #endif @@ -2121,15 +2173,14 @@ static void mesh_set_custom_normals(Mesh *mesh, float (*r_custom_nors)[3], const bool free_polynors = false; if (polynors == nullptr) { polynors = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (size_t)mesh->totpoly, __func__); - BKE_mesh_calc_normals_poly(mesh->mvert, - nullptr, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - polynors, - false); + BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert, + mesh->totvert, + mesh->mloop, + mesh->totloop, + mesh->mpoly, + mesh->totpoly, + polynors, + nullptr); free_polynors = true; } diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c index c5e8858ea12..53a31cbbc7a 100644 --- a/source/blender/blenkernel/intern/mesh_remap.c +++ b/source/blender/blenkernel/intern/mesh_remap.c @@ -1379,14 +1379,12 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, } if (dirty_nors_dst || do_poly_nors_dst) { BKE_mesh_calc_normals_poly(verts_dst, - NULL, numverts_dst, loops_dst, - polys_dst, numloops_dst, + polys_dst, numpolys_dst, - poly_nors_dst, - true); + poly_nors_dst); } } if (need_lnors_dst) { @@ -2231,14 +2229,12 @@ void BKE_mesh_remap_calc_polys_from_mesh(const int mode, } if (dirty_nors_dst) { BKE_mesh_calc_normals_poly(verts_dst, - NULL, numverts_dst, loops_dst, - polys_dst, numloops_dst, + polys_dst, numpolys_dst, - poly_nors_dst, - true); + poly_nors_dst); } } diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index b2379610f65..c075d2550cb 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -1007,15 +1007,8 @@ static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator if (me->flag & ME_AUTOSMOOTH) { float(*polynors)[3] = MEM_mallocN(sizeof(*polynors) * (size_t)me->totpoly, __func__); - BKE_mesh_calc_normals_poly(me->mvert, - NULL, - me->totvert, - me->mloop, - me->mpoly, - me->totloop, - me->totpoly, - polynors, - true); + BKE_mesh_calc_normals_poly( + me->mvert, me->totvert, me->mloop, me->totloop, me->mpoly, me->totpoly, polynors); BKE_edges_sharp_from_angle_set(me->mvert, me->totvert, diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index e7750f0a0d1..1dbdcf87d63 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -556,15 +556,13 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd, polynors = CustomData_add_layer(pdata, CD_NORMAL, CD_CALLOC, NULL, num_polys); CustomData_set_layer_flag(pdata, CD_NORMAL, CD_FLAG_TEMPORARY); } - BKE_mesh_calc_normals_poly(mvert, - NULL, - num_verts, - mloop, - mpoly, - num_loops, - num_polys, - polynors, - (result->runtime.cd_dirty_vert & CD_MASK_NORMAL) ? false : true); + if (result->runtime.cd_dirty_vert & CD_MASK_NORMAL) { + BKE_mesh_calc_normals_poly_and_vertex( + mvert, num_verts, mloop, num_loops, mpoly, num_polys, polynors, NULL); + } + else { + BKE_mesh_calc_normals_poly(mvert, num_verts, mloop, num_loops, mpoly, num_polys, polynors); + } result->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL; diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index 64e1eb92fda..00fa6e24a64 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -259,14 +259,12 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex /* calculate only face normals */ poly_nors = MEM_malloc_arrayN(numPolys, sizeof(*poly_nors), __func__); BKE_mesh_calc_normals_poly(orig_mvert, - NULL, (int)numVerts, orig_mloop, - orig_mpoly, (int)numLoops, + orig_mpoly, (int)numPolys, - poly_nors, - true); + poly_nors); } STACK_INIT(new_vert_arr, numVerts * 2); diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index 18d308e5f02..5b4716a1a43 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -211,15 +211,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, /* Calculate only face normals. */ poly_nors = MEM_malloc_arrayN(numPolys, sizeof(*poly_nors), __func__); - BKE_mesh_calc_normals_poly(orig_mvert, - NULL, - (int)numVerts, - orig_mloop, - orig_mpoly, - (int)numLoops, - (int)numPolys, - poly_nors, - true); + BKE_mesh_calc_normals_poly( + orig_mvert, (int)numVerts, orig_mloop, (int)numLoops, orig_mpoly, (int)numPolys, poly_nors); NewFaceRef *face_sides_arr = MEM_malloc_arrayN( numPolys * 2, sizeof(*face_sides_arr), "face_sides_arr in solidify"); diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c index 3b147c69716..1ee64b935b7 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ b/source/blender/modifiers/intern/MOD_weighted_normal.c @@ -615,8 +615,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * polynors = CustomData_add_layer(pdata, CD_NORMAL, CD_CALLOC, NULL, numPolys); CustomData_set_layer_flag(pdata, CD_NORMAL, CD_FLAG_TEMPORARY); } - BKE_mesh_calc_normals_poly( - mvert, NULL, numVerts, mloop, mpoly, numLoops, numPolys, polynors, false); + BKE_mesh_calc_normals_poly_and_vertex( + mvert, numVerts, mloop, numLoops, mpoly, numPolys, polynors, NULL); const float split_angle = mesh->smoothresh; short(*clnors)[2]; -- cgit v1.2.3 From ab344775c2c5aa6553981469a27062fd9b15dc87 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 13 Aug 2021 13:53:27 +1000 Subject: Cleanup: code-comments Use capitalization, remove unnecessary ellipsis. --- source/blender/blenkernel/intern/mesh_normals.cc | 124 +++++++++++------------ 1 file changed, 60 insertions(+), 64 deletions(-) diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index fe28f10d2db..a1c34be4a74 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -197,8 +197,8 @@ static void mesh_calc_normals_poly_and_vertex_accum_fn( const int i_end = mp->totloop - 1; - /* Polygon Normal and edge-vector */ - /* inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors */ + /* Polygon Normal and edge-vector. */ + /* Inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors. */ { zero_v3(pnor); /* Newell's Method */ @@ -209,12 +209,12 @@ static void mesh_calc_normals_poly_and_vertex_accum_fn( v_curr = v_next; } if (UNLIKELY(normalize_v3(pnor) == 0.0f)) { - pnor[2] = 1.0f; /* other axes set to 0.0 */ + pnor[2] = 1.0f; /* Other axes set to zero. */ } } /* Accumulate angle weighted face normal into the vertex normal. */ - /* inline version of #accumulate_vertex_normals_poly_v3. */ + /* Inline version of #accumulate_vertex_normals_poly_v3. */ { float edvec_prev[3], edvec_next[3], edvec_end[3]; const float *v_curr = mverts[ml[i_end].v].co; @@ -254,7 +254,7 @@ static void mesh_calc_normals_poly_and_vertex_finalize_fn( float *no = data->vnors[vidx]; if (UNLIKELY(normalize_v3(no) == 0.0f)) { - /* following Mesh convention; we use vertex coordinate itself for normal in this case */ + /* Following Mesh convention; we use vertex coordinate itself for normal in this case. */ normalize_v3_v3(no, mv->co); } @@ -277,7 +277,7 @@ void BKE_mesh_calc_normals_poly_and_vertex(MVert *mvert, float(*vnors)[3] = r_vert_normals; bool free_vnors = false; - /* first go through and calculate normals for all the polys */ + /* First go through and calculate normals for all the polys. */ if (vnors == nullptr) { vnors = (float(*)[3])MEM_calloc_arrayN((size_t)mvert_len, sizeof(*vnors), __func__); free_vnors = true; @@ -375,7 +375,9 @@ void BKE_mesh_calc_normals_mapping(MVert *mverts, r_faceNors, false); } -/* extended version of 'BKE_mesh_calc_normals_poly' with option not to calc vertex normals */ +/** + * Extended version of 'BKE_mesh_calc_normals_poly' with option not to calc vertex normals. + */ void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, int numVerts, const MLoop *mloop, @@ -398,7 +400,7 @@ void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, return; } - /* if we are not calculating verts and no verts were passes then we have nothing to do */ + /* If we are not calculating verts and no verts were passes then we have nothing to do. */ if ((only_face_normals == true) && (r_polyNors == nullptr) && (r_faceNors == nullptr)) { CLOG_WARN(&LOG, "called with nothing to do"); return; @@ -411,13 +413,12 @@ void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, // if (!fnors) {fnors = MEM_calloc_arrayN(numFaces, sizeof(float[3]), "face nors mesh.c"); } if (only_face_normals == false) { - /* vertex normals are optional, they require some extra calculations, - * so make them optional */ + /* Vertex normals are optional, they require some extra calculations, so make them optional. */ BKE_mesh_calc_normals_poly_and_vertex( mverts, numVerts, mloop, numLoops, mpolys, numPolys, pnors, nullptr); } else { - /* only calc poly normals */ + /* Only calc poly normals. */ const MPoly *mp = mpolys; for (int i = 0; i < numPolys; i++, mp++) { BKE_mesh_calc_poly_normal(mp, mloop + mp->loopstart, mverts, pnors[i]); @@ -425,7 +426,7 @@ void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, } if (origIndexFace && - /* fnors == r_faceNors */ /* NO NEED TO ALLOC YET */ + /* `fnors == r_faceNors` */ /* NO NEED TO ALLOC YET */ fnors != nullptr && numFaces) { const MFace *mf = mfaces; @@ -434,8 +435,8 @@ void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, copy_v3_v3(fnors[i], pnors[*origIndexFace]); } else { - /* eek, we're not corresponding to polys */ - CLOG_ERROR(&LOG, "tessellation face indices are incorrect. normals may look bad."); + /* Yikes, we're not corresponding to polys. */ + CLOG_ERROR(&LOG, "tessellation face indices are incorrect. Normals may look bad."); } } } @@ -488,7 +489,7 @@ void BKE_mesh_ensure_normals_for_display(Mesh *mesh) (size_t)mesh->totpoly, sizeof(*poly_nors), __func__); } - /* calculate poly/vert normals */ + /* Calculate poly/vert normals. */ if (do_vert_normals) { BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert, mesh->totvert, @@ -518,8 +519,10 @@ void BKE_mesh_ensure_normals_for_display(Mesh *mesh) } } -/* Note that this does not update the CD_NORMAL layer, - * but does update the normals in the CD_MVERT layer. */ +/** + * NOTE: this does not update the #CD_NORMAL layer, + * but does update the normals in the #CD_MVERT layer. + */ void BKE_mesh_calc_normals(Mesh *mesh) { #ifdef DEBUG_TIME @@ -575,7 +578,7 @@ void BKE_mesh_calc_normals_looptri(MVert *mverts, mverts[vtri[2]].co); } - /* following Mesh convention; we use vertex coordinate itself for normal in this case */ + /* Following Mesh convention; we use vertex coordinate itself for normal in this case. */ for (int i = 0; i < numVerts; i++) { MVert *mv = &mverts[i]; float *no = tnorms[i]; @@ -715,11 +718,11 @@ void BKE_lnor_space_define(MLoopNorSpace *lnor_space, BLI_stack_discard(edge_vectors); nbr++; } - /* NOTE: In theory, this could be 'nbr > 2', - * but there is one case where we only have two edges for two loops: - * a smooth vertex with only two edges and two faces (our Monkey's nose has that, e.g.). + /* NOTE: In theory, this could be `nbr > 2`, + * but there is one case where we only have two edges for two loops: + * a smooth vertex with only two edges and two faces (our Monkey's nose has that, e.g.). */ - BLI_assert(nbr >= 2); /* This piece of code shall only be called for more than one loop... */ + BLI_assert(nbr >= 2); /* This piece of code shall only be called for more than one loop. */ lnor_space->ref_alpha = alpha / (float)nbr; } else { @@ -791,7 +794,7 @@ MINLINE float unit_short_to_float(const short val) MINLINE short unit_float_to_short(const float val) { - /* Rounding... */ + /* Rounding. */ return (short)floorf(val * (float)SHRT_MAX + 0.5f); } @@ -1002,7 +1005,7 @@ static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data, e2l[1] = INDEX_INVALID; /* We want to avoid tagging edges as sharp when it is already defined as such by - * other causes than angle threshold... */ + * other causes than angle threshold. */ if (do_sharp_edges_tag && is_angle_sharp) { BLI_BITMAP_SET(sharp_edges, ml_curr->e, true); } @@ -1016,7 +1019,7 @@ static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data, e2l[1] = INDEX_INVALID; /* We want to avoid tagging edges as sharp when it is already defined as such by - * other causes than angle threshold... */ + * other causes than angle threshold. */ if (do_sharp_edges_tag) { BLI_BITMAP_SET(sharp_edges, ml_curr->e, false); } @@ -1098,14 +1101,13 @@ void BKE_mesh_loop_manifold_fan_around_vert_next(const MLoop *mloops, const MLoop *mlfan_next; const MPoly *mpfan_next; - /* Warning! This is rather complex! + /* WARNING: This is rather complex! * We have to find our next edge around the vertex (fan mode). * First we find the next loop, which is either previous or next to mlfan_curr_index, depending * whether both loops using current edge are in the same direction or not, and whether * mlfan_curr_index actually uses the vertex we are fanning around! * mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one - * (i.e. not the future mlfan_curr)... - */ + * (i.e. not the future `mlfan_curr`). */ *r_mlfan_curr_index = (e2lfan_curr[0] == *r_mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0]; *r_mpfan_curr_index = loop_to_poly[*r_mlfan_curr_index]; @@ -1130,7 +1132,7 @@ void BKE_mesh_loop_manifold_fan_around_vert_next(const MLoop *mloops, *r_mlfan_vert_index = *r_mlfan_curr_index; } *r_mlfan_curr = &mloops[*r_mlfan_curr_index]; - /* And now we are back in sync, mlfan_curr_index is the index of mlfan_curr! Pff! */ + /* And now we are back in sync, mlfan_curr_index is the index of `mlfan_curr`! Pff! */ } static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data) @@ -1185,8 +1187,7 @@ static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopS normalize_v3(vec_prev); BKE_lnor_space_define(lnor_space, *lnor, vec_curr, vec_prev, nullptr); - /* We know there is only one loop in this space, - * no need to create a linklist in this case... */ + /* We know there is only one loop in this space, no need to create a link-list in this case. */ BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, ml_curr_index, nullptr, true); if (clnors_data) { @@ -1222,24 +1223,24 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli BLI_Stack *edge_vectors = data->edge_vectors; - /* Gah... We have to fan around current vertex, until we find the other non-smooth edge, + /* Sigh! we have to fan around current vertex, until we find the other non-smooth edge, * and accumulate face normals into the vertex! * Note in case this vertex has only one sharp edges, this is a waste because the normal is the * same as the vertex normal, but I do not see any easy way to detect that (would need to count * number of sharp edges per vertex, I doubt the additional memory usage would be worth it, - * especially as it should not be a common case in real-life meshes anyway). - */ + * especially as it should not be a common case in real-life meshes anyway). */ const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ const MVert *mv_pivot = &mverts[mv_pivot_index]; - /* ml_curr would be mlfan_prev if we needed that one. */ + /* `ml_curr` would be mlfan_prev if we needed that one. */ const MEdge *me_org = &medges[ml_curr->e]; const int *e2lfan_curr; float vec_curr[3], vec_prev[3], vec_org[3]; const MLoop *mlfan_curr; float lnor[3] = {0.0f, 0.0f, 0.0f}; - /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ + /* `mlfan_vert_index` the loop of our current edge might not be the loop of our current vertex! + */ int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index; /* We validate clnors data on the fly - cheapest way to do! */ @@ -1283,7 +1284,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli /* Compute edge vectors. * NOTE: We could pre-compute those into an array, in the first iteration, instead of computing * them twice (or more) here. However, time gained is not worth memory and time lost, - * given the fact that this code should not be called that much in real-life meshes... + * given the fact that this code should not be called that much in real-life meshes. */ { const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] : @@ -1475,12 +1476,13 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops, const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ const int *e2lfan_curr; const MLoop *mlfan_curr; - /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ + /* `mlfan_vert_index` the loop of our current edge might not be the loop of our current vertex! + */ int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index; e2lfan_curr = e2l_prev; if (IS_EDGE_SHARP(e2lfan_curr)) { - /* Sharp loop, so not a cyclic smooth fan... */ + /* Sharp loop, so not a cyclic smooth fan. */ return false; } @@ -1511,21 +1513,21 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops, e2lfan_curr = edge_to_loops[mlfan_curr->e]; if (IS_EDGE_SHARP(e2lfan_curr)) { - /* Sharp loop/edge, so not a cyclic smooth fan... */ + /* Sharp loop/edge, so not a cyclic smooth fan. */ return false; } - /* Smooth loop/edge... */ + /* Smooth loop/edge. */ if (BLI_BITMAP_TEST(skip_loops, mlfan_vert_index)) { if (mlfan_vert_index == ml_curr_index) { /* We walked around a whole cyclic smooth fan without finding any already-processed loop, - * means we can use initial ml_curr/ml_prev edge as start for this smooth fan. */ + * means we can use initial `ml_curr` / `ml_prev` edge as start for this smooth fan. */ return true; } - /* ... already checked in some previous looping, we can abort. */ + /* Already checked in some previous looping, we can abort. */ return false; } - /* ... we can skip it in future, and keep checking the smooth fan. */ + /* We can skip it in future, and keep checking the smooth fan. */ BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index); } } @@ -1587,7 +1589,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common const int *e2l_prev = edge_to_loops[ml_prev->e]; #if 0 - printf("Checking loop %d / edge %u / vert %u (sharp edge: %d, skiploop: %d)...", + printf("Checking loop %d / edge %u / vert %u (sharp edge: %d, skiploop: %d)", ml_curr_index, ml_curr->e, ml_curr->v, @@ -1691,7 +1693,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common } } - /* Last block of data... Since it is calloc'ed and we use first nullptr item as stopper, + /* Last block of data. Since it is calloc'ed and we use first nullptr item as stopper, * everything is fine. */ if (pool && data_idx) { BLI_task_pool_push(pool, loop_split_worker, data_buff, true, nullptr); @@ -1738,8 +1740,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts, * since we may want to use lnors even when mesh's 'autosmooth' is disabled * (see e.g. mesh mapping code). * As usual, we could handle that on case-by-case basis, - * but simpler to keep it well confined here. - */ + * but simpler to keep it well confined here. */ int mp_index; for (mp_index = 0; mp_index < numPolys; mp_index++) { @@ -1822,7 +1823,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts, mesh_edges_sharp_tag(&common_data, check_angle, split_angle, false); if (numLoops < LOOP_SPLIT_TASK_BLOCK_SIZE * 8) { - /* Not enough loops to be worth the whole threading overhead... */ + /* Not enough loops to be worth the whole threading overhead. */ loop_split_generator(nullptr, &common_data); } else { @@ -1877,13 +1878,12 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, short (*r_clnors_data)[2], const bool use_vertices) { - /* We *may* make that poor BKE_mesh_normals_loop_split() even more complex by making it handling + /* We *may* make that poor #BKE_mesh_normals_loop_split() even more complex by making it handling * that feature too, would probably be more efficient in absolute. * However, this function *is not* performance-critical, since it is mostly expected to be called - * by io addons when importing custom normals, and modifier + * by io add-ons when importing custom normals, and modifier * (and perhaps from some editing tools later?). - * So better to keep some simplicity here, and just call BKE_mesh_normals_loop_split() twice! - */ + * So better to keep some simplicity here, and just call #BKE_mesh_normals_loop_split() twice! */ MLoopNorSpaceArray lnors_spacearr = {nullptr}; BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)numLoops, __func__); float(*lnors)[3] = (float(*)[3])MEM_calloc_arrayN((size_t)numLoops, sizeof(*lnors), __func__); @@ -1935,15 +1935,13 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, * This way, next time we run BKE_mesh_normals_loop_split(), we'll get lnor spacearr/smooth fans * matching given custom lnors. * Note this code *will never* unsharp edges! And quite obviously, - * when we set custom normals per vertices, running this is absolutely useless. - */ + * when we set custom normals per vertices, running this is absolutely useless. */ if (!use_vertices) { for (int i = 0; i < numLoops; i++) { if (!lnors_spacearr.lspacearr[i]) { /* This should not happen in theory, but in some rare case (probably ugly geometry) * we can get some nullptr loopspacearr at this point. :/ - * Maybe we should set those loops' edges as sharp? - */ + * Maybe we should set those loops' edges as sharp? */ BLI_BITMAP_ENABLE(done_loops, i); if (G.debug & G_DEBUG) { printf("WARNING! Getting invalid nullptr loop space for loop %d!\n", i); @@ -1953,12 +1951,12 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, if (!BLI_BITMAP_TEST(done_loops, i)) { /* Notes: - * * In case of mono-loop smooth fan, we have nothing to do. - * * Loops in this linklist are ordered (in reversed order compared to how they were + * - In case of mono-loop smooth fan, we have nothing to do. + * - Loops in this linklist are ordered (in reversed order compared to how they were * discovered by BKE_mesh_normals_loop_split(), but this is not a problem). * Which means if we find a mismatching clnor, * we know all remaining loops will have to be in a new, different smooth fan/lnor space. - * * In smooth fan case, we compare each clnor against a ref one, + * - In smooth fan case, we compare each clnor against a ref one, * to avoid small differences adding up into a real big one in the end! */ if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { @@ -1983,8 +1981,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, /* Current normal differs too much from org one, we have to tag the edge between * previous loop's face and current's one as sharp. * We know those two loops do not point to the same edge, - * since we do not allow reversed winding in a same smooth fan. - */ + * since we do not allow reversed winding in a same smooth fan. */ const MPoly *mp = &mpolys[loop_to_poly[lidx]]; const MLoop *mlp = &mloops[(lidx == mp->loopstart) ? mp->loopstart + mp->totloop - 1 : lidx - 1]; @@ -2056,8 +2053,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, if (BLI_BITMAP_TEST_BOOL(done_loops, i)) { /* Note we accumulate and average all custom normals in current smooth fan, * to avoid getting different clnors data (tiny differences in plain custom normals can - * give rather huge differences in computed 2D factors). - */ + * give rather huge differences in computed 2D factors). */ LinkNode *loops = lnors_spacearr.lspacearr[i]->loops; if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { BLI_assert(POINTER_AS_INT(loops) == i); -- cgit v1.2.3 From b51a473e294e83899a4c28a5659dab7ab55968d6 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 13 Aug 2021 14:37:30 +1000 Subject: Cleanup: remove use of BKE_mesh_calc_normals_mapping_simple Use BKE_mesh_calc_normals instead of BKE_mesh_calc_normals_mapping_simple for curve modifier calculation. This only made sense for derived-mesh which is no longer used. --- source/blender/blenkernel/intern/displist.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index 99dc1db9d38..c97e07ad487 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -1003,7 +1003,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, modified = temp_mesh; BKE_mesh_vert_coords_apply(modified, vertCos); - BKE_mesh_calc_normals_mapping_simple(modified); + BKE_mesh_calc_normals(modified); MEM_freeN(vertCos); } -- cgit v1.2.3 From 92f4abc37f0febbce7bedfdc6284d282a26ca454 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 13 Aug 2021 14:41:42 +1000 Subject: Cleanup: remove unused BKE_mesh_calc_normals_mapping functions This supported calculating normals for MPoly array which was copied to an MFace aligned array. Remove the functions entirely since MFace use is being phased out and these function isn't used anywhere. --- source/blender/blenkernel/BKE_mesh.h | 24 ---- source/blender/blenkernel/intern/mesh_normals.cc | 141 ----------------------- 2 files changed, 165 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 4eb3e6a3136..8000e57e08e 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -281,30 +281,6 @@ void BKE_mesh_recalc_looptri_with_normals(const struct MLoop *mloop, /* *** mesh_normals.cc *** */ void BKE_mesh_normals_tag_dirty(struct Mesh *mesh); -void BKE_mesh_calc_normals_mapping_simple(struct Mesh *me); -void BKE_mesh_calc_normals_mapping(struct MVert *mverts, - int numVerts, - const struct MLoop *mloop, - const struct MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polyNors)[3], - const struct MFace *mfaces, - int numFaces, - const int *origIndexFace, - float (*r_faceNors)[3]); -void BKE_mesh_calc_normals_mapping_ex(struct MVert *mverts, - int numVerts, - const struct MLoop *mloop, - const struct MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polyNors)[3], - const struct MFace *mfaces, - int numFaces, - const int *origIndexFace, - float (*r_faceNors)[3], - const bool only_face_normals); void BKE_mesh_calc_normals_poly(const struct MVert *mvert, int mvert_len, const struct MLoop *mloop, diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index a1c34be4a74..9a761c6fa11 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -27,8 +27,6 @@ #include -#include "CLG_log.h" - #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" @@ -59,8 +57,6 @@ # include "PIL_time_utildefines.h" #endif -static CLG_LogRef LOG = {"bke.mesh_normals"}; - /* -------------------------------------------------------------------- */ /** \name Private Utility Functions * \{ */ @@ -312,143 +308,6 @@ void BKE_mesh_calc_normals_poly_and_vertex(MVert *mvert, /** \name Mesh Normal Calculation * \{ */ -/** - * Call when there are no polygons. - */ -static void mesh_calc_normals_vert_fallback(MVert *mverts, int numVerts) -{ - for (int i = 0; i < numVerts; i++) { - MVert *mv = &mverts[i]; - float no[3]; - - normalize_v3_v3(no, mv->co); - normal_float_to_short_v3(mv->no, no); - } -} - -/* TODO(Sybren): we can probably rename this to BKE_mesh_calc_normals_mapping(), - * and remove the function of the same name below, as that one doesn't seem to be - * called anywhere. */ -void BKE_mesh_calc_normals_mapping_simple(struct Mesh *mesh) -{ - const bool only_face_normals = CustomData_is_referenced_layer(&mesh->vdata, CD_MVERT); - - BKE_mesh_calc_normals_mapping_ex(mesh->mvert, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - nullptr, - mesh->mface, - mesh->totface, - nullptr, - nullptr, - only_face_normals); -} - -/* Calculate vertex and face normals, face normals are returned in *r_faceNors if non-nullptr - * and vertex normals are stored in actual mverts. - */ -void BKE_mesh_calc_normals_mapping(MVert *mverts, - int numVerts, - const MLoop *mloop, - const MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polyNors)[3], - const MFace *mfaces, - int numFaces, - const int *origIndexFace, - float (*r_faceNors)[3]) -{ - BKE_mesh_calc_normals_mapping_ex(mverts, - numVerts, - mloop, - mpolys, - numLoops, - numPolys, - r_polyNors, - mfaces, - numFaces, - origIndexFace, - r_faceNors, - false); -} -/** - * Extended version of 'BKE_mesh_calc_normals_poly' with option not to calc vertex normals. - */ -void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, - int numVerts, - const MLoop *mloop, - const MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polyNors)[3], - const MFace *mfaces, - int numFaces, - const int *origIndexFace, - float (*r_faceNors)[3], - const bool only_face_normals) -{ - float(*pnors)[3] = r_polyNors, (*fnors)[3] = r_faceNors; - - if (numPolys == 0) { - if (only_face_normals == false) { - mesh_calc_normals_vert_fallback(mverts, numVerts); - } - return; - } - - /* If we are not calculating verts and no verts were passes then we have nothing to do. */ - if ((only_face_normals == true) && (r_polyNors == nullptr) && (r_faceNors == nullptr)) { - CLOG_WARN(&LOG, "called with nothing to do"); - return; - } - - if (!pnors) { - pnors = (float(*)[3])MEM_calloc_arrayN((size_t)numPolys, sizeof(float[3]), __func__); - } - /* NO NEED TO ALLOC YET */ - // if (!fnors) {fnors = MEM_calloc_arrayN(numFaces, sizeof(float[3]), "face nors mesh.c"); } - - if (only_face_normals == false) { - /* Vertex normals are optional, they require some extra calculations, so make them optional. */ - BKE_mesh_calc_normals_poly_and_vertex( - mverts, numVerts, mloop, numLoops, mpolys, numPolys, pnors, nullptr); - } - else { - /* Only calc poly normals. */ - const MPoly *mp = mpolys; - for (int i = 0; i < numPolys; i++, mp++) { - BKE_mesh_calc_poly_normal(mp, mloop + mp->loopstart, mverts, pnors[i]); - } - } - - if (origIndexFace && - /* `fnors == r_faceNors` */ /* NO NEED TO ALLOC YET */ - fnors != nullptr && - numFaces) { - const MFace *mf = mfaces; - for (int i = 0; i < numFaces; i++, mf++, origIndexFace++) { - if (*origIndexFace < numPolys) { - copy_v3_v3(fnors[i], pnors[*origIndexFace]); - } - else { - /* Yikes, we're not corresponding to polys. */ - CLOG_ERROR(&LOG, "tessellation face indices are incorrect. Normals may look bad."); - } - } - } - - if (pnors != r_polyNors) { - MEM_freeN(pnors); - } - // if (fnors != r_faceNors) { MEM_freeN(fnors); } /* NO NEED TO ALLOC YET */ - - fnors = pnors = nullptr; -} - void BKE_mesh_ensure_normals(Mesh *mesh) { if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) { -- cgit v1.2.3 From 8fa05efe0a19126b44cc283232e05e0e53d6db84 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 13 Aug 2021 15:10:27 +1000 Subject: Docs: note that normalize_v# functions zero out input containing nan --- source/blender/blenlib/intern/math_vector_inline.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index dddefd60b1b..ddfdaffb706 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -1145,6 +1145,9 @@ MINLINE float len_v3v3(const float a[3], const float b[3]) return len_v3(d); } +/** + * \note any vectors containing `nan` will be zeroed out. + */ MINLINE float normalize_v2_v2_length(float r[2], const float a[2], const float unit_length) { float d = dot_v2v2(a, a); @@ -1154,6 +1157,7 @@ MINLINE float normalize_v2_v2_length(float r[2], const float a[2], const float u mul_v2_v2fl(r, a, unit_length / d); } else { + /* Either the vector is small or one of it's values contained `nan`. */ zero_v2(r); d = 0.0f; } @@ -1175,17 +1179,20 @@ MINLINE float normalize_v2_length(float n[2], const float unit_length) return normalize_v2_v2_length(n, n, unit_length); } +/** + * \note any vectors containing `nan` will be zeroed out. + */ MINLINE float normalize_v3_v3_length(float r[3], const float a[3], const float unit_length) { float d = dot_v3v3(a, a); - /* a larger value causes normalize errors in a - * scaled down models with camera extreme close */ + /* A larger value causes normalize errors in a scaled down models with camera extreme close. */ if (d > 1.0e-35f) { d = sqrtf(d); mul_v3_v3fl(r, a, unit_length / d); } else { + /* Either the vector is small or one of it's values contained `nan`. */ zero_v3(r); d = 0.0f; } -- cgit v1.2.3 From 41e650981861c2f18ab0548e18851d1d761066ff Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 13 Aug 2021 15:36:49 +1000 Subject: Mesh: replace saacos with acosf for normal calculation The clamped version of acos isn't needed as degenerate (nan) coordinates result in zeroed vectors which don't need clamping. --- source/blender/blenkernel/intern/mesh_normals.cc | 14 +++++++++----- source/blender/blenlib/intern/math_geom.c | 12 ++++++++---- source/blender/bmesh/intern/bmesh_mesh_normals.c | 18 +++++++++++------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 9a761c6fa11..aae50fa165e 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -230,8 +230,10 @@ static void mesh_calc_normals_poly_and_vertex_accum_fn( copy_v3_v3(edvec_next, edvec_end); } - /* Calculate angle between the two poly edges incident on this vertex. */ - const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next)); + /* Calculate angle between the two poly edges incident on this vertex. + * NOTE: no need for #saacos here as the input has been sanitized, + * `nan` values in coordinates normalize to zero which works for `acosf`. */ + const float fac = acosf(-dot_v3v3(edvec_prev, edvec_next)); const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac}; add_v3_v3_atomic(vnors[ml[i_curr].v], vnor_add); @@ -1156,9 +1158,11 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli // printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); { - /* Code similar to accumulate_vertex_normals_poly_v3. */ - /* Calculate angle between the two poly edges incident on this vertex. */ - const float fac = saacos(dot_v3v3(vec_curr, vec_prev)); + /* Code similar to #accumulate_vertex_normals_poly_v3. */ + /* Calculate angle between the two poly edges incident on this vertex. + * NOTE: no need for #saacos here as the input has been sanitized, + * `nan` values in coordinates normalize to zero which works for `acosf`. */ + const float fac = acosf(dot_v3v3(vec_curr, vec_prev)); /* Accumulate */ madd_v3_v3fl(lnor, polynors[mpfan_curr_index], fac); diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 8afb6b5a2be..43f2e08cf69 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -5307,7 +5307,10 @@ void accumulate_vertex_normals_tri_v3(float n1[3], for (i = 0; i < nverts; i++) { const float *cur_edge = vdiffs[i]; - const float fac = saacos(-dot_v3v3(cur_edge, prev_edge)); + /* Calculate angle between the two poly edges incident on this vertex. + * NOTE: no need for #saacos here as the input has been sanitized, + * `nan` values in coordinates normalize to zero which works for `acosf`. */ + const float fac = acosf(-dot_v3v3(cur_edge, prev_edge)); /* accumulate */ madd_v3_v3fl(vn[i], f_no, fac); @@ -5386,9 +5389,10 @@ void accumulate_vertex_normals_poly_v3(float **vertnos, for (i = 0; i < nverts; i++) { const float *cur_edge = vdiffs[i]; - /* calculate angle between the two poly edges incident on - * this vertex */ - const float fac = saacos(-dot_v3v3(cur_edge, prev_edge)); + /* Calculate angle between the two poly edges incident on this vertex. + * NOTE: no need for #saacos here as the input has been sanitized, + * `nan` values in coordinates normalize to zero which works for `acosf`. */ + const float fac = acosf(-dot_v3v3(cur_edge, prev_edge)); /* accumulate */ madd_v3_v3fl(vertnos[i], polyno, fac); diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index a5e41b74ee1..6a2cfdb056c 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -84,11 +84,13 @@ BLI_INLINE void bm_vert_calc_normals_accum_loop(const BMLoop *l_iter, if ((l_iter->prev->e->v1 == l_iter->prev->v) ^ (l_iter->e->v1 == l_iter->v)) { dotprod = -dotprod; } - const float fac = saacos(-dotprod); - /* NAN detection, otherwise this is a degenerated case, ignore that vertex in this case. */ - if (fac == fac) { - madd_v3_v3fl(v_no, f_no, fac); - } + /* Calculate angle between the two poly edges incident on this vertex. + * NOTE: no need for #saacos here as the input has been sanitized, + * `nan` values in coordinates normalize to zero which works for `acosf`. */ + const float fac = acosf(-dotprod); + /* NAN values should never happen. */ + BLI_assert(fac == fac); + madd_v3_v3fl(v_no, f_no, fac); } static void bm_vert_calc_normals_impl(BMVert *v) @@ -680,9 +682,11 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm, { /* Code similar to accumulate_vertex_normals_poly_v3. */ - /* Calculate angle between the two poly edges incident on this vertex. */ + /* Calculate angle between the two poly edges incident on this vertex. + * NOTE: no need for #saacos here as the input has been sanitized, + * `nan` values in coordinates normalize to zero which works for `acosf`. */ const BMFace *f = lfan_pivot->f; - const float fac = saacos(dot_v3v3(vec_next, vec_curr)); + const float fac = acosf(dot_v3v3(vec_next, vec_curr)); const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no; /* Accumulate */ madd_v3_v3fl(lnor, no, fac); -- cgit v1.2.3 From 3e775a4fc57cfd48954adcf284354312f34d5412 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 13 Aug 2021 15:51:08 +1000 Subject: PyAPI: remove the .py extension requirement for startup registration This was left over from when these scripts were loaded as modules, where their names needed to be compatible with Pythons module naming. Version patch existing files so text with register enabled without a `.py` extension wont start executing on startup. Resolves T89532. --- release/scripts/startup/bl_ui/space_text.py | 5 +--- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenloader/intern/versioning_300.c | 36 +++++++++++++++-------- source/blender/python/intern/bpy_interface.c | 2 +- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py index 93ab12e8462..811e7fd41c5 100644 --- a/release/scripts/startup/bl_ui/space_text.py +++ b/release/scripts/startup/bl_ui/space_text.py @@ -268,10 +268,7 @@ class TEXT_MT_text(Menu): layout.operator("text.make_internal") layout.separator() - row = layout.row() - row.active = text.name.endswith(".py") - row.prop(text, "use_module") - row = layout.row() + layout.prop(text, "use_module") layout.prop(st, "use_live_edit") diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 5ef56fab9cb..4ed4225c836 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 17 +#define BLENDER_FILE_SUBVERSION 18 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 862ef99fca5..cac607ed152 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -22,6 +22,7 @@ #include "BLI_listbase.h" #include "BLI_math_vector.h" +#include "BLI_path_util.h" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -738,18 +739,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - + if (!MAIN_VERSION_ATLEAST(bmain, 300, 18)) { if (!DNA_struct_elem_find( fd->filesdna, "WorkSpace", "AssetLibraryReference", "asset_library_ref")) { LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { @@ -775,5 +765,27 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Previously, only text ending with `.py` would run, apply this logic + * to existing files so text that happens to have the "Register" enabled + * doesn't suddenly start running code on startup that was previously ignored. */ + LISTBASE_FOREACH (Text *, text, &bmain->texts) { + if ((text->flags & TXT_ISSCRIPT) && !BLI_path_extension_check(text->id.name + 2, ".py")) { + text->flags &= ~TXT_ISSCRIPT; + } + } + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 945933dd8b7..1a308414bc3 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -655,7 +655,7 @@ void BPY_modules_load_user(bContext *C) bpy_context_set(C, &gilstate); for (text = bmain->texts.first; text; text = text->id.next) { - if (text->flags & TXT_ISSCRIPT && BLI_path_extension_check(text->id.name + 2, ".py")) { + if (text->flags & TXT_ISSCRIPT) { if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC)) { if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET)) { G.f |= G_FLAG_SCRIPT_AUTOEXEC_FAIL; -- cgit v1.2.3 From 160d57d33c7f100556b6c69b524783a3ac52b53b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 13 Aug 2021 16:01:45 +1000 Subject: Docs: tooltip update missing from 3e775a4fc57cfd48954adcf284354312f34d5412 --- source/blender/makesrna/intern/rna_text.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_text.c b/source/blender/makesrna/intern/rna_text.c index 1351c027004..52f762e5494 100644 --- a/source/blender/makesrna/intern/rna_text.c +++ b/source/blender/makesrna/intern/rna_text.c @@ -240,8 +240,7 @@ static void rna_def_text(BlenderRNA *brna) prop = RNA_def_property(srna, "use_module", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", TXT_ISSCRIPT); - RNA_def_property_ui_text( - prop, "Register", "Run this text as a script on loading, Text name must end with \".py\""); + RNA_def_property_ui_text(prop, "Register", "Run this text as a Python script on loading"); prop = RNA_def_property(srna, "indentation", PROP_ENUM, PROP_NONE); /* as an enum */ RNA_def_property_enum_bitflag_sdna(prop, NULL, "flags"); -- cgit v1.2.3 From 7b5acc80091d8d92869d83f1308f5af24b45ce9a Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 13 Aug 2021 08:34:10 +0200 Subject: Cleanup: remove unused draw_gpencil_channel. Code is integrated with draw_scene_channel since 2.80. --- source/blender/editors/animation/keyframes_draw.c | 14 -------------- source/blender/editors/include/ED_keyframes_draw.h | 7 ------- 2 files changed, 21 deletions(-) diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index d25f81005c5..5407e04af12 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -685,20 +685,6 @@ void draw_action_channel(AnimKeylistDrawList *draw_list, draw_elem->channel_locked = locked; } -void draw_gpencil_channel( - View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos, float yscale_fac, int saction_flag) -{ - struct AnimKeylist *keylist = ED_keylist_create(); - - saction_flag &= ~SACTION_SHOW_EXTREMES; - - gpencil_to_keylist(ads, gpd, keylist, false); - - draw_keylist(v2d, keylist, ypos, yscale_fac, false, saction_flag); - - ED_keylist_free(keylist); -} - void draw_gpl_channel(AnimKeylistDrawList *draw_list, bDopeSheet *ads, bGPDlayer *gpl, diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index c9bbf58ff7a..50823045936 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -102,13 +102,6 @@ void draw_summary_channel(struct AnimKeylistDrawList *draw_list, float ypos, float yscale_fac, int saction_flag); -/* Grease Pencil datablock summary */ -void draw_gpencil_channel(struct View2D *v2d, - struct bDopeSheet *ads, - struct bGPdata *gpd, - float ypos, - float yscale_fac, - int saction_flag); /* Grease Pencil Layer */ void draw_gpl_channel(struct AnimKeylistDrawList *draw_list, struct bDopeSheet *ads, -- cgit v1.2.3 From 5f6033e0919a9a6bfd00a8a977d28fa60fe7f079 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 13 Aug 2021 09:37:38 +0200 Subject: Keyframe: Reduce GPU context switches. This change reduces the GPU context switches when drawing keyframes. In the previous situation the keyframe blocks and keyframe keys were drawn per channel. With this patch first all the keyframe blocks are drawn for all channels and after that the keyframe keys are collected for all channels and send to the GPU in a single draw call. --- source/blender/editors/animation/keyframes_draw.c | 216 +++++++++++---------- source/blender/editors/include/ED_keyframes_draw.h | 14 +- source/blender/editors/interface/interface_icons.c | 17 +- source/blender/editors/space_nla/nla_draw.c | 19 +- 4 files changed, 143 insertions(+), 123 deletions(-) diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 5407e04af12..61918871b90 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -57,11 +57,7 @@ void draw_keyframe_shape(float x, short key_type, short mode, float alpha, - uint pos_id, - uint size_id, - uint color_id, - uint outline_color_id, - uint flags_id, + const KeyframeShaderBindings *sh_bindings, short handle_type, short extreme_type) { @@ -178,11 +174,11 @@ void draw_keyframe_shape(float x, } } - immAttr1f(size_id, size); - immAttr4ubv(color_id, fill_col); - immAttr4ubv(outline_color_id, outline_col); - immAttr1u(flags_id, flags); - immVertex2f(pos_id, x, y); + immAttr1f(sh_bindings->size_id, size); + immAttr4ubv(sh_bindings->color_id, fill_col); + immAttr4ubv(sh_bindings->outline_color_id, outline_col); + immAttr1u(sh_bindings->flags_id, flags); + immVertex2f(sh_bindings->pos_id, x, y); } /* Common attributes shared between the draw calls. */ @@ -363,98 +359,36 @@ static bool draw_keylist_is_visible_key(const View2D *v2d, const ActKeyColumn *a return IN_RANGE_INCL(ak->cfra, v2d->cur.xmin, v2d->cur.xmax); } -static int draw_keylist_visible_key_len(View2D *v2d, const ListBase * /*ActKeyColumn*/ keys) -{ - /* count keys */ - uint len = 0; - - LISTBASE_FOREACH (ActKeyColumn *, ak, keys) { - /* Optimization: if keyframe doesn't appear within 5 units (screenspace) - * in visible area, don't draw. - * This might give some improvements, - * since we current have to flip between view/region matrices. - */ - if (draw_keylist_is_visible_key(v2d, ak)) { - len++; - } - } - return len; -} - static void draw_keylist_keys(const DrawKeylistUIData *ctx, View2D *v2d, + const KeyframeShaderBindings *sh_bindings, const ListBase * /*ActKeyColumn*/ keys, float ypos, eSAction_Flag saction_flag) { - GPU_blend(GPU_BLEND_ALPHA); - const int key_len = draw_keylist_visible_key_len(v2d, keys); - if (key_len > 0) { - /* draw keys */ - GPUVertFormat *format = immVertexFormat(); - uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color_id = GPU_vertformat_attr_add( - format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint outline_color_id = GPU_vertformat_attr_add( - format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); - - GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); - immUniform1f("outline_scale", 1.0f); - immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); - immBegin(GPU_PRIM_POINTS, key_len); - - short handle_type = KEYFRAME_HANDLE_NONE, extreme_type = KEYFRAME_EXTREME_NONE; - - LISTBASE_FOREACH (ActKeyColumn *, ak, keys) { - if (draw_keylist_is_visible_key(v2d, ak)) { - if (ctx->show_ipo) { - handle_type = ak->handle_type; - } - if (saction_flag & SACTION_SHOW_EXTREMES) { - extreme_type = ak->extreme_type; - } - - draw_keyframe_shape(ak->cfra, - ypos, - ctx->icon_sz, - (ak->sel & SELECT), - ak->key_type, - KEYFRAME_SHAPE_BOTH, - ctx->alpha, - pos_id, - size_id, - color_id, - outline_color_id, - flags_id, - handle_type, - extreme_type); + short handle_type = KEYFRAME_HANDLE_NONE, extreme_type = KEYFRAME_EXTREME_NONE; + + LISTBASE_FOREACH (ActKeyColumn *, ak, keys) { + if (draw_keylist_is_visible_key(v2d, ak)) { + if (ctx->show_ipo) { + handle_type = ak->handle_type; + } + if (saction_flag & SACTION_SHOW_EXTREMES) { + extreme_type = ak->extreme_type; } - } - immEnd(); - GPU_program_point_size(false); - immUnbindProgram(); + draw_keyframe_shape(ak->cfra, + ypos, + ctx->icon_sz, + (ak->sel & SELECT), + ak->key_type, + KEYFRAME_SHAPE_BOTH, + ctx->alpha, + sh_bindings, + handle_type, + extreme_type); + } } - - GPU_blend(GPU_BLEND_NONE); -} - -static void draw_keylist(View2D *v2d, - const struct AnimKeylist *keylist, - float ypos, - float yscale_fac, - bool channelLocked, - eSAction_Flag saction_flag) -{ - DrawKeylistUIData ctx; - draw_keylist_ui_data_init(&ctx, v2d, yscale_fac, channelLocked, saction_flag); - - const ListBase *columns = ED_keylist_listbase(keylist); - draw_keylist_blocks(&ctx, columns, ypos); - draw_keylist_keys(&ctx, v2d, columns, ypos, saction_flag); } /* *************************** Drawing Stack *************************** */ @@ -530,10 +464,23 @@ static void ED_keylist_draw_list_elem_build_keylist(AnimKeylistDrawListElem *ele } } -static void ED_keylist_draw_list_elem_draw(AnimKeylistDrawListElem *elem, View2D *v2d) +static void ED_keylist_draw_list_elem_draw_blocks(AnimKeylistDrawListElem *elem, View2D *v2d) +{ + DrawKeylistUIData ctx; + draw_keylist_ui_data_init(&ctx, v2d, elem->yscale_fac, elem->channel_locked, elem->saction_flag); + + const ListBase *keys = ED_keylist_listbase(elem->keylist); + draw_keylist_blocks(&ctx, keys, elem->ypos); +} + +static void ED_keylist_draw_list_elem_draw_keys(AnimKeylistDrawListElem *elem, + View2D *v2d, + const KeyframeShaderBindings *sh_bindings) { - draw_keylist( - v2d, elem->keylist, elem->ypos, elem->yscale_fac, elem->channel_locked, elem->saction_flag); + DrawKeylistUIData ctx; + draw_keylist_ui_data_init(&ctx, v2d, elem->yscale_fac, elem->channel_locked, elem->saction_flag); + const ListBase *keys = ED_keylist_listbase(elem->keylist); + draw_keylist_keys(&ctx, v2d, sh_bindings, keys, elem->ypos, elem->saction_flag); } typedef struct AnimKeylistDrawList { @@ -552,13 +499,86 @@ static void ED_keylist_draw_list_build_keylists(AnimKeylistDrawList *draw_list) } } -static void ED_keylist_draw_list_draw(AnimKeylistDrawList *draw_list, View2D *v2d) +static void ED_keylist_draw_list_draw_blocks(AnimKeylistDrawList *draw_list, View2D *v2d) { LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { - ED_keylist_draw_list_elem_draw(elem, v2d); + ED_keylist_draw_list_elem_draw_blocks(elem, v2d); } } +static int ED_keylist_draw_keylist_visible_key_len(const View2D *v2d, + const ListBase * /*ActKeyColumn*/ keys) +{ + /* count keys */ + uint len = 0; + + LISTBASE_FOREACH (ActKeyColumn *, ak, keys) { + /* Optimization: if keyframe doesn't appear within 5 units (screenspace) + * in visible area, don't draw. + * This might give some improvements, + * since we current have to flip between view/region matrices. + */ + if (draw_keylist_is_visible_key(v2d, ak)) { + len++; + } + } + return len; +} + +static int ED_keylist_draw_list_visible_key_len(const AnimKeylistDrawList *draw_list, + const View2D *v2d) +{ + uint len = 0; + LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { + const ListBase *keys = ED_keylist_listbase(elem->keylist); + len += ED_keylist_draw_keylist_visible_key_len(v2d, keys); + } + return len; +} + +static void ED_keylist_draw_list_draw_keys(AnimKeylistDrawList *draw_list, View2D *v2d) +{ + const int visible_key_len = ED_keylist_draw_list_visible_key_len(draw_list, v2d); + if (visible_key_len == 0) { + return; + } + + GPU_blend(GPU_BLEND_ALPHA); + + GPUVertFormat *format = immVertexFormat(); + KeyframeShaderBindings sh_bindings; + + sh_bindings.pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + sh_bindings.size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + sh_bindings.color_id = GPU_vertformat_attr_add( + format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + sh_bindings.outline_color_id = GPU_vertformat_attr_add( + format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); + + GPU_program_point_size(true); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immUniform1f("outline_scale", 1.0f); + immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); + immBegin(GPU_PRIM_POINTS, visible_key_len); + + LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { + ED_keylist_draw_list_elem_draw_keys(elem, v2d, &sh_bindings); + } + + immEnd(); + GPU_program_point_size(false); + immUnbindProgram(); + + GPU_blend(GPU_BLEND_NONE); +} + +static void ED_keylist_draw_list_draw(AnimKeylistDrawList *draw_list, View2D *v2d) +{ + ED_keylist_draw_list_draw_blocks(draw_list, v2d); + ED_keylist_draw_list_draw_keys(draw_list, v2d); +} + void ED_keylist_draw_list_flush(AnimKeylistDrawList *draw_list, View2D *v2d) { ED_keylist_draw_list_build_keylists(draw_list); diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index 50823045936..61e37f20b1b 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -43,6 +43,14 @@ struct bGPDlayer; /* draw simple diamond-shape keyframe */ /* caller should set up vertex format, bind GPU_SHADER_KEYFRAME_DIAMOND, * immBegin(GPU_PRIM_POINTS, n), then call this n times */ +typedef struct KeyframeShaderBindings { + uint pos_id; + uint size_id; + uint color_id; + uint outline_color_id; + uint flags_id; +} KeyframeShaderBindings; + void draw_keyframe_shape(float x, float y, float size, @@ -50,11 +58,7 @@ void draw_keyframe_shape(float x, short key_type, short mode, float alpha, - unsigned int pos_id, - unsigned int size_id, - unsigned int color_id, - unsigned int outline_color_id, - unsigned int flags_id, + const KeyframeShaderBindings *sh_bindings, short handle_type, short extreme_type); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 2d59bfb92c8..f739830cfdb 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -299,13 +299,14 @@ static void vicon_keytype_draw_wrapper( const float yco = y + h / 2 + 0.5f; GPUVertFormat *format = immVertexFormat(); - const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - const uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color_id = GPU_vertformat_attr_add( + KeyframeShaderBindings sh_bindings; + sh_bindings.pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + sh_bindings.size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + sh_bindings.color_id = GPU_vertformat_attr_add( format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint outline_color_id = GPU_vertformat_attr_add( + sh_bindings.outline_color_id = GPU_vertformat_attr_add( format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - const uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); + sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); @@ -326,11 +327,7 @@ static void vicon_keytype_draw_wrapper( key_type, KEYFRAME_SHAPE_BOTH, alpha, - pos_id, - size_id, - color_id, - outline_color_id, - flags_id, + &sh_bindings, handle_type, KEYFRAME_EXTREME_NONE); diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 2bf4c7d4344..c1b308d213f 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -140,13 +140,16 @@ static void nla_action_draw_keyframes( if (key_len > 0) { format = immVertexFormat(); - pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color_id = GPU_vertformat_attr_add( + KeyframeShaderBindings sh_bindings; + sh_bindings.pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + sh_bindings.size_id = GPU_vertformat_attr_add( + format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + sh_bindings.color_id = GPU_vertformat_attr_add( format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint outline_color_id = GPU_vertformat_attr_add( + sh_bindings.outline_color_id = GPU_vertformat_attr_add( format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); + sh_bindings.flags_id = GPU_vertformat_attr_add( + format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); @@ -165,11 +168,7 @@ static void nla_action_draw_keyframes( ak->key_type, KEYFRAME_SHAPE_FRAME, 1.0f, - pos_id, - size_id, - color_id, - outline_color_id, - flags_id, + &sh_bindings, KEYFRAME_HANDLE_NONE, KEYFRAME_EXTREME_NONE); } -- cgit v1.2.3 From 0ae23636e7e337b70ade5b172af291ac46a517ff Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 21 Apr 2021 17:26:18 +0200 Subject: Fix missing animation UI update in the Properties Editor Animation indicators as well as decorators for properties were not updating correctly in the following cases: - NLA pushdown (this was reported in T87681) - NLA enter/exit tweakmode - Outliner unlinking/setting action These actions all send a ND_NLA_ACTCHANGE notifier which the Properties Editor was not listening to [which is now added]. part of T87681. Maniphest Tasks: T87681 Differential Revision: https://developer.blender.org/D11040 --- source/blender/editors/space_buttons/space_buttons.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 57a7fe894b0..b04291b7ab4 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -811,6 +811,9 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params) break; case NC_ANIMATION: switch (wmn->data) { + case ND_NLA_ACTCHANGE: + ED_area_tag_redraw(area); + break; case ND_KEYFRAME: if (ELEM(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED)) { ED_area_tag_redraw(area); -- cgit v1.2.3 From 00f264ea42fb657cd4c62be54ae43459f480a6d8 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 13 Aug 2021 10:52:53 +0200 Subject: Cleanup: correct comment exiting NLA tweakmode Comment was pasted from entering tweakmode. --- source/blender/editors/space_nla/nla_edit.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 56efcd8571f..c75b874833a 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -241,9 +241,7 @@ bool nlaedit_disable_tweakmode(bAnimContext *ac, bool do_solo) ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); - /* if we managed to enter tweak-mode on at least one AnimData block, - * set the flag for this in the active scene and send notifiers - */ + /* Clear the tweak-mode flag in the active scene and send notifiers. */ if (ac->scene) { /* clear editing flag */ ac->scene->flag &= ~SCE_NLA_EDIT_ON; -- cgit v1.2.3 From fd29a161cc892730859f6b28a026460f9dd4a1c5 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 22 Apr 2021 15:50:29 +0200 Subject: Fix Action Editor unlink button when in tweak mode When in NLA tweak mode, the action unlink button in the Dopesheet / Action Editor should be a mere shortcut to exiting tweak mode [nothing else]. Instead, it was also clearing the action fully, not returning to the previous edited action before going into tweak mode. Now dont "flush" by clearing the action, instead exit tweakmode, clear the scenes SCE_NLA_EDIT_ON flag (if this isnt done some NLA operators like pushdown were not immediately available because their poll checked this flag) and send appropriate notifier to have everything update nicely. Part of T87681 (Bug 4/5/6). Maniphest Tasks: T87681 Differential Revision: https://developer.blender.org/D11052 --- source/blender/editors/space_action/action_data.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index d69c7ab8d48..717d87c4972 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -599,16 +599,13 @@ void ED_animedit_unlink_action( id_fake_user_clear(&act->id); } - /* If in Tweak Mode, don't unlink. Instead, this - * becomes a shortcut to exit Tweak Mode instead - */ + /* If in Tweak Mode, don't unlink. Instead, this becomes a shortcut to exit Tweak Mode. */ if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) { - /* Exit Tweak Mode */ BKE_nla_tweakmode_exit(adt); - /* Flush this to the Action Editor (if that's where this change was initiated) */ - if (area->spacetype == SPACE_ACTION) { - actedit_change_action(C, NULL); + Scene *scene = CTX_data_scene(C); + if (scene != NULL) { + scene->flag &= ~SCE_NLA_EDIT_ON; } } else { @@ -660,6 +657,9 @@ static int action_unlink_exec(bContext *C, wmOperator *op) ED_animedit_unlink_action(C, NULL, adt, adt->action, op->reports, force_delete); } + /* Unlink is also abused to exit NLA tweak mode. */ + WM_main_add_notifier(NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); + return OPERATOR_FINISHED; } -- cgit v1.2.3 From b9486c39bcd57098b2baf8fb5d69088768a88e35 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 13 Aug 2021 11:16:01 +0200 Subject: Cleanup: Unused function parameter warning --- intern/cycles/blender/blender_shader.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index f3ad9c3639a..1404c58290d 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -1007,9 +1007,7 @@ static bool node_use_modified_socket_name(ShaderNode *node) return true; } -static ShaderInput *node_find_input_by_name(ShaderNode *node, - BL::Node &b_node, - BL::NodeSocket &b_socket) +static ShaderInput *node_find_input_by_name(ShaderNode *node, BL::NodeSocket &b_socket) { string name = b_socket.identifier(); ShaderInput *input = node->input(name.c_str()); @@ -1037,9 +1035,7 @@ static ShaderInput *node_find_input_by_name(ShaderNode *node, return input; } -static ShaderOutput *node_find_output_by_name(ShaderNode *node, - BL::Node &b_node, - BL::NodeSocket &b_socket) +static ShaderOutput *node_find_output_by_name(ShaderNode *node, BL::NodeSocket &b_socket) { string name = b_socket.identifier(); ShaderOutput *output = node->output(name.c_str()); @@ -1195,7 +1191,7 @@ static void add_nodes(Scene *scene, if (node) { /* map node sockets for linking */ for (BL::NodeSocket &b_input : b_node.inputs) { - ShaderInput *input = node_find_input_by_name(node, b_node, b_input); + ShaderInput *input = node_find_input_by_name(node, b_input); if (!input) { /* XXX should not happen, report error? */ continue; @@ -1205,7 +1201,7 @@ static void add_nodes(Scene *scene, set_default_value(input, b_input, b_data, b_ntree); } for (BL::NodeSocket &b_output : b_node.outputs) { - ShaderOutput *output = node_find_output_by_name(node, b_node, b_output); + ShaderOutput *output = node_find_output_by_name(node, b_output); if (!output) { /* XXX should not happen, report error? */ continue; -- cgit v1.2.3 From 4cadccebfacfa8533ba06403960e8b1218da61f5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 13 Aug 2021 19:38:19 +1000 Subject: Revert "Mesh: replace saacos with acosf for normal calculation" This reverts commit 41e650981861c2f18ab0548e18851d1d761066ff. This broke "CubeMaskFirst" test. Any value even slightly outside the [-1.0..1.0] range caused the result to be nan, which can happen when calculating the dot-product between two unit length vectors. --- source/blender/blenkernel/intern/mesh_normals.cc | 14 +++++--------- source/blender/blenlib/intern/math_geom.c | 12 ++++-------- source/blender/bmesh/intern/bmesh_mesh_normals.c | 18 +++++++----------- 3 files changed, 16 insertions(+), 28 deletions(-) diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index aae50fa165e..9a761c6fa11 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -230,10 +230,8 @@ static void mesh_calc_normals_poly_and_vertex_accum_fn( copy_v3_v3(edvec_next, edvec_end); } - /* Calculate angle between the two poly edges incident on this vertex. - * NOTE: no need for #saacos here as the input has been sanitized, - * `nan` values in coordinates normalize to zero which works for `acosf`. */ - const float fac = acosf(-dot_v3v3(edvec_prev, edvec_next)); + /* Calculate angle between the two poly edges incident on this vertex. */ + const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next)); const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac}; add_v3_v3_atomic(vnors[ml[i_curr].v], vnor_add); @@ -1158,11 +1156,9 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli // printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); { - /* Code similar to #accumulate_vertex_normals_poly_v3. */ - /* Calculate angle between the two poly edges incident on this vertex. - * NOTE: no need for #saacos here as the input has been sanitized, - * `nan` values in coordinates normalize to zero which works for `acosf`. */ - const float fac = acosf(dot_v3v3(vec_curr, vec_prev)); + /* Code similar to accumulate_vertex_normals_poly_v3. */ + /* Calculate angle between the two poly edges incident on this vertex. */ + const float fac = saacos(dot_v3v3(vec_curr, vec_prev)); /* Accumulate */ madd_v3_v3fl(lnor, polynors[mpfan_curr_index], fac); diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 43f2e08cf69..8afb6b5a2be 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -5307,10 +5307,7 @@ void accumulate_vertex_normals_tri_v3(float n1[3], for (i = 0; i < nverts; i++) { const float *cur_edge = vdiffs[i]; - /* Calculate angle between the two poly edges incident on this vertex. - * NOTE: no need for #saacos here as the input has been sanitized, - * `nan` values in coordinates normalize to zero which works for `acosf`. */ - const float fac = acosf(-dot_v3v3(cur_edge, prev_edge)); + const float fac = saacos(-dot_v3v3(cur_edge, prev_edge)); /* accumulate */ madd_v3_v3fl(vn[i], f_no, fac); @@ -5389,10 +5386,9 @@ void accumulate_vertex_normals_poly_v3(float **vertnos, for (i = 0; i < nverts; i++) { const float *cur_edge = vdiffs[i]; - /* Calculate angle between the two poly edges incident on this vertex. - * NOTE: no need for #saacos here as the input has been sanitized, - * `nan` values in coordinates normalize to zero which works for `acosf`. */ - const float fac = acosf(-dot_v3v3(cur_edge, prev_edge)); + /* calculate angle between the two poly edges incident on + * this vertex */ + const float fac = saacos(-dot_v3v3(cur_edge, prev_edge)); /* accumulate */ madd_v3_v3fl(vertnos[i], polyno, fac); diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index 6a2cfdb056c..a5e41b74ee1 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -84,13 +84,11 @@ BLI_INLINE void bm_vert_calc_normals_accum_loop(const BMLoop *l_iter, if ((l_iter->prev->e->v1 == l_iter->prev->v) ^ (l_iter->e->v1 == l_iter->v)) { dotprod = -dotprod; } - /* Calculate angle between the two poly edges incident on this vertex. - * NOTE: no need for #saacos here as the input has been sanitized, - * `nan` values in coordinates normalize to zero which works for `acosf`. */ - const float fac = acosf(-dotprod); - /* NAN values should never happen. */ - BLI_assert(fac == fac); - madd_v3_v3fl(v_no, f_no, fac); + const float fac = saacos(-dotprod); + /* NAN detection, otherwise this is a degenerated case, ignore that vertex in this case. */ + if (fac == fac) { + madd_v3_v3fl(v_no, f_no, fac); + } } static void bm_vert_calc_normals_impl(BMVert *v) @@ -682,11 +680,9 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm, { /* Code similar to accumulate_vertex_normals_poly_v3. */ - /* Calculate angle between the two poly edges incident on this vertex. - * NOTE: no need for #saacos here as the input has been sanitized, - * `nan` values in coordinates normalize to zero which works for `acosf`. */ + /* Calculate angle between the two poly edges incident on this vertex. */ const BMFace *f = lfan_pivot->f; - const float fac = acosf(dot_v3v3(vec_next, vec_curr)); + const float fac = saacos(dot_v3v3(vec_next, vec_curr)); const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no; /* Accumulate */ madd_v3_v3fl(lnor, no, fac); -- cgit v1.2.3 From 0b3c7544b532d99d80a25f3cb1f63f14480b46c8 Mon Sep 17 00:00:00 2001 From: "Matteo F. Vescovi" Date: Fri, 13 Aug 2021 14:33:37 +0200 Subject: Fix FTBFS on mips64el architecture While trying to get Blender 2.93.x LTS to build fine on all release architectures in Debian, I noticed that the misleading use of "mips" as integer variable caused problems when compiling on mips64el. The patch should fix the issue. Reviewed By: fclem Differential Revision: https://developer.blender.org/D12194 --- source/blender/draw/intern/draw_manager_texture.c | 24 +++++++++++------------ source/blender/gpu/GPU_texture.h | 14 ++++++------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/source/blender/draw/intern/draw_manager_texture.c b/source/blender/draw/intern/draw_manager_texture.c index 73afdd6e1e3..99e8ba968a2 100644 --- a/source/blender/draw/intern/draw_manager_texture.c +++ b/source/blender/draw/intern/draw_manager_texture.c @@ -83,8 +83,8 @@ GPUTexture *DRW_texture_create_1d(int w, DRWTextureFlag flags, const float *fpixels) { - int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_1d(__func__, w, mips, format, fpixels); + int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; + GPUTexture *tex = GPU_texture_create_1d(__func__, w, mip_len, format, fpixels); drw_texture_set_parameters(tex, flags); return tex; @@ -93,8 +93,8 @@ GPUTexture *DRW_texture_create_1d(int w, GPUTexture *DRW_texture_create_2d( int w, int h, eGPUTextureFormat format, DRWTextureFlag flags, const float *fpixels) { - int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_2d(__func__, w, h, mips, format, fpixels); + int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; + GPUTexture *tex = GPU_texture_create_2d(__func__, w, h, mip_len, format, fpixels); drw_texture_set_parameters(tex, flags); return tex; @@ -103,8 +103,8 @@ GPUTexture *DRW_texture_create_2d( GPUTexture *DRW_texture_create_2d_array( int w, int h, int d, eGPUTextureFormat format, DRWTextureFlag flags, const float *fpixels) { - int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_2d_array(__func__, w, h, d, mips, format, fpixels); + int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; + GPUTexture *tex = GPU_texture_create_2d_array(__func__, w, h, d, mip_len, format, fpixels); drw_texture_set_parameters(tex, flags); return tex; @@ -113,9 +113,9 @@ GPUTexture *DRW_texture_create_2d_array( GPUTexture *DRW_texture_create_3d( int w, int h, int d, eGPUTextureFormat format, DRWTextureFlag flags, const float *fpixels) { - int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; + int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; GPUTexture *tex = GPU_texture_create_3d( - __func__, w, h, d, mips, format, GPU_DATA_FLOAT, fpixels); + __func__, w, h, d, mip_len, format, GPU_DATA_FLOAT, fpixels); drw_texture_set_parameters(tex, flags); return tex; @@ -126,8 +126,8 @@ GPUTexture *DRW_texture_create_cube(int w, DRWTextureFlag flags, const float *fpixels) { - int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_cube(__func__, w, mips, format, fpixels); + int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; + GPUTexture *tex = GPU_texture_create_cube(__func__, w, mip_len, format, fpixels); drw_texture_set_parameters(tex, flags); return tex; } @@ -135,8 +135,8 @@ GPUTexture *DRW_texture_create_cube(int w, GPUTexture *DRW_texture_create_cube_array( int w, int d, eGPUTextureFormat format, DRWTextureFlag flags, const float *fpixels) { - int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_cube_array(__func__, w, d, mips, format, fpixels); + int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; + GPUTexture *tex = GPU_texture_create_cube_array(__func__, w, d, mip_len, format, fpixels); drw_texture_set_parameters(tex, flags); return tex; } diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index f980c8fdcd7..ee4d08d4059 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -187,25 +187,25 @@ unsigned int GPU_texture_memory_usage_get(void); * \a mips is the number of mip level to allocate. It must be >= 1. */ GPUTexture *GPU_texture_create_1d( - const char *name, int w, int mips, eGPUTextureFormat format, const float *data); + const char *name, int w, int mip_len, eGPUTextureFormat format, const float *data); GPUTexture *GPU_texture_create_1d_array( - const char *name, int w, int h, int mips, eGPUTextureFormat format, const float *data); + const char *name, int w, int h, int mip_len, eGPUTextureFormat format, const float *data); GPUTexture *GPU_texture_create_2d( - const char *name, int w, int h, int mips, eGPUTextureFormat format, const float *data); + const char *name, int w, int h, int mip_len, eGPUTextureFormat format, const float *data); GPUTexture *GPU_texture_create_2d_array( - const char *name, int w, int h, int d, int mips, eGPUTextureFormat format, const float *data); + const char *name, int w, int h, int d, int mip_len, eGPUTextureFormat format, const float *data); GPUTexture *GPU_texture_create_3d(const char *name, int w, int h, int d, - int mips, + int mip_len, eGPUTextureFormat texture_format, eGPUDataFormat data_format, const void *data); GPUTexture *GPU_texture_create_cube( - const char *name, int w, int mips, eGPUTextureFormat format, const float *data); + const char *name, int w, int mip_len, eGPUTextureFormat format, const float *data); GPUTexture *GPU_texture_create_cube_array( - const char *name, int w, int d, int mips, eGPUTextureFormat format, const float *data); + const char *name, int w, int d, int mip_len, eGPUTextureFormat format, const float *data); /* Special textures. */ GPUTexture *GPU_texture_create_from_vertbuf(const char *name, struct GPUVertBuf *vert); -- cgit v1.2.3 From 77744b581d082a692b35345bab57128c6915bc6d Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 11 Aug 2021 13:53:19 +0200 Subject: Fix T90595: some VSE strip properties do not identify strip name in anim channel names Working with multiple strips keyframes was unneccessarily difficult in Animation Editors (since some anim channels could not be distinguished). Namely `Crop` and `Transform` are nested structs (nested under `Sequence`), so these were just displaying the raw struct name. Also strip modifiers did not have their strip name in their channel names. Now include the strip name for these. before {F10277439} after {F10277441} Maniphest Tasks: T90595 Differential Revision: https://developer.blender.org/D12193 --- source/blender/editors/animation/anim_ipo_utils.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c index 5992545bdbe..eda87cf1897 100644 --- a/source/blender/editors/animation/anim_ipo_utils.c +++ b/source/blender/editors/animation/anim_ipo_utils.c @@ -134,6 +134,28 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) else { structname = RNA_struct_ui_name(ptr.type); } + + /* For the VSE, a strip's 'Transform' or 'Crop' is a nested (under Sequence) struct, but + * displaying the struct name alone is no meaningful information (and also cannot be + * filtered well), same for modifiers. So display strip name alongside as well. */ + if (GS(ptr.owner_id->name) == ID_SCE) { + if (BLI_str_startswith(fcu->rna_path, "sequence_editor.sequences_all[\"")) { + if (strstr(fcu->rna_path, ".transform.") || strstr(fcu->rna_path, ".crop.") || + strstr(fcu->rna_path, ".modifiers[")) { + char *stripname = BLI_str_quoted_substrN(fcu->rna_path, "sequences_all["); + const char *structname_all = BLI_sprintfN( + "%s : %s", stripname ? stripname : "", structname); + if (free_structname) { + MEM_freeN((void *)structname); + } + if (stripname) { + MEM_freeN(stripname); + } + structname = structname_all; + free_structname = 1; + } + } + } } /* Property Name is straightforward */ -- cgit v1.2.3 From 7772880d696c306fa41a27af65f7a21a9168c616 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 13 Aug 2021 12:40:56 +0200 Subject: ViewLayer resync: Add sanity checks for objects/bases mappings. Add a debug-only check regarding consistency of the cache (mapping from objects to their bases) for a given ViewLayer. Issues can happen otherwise when some code does remapping of objects, and forgets to call `BKE_main_collection_sync_remap()` (which clears those caches) instead of `BKE_main_collection_sync()`. --- source/blender/blenkernel/intern/layer.c | 54 ++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index e7d83c668c8..b489675cd74 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -1174,6 +1174,52 @@ static void layer_collection_sync(ViewLayer *view_layer, parent_local_collections_bits); } +#ifndef NDEBUG +static bool view_layer_objects_base_cache_validate(ViewLayer *view_layer, LayerCollection *layer) +{ + bool is_valid = true; + + if (layer == NULL) { + layer = view_layer->layer_collections.first; + } + + /* Only check for a collection's objects if its layer is not excluded. */ + if ((layer->flag & LAYER_COLLECTION_EXCLUDE) == 0) { + LISTBASE_FOREACH (CollectionObject *, cob, &layer->collection->gobject) { + if (cob->ob == NULL) { + continue; + } + if (BLI_ghash_lookup(view_layer->object_bases_hash, cob->ob) == NULL) { + CLOG_FATAL( + &LOG, + "Object '%s' from collection '%s' has no entry in view layer's object bases cache", + cob->ob->id.name + 2, + layer->collection->id.name + 2); + is_valid = false; + break; + } + } + } + + if (is_valid) { + LISTBASE_FOREACH (LayerCollection *, layer_child, &layer->layer_collections) { + if (!view_layer_objects_base_cache_validate(view_layer, layer_child)) { + is_valid = false; + break; + } + } + } + + return is_valid; +} +#else +static bool view_layer_objects_base_cache_validate(ViewLayer *UNUSED(view_layer), + LayerCollection *UNUSED(layer)) +{ + return true; +} +#endif + /** * Update view layer collection tree from collections used in the scene. * This is used when collections are removed or added, both while editing @@ -1240,6 +1286,12 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) } if (base->object) { + /* Those asserts are commented, since they are too expensive to perform even in debug, as + * this layer resync function currently gets called way too often. */ +#if 0 + BLI_assert(BLI_findindex(&new_object_bases, base) == -1); + BLI_assert(BLI_findptr(&new_object_bases, base->object, offsetof(Base, object)) == NULL); +#endif BLI_ghash_remove(view_layer->object_bases_hash, base->object, NULL, NULL); } } @@ -1247,6 +1299,8 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) BLI_freelistN(&view_layer->object_bases); view_layer->object_bases = new_object_bases; + view_layer_objects_base_cache_validate(view_layer, NULL); + LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { BKE_base_eval_flags(base); } -- cgit v1.2.3 From bb0e29c922df43e13cfe5d18928c6ae2b942a945 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 13 Aug 2021 16:26:58 +0200 Subject: Blendloader: Option to reports to skip list of recursively liboverride-resynced libs. This extra info is not always needed/convinient to use, and requires special attention to free the list, so allow not generating it. --- source/blender/blenkernel/intern/lib_override.c | 2 +- source/blender/blenloader/BLO_readfile.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index bebc49e090d..8083585b594 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1631,7 +1631,7 @@ static void lib_override_library_main_resync_on_library_indirect_level( CLOG_INFO(&LOG, 2, "\tSuccess: %d", success); if (success) { reports->count.resynced_lib_overrides++; - if (library_indirect_level > 0 && + if (library_indirect_level > 0 && reports->do_resynced_lib_overrides_libraries_list && BLI_linklist_index(reports->resynced_lib_overrides_libraries, library) < 0) { BLI_linklist_prepend(&reports->resynced_lib_overrides_libraries, library); reports->resynced_lib_overrides_libraries_count++; diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index c3a33115613..dbdb181281a 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -118,6 +118,7 @@ typedef struct BlendFileReadReport { /* Number of libraries which had overrides that needed to be resynced, and a single linked list * of those. */ int resynced_lib_overrides_libraries_count; + bool do_resynced_lib_overrides_libraries_list; struct LinkNode *resynced_lib_overrides_libraries; } BlendFileReadReport; -- cgit v1.2.3 From 5225e459da07b10b5d1a77f74fed2d3df1fee594 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 13 Aug 2021 16:34:06 +0200 Subject: Fix T86883: Add/fix suport of liboverrides in relocate/reload library case. There was already some code for that, but it was broken, and proper resync was completely missing. There might still be more resync needed in library linking operators though. --- .../blender/windowmanager/intern/wm_files_link.c | 28 ++++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 7c3fce7fcb2..cdcb6c5163f 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -831,7 +831,7 @@ static void lib_relocate_do_remap(Main *bmain, } } -static void lib_relocate_do(Main *bmain, +static void lib_relocate_do(bContext *C, Library *library, WMLinkAppendData *lapp_data, ReportList *reports, @@ -843,6 +843,10 @@ static void lib_relocate_do(Main *bmain, LinkNode *itemlink; int item_idx; + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + /* Remove all IDs to be reloaded from Main. */ lba_idx = set_listbasepointers(bmain, lbarray); while (lba_idx--) { @@ -990,21 +994,31 @@ static void lib_relocate_do(Main *bmain, } } - /* Update overrides of reloaded linked data-blocks. - * Note that this will not necessarily fully update the override, it might need to be manually - * 're-generated' depending on changes in linked data. */ + /* Update overrides of reloaded linked data-blocks. */ ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { if (ID_IS_LINKED(id) || !ID_IS_OVERRIDE_LIBRARY_REAL(id) || (id->tag & LIB_TAG_PRE_EXISTING) == 0) { continue; } - if (id->override_library->reference->lib == library) { + if ((id->override_library->reference->tag & LIB_TAG_PRE_EXISTING) == 0) { BKE_lib_override_library_update(bmain, id); } } FOREACH_MAIN_ID_END; + /* Resync overrides if needed. */ + if (!USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync)) { + BKE_lib_override_library_main_resync(bmain, + scene, + view_layer, + &(struct BlendFileReadReport){ + .reports = reports, + }); + /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */ + BKE_lib_override_library_main_operations_create(bmain, true); + } + BKE_main_collection_sync(bmain); BKE_main_lib_objects_recalc_all(bmain); @@ -1039,7 +1053,7 @@ void WM_lib_reload(Library *lib, bContext *C, ReportList *reports) wm_link_append_data_library_add(lapp_data, lib->filepath_abs); - lib_relocate_do(CTX_data_main(C), lib, lapp_data, reports, true); + lib_relocate_do(C, lib, lapp_data, reports, true); wm_link_append_data_free(lapp_data); @@ -1162,7 +1176,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) lapp_data->flag |= BLO_LIBLINK_USE_PLACEHOLDERS | BLO_LIBLINK_FORCE_INDIRECT; } - lib_relocate_do(bmain, lib, lapp_data, op->reports, do_reload); + lib_relocate_do(C, lib, lapp_data, op->reports, do_reload); wm_link_append_data_free(lapp_data); -- cgit v1.2.3 From 5655b3d1c55a1fb631a8eb40ac96d875b2ab213d Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 13 Aug 2021 15:45:07 -0300 Subject: Cleanup: fix typos in static variables _desps --> _deps --- source/blender/draw/intern/draw_cache_impl_mesh.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 359788545e4..86c14330409 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -95,7 +95,7 @@ #define MDEPS_CREATE(buff_name, ...) [_BUFFER_INDEX(buff_name)] = VA_NARGS_CALL_OVERLOAD(_MDEPS_CREATE, __VA_ARGS__) -#define _MDEPS_CREATE_MAP1(a) g_buffer_desps[_BUFFER_INDEX(a)] +#define _MDEPS_CREATE_MAP1(a) g_buffer_deps[_BUFFER_INDEX(a)] #define _MDEPS_CREATE_MAP2(a, b) _MDEPS_CREATE_MAP1(a) | _MDEPS_CREATE_MAP1(b) #define _MDEPS_CREATE_MAP3(a, b, c) _MDEPS_CREATE_MAP2(a, b) | _MDEPS_CREATE_MAP1(c) #define _MDEPS_CREATE_MAP4(a, b, c, d) _MDEPS_CREATE_MAP3(a, b, c) | _MDEPS_CREATE_MAP1(d) @@ -110,8 +110,8 @@ #ifndef NDEBUG # define _MDEPS_ASSERT2(b, name) \ - g_buffer_desps_d[_BUFFER_INDEX(name)] |= _MDEPS_CREATE1(b); \ - BLI_assert(g_buffer_desps[_BUFFER_INDEX(name)] & _MDEPS_CREATE1(b)) + g_buffer_deps_d[_BUFFER_INDEX(name)] |= _MDEPS_CREATE1(b); \ + BLI_assert(g_buffer_deps[_BUFFER_INDEX(name)] & _MDEPS_CREATE1(b)) # define _MDEPS_ASSERT3(b, n1, n2) _MDEPS_ASSERT2(b, n1); _MDEPS_ASSERT2(b, n2) # define _MDEPS_ASSERT4(b, n1, n2, n3) _MDEPS_ASSERT3(b, n1, n2); _MDEPS_ASSERT2(b, n3) # define _MDEPS_ASSERT5(b, n1, n2, n3, n4) _MDEPS_ASSERT4(b, n1, n2, n3); _MDEPS_ASSERT2(b, n4) @@ -120,7 +120,7 @@ # define _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7) _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6); _MDEPS_ASSERT2(b, n7) # define MDEPS_ASSERT(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__) -# define MDEPS_ASSERT_MAP(name) BLI_assert(g_buffer_desps_d[_BUFFER_INDEX(name)] == g_buffer_desps[_BUFFER_INDEX(name)]) +# define MDEPS_ASSERT_MAP(name) BLI_assert(g_buffer_deps_d[_BUFFER_INDEX(name)] == g_buffer_deps[_BUFFER_INDEX(name)]) #else # define MDEPS_ASSERT(...) # define MDEPS_ASSERT_MAP(name) @@ -128,7 +128,7 @@ /* clang-format on */ -static const DRWBatchFlag g_buffer_desps[] = { +static const DRWBatchFlag g_buffer_deps[] = { MDEPS_CREATE(vbo.pos_nor, batch.surface, batch.surface_weights, @@ -215,7 +215,7 @@ static const DRWBatchFlag g_buffer_desps[] = { }; #ifndef NDEBUG -static DRWBatchFlag g_buffer_desps_d[sizeof(g_buffer_desps)] = {0}; +static DRWBatchFlag g_buffer_deps_d[sizeof(g_buffer_deps)] = {0}; #endif static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache); -- cgit v1.2.3 From 0ed2df81cc3878d83a179ade256ba2d690ce9cde Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 13 Aug 2021 15:51:02 -0300 Subject: Fix T90637: Outliner: VSE context menu options are not working Some of the enum options in the context menu operations are not supported for all element types. `TSE_SEQUENCE`, for example, only supports the `Select` option. So, populate the enum list dynamically depending on the type. Also add some calls that were missing for the `TSE_SEQUENCE` type. (`WM_event_add_notifier` and `ED_undo_push`). --- .../editors/space_outliner/outliner_tools.c | 80 +++++++++++++++++----- 1 file changed, 62 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index a994368a0ec..2474b54384d 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -78,6 +78,8 @@ #include "ED_sequencer.h" #include "ED_undo.h" +#include "SEQ_relations.h" + #include "WM_api.h" #include "WM_message.h" #include "WM_types.h" @@ -1281,18 +1283,31 @@ static void ebone_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), } } -static void sequence_fn(int event, TreeElement *te, TreeStoreElem *tselem, void *scene_ptr) +static void sequence_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *scene_ptr) { Sequence *seq = (Sequence *)te->directdata; - if (event == OL_DOP_SELECT) { - Scene *scene = (Scene *)scene_ptr; - Editing *ed = SEQ_editing_get(scene, false); - if (BLI_findindex(ed->seqbasep, seq) != -1) { + Scene *scene = (Scene *)scene_ptr; + Editing *ed = SEQ_editing_get(scene, false); + if (BLI_findindex(ed->seqbasep, seq) != -1) { + if (event == OL_DOP_SELECT) { ED_sequencer_select_sequence_single(scene, seq, true); } + else if (event == OL_DOP_DESELECT) { + seq->flag &= ~SELECT; + } + else if (event == OL_DOP_HIDE) { + if (!(seq->flag & SEQ_MUTE)) { + seq->flag |= SEQ_MUTE; + SEQ_relations_invalidate_dependent(scene, seq); + } + } + else if (event == OL_DOP_UNHIDE) { + if (seq->flag & SEQ_MUTE) { + seq->flag &= ~SEQ_MUTE; + SEQ_relations_invalidate_dependent(scene, seq); + } + } } - - (void)tselem; } static void gpencil_layer_fn(int event, @@ -2709,16 +2724,6 @@ void OUTLINER_OT_modifier_operation(wmOperatorType *ot) /** \name Data Menu Operator * \{ */ -/* XXX: select linked is for RNA structs only. */ -static const EnumPropertyItem prop_data_op_types[] = { - {OL_DOP_SELECT, "SELECT", 0, "Select", ""}, - {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""}, - {OL_DOP_HIDE, "HIDE", 0, "Hide", ""}, - {OL_DOP_UNHIDE, "UNHIDE", 0, "Unhide", ""}, - {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""}, - {0, NULL, 0, NULL, NULL}, -}; - static int outliner_data_operation_exec(bContext *C, wmOperator *op) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -2762,6 +2767,8 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); outliner_do_data_operation( space_outliner, datalevel, event, &space_outliner->tree, sequence_fn, scene); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); + ED_undo_push(C, "Sequencer operation"); break; } @@ -2789,6 +2796,42 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +/* Dynamically populate an enum of Keying Sets */ +static const EnumPropertyItem *outliner_data_op_sets_enum_item_fn(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *UNUSED(r_free)) +{ + /* Check for invalid states. */ + if (C == NULL) { + return DummyRNA_DEFAULT_items; + } + + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); + if (space_outliner == NULL) { + return DummyRNA_DEFAULT_items; + } + + static const EnumPropertyItem optype_sel_and_hide[] = { + {OL_DOP_SELECT, "SELECT", 0, "Select", ""}, + {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""}, + {OL_DOP_HIDE, "HIDE", 0, "Hide", ""}, + {OL_DOP_UNHIDE, "UNHIDE", 0, "Unhide", ""}, + {0, NULL, 0, NULL, NULL}}; + + static const EnumPropertyItem optype_sel_linked[] = { + {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""}, {0, NULL, 0, NULL, NULL}}; + + TreeElement *te = get_target_element(space_outliner); + TreeStoreElem *tselem = TREESTORE(te); + + if (tselem->type == TSE_RNA_STRUCT) { + return optype_sel_linked; + } + + return optype_sel_and_hide; +} + void OUTLINER_OT_data_operation(wmOperatorType *ot) { /* identifiers */ @@ -2802,7 +2845,8 @@ void OUTLINER_OT_data_operation(wmOperatorType *ot) ot->flag = 0; - ot->prop = RNA_def_enum(ot->srna, "type", prop_data_op_types, 0, "Data Operation", ""); + ot->prop = RNA_def_enum(ot->srna, "type", DummyRNA_DEFAULT_items, 0, "Data Operation", ""); + RNA_def_enum_funcs(ot->prop, outliner_data_op_sets_enum_item_fn); } /** \} */ -- cgit v1.2.3 From ae3472998abd8e564e49741f84a9ed9354954e04 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 13 Aug 2021 17:36:35 -0300 Subject: Cleanup: rearrange includes Bring the includes from the same project together. --- source/blender/editors/space_outliner/outliner_tools.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 2474b54384d..d88ae82cc9a 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -78,8 +78,6 @@ #include "ED_sequencer.h" #include "ED_undo.h" -#include "SEQ_relations.h" - #include "WM_api.h" #include "WM_message.h" #include "WM_types.h" @@ -94,6 +92,7 @@ #include "RNA_define.h" #include "RNA_enum_types.h" +#include "SEQ_relations.h" #include "SEQ_sequencer.h" #include "outliner_intern.h" -- cgit v1.2.3 From 6a4533dd02c053fc24738fdd285b398e3d7767c1 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Fri, 13 Aug 2021 14:31:10 -0700 Subject: BLF Cleanup: Size Defines, Comments, etc This patch makes some non-functional changes to BLF code. Some size defines added, comments changed, simplification of macro BLF_KERNING_VARS. See D12200 for more details. Differential Revision: https://developer.blender.org/D12200 Reviewed by Campbell Barton --- source/blender/blenfont/intern/blf_font.c | 19 +++++++++---------- source/blender/blenfont/intern/blf_glyph.c | 7 ++----- .../blender/blenfont/intern/blf_internal_types.h | 22 +++++++++++++--------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 8e306730e3c..0dfb2e843c9 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -318,7 +318,7 @@ static GlyphBLF **blf_font_ensure_ascii_table(FontBLF *font, GlyphCacheBLF *gc) if (glyph_ascii_table['0'] == NULL) { GlyphBLF *g; /* Skip control characters and just cache rendered glyphs for visible ASCII range. */ - for (uint i = 32; i < 128; i++) { + for (uint i = GLYPH_ASCII_CACHE_MIN; i <= GLYPH_ASCII_CACHE_MAX; i++) { g = blf_glyph_search(gc, i); if (!g) { FT_UInt glyph_index = FT_Get_Char_Index(font->face, i); @@ -355,7 +355,7 @@ static void blf_font_ensure_ascii_kerning(FontBLF *font, /* NOTE: `blf_font_ensure_ascii_table(font, gc);` must be called before this macro. */ #define BLF_UTF8_NEXT_FAST(_font, _gc, _g, _str, _i, _c, _glyph_ascii_table) \ - if (((_c) = (_str)[_i]) < 0x80) { \ + if (((_c) = (_str)[_i]) < GLYPH_ASCII_TABLE_SIZE) { \ _g = (_glyph_ascii_table)[_c]; \ _i++; \ } \ @@ -370,11 +370,10 @@ static void blf_font_ensure_ascii_kerning(FontBLF *font, (void)0 #define BLF_KERNING_VARS(_font, _has_kerning, _kern_mode) \ - const bool _has_kerning = FT_HAS_KERNING((_font)->face) != 0; \ - const FT_UInt _kern_mode = (_has_kerning == 0) ? 0 : \ - (((_font)->flags & BLF_KERNING_DEFAULT) ? \ - ft_kerning_default : \ - (FT_UInt)FT_KERNING_UNFITTED) + const bool _has_kerning = FT_HAS_KERNING((_font)->face); \ + const FT_UInt _kern_mode = (_has_kerning && !((_font)->flags & BLF_KERNING_DEFAULT)) ? \ + FT_KERNING_UNFITTED : \ + FT_KERNING_DEFAULT; /* NOTE: `blf_font_ensure_ascii_kerning(font, gc, kern_mode);` must be called before this macro. */ @@ -382,7 +381,7 @@ static void blf_font_ensure_ascii_kerning(FontBLF *font, { \ if (_g_prev) { \ FT_Vector _delta; \ - if (_c_prev < 0x80 && _c < 0x80) { \ + if (_c_prev < KERNING_CACHE_TABLE_SIZE && _c < GLYPH_ASCII_TABLE_SIZE) { \ _pen_x += (_font)->kerning_cache->table[_c][_c_prev]; \ } \ else if (FT_Get_Kerning((_font)->face, (_g_prev)->idx, (_g)->idx, _kern_mode, &(_delta)) == \ @@ -482,7 +481,7 @@ static void blf_font_draw_ascii_ex( blf_batch_draw_begin(font); while ((c = *(str++)) && len--) { - BLI_assert(c < 128); + BLI_assert(c < GLYPH_ASCII_TABLE_SIZE); if ((g = glyph_ascii_table[c]) == NULL) { continue; } @@ -1262,7 +1261,7 @@ int blf_font_count_missing_chars(FontBLF *font, while (i < len) { unsigned int c; - if ((c = str[i]) < 0x80) { + if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) { i++; } else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) { diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 3f01501fda4..35938a7d5c3 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -80,8 +80,8 @@ KerningCacheBLF *blf_kerning_cache_new(FontBLF *font, GlyphCacheBLF *gc) kc->mode = font->kerning_mode; unsigned int i, j; - for (i = 0; i < 0x80; i++) { - for (j = 0; j < 0x80; j++) { + for (i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { + for (j = 0; j < KERNING_CACHE_TABLE_SIZE; j++) { GlyphBLF *g = blf_glyph_search(gc, i); if (!g) { FT_UInt glyph_index = FT_Get_Char_Index(font->face, i); @@ -144,8 +144,6 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table)); memset(gc->bucket, 0, sizeof(gc->bucket)); - gc->glyphs_len_max = (int)font->face->num_glyphs; - gc->glyphs_len_free = (int)font->face->num_glyphs; gc->ascender = ((float)font->face->size->metrics.ascender) / 64.0f; gc->descender = ((float)font->face->size->metrics.descender) / 64.0f; @@ -514,7 +512,6 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl memcpy(&gc->bitmap_result[gc->bitmap_len], g->bitmap, (size_t)buff_size); gc->bitmap_len = bitmap_len; - gc->glyphs_len_free--; g->glyph_cache = gc; } diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 6816c97321d..1cd004ce77f 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -28,6 +28,16 @@ #define BLF_BATCH_DRAW_LEN_MAX 2048 /* in glyph */ +/* Number of characters in GlyphCacheBLF.glyph_ascii_table. */ +#define GLYPH_ASCII_TABLE_SIZE 128 + +/* First and last characters (inclusive) of range to cache within GLYPH_ASCII_TABLE_SIZE. */ +#define GLYPH_ASCII_CACHE_MIN 32 /* Space */ +#define GLYPH_ASCII_CACHE_MAX 126 /* Tilde */ + +/* Number of characters in KerningCacheBLF.table. */ +#define KERNING_CACHE_TABLE_SIZE 128 + typedef struct BatchBLF { struct FontBLF *font; /* can only batch glyph from the same font */ struct GPUBatch *batch; @@ -51,7 +61,7 @@ typedef struct KerningCacheBLF { /* only cache a ascii glyph pairs. Only store the x * offset we are interested in, instead of the full FT_Vector. */ - int table[0x80][0x80]; + int table[KERNING_CACHE_TABLE_SIZE][KERNING_CACHE_TABLE_SIZE]; } KerningCacheBLF; typedef struct GlyphCacheBLF { @@ -71,7 +81,7 @@ typedef struct GlyphCacheBLF { ListBase bucket[257]; /* fast ascii lookup */ - struct GlyphBLF *glyph_ascii_table[128]; + struct GlyphBLF *glyph_ascii_table[GLYPH_ASCII_TABLE_SIZE]; /* texture array, to draw the glyphs. */ GPUTexture *texture; @@ -84,12 +94,6 @@ typedef struct GlyphCacheBLF { int glyph_width_max; int glyph_height_max; - /* number of glyphs in the font. */ - int glyphs_len_max; - - /* number of glyphs not yet loaded (decreases every glyph loaded). */ - int glyphs_len_free; - /* ascender and descender value. */ float ascender; float descender; @@ -99,7 +103,7 @@ typedef struct GlyphBLF { struct GlyphBLF *next; struct GlyphBLF *prev; - /* and the character, as UTF8 */ + /* and the character, as UTF-32 */ unsigned int c; /* freetype2 index, to speed-up the search. */ -- cgit v1.2.3 From 8f2b60ddbfea800ac155d9419e8d237c791fcda9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 13 Aug 2021 21:46:59 -0500 Subject: Cleanup: Variable names, formatting, reduce indentation --- .../nodes/geometry/nodes/node_geo_raycast.cc | 152 ++++++++++----------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 0c1d8645411..88e3bf17d43 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -63,20 +63,20 @@ static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } +namespace blender::nodes { + static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage; - blender::nodes::update_attribute_input_socket_availabilities( + update_attribute_input_socket_availabilities( *node, "Ray Direction", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction); - blender::nodes::update_attribute_input_socket_availabilities( + update_attribute_input_socket_availabilities( *node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length); } -namespace blender::nodes { - -static void raycast_to_mesh(const Mesh *mesh, +static void raycast_to_mesh(const Mesh &mesh, const VArray &ray_origins, const VArray &ray_directions, const VArray &ray_lengths, @@ -95,62 +95,64 @@ 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, mesh, BVHTREE_FROM_LOOPTRI, 4); - - if (tree_data.tree != nullptr) { - for (const int i : ray_origins.index_range()) { - const float ray_length = ray_lengths[i]; - const float3 ray_origin = ray_origins[i]; - const float3 ray_direction = ray_directions[i].normalized(); - - BVHTreeRayHit hit; - hit.index = -1; - hit.dist = ray_length; - if (BLI_bvhtree_ray_cast(tree_data.tree, - ray_origin, - ray_direction, - 0.0f, - &hit, - tree_data.raycast_callback, - &tree_data) != -1) { - if (!r_hit.is_empty()) { - r_hit[i] = hit.index >= 0; - } - if (!r_hit_indices.is_empty()) { - /* Index should always be a valid looptri index, use 0 when hit failed. */ - r_hit_indices[i] = max_ii(hit.index, 0); - } - if (!r_hit_positions.is_empty()) { - r_hit_positions[i] = hit.co; - } - if (!r_hit_normals.is_empty()) { - r_hit_normals[i] = hit.no; - } - if (!r_hit_distances.is_empty()) { - r_hit_distances[i] = hit.dist; - } + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4); + if (tree_data.tree == nullptr) { + free_bvhtree_from_mesh(&tree_data); + return; + } + + for (const int i : ray_origins.index_range()) { + const float ray_length = ray_lengths[i]; + const float3 ray_origin = ray_origins[i]; + const float3 ray_direction = ray_directions[i].normalized(); + + BVHTreeRayHit hit; + hit.index = -1; + hit.dist = ray_length; + if (BLI_bvhtree_ray_cast(tree_data.tree, + ray_origin, + ray_direction, + 0.0f, + &hit, + tree_data.raycast_callback, + &tree_data) != -1) { + if (!r_hit.is_empty()) { + r_hit[i] = hit.index >= 0; + } + if (!r_hit_indices.is_empty()) { + /* Index should always be a valid looptri index, use 0 when hit failed. */ + r_hit_indices[i] = max_ii(hit.index, 0); + } + if (!r_hit_positions.is_empty()) { + r_hit_positions[i] = hit.co; } - else { - if (!r_hit.is_empty()) { - r_hit[i] = false; - } - if (!r_hit_indices.is_empty()) { - r_hit_indices[i] = 0; - } - if (!r_hit_positions.is_empty()) { - r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f); - } - if (!r_hit_normals.is_empty()) { - r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f); - } - if (!r_hit_distances.is_empty()) { - r_hit_distances[i] = ray_length; - } + if (!r_hit_normals.is_empty()) { + r_hit_normals[i] = hit.no; + } + if (!r_hit_distances.is_empty()) { + r_hit_distances[i] = hit.dist; + } + } + else { + if (!r_hit.is_empty()) { + r_hit[i] = false; + } + if (!r_hit_indices.is_empty()) { + r_hit_indices[i] = 0; + } + if (!r_hit_positions.is_empty()) { + r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f); + } + if (!r_hit_normals.is_empty()) { + r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f); + } + if (!r_hit_distances.is_empty()) { + r_hit_distances[i] = ray_length; } } - - free_bvhtree_from_mesh(&tree_data); } + + free_bvhtree_from_mesh(&tree_data); } static bke::mesh_surface_sample::eAttributeMapMode get_map_mode( @@ -166,7 +168,7 @@ static bke::mesh_surface_sample::eAttributeMapMode get_map_mode( } static void raycast_from_points(const GeoNodeExecParams ¶ms, - const GeometrySet &src_geometry, + const GeometrySet &target_geometry, GeometryComponent &dst_component, const StringRef hit_name, const StringRef hit_position_name, @@ -177,7 +179,8 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, { BLI_assert(hit_attribute_names.size() == hit_attribute_output_names.size()); - const MeshComponent *src_mesh_component = src_geometry.get_component_for_read(); + const MeshComponent *src_mesh_component = + target_geometry.get_component_for_read(); if (src_mesh_component == nullptr) { return; } @@ -211,8 +214,7 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, dst_component.attribute_try_get_for_output_only(hit_distance_name, result_domain); /* Positions and looptri indices are always needed for interpolation, - * so create temporary arrays if no output attribute is given. - */ + * so create temporary arrays if no output attribute is given. */ Array hit_indices; Array hit_positions_internal; if (!hit_attribute_names.is_empty()) { @@ -232,7 +234,7 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, hit_distance_attribute.as_span() : MutableSpan(); - raycast_to_mesh(src_mesh, + raycast_to_mesh(*src_mesh, ray_origins, ray_directions, ray_lengths, @@ -268,34 +270,32 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, static void geo_node_raycast_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); - GeometrySet cast_geometry_set = params.extract_input("Target Geometry"); + GeometrySet target_geometry_set = params.extract_input("Target Geometry"); const std::string hit_name = params.extract_input("Is Hit"); const std::string hit_position_name = params.extract_input("Hit Position"); const std::string hit_normal_name = params.extract_input("Hit Normal"); const std::string hit_distance_name = params.extract_input("Hit Distance"); - const Array hit_attribute_names = { - params.extract_input("Target Attribute")}; - const Array hit_attribute_output_names = { - params.extract_input("Hit Attribute")}; + const Array hit_names = {params.extract_input("Target Attribute")}; + const Array hit_output_names = {params.extract_input("Hit Attribute")}; geometry_set = bke::geometry_set_realize_instances(geometry_set); - cast_geometry_set = bke::geometry_set_realize_instances(cast_geometry_set); + target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set); - static const Array SupportedTypes = { + static const Array types = { GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; - for (GeometryComponentType geo_type : SupportedTypes) { - if (geometry_set.has(geo_type)) { + for (const GeometryComponentType type : types) { + if (geometry_set.has(type)) { raycast_from_points(params, - cast_geometry_set, - geometry_set.get_component_for_write(geo_type), + target_geometry_set, + geometry_set.get_component_for_write(type), hit_name, hit_position_name, hit_normal_name, hit_distance_name, - hit_attribute_names, - hit_attribute_output_names); + hit_names, + hit_output_names); } } @@ -312,7 +312,7 @@ void register_node_type_geo_raycast() node_type_socket_templates(&ntype, geo_node_raycast_in, geo_node_raycast_out); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_init(&ntype, geo_node_raycast_init); - node_type_update(&ntype, geo_node_raycast_update); + node_type_update(&ntype, blender::nodes::geo_node_raycast_update); node_type_storage( &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec; -- cgit v1.2.3 From d5261e973b5624b40b80771903e9d2a0c9ff37e4 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Sat, 14 Aug 2021 13:50:51 -0700 Subject: BLF: Do Not Preload Glyph Cache This patch turns off the preloading of ascii glyphs and instead caches each glyph the first time it is actually used. See D12215 for much more detail. Differential Revision: https://developer.blender.org/D12215 Reviewed by Campbell Barton --- source/blender/blenfont/intern/blf_font.c | 77 ++++++---------------- .../blender/blenfont/intern/blf_internal_types.h | 4 -- 2 files changed, 19 insertions(+), 62 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 0dfb2e843c9..28039ff11ac 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -308,29 +308,6 @@ void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) blf_glyph_cache_release(font); } -static GlyphBLF **blf_font_ensure_ascii_table(FontBLF *font, GlyphCacheBLF *gc) -{ - GlyphBLF **glyph_ascii_table; - - glyph_ascii_table = gc->glyph_ascii_table; - - /* build ascii on demand */ - if (glyph_ascii_table['0'] == NULL) { - GlyphBLF *g; - /* Skip control characters and just cache rendered glyphs for visible ASCII range. */ - for (uint i = GLYPH_ASCII_CACHE_MIN; i <= GLYPH_ASCII_CACHE_MAX; i++) { - g = blf_glyph_search(gc, i); - if (!g) { - FT_UInt glyph_index = FT_Get_Char_Index(font->face, i); - g = blf_glyph_add(font, gc, glyph_index, i); - } - glyph_ascii_table[i] = g; - } - } - - return glyph_ascii_table; -} - static void blf_font_ensure_ascii_kerning(FontBLF *font, GlyphCacheBLF *gc, const FT_UInt kern_mode) @@ -352,11 +329,12 @@ static void blf_font_ensure_ascii_kerning(FontBLF *font, * characters. */ -/* NOTE: `blf_font_ensure_ascii_table(font, gc);` must be called before this macro. */ - -#define BLF_UTF8_NEXT_FAST(_font, _gc, _g, _str, _i, _c, _glyph_ascii_table) \ +#define BLF_UTF8_NEXT_FAST(_font, _gc, _g, _str, _i, _c) \ if (((_c) = (_str)[_i]) < GLYPH_ASCII_TABLE_SIZE) { \ - _g = (_glyph_ascii_table)[_c]; \ + if ((_g = (_gc->glyph_ascii_table)[_c]) == NULL) { \ + _g = blf_glyph_add(_font, _gc, FT_Get_Char_Index((_font)->face, _c), _c); \ + _gc->glyph_ascii_table[_c] = _g; \ + } \ _i++; \ } \ else if ((_c = BLI_str_utf8_as_unicode_step(_str, &(_i))) != BLI_UTF8_ERR) { \ @@ -420,8 +398,6 @@ static void blf_font_draw_ex(FontBLF *font, return; } - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - BLF_KERNING_VARS(font, has_kerning, kern_mode); blf_font_ensure_ascii_kerning(font, gc, kern_mode); @@ -429,7 +405,7 @@ static void blf_font_draw_ex(FontBLF *font, blf_batch_draw_begin(font); while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -472,7 +448,6 @@ static void blf_font_draw_ascii_ex( int pen_x = 0; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); BLF_KERNING_VARS(font, has_kerning, kern_mode); @@ -482,8 +457,11 @@ static void blf_font_draw_ascii_ex( while ((c = *(str++)) && len--) { BLI_assert(c < GLYPH_ASCII_TABLE_SIZE); - if ((g = glyph_ascii_table[c]) == NULL) { - continue; + if ((g = gc->glyph_ascii_table[c]) == NULL) { + g = blf_glyph_add(font, gc, FT_Get_Char_Index((font)->face, c), c); + if ((gc->glyph_ascii_table[c] = g) == NULL) { + continue; + } } if (has_kerning) { BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); @@ -522,12 +500,11 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) size_t i = 0; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); blf_batch_draw_begin(font); while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -568,8 +545,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font, int pen_y_basis = (int)font->pos[1] + pen_y; size_t i = 0; - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - /* buffer specific vars */ FontBufInfoBLF *buf_info = &font->buf_info; const float *b_col_float = buf_info->col_float; @@ -584,7 +559,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, /* another buffer specific call for color conversion */ while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -741,7 +716,6 @@ size_t blf_font_width_to_strlen( size_t i, i_prev; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); const int width_i = (int)width; BLF_KERNING_VARS(font, has_kerning, kern_mode); @@ -752,7 +726,7 @@ size_t blf_font_width_to_strlen( for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < len) && str[i]; i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c); if (blf_font_width_to_strlen_glyph_process( font, has_kerning, kern_mode, c_prev, c, g_prev, g, &pen_x, width_i)) { @@ -778,7 +752,6 @@ size_t blf_font_width_to_rstrlen( char *s, *s_prev; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); const int width_i = (int)width; BLF_KERNING_VARS(font, has_kerning, kern_mode); @@ -794,7 +767,7 @@ size_t blf_font_width_to_rstrlen( i_prev = (size_t)((s_prev != NULL) ? s_prev - str : 0); i_tmp = i; - BLF_UTF8_NEXT_FAST(font, gc, g, str, i_tmp, c, glyph_ascii_table); + BLF_UTF8_NEXT_FAST(font, gc, g, str, i_tmp, c); for (width_new = pen_x = 0; (s != NULL); i = i_prev, s = s_prev, c = c_prev, g = g_prev, g_prev = NULL, width_new = pen_x) { s_prev = BLI_str_find_prev_char_utf8(str, s); @@ -802,7 +775,7 @@ size_t blf_font_width_to_rstrlen( if (s_prev != NULL) { i_tmp = i_prev; - BLF_UTF8_NEXT_FAST(font, gc, g_prev, str, i_tmp, c_prev, glyph_ascii_table); + BLF_UTF8_NEXT_FAST(font, gc, g_prev, str, i_tmp, c_prev); BLI_assert(i_tmp == i); } @@ -832,9 +805,6 @@ static void blf_font_boundbox_ex(FontBLF *font, GlyphBLF *g, *g_prev = NULL; int pen_x = 0; size_t i = 0; - - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - rctf gbox; BLF_KERNING_VARS(font, has_kerning, kern_mode); @@ -847,7 +817,7 @@ static void blf_font_boundbox_ex(FontBLF *font, blf_font_ensure_ascii_kerning(font, gc, kern_mode); while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -937,7 +907,6 @@ static void blf_font_wrap_apply(FontBLF *font, int pen_x_next = 0; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); BLF_KERNING_VARS(font, has_kerning, kern_mode); @@ -953,7 +922,7 @@ static void blf_font_wrap_apply(FontBLF *font, size_t i_curr = i; bool do_draw = false; - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -1154,8 +1123,6 @@ float blf_font_fixed_width(FontBLF *font) const unsigned int c = ' '; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); - GlyphBLF *g = blf_glyph_search(gc, c); if (!g) { g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c); @@ -1195,15 +1162,13 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, return; } - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - BLF_KERNING_VARS(font, has_kerning, kern_mode); blf_font_ensure_ascii_kerning(font, gc, kern_mode); while ((i < len) && str[i]) { i_curr = i; - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -1429,7 +1394,6 @@ int blf_font_height_max(FontBLF *font) int height_max; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); height_max = gc->glyph_height_max; blf_glyph_cache_release(font); @@ -1441,7 +1405,6 @@ int blf_font_width_max(FontBLF *font) int width_max; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); width_max = gc->glyph_width_max; blf_glyph_cache_release(font); @@ -1453,7 +1416,6 @@ float blf_font_descender(FontBLF *font) float descender; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); descender = gc->descender; blf_glyph_cache_release(font); @@ -1465,7 +1427,6 @@ float blf_font_ascender(FontBLF *font) float ascender; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); ascender = gc->ascender; blf_glyph_cache_release(font); diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 1cd004ce77f..6bcacc62a30 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -31,10 +31,6 @@ /* Number of characters in GlyphCacheBLF.glyph_ascii_table. */ #define GLYPH_ASCII_TABLE_SIZE 128 -/* First and last characters (inclusive) of range to cache within GLYPH_ASCII_TABLE_SIZE. */ -#define GLYPH_ASCII_CACHE_MIN 32 /* Space */ -#define GLYPH_ASCII_CACHE_MAX 126 /* Tilde */ - /* Number of characters in KerningCacheBLF.table. */ #define KERNING_CACHE_TABLE_SIZE 128 -- cgit v1.2.3