From de91cdd930614d5396ca4691b37cc0502481ead7 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 2 Aug 2021 10:34:50 +0200 Subject: Cleanup: separate base and geometry nodes specific socket cpp type This simplifies changing how geometry nodes handles different socket types without affecting other systems. --- source/blender/blenkernel/BKE_node.h | 13 ++++- source/blender/modifiers/intern/MOD_nodes.cc | 8 +-- .../modifiers/intern/MOD_nodes_evaluator.cc | 15 ++++- source/blender/nodes/NOD_type_callbacks.hh | 2 - source/blender/nodes/intern/node_geometry_exec.cc | 4 +- source/blender/nodes/intern/node_socket.cc | 68 +++++++++++++++------- source/blender/nodes/intern/type_callbacks.cc | 27 ++------- 7 files changed, 80 insertions(+), 57 deletions(-) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index cecb3118038..a1bc6fa505b 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -125,6 +125,9 @@ using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuil using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params); using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)(); using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); +using SocketGetGeometryNodesCPPTypeFunction = const blender::fn::CPPType *(*)(); +using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket &socket, + void *r_value); using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetworkBuilder &builder); #else @@ -132,6 +135,8 @@ typedef void *NodeExpandInMFNetworkFunction; typedef void *SocketExpandInMFNetworkFunction; typedef void *NodeGeometryExecFunction; typedef void *SocketGetCPPTypeFunction; +typedef void *SocketGetGeometryNodesCPPTypeFunction; +typedef void *SocketGetGeometryNodesCPPValueFunction; typedef void *SocketGetCPPValueFunction; #endif @@ -194,9 +199,13 @@ typedef struct bNodeSocketType { /* Expands the socket into a multi-function node that outputs the socket value. */ SocketExpandInMFNetworkFunction expand_in_mf_network; /* Return the CPPType of this socket. */ - SocketGetCPPTypeFunction get_cpp_type; + SocketGetCPPTypeFunction get_base_cpp_type; /* Get the value of this socket in a generic way. */ - SocketGetCPPValueFunction get_cpp_value; + SocketGetCPPValueFunction get_base_cpp_value; + /* Get geometry nodes cpp type. */ + SocketGetGeometryNodesCPPTypeFunction get_geometry_nodes_cpp_type; + /* Get geometry nodes cpp value. */ + SocketGetGeometryNodesCPPValueFunction get_geometry_nodes_cpp_value; } bNodeSocketType; typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context, diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 5fa11ffdd10..3853b345c14 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -721,17 +721,17 @@ static void initialize_group_input(NodesModifierData &nmd, return; } if (nmd.settings.properties == nullptr) { - blender::nodes::socket_cpp_value_get(socket, r_value); + socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); return; } const IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties, socket.identifier); if (property == nullptr) { - blender::nodes::socket_cpp_value_get(socket, r_value); + socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); return; } if (!property_type->is_correct_type(*property)) { - blender::nodes::socket_cpp_value_get(socket, r_value); + socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); return; } property_type->init_cpp_value(*property, r_value); @@ -883,7 +883,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, /* Initialize remaining group inputs. */ for (const OutputSocketRef *socket : remaining_input_sockets) { - const CPPType &cpp_type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo()); + const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type(); void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment()); initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in); group_inputs.add_new({root_context, socket}, {cpp_type, value_in}); diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index e652eb8353d..2157092d639 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -294,7 +294,11 @@ class LockedNode : NonCopyable, NonMovable { static const CPPType *get_socket_cpp_type(const SocketRef &socket) { - const CPPType *type = nodes::socket_cpp_type_get(*socket.typeinfo()); + const bNodeSocketType *typeinfo = socket.typeinfo(); + if (typeinfo->get_geometry_nodes_cpp_type == nullptr) { + return nullptr; + } + const CPPType *type = typeinfo->get_geometry_nodes_cpp_type(); if (type == nullptr) { return nullptr; } @@ -310,6 +314,12 @@ static const CPPType *get_socket_cpp_type(const DSocket socket) return get_socket_cpp_type(*socket.socket_ref()); } +static void get_socket_value(const SocketRef &socket, void *r_value) +{ + const bNodeSocketType *typeinfo = socket.typeinfo(); + typeinfo->get_geometry_nodes_cpp_value(*socket.bsocket(), r_value); +} + static bool node_supports_laziness(const DNode node) { return node->typeinfo()->geometry_node_execute_supports_laziness; @@ -1363,10 +1373,9 @@ class GeometryNodesEvaluator { { LinearAllocator<> &allocator = local_allocators_.local(); - bNodeSocket *bsocket = socket->bsocket(); const CPPType &type = *get_socket_cpp_type(socket); void *buffer = allocator.allocate(type.size(), type.alignment()); - blender::nodes::socket_cpp_value_get(*bsocket, buffer); + get_socket_value(*socket.socket_ref(), buffer); if (type == required_type) { return {type, buffer}; diff --git a/source/blender/nodes/NOD_type_callbacks.hh b/source/blender/nodes/NOD_type_callbacks.hh index d1a4bd3ad7a..2be78f929db 100644 --- a/source/blender/nodes/NOD_type_callbacks.hh +++ b/source/blender/nodes/NOD_type_callbacks.hh @@ -27,10 +27,8 @@ namespace blender::nodes { using fn::CPPType; using fn::MFDataType; -const CPPType *socket_cpp_type_get(const bNodeSocketType &stype); std::optional socket_mf_type_get(const bNodeSocketType &stype); bool socket_is_mf_data_socket(const bNodeSocketType &stype); -bool socket_cpp_value_get(const bNodeSocket &socket, void *r_value); void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder); } // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index bfd1ad02d36..ffa20579acc 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -218,7 +218,7 @@ void GeoNodeExecParams::check_input_access(StringRef identifier, BLI_assert_unreachable(); } else if (requested_type != nullptr) { - const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo); + const CPPType &expected_type = *found_socket->typeinfo->get_geometry_nodes_cpp_type(); if (*requested_type != expected_type) { std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '" << expected_type.name() << "'.\n"; @@ -258,7 +258,7 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType BLI_assert_unreachable(); } else { - const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo); + const CPPType &expected_type = *found_socket->typeinfo->get_geometry_nodes_cpp_type(); if (value_type != expected_type) { std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '" << expected_type.name() << "'.\n"; diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 8fdad0bb242..4be3fd2468b 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -607,60 +607,74 @@ static bNodeSocketType *make_socket_type_virtual() static bNodeSocketType *make_socket_type_bool() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_float(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_int(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_rgba() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { + return &blender::fn::CPPType::get(); + }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_string() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value); }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } @@ -672,50 +686,60 @@ MAKE_CPP_TYPE(Material, Material *, CPPTypeFlags::BasicType) static bNodeSocketType *make_socket_type_object() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Object **)r_value = ((bNodeSocketValueObject *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_geometry() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_GEOMETRY, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; - socktype->get_cpp_value = [](const bNodeSocket &UNUSED(socket), void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &UNUSED(socket), void *r_value) { new (r_value) GeometrySet(); }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_collection() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Collection **)r_value = ((bNodeSocketValueCollection *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_texture() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Tex **)r_value = ((bNodeSocketValueTexture *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_material() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } diff --git a/source/blender/nodes/intern/type_callbacks.cc b/source/blender/nodes/intern/type_callbacks.cc index 5160432aea5..881a02c92e9 100644 --- a/source/blender/nodes/intern/type_callbacks.cc +++ b/source/blender/nodes/intern/type_callbacks.cc @@ -19,17 +19,9 @@ namespace blender::nodes { -const CPPType *socket_cpp_type_get(const bNodeSocketType &stype) -{ - if (stype.get_cpp_type != nullptr) { - return stype.get_cpp_type(); - } - return nullptr; -} - std::optional socket_mf_type_get(const bNodeSocketType &stype) { - const CPPType *cpp_type = socket_cpp_type_get(stype); + const CPPType *cpp_type = stype.get_base_cpp_type ? stype.get_base_cpp_type() : nullptr; if (cpp_type != nullptr) { return MFDataType::ForSingle(*cpp_type); } @@ -41,32 +33,23 @@ bool socket_is_mf_data_socket(const bNodeSocketType &stype) if (!socket_mf_type_get(stype).has_value()) { return false; } - if (stype.expand_in_mf_network == nullptr && stype.get_cpp_value == nullptr) { + if (stype.expand_in_mf_network == nullptr && stype.get_base_cpp_value == nullptr) { return false; } return true; } -bool socket_cpp_value_get(const bNodeSocket &socket, void *r_value) -{ - if (socket.typeinfo->get_cpp_value != nullptr) { - socket.typeinfo->get_cpp_value(socket, r_value); - return true; - } - return false; -} - void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder) { bNodeSocket &socket = builder.bsocket(); if (socket.typeinfo->expand_in_mf_network != nullptr) { socket.typeinfo->expand_in_mf_network(builder); } - else if (socket.typeinfo->get_cpp_value != nullptr) { - const CPPType &type = *socket_cpp_type_get(*socket.typeinfo); + else if (socket.typeinfo->get_base_cpp_value != nullptr) { + const CPPType &type = *socket.typeinfo->get_base_cpp_type(); void *buffer = builder.resource_scope().linear_allocator().allocate(type.size(), type.alignment()); - socket.typeinfo->get_cpp_value(socket, buffer); + socket.typeinfo->get_base_cpp_value(socket, buffer); builder.set_constant_value(type, buffer); } else { -- cgit v1.2.3 From 3fd5c93f9ce805b1a59bb6a03a9d39767697e336 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 2 Aug 2021 10:38:11 +0200 Subject: Geometry Nodes: increase threshold to make regression tests pass The real fix here is to use some kind of relative error in `customdata_compare` instead of the absolute error used now. If vertex coordinates get larger in magnitude, the allowed error should increase as well. --- tests/python/geo_node_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/geo_node_test.py b/tests/python/geo_node_test.py index fa87fb23bee..5da28977ba4 100644 --- a/tests/python/geo_node_test.py +++ b/tests/python/geo_node_test.py @@ -25,7 +25,7 @@ import sys sys.path.append(os.path.dirname(os.path.realpath(__file__))) from modules.mesh_test import BlendFileTest -geo_node_test = BlendFileTest("test_object", "expected_object") +geo_node_test = BlendFileTest("test_object", "expected_object", threshold=1e-4) result = geo_node_test.run_test() # Telling `ctest` about the failed test by raising Exception. -- cgit v1.2.3 From 1f0d6f763573b22772dcdb61320a12e1c11949e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 2 Aug 2021 11:02:47 +0200 Subject: Asset Catalogs: loading a catalog definition file Initial, limited implementation of loading a single asset catalog definition file. These files are structured as follows: CATALOG_ID virtual/path/of/catalog SUBCATALOG_ID virtual/path/of/catalog/child SOMETHING_ELSE some/unrelated/hierarchy These virtual paths will be used to show the catalog in a tree structure; the tree structure itself is not part of this commit. Each asset will have one catalog ID that determines where in that tree the asset shows up. Currently only a single catalog definition file can be read; merging data from multiple such files, and writing them out again after changes are made, is for future commits. This commit only contains the code to load a single file, and unittests to check that this actually works. No UI, no user-facing functionality yet. --- source/blender/blenkernel/BKE_asset_catalog.hh | 97 ++++++++++++++ source/blender/blenkernel/CMakeLists.txt | 3 + source/blender/blenkernel/intern/asset_catalog.cc | 148 +++++++++++++++++++++ .../blenkernel/intern/asset_catalog_test.cc | 62 +++++++++ 4 files changed, 310 insertions(+) create mode 100644 source/blender/blenkernel/BKE_asset_catalog.hh create mode 100644 source/blender/blenkernel/intern/asset_catalog.cc create mode 100644 source/blender/blenkernel/intern/asset_catalog_test.cc diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh new file mode 100644 index 00000000000..82449bcdbcc --- /dev/null +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -0,0 +1,97 @@ +/* + * 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. + */ + +/** \file + * \ingroup bke + */ + +#pragma once + +#ifndef __cplusplus +# error This is a C++ header. The C interface is yet to be implemented/designed. +#endif + +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector_set.hh" + +#include +#include +#include + +namespace blender::bke { + +using CatalogID = std::string; +using CatalogPath = std::string; +using CatalogPathReference = StringRef; +using CatalogPathComponent = std::string; +using CatalogFilePath = std::filesystem::path; + +class AssetCatalog; +class AssetCatalogDefinitionFile; +class AssetCatalogTreeNode; + +/* Manages the asset catalogs of a single asset library (i.e. of catalogs defined in a single + * directory hierarchy). */ +class AssetCatalogService { + const char path_separator = '/'; + + /* TODO(@sybren): determine which properties should be private / get accessors. */ + + // These pointers are owned by this AssetCatalogService. + Map> catalogs; + std::unique_ptr catalog_definition_file; + + public: + AssetCatalogService(); + + // Return nullptr if not found. + AssetCatalog *find_catalog(const CatalogID &catalog_id); + + void load_from_disk(const CatalogFilePath &asset_library_root); + + protected: + void load_directory_recursive(const CatalogFilePath &directory_path); + void load_single_file(const CatalogFilePath &catalog_definition_file_path); + + std::unique_ptr parse_catalog_file( + const CatalogFilePath &catalog_definition_file_path); + + std::unique_ptr parse_catalog_line( + StringRef line, const AssetCatalogDefinitionFile *catalog_definition_file); +}; + +class AssetCatalogDefinitionFile { + /* TODO(@sybren): determine which properties should be private / get accessors. */ + public: + CatalogFilePath file_path; + Map catalogs; + + AssetCatalogDefinitionFile(); +}; + +class AssetCatalog { + /* TODO(@sybren): determine which properties should be private / get accessors. */ + public: + AssetCatalog(); + AssetCatalog(const CatalogID &catalog_id, const CatalogPath &name); + + CatalogID catalog_id; + CatalogPath path; +}; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 78bfe8c9afb..7c02ffbc105 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -82,6 +82,7 @@ set(SRC intern/armature_deform.c intern/armature_pose.cc intern/armature_update.c + intern/asset_catalog.cc intern/asset.cc intern/attribute.c intern/attribute_access.cc @@ -298,6 +299,7 @@ set(SRC BKE_appdir.h BKE_armature.h BKE_armature.hh + BKE_asset_catalog.hh BKE_asset.h BKE_attribute.h BKE_attribute_access.hh @@ -771,6 +773,7 @@ if(WITH_GTESTS) set(TEST_SRC intern/action_test.cc intern/armature_test.cc + intern/asset_catalog_test.cc intern/cryptomatte_test.cc intern/fcurve_test.cc intern/lattice_deform_test.cc diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc new file mode 100644 index 00000000000..0139b7a4abf --- /dev/null +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -0,0 +1,148 @@ +/* + * 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. + */ + +/** \file + * \ingroup bke + */ + +#include "BKE_asset_catalog.hh" + +#include "BLI_string_ref.hh" + +#include +#include + +namespace fs = std::filesystem; + +namespace blender::bke { + +AssetCatalogService::AssetCatalogService() +{ +} + +AssetCatalog *AssetCatalogService::find_catalog(const CatalogID &catalog_id) +{ + std::unique_ptr *catalog_uptr_ptr = this->catalogs.lookup_ptr(catalog_id); + if (catalog_uptr_ptr == nullptr) { + return nullptr; + } + return catalog_uptr_ptr->get(); +} + +void AssetCatalogService::load_from_disk(const CatalogFilePath &asset_library_root) +{ + fs::file_status status = fs::status(asset_library_root); + switch (status.type()) { + case fs::file_type::regular: + load_single_file(asset_library_root); + break; + case fs::file_type::directory: + load_directory_recursive(asset_library_root); + break; + default: + // TODO(@sybren): throw an appropriate exception. + return; + } +} + +void AssetCatalogService::load_directory_recursive(const CatalogFilePath & /*directory_path*/) +{ + // TODO(@sybren): implement +} + +void AssetCatalogService::load_single_file(const CatalogFilePath &catalog_definition_file_path) +{ + std::unique_ptr cdf = parse_catalog_file( + catalog_definition_file_path); + + BLI_assert_msg(!this->catalog_definition_file, + "Only loading of a single catalog definition file is supported."); + this->catalog_definition_file = std::move(cdf); +} + +std::unique_ptr AssetCatalogService::parse_catalog_file( + const CatalogFilePath &catalog_definition_file_path) +{ + auto cdf = std::make_unique(); + cdf->file_path = catalog_definition_file_path; + + std::fstream infile(catalog_definition_file_path); + std::string line; + while (std::getline(infile, line)) { + const StringRef trimmed_line = StringRef(line).trim().trim(path_separator); + if (trimmed_line.is_empty() || trimmed_line[0] == '#') { + continue; + } + + std::unique_ptr catalog = this->parse_catalog_line(trimmed_line, cdf.get()); + + const bool is_catalog_id_reused_in_file = cdf->catalogs.contains(catalog->catalog_id); + /* The AssetDefinitionFile should include this catalog when writing it back to disk, even if it + * was a duplicate. */ + cdf->catalogs.add_new(catalog->catalog_id, catalog.get()); + + if (is_catalog_id_reused_in_file) { + std::cerr << catalog_definition_file_path << ": multiple definitions of catalog " + << catalog->catalog_id << ", using first occurrence." << std::endl; + /* Don't store 'catalog'; unique_ptr will free its memory. */ + continue; + } + + if (this->catalogs.contains(catalog->catalog_id)) { + // TODO(@sybren): apparently another CDF was already loaded. This is not supported yet. + std::cerr << catalog_definition_file_path << ": multiple definitions of catalog " + << catalog->catalog_id << " in multiple files, ignoring this one." << std::endl; + /* Don't store 'catalog'; unique_ptr will free its memory. */ + continue; + } + + /* The AssetCatalog pointer is owned by the AssetCatalogService. */ + this->catalogs.add_new(catalog->catalog_id, std::move(catalog)); + } + + return cdf; +} + +std::unique_ptr AssetCatalogService::parse_catalog_line( + const StringRef line, const AssetCatalogDefinitionFile *catalog_definition_file) +{ + const int64_t first_space = line.find_first_of(' '); + if (first_space == StringRef::not_found) { + std::cerr << "Invalid line in " << catalog_definition_file->file_path << ": " << line + << std::endl; + return std::unique_ptr(nullptr); + } + + const StringRef catalog_id = line.substr(0, first_space); + const StringRef catalog_path = line.substr(first_space + 1).trim().trim(path_separator); + + return std::make_unique(catalog_id, catalog_path); +} + +AssetCatalogDefinitionFile::AssetCatalogDefinitionFile() +{ +} + +AssetCatalog::AssetCatalog() +{ +} + +AssetCatalog::AssetCatalog(const CatalogID &catalog_id, const CatalogPath &path) + : catalog_id(catalog_id), path(path) +{ +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc new file mode 100644 index 00000000000..d6c00670bd6 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -0,0 +1,62 @@ +/* + * 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) 2020 Blender Foundation + * All rights reserved. + */ + +#include "BKE_asset_catalog.hh" + +#include "testing/testing.h" + +#include + +namespace fs = std::filesystem; + +namespace blender::bke::tests { + +TEST(AssetCatalogTest, load_single_file) +{ + const fs::path test_files_dir = blender::tests::flags_test_asset_dir(); + if (test_files_dir.empty()) { + FAIL(); + } + + AssetCatalogService service; + service.load_from_disk(test_files_dir / "asset_library/single_catalog_definition_file.cats.txt"); + + // Test getting a non-existant catalog ID. + EXPECT_EQ(nullptr, service.find_catalog("NONEXISTANT")); + + // Test getting a 7-bit ASCII catalog ID. + AssetCatalog *poses_elly = service.find_catalog("POSES_ELLY"); + ASSERT_NE(nullptr, poses_elly); + EXPECT_EQ("POSES_ELLY", poses_elly->catalog_id); + EXPECT_EQ("character/Elly/poselib", poses_elly->path); + + // Test whitespace stripping. + AssetCatalog *poses_whitespace = service.find_catalog("POSES_ELLY_WHITESPACE"); + ASSERT_NE(nullptr, poses_whitespace); + EXPECT_EQ("POSES_ELLY_WHITESPACE", poses_whitespace->catalog_id); + EXPECT_EQ("character/Elly/poselib/whitespace", poses_whitespace->path); + + // Test getting a UTF-8 catalog ID. + AssetCatalog *poses_ruzena = service.find_catalog("POSES_RUŽENA"); + ASSERT_NE(nullptr, poses_ruzena); + EXPECT_EQ("POSES_RUŽENA", poses_ruzena->catalog_id); + EXPECT_EQ("character/Ružena/poselib", poses_ruzena->path); +} + +} // namespace blender::bke::tests -- cgit v1.2.3 From 06cb48e1b284e6438ce14f1ea543143fcc74ca59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 2 Aug 2021 11:15:50 +0200 Subject: Cleanup: Asset catalogs, C++ to C-style comments No functional changes. --- source/blender/blenkernel/BKE_asset_catalog.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 82449bcdbcc..981c5338469 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -52,14 +52,14 @@ class AssetCatalogService { /* TODO(@sybren): determine which properties should be private / get accessors. */ - // These pointers are owned by this AssetCatalogService. + /* These pointers are owned by this AssetCatalogService. */ Map> catalogs; std::unique_ptr catalog_definition_file; public: AssetCatalogService(); - // Return nullptr if not found. + /* Return nullptr if not found. */ AssetCatalog *find_catalog(const CatalogID &catalog_id); void load_from_disk(const CatalogFilePath &asset_library_root); -- cgit v1.2.3 From aa60416361364f56f9a173db9649e36f81442ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 2 Aug 2021 12:07:40 +0200 Subject: Revert "Asset Catalogs: loading a catalog definition file" This reverts commit 1f0d6f763573b22772dcdb61320a12e1c11949e0 and the cleanup 06cb48e1b284e6438ce14f1ea543143fcc74ca59. Committed too early on Monday morning, still has issues that should be resolved first. --- source/blender/blenkernel/BKE_asset_catalog.hh | 97 -------------- source/blender/blenkernel/CMakeLists.txt | 3 - source/blender/blenkernel/intern/asset_catalog.cc | 148 --------------------- .../blenkernel/intern/asset_catalog_test.cc | 62 --------- 4 files changed, 310 deletions(-) delete mode 100644 source/blender/blenkernel/BKE_asset_catalog.hh delete mode 100644 source/blender/blenkernel/intern/asset_catalog.cc delete mode 100644 source/blender/blenkernel/intern/asset_catalog_test.cc diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh deleted file mode 100644 index 981c5338469..00000000000 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ /dev/null @@ -1,97 +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. - */ - -/** \file - * \ingroup bke - */ - -#pragma once - -#ifndef __cplusplus -# error This is a C++ header. The C interface is yet to be implemented/designed. -#endif - -#include "BLI_map.hh" -#include "BLI_set.hh" -#include "BLI_string_ref.hh" -#include "BLI_vector_set.hh" - -#include -#include -#include - -namespace blender::bke { - -using CatalogID = std::string; -using CatalogPath = std::string; -using CatalogPathReference = StringRef; -using CatalogPathComponent = std::string; -using CatalogFilePath = std::filesystem::path; - -class AssetCatalog; -class AssetCatalogDefinitionFile; -class AssetCatalogTreeNode; - -/* Manages the asset catalogs of a single asset library (i.e. of catalogs defined in a single - * directory hierarchy). */ -class AssetCatalogService { - const char path_separator = '/'; - - /* TODO(@sybren): determine which properties should be private / get accessors. */ - - /* These pointers are owned by this AssetCatalogService. */ - Map> catalogs; - std::unique_ptr catalog_definition_file; - - public: - AssetCatalogService(); - - /* Return nullptr if not found. */ - AssetCatalog *find_catalog(const CatalogID &catalog_id); - - void load_from_disk(const CatalogFilePath &asset_library_root); - - protected: - void load_directory_recursive(const CatalogFilePath &directory_path); - void load_single_file(const CatalogFilePath &catalog_definition_file_path); - - std::unique_ptr parse_catalog_file( - const CatalogFilePath &catalog_definition_file_path); - - std::unique_ptr parse_catalog_line( - StringRef line, const AssetCatalogDefinitionFile *catalog_definition_file); -}; - -class AssetCatalogDefinitionFile { - /* TODO(@sybren): determine which properties should be private / get accessors. */ - public: - CatalogFilePath file_path; - Map catalogs; - - AssetCatalogDefinitionFile(); -}; - -class AssetCatalog { - /* TODO(@sybren): determine which properties should be private / get accessors. */ - public: - AssetCatalog(); - AssetCatalog(const CatalogID &catalog_id, const CatalogPath &name); - - CatalogID catalog_id; - CatalogPath path; -}; - -} // namespace blender::bke diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 7c02ffbc105..78bfe8c9afb 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -82,7 +82,6 @@ set(SRC intern/armature_deform.c intern/armature_pose.cc intern/armature_update.c - intern/asset_catalog.cc intern/asset.cc intern/attribute.c intern/attribute_access.cc @@ -299,7 +298,6 @@ set(SRC BKE_appdir.h BKE_armature.h BKE_armature.hh - BKE_asset_catalog.hh BKE_asset.h BKE_attribute.h BKE_attribute_access.hh @@ -773,7 +771,6 @@ if(WITH_GTESTS) set(TEST_SRC intern/action_test.cc intern/armature_test.cc - intern/asset_catalog_test.cc intern/cryptomatte_test.cc intern/fcurve_test.cc intern/lattice_deform_test.cc diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc deleted file mode 100644 index 0139b7a4abf..00000000000 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ /dev/null @@ -1,148 +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. - */ - -/** \file - * \ingroup bke - */ - -#include "BKE_asset_catalog.hh" - -#include "BLI_string_ref.hh" - -#include -#include - -namespace fs = std::filesystem; - -namespace blender::bke { - -AssetCatalogService::AssetCatalogService() -{ -} - -AssetCatalog *AssetCatalogService::find_catalog(const CatalogID &catalog_id) -{ - std::unique_ptr *catalog_uptr_ptr = this->catalogs.lookup_ptr(catalog_id); - if (catalog_uptr_ptr == nullptr) { - return nullptr; - } - return catalog_uptr_ptr->get(); -} - -void AssetCatalogService::load_from_disk(const CatalogFilePath &asset_library_root) -{ - fs::file_status status = fs::status(asset_library_root); - switch (status.type()) { - case fs::file_type::regular: - load_single_file(asset_library_root); - break; - case fs::file_type::directory: - load_directory_recursive(asset_library_root); - break; - default: - // TODO(@sybren): throw an appropriate exception. - return; - } -} - -void AssetCatalogService::load_directory_recursive(const CatalogFilePath & /*directory_path*/) -{ - // TODO(@sybren): implement -} - -void AssetCatalogService::load_single_file(const CatalogFilePath &catalog_definition_file_path) -{ - std::unique_ptr cdf = parse_catalog_file( - catalog_definition_file_path); - - BLI_assert_msg(!this->catalog_definition_file, - "Only loading of a single catalog definition file is supported."); - this->catalog_definition_file = std::move(cdf); -} - -std::unique_ptr AssetCatalogService::parse_catalog_file( - const CatalogFilePath &catalog_definition_file_path) -{ - auto cdf = std::make_unique(); - cdf->file_path = catalog_definition_file_path; - - std::fstream infile(catalog_definition_file_path); - std::string line; - while (std::getline(infile, line)) { - const StringRef trimmed_line = StringRef(line).trim().trim(path_separator); - if (trimmed_line.is_empty() || trimmed_line[0] == '#') { - continue; - } - - std::unique_ptr catalog = this->parse_catalog_line(trimmed_line, cdf.get()); - - const bool is_catalog_id_reused_in_file = cdf->catalogs.contains(catalog->catalog_id); - /* The AssetDefinitionFile should include this catalog when writing it back to disk, even if it - * was a duplicate. */ - cdf->catalogs.add_new(catalog->catalog_id, catalog.get()); - - if (is_catalog_id_reused_in_file) { - std::cerr << catalog_definition_file_path << ": multiple definitions of catalog " - << catalog->catalog_id << ", using first occurrence." << std::endl; - /* Don't store 'catalog'; unique_ptr will free its memory. */ - continue; - } - - if (this->catalogs.contains(catalog->catalog_id)) { - // TODO(@sybren): apparently another CDF was already loaded. This is not supported yet. - std::cerr << catalog_definition_file_path << ": multiple definitions of catalog " - << catalog->catalog_id << " in multiple files, ignoring this one." << std::endl; - /* Don't store 'catalog'; unique_ptr will free its memory. */ - continue; - } - - /* The AssetCatalog pointer is owned by the AssetCatalogService. */ - this->catalogs.add_new(catalog->catalog_id, std::move(catalog)); - } - - return cdf; -} - -std::unique_ptr AssetCatalogService::parse_catalog_line( - const StringRef line, const AssetCatalogDefinitionFile *catalog_definition_file) -{ - const int64_t first_space = line.find_first_of(' '); - if (first_space == StringRef::not_found) { - std::cerr << "Invalid line in " << catalog_definition_file->file_path << ": " << line - << std::endl; - return std::unique_ptr(nullptr); - } - - const StringRef catalog_id = line.substr(0, first_space); - const StringRef catalog_path = line.substr(first_space + 1).trim().trim(path_separator); - - return std::make_unique(catalog_id, catalog_path); -} - -AssetCatalogDefinitionFile::AssetCatalogDefinitionFile() -{ -} - -AssetCatalog::AssetCatalog() -{ -} - -AssetCatalog::AssetCatalog(const CatalogID &catalog_id, const CatalogPath &path) - : catalog_id(catalog_id), path(path) -{ -} - -} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc deleted file mode 100644 index d6c00670bd6..00000000000 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ /dev/null @@ -1,62 +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) 2020 Blender Foundation - * All rights reserved. - */ - -#include "BKE_asset_catalog.hh" - -#include "testing/testing.h" - -#include - -namespace fs = std::filesystem; - -namespace blender::bke::tests { - -TEST(AssetCatalogTest, load_single_file) -{ - const fs::path test_files_dir = blender::tests::flags_test_asset_dir(); - if (test_files_dir.empty()) { - FAIL(); - } - - AssetCatalogService service; - service.load_from_disk(test_files_dir / "asset_library/single_catalog_definition_file.cats.txt"); - - // Test getting a non-existant catalog ID. - EXPECT_EQ(nullptr, service.find_catalog("NONEXISTANT")); - - // Test getting a 7-bit ASCII catalog ID. - AssetCatalog *poses_elly = service.find_catalog("POSES_ELLY"); - ASSERT_NE(nullptr, poses_elly); - EXPECT_EQ("POSES_ELLY", poses_elly->catalog_id); - EXPECT_EQ("character/Elly/poselib", poses_elly->path); - - // Test whitespace stripping. - AssetCatalog *poses_whitespace = service.find_catalog("POSES_ELLY_WHITESPACE"); - ASSERT_NE(nullptr, poses_whitespace); - EXPECT_EQ("POSES_ELLY_WHITESPACE", poses_whitespace->catalog_id); - EXPECT_EQ("character/Elly/poselib/whitespace", poses_whitespace->path); - - // Test getting a UTF-8 catalog ID. - AssetCatalog *poses_ruzena = service.find_catalog("POSES_RUŽENA"); - ASSERT_NE(nullptr, poses_ruzena); - EXPECT_EQ("POSES_RUŽENA", poses_ruzena->catalog_id); - EXPECT_EQ("character/Ružena/poselib", poses_ruzena->path); -} - -} // namespace blender::bke::tests -- cgit v1.2.3 From 8edb2222ae0c954771a9cddc2d8c02a7a4d68eae Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 2 Aug 2021 12:43:35 +0200 Subject: Cleanup: simplify subclassing CPPType `CPPType` can wrap any C++ type so that code can work with the wrapped type in a generic way. The goal of subclassing `CPPType` is to provide additional methods for some types. For example, the `CPPType` for `Array` could have a `.element_type()` method that returns the `CPPType` for `int`. --- source/blender/functions/FN_cpp_type.hh | 178 +++++++++++---------- source/blender/functions/FN_cpp_type_make.hh | 96 +++++------ .../intern/multi_function_network_optimization.cc | 2 +- 3 files changed, 133 insertions(+), 143 deletions(-) diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh index bc3f398c8e9..7277bf99c12 100644 --- a/source/blender/functions/FN_cpp_type.hh +++ b/source/blender/functions/FN_cpp_type.hh @@ -70,64 +70,73 @@ #include "BLI_string_ref.hh" #include "BLI_utility_mixins.hh" +/** + * Different types support different features. Features like copy constructability can be detected + * automatically easily. For some features this is harder as of C++17. Those have flags in this + * enum and need to be determined by the programmer. + */ +enum class CPPTypeFlags { + None = 0, + Hashable = 1 << 0, + Printable = 1 << 1, + EqualityComparable = 1 << 2, + + BasicType = Hashable | Printable | EqualityComparable, +}; +ENUM_OPERATORS(CPPTypeFlags, CPPTypeFlags::EqualityComparable) + namespace blender::fn { -struct CPPTypeMembers { - int64_t size = 0; - int64_t alignment = 0; - uintptr_t alignment_mask = 0; - bool is_trivially_destructible = false; - bool has_special_member_functions = false; +/** Utility class to pass template parameters to constructor of `CPPType`. */ +template struct CPPTypeParam { +}; - void (*default_construct)(void *ptr) = nullptr; - void (*default_construct_indices)(void *ptr, IndexMask mask) = nullptr; +class CPPType : NonCopyable, NonMovable { + private: + int64_t size_ = 0; + int64_t alignment_ = 0; + uintptr_t alignment_mask_ = 0; + bool is_trivially_destructible_ = false; + bool has_special_member_functions_ = false; - void (*destruct)(void *ptr) = nullptr; - void (*destruct_indices)(void *ptr, IndexMask mask) = nullptr; + void (*default_construct_)(void *ptr) = nullptr; + void (*default_construct_indices_)(void *ptr, IndexMask mask) = nullptr; - void (*copy_assign)(const void *src, void *dst) = nullptr; - void (*copy_assign_indices)(const void *src, void *dst, IndexMask mask) = nullptr; + void (*destruct_)(void *ptr) = nullptr; + void (*destruct_indices_)(void *ptr, IndexMask mask) = nullptr; - void (*copy_construct)(const void *src, void *dst) = nullptr; - void (*copy_construct_indices)(const void *src, void *dst, IndexMask mask) = nullptr; + void (*copy_assign_)(const void *src, void *dst) = nullptr; + void (*copy_assign_indices_)(const void *src, void *dst, IndexMask mask) = nullptr; - void (*move_assign)(void *src, void *dst) = nullptr; - void (*move_assign_indices)(void *src, void *dst, IndexMask mask) = nullptr; + void (*copy_construct_)(const void *src, void *dst) = nullptr; + void (*copy_construct_indices_)(const void *src, void *dst, IndexMask mask) = nullptr; - void (*move_construct)(void *src, void *dst) = nullptr; - void (*move_construct_indices)(void *src, void *dst, IndexMask mask) = nullptr; + void (*move_assign_)(void *src, void *dst) = nullptr; + void (*move_assign_indices_)(void *src, void *dst, IndexMask mask) = nullptr; - void (*relocate_assign)(void *src, void *dst) = nullptr; - void (*relocate_assign_indices)(void *src, void *dst, IndexMask mask) = nullptr; + void (*move_construct_)(void *src, void *dst) = nullptr; + void (*move_construct_indices_)(void *src, void *dst, IndexMask mask) = nullptr; - void (*relocate_construct)(void *src, void *dst) = nullptr; - void (*relocate_construct_indices)(void *src, void *dst, IndexMask mask) = nullptr; + void (*relocate_assign_)(void *src, void *dst) = nullptr; + void (*relocate_assign_indices_)(void *src, void *dst, IndexMask mask) = nullptr; - void (*fill_assign_indices)(const void *value, void *dst, IndexMask mask) = nullptr; + void (*relocate_construct_)(void *src, void *dst) = nullptr; + void (*relocate_construct_indices_)(void *src, void *dst, IndexMask mask) = nullptr; - void (*fill_construct_indices)(const void *value, void *dst, IndexMask mask) = nullptr; + void (*fill_assign_indices_)(const void *value, void *dst, IndexMask mask) = nullptr; - void (*print)(const void *value, std::stringstream &ss) = nullptr; - bool (*is_equal)(const void *a, const void *b) = nullptr; - uint64_t (*hash)(const void *value) = nullptr; + void (*fill_construct_indices_)(const void *value, void *dst, IndexMask mask) = nullptr; - const void *default_value = nullptr; - std::string name; -}; + void (*print_)(const void *value, std::stringstream &ss) = nullptr; + bool (*is_equal_)(const void *a, const void *b) = nullptr; + uint64_t (*hash_)(const void *value) = nullptr; -class CPPType : NonCopyable, NonMovable { - private: - CPPTypeMembers m_; + const void *default_value_ = nullptr; + std::string debug_name_; public: - CPPType(CPPTypeMembers members) : m_(std::move(members)) - { - BLI_assert(is_power_of_2_i(m_.alignment)); - m_.alignment_mask = (uintptr_t)members.alignment - (uintptr_t)1; - m_.has_special_member_functions = (m_.default_construct && m_.copy_construct && - m_.copy_assign && m_.move_construct && m_.move_assign && - m_.destruct); - } + template CPPType(CPPTypeParam, StringRef debug_name); + virtual ~CPPType() = default; /** * Two types only compare equal when their pointer is equal. No two instances of CPPType for the @@ -148,7 +157,11 @@ class CPPType : NonCopyable, NonMovable { * This only works for types that actually implement the template specialization using * `MAKE_CPP_TYPE`. */ - template static const CPPType &get(); + template static const CPPType &get() + { + return CPPType::get_impl>(); + } + template static const CPPType &get_impl(); /** * Returns the name of the type for debugging purposes. This name should not be used as @@ -156,7 +169,7 @@ class CPPType : NonCopyable, NonMovable { */ StringRefNull name() const { - return m_.name; + return debug_name_; } /** @@ -167,7 +180,7 @@ class CPPType : NonCopyable, NonMovable { */ int64_t size() const { - return m_.size; + return size_; } /** @@ -178,7 +191,7 @@ class CPPType : NonCopyable, NonMovable { */ int64_t alignment() const { - return m_.alignment; + return alignment_; } /** @@ -190,52 +203,52 @@ class CPPType : NonCopyable, NonMovable { */ bool is_trivially_destructible() const { - return m_.is_trivially_destructible; + return is_trivially_destructible_; } bool is_default_constructible() const { - return m_.default_construct != nullptr; + return default_construct_ != nullptr; } bool is_copy_constructible() const { - return m_.copy_assign != nullptr; + return copy_assign_ != nullptr; } bool is_move_constructible() const { - return m_.move_assign != nullptr; + return move_assign_ != nullptr; } bool is_destructible() const { - return m_.destruct != nullptr; + return destruct_ != nullptr; } bool is_copy_assignable() const { - return m_.copy_assign != nullptr; + return copy_assign_ != nullptr; } bool is_move_assignable() const { - return m_.copy_construct != nullptr; + return copy_construct_ != nullptr; } bool is_printable() const { - return m_.print != nullptr; + return print_ != nullptr; } bool is_equality_comparable() const { - return m_.is_equal != nullptr; + return is_equal_ != nullptr; } bool is_hashable() const { - return m_.hash != nullptr; + return hash_ != nullptr; } /** @@ -249,7 +262,7 @@ class CPPType : NonCopyable, NonMovable { */ bool has_special_member_functions() const { - return m_.has_special_member_functions; + return has_special_member_functions_; } /** @@ -257,7 +270,7 @@ class CPPType : NonCopyable, NonMovable { */ bool pointer_has_valid_alignment(const void *ptr) const { - return ((uintptr_t)ptr & m_.alignment_mask) == 0; + return ((uintptr_t)ptr & alignment_mask_) == 0; } bool pointer_can_point_to_instance(const void *ptr) const @@ -277,7 +290,7 @@ class CPPType : NonCopyable, NonMovable { { BLI_assert(this->pointer_can_point_to_instance(ptr)); - m_.default_construct(ptr); + default_construct_(ptr); } void default_construct_n(void *ptr, int64_t n) const @@ -289,7 +302,7 @@ class CPPType : NonCopyable, NonMovable { { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(ptr)); - m_.default_construct_indices(ptr, mask); + default_construct_indices_(ptr, mask); } /** @@ -304,7 +317,7 @@ class CPPType : NonCopyable, NonMovable { { BLI_assert(this->pointer_can_point_to_instance(ptr)); - m_.destruct(ptr); + destruct_(ptr); } void destruct_n(void *ptr, int64_t n) const @@ -316,7 +329,7 @@ class CPPType : NonCopyable, NonMovable { { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(ptr)); - m_.destruct_indices(ptr, mask); + destruct_indices_(ptr, mask); } /** @@ -331,7 +344,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); - m_.copy_assign(src, dst); + copy_assign_(src, dst); } void copy_assign_n(const void *src, void *dst, int64_t n) const @@ -345,7 +358,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.copy_assign_indices(src, dst, mask); + copy_assign_indices_(src, dst, mask); } /** @@ -362,7 +375,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); - m_.copy_construct(src, dst); + copy_construct_(src, dst); } void copy_construct_n(const void *src, void *dst, int64_t n) const @@ -376,7 +389,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.copy_construct_indices(src, dst, mask); + copy_construct_indices_(src, dst, mask); } /** @@ -393,7 +406,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); - m_.move_assign(src, dst); + move_assign_(src, dst); } void move_assign_n(void *src, void *dst, int64_t n) const @@ -407,7 +420,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.move_assign_indices(src, dst, mask); + move_assign_indices_(src, dst, mask); } /** @@ -424,7 +437,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); - m_.move_construct(src, dst); + move_construct_(src, dst); } void move_construct_n(void *src, void *dst, int64_t n) const @@ -438,7 +451,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.move_construct_indices(src, dst, mask); + move_construct_indices_(src, dst, mask); } /** @@ -455,7 +468,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); - m_.relocate_assign(src, dst); + relocate_assign_(src, dst); } void relocate_assign_n(void *src, void *dst, int64_t n) const @@ -469,7 +482,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.relocate_assign_indices(src, dst, mask); + relocate_assign_indices_(src, dst, mask); } /** @@ -486,7 +499,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); - m_.relocate_construct(src, dst); + relocate_construct_(src, dst); } void relocate_construct_n(void *src, void *dst, int64_t n) const @@ -500,7 +513,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.relocate_construct_indices(src, dst, mask); + relocate_construct_indices_(src, dst, mask); } /** @@ -518,7 +531,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(value)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.fill_assign_indices(value, dst, mask); + fill_assign_indices_(value, dst, mask); } /** @@ -536,13 +549,13 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(value)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.fill_construct_indices(value, dst, mask); + fill_construct_indices_(value, dst, mask); } void print(const void *value, std::stringstream &ss) const { BLI_assert(this->pointer_can_point_to_instance(value)); - m_.print(value, ss); + print_(value, ss); } std::string to_string(const void *value) const @@ -566,7 +579,7 @@ class CPPType : NonCopyable, NonMovable { { BLI_assert(this->pointer_can_point_to_instance(a)); BLI_assert(this->pointer_can_point_to_instance(b)); - return m_.is_equal(a, b); + return is_equal_(a, b); } bool is_equal_or_false(const void *a, const void *b) const @@ -580,7 +593,7 @@ class CPPType : NonCopyable, NonMovable { uint64_t hash(const void *value) const { BLI_assert(this->pointer_can_point_to_instance(value)); - return m_.hash(value); + return hash_(value); } uint64_t hash_or_fallback(const void *value, uint64_t fallback_hash) const @@ -597,7 +610,7 @@ class CPPType : NonCopyable, NonMovable { */ const void *default_value() const { - return m_.default_value; + return default_value_; } uint64_t hash() const @@ -605,12 +618,9 @@ class CPPType : NonCopyable, NonMovable { return get_default_hash(this); } - /** - * Low level access to the callbacks for this CPPType. - */ - const CPPTypeMembers &members() const + void (*destruct_fn() const)(void *) { - return m_; + return destruct_; } template bool is() const diff --git a/source/blender/functions/FN_cpp_type_make.hh b/source/blender/functions/FN_cpp_type_make.hh index b8e5373ccf7..088f6b469f4 100644 --- a/source/blender/functions/FN_cpp_type_make.hh +++ b/source/blender/functions/FN_cpp_type_make.hh @@ -185,100 +185,80 @@ template uint64_t hash_cb(const void *value) } // namespace blender::fn::cpp_type_util -/** - * Different types support different features. Features like copy constructability can be detected - * automatically easily. For some features this is harder as of C++17. Those have flags in this - * enum and need to be determined by the programmer. - */ -enum class CPPTypeFlags { - None = 0, - Hashable = 1 << 0, - Printable = 1 << 1, - EqualityComparable = 1 << 2, - - BasicType = Hashable | Printable | EqualityComparable, -}; -ENUM_OPERATORS(CPPTypeFlags, CPPTypeFlags::EqualityComparable) - namespace blender::fn { -template -inline std::unique_ptr create_cpp_type(StringRef name) +template +CPPType::CPPType(CPPTypeParam /* unused */, StringRef debug_name) { using namespace cpp_type_util; - CPPTypeMembers m; - m.name = name; - m.size = (int64_t)sizeof(T); - m.alignment = (int64_t)alignof(T); - m.is_trivially_destructible = std::is_trivially_destructible_v; + debug_name_ = debug_name; + size_ = (int64_t)sizeof(T); + alignment_ = (int64_t)alignof(T); + is_trivially_destructible_ = std::is_trivially_destructible_v; if constexpr (std::is_default_constructible_v) { - m.default_construct = default_construct_cb; - m.default_construct_indices = default_construct_indices_cb; + default_construct_ = default_construct_cb; + default_construct_indices_ = default_construct_indices_cb; static T default_value; - m.default_value = (void *)&default_value; + default_value_ = (void *)&default_value; } if constexpr (std::is_destructible_v) { - m.destruct = destruct_cb; - m.destruct_indices = destruct_indices_cb; + destruct_ = destruct_cb; + destruct_indices_ = destruct_indices_cb; } if constexpr (std::is_copy_assignable_v) { - m.copy_assign = copy_assign_cb; - m.copy_assign_indices = copy_assign_indices_cb; + copy_assign_ = copy_assign_cb; + copy_assign_indices_ = copy_assign_indices_cb; } if constexpr (std::is_copy_constructible_v) { - m.copy_construct = copy_construct_cb; - m.copy_construct_indices = copy_construct_indices_cb; + copy_construct_ = copy_construct_cb; + copy_construct_indices_ = copy_construct_indices_cb; } if constexpr (std::is_move_assignable_v) { - m.move_assign = move_assign_cb; - m.move_assign_indices = move_assign_indices_cb; + move_assign_ = move_assign_cb; + move_assign_indices_ = move_assign_indices_cb; } if constexpr (std::is_move_constructible_v) { - m.move_construct = move_construct_cb; - m.move_construct_indices = move_construct_indices_cb; + move_construct_ = move_construct_cb; + move_construct_indices_ = move_construct_indices_cb; } if constexpr (std::is_destructible_v) { if constexpr (std::is_move_assignable_v) { - m.relocate_assign = relocate_assign_cb; - m.relocate_assign_indices = relocate_assign_indices_cb; + relocate_assign_ = relocate_assign_cb; + relocate_assign_indices_ = relocate_assign_indices_cb; } if constexpr (std::is_move_constructible_v) { - m.relocate_construct = relocate_construct_cb; - m.relocate_construct_indices = relocate_construct_indices_cb; + relocate_construct_ = relocate_construct_cb; + relocate_construct_indices_ = relocate_construct_indices_cb; } } if constexpr (std::is_copy_assignable_v) { - m.fill_assign_indices = fill_assign_indices_cb; + fill_assign_indices_ = fill_assign_indices_cb; } if constexpr (std::is_copy_constructible_v) { - m.fill_construct_indices = fill_construct_indices_cb; + fill_construct_indices_ = fill_construct_indices_cb; } - if constexpr ((bool)(flags & CPPTypeFlags::Hashable)) { - m.hash = hash_cb; + if constexpr ((bool)(Flags & CPPTypeFlags::Hashable)) { + hash_ = hash_cb; } - if constexpr ((bool)(flags & CPPTypeFlags::Printable)) { - m.print = print_cb; + if constexpr ((bool)(Flags & CPPTypeFlags::Printable)) { + print_ = print_cb; } - if constexpr ((bool)(flags & CPPTypeFlags::EqualityComparable)) { - m.is_equal = is_equal_cb; + if constexpr ((bool)(Flags & CPPTypeFlags::EqualityComparable)) { + is_equal_ = is_equal_cb; } - const CPPType *type = new CPPType(std::move(m)); - return std::unique_ptr(type); + alignment_mask_ = (uintptr_t)alignment_ - (uintptr_t)1; + has_special_member_functions_ = (default_construct_ && copy_construct_ && copy_assign_ && + move_construct_ && move_assign_ && destruct_); } } // namespace blender::fn #define MAKE_CPP_TYPE(IDENTIFIER, TYPE_NAME, FLAGS) \ - template<> const blender::fn::CPPType &blender::fn::CPPType::get() \ - { \ - static std::unique_ptr cpp_type = \ - blender::fn::create_cpp_type(STRINGIFY(IDENTIFIER)); \ - return *cpp_type; \ - } \ - /* Support using `CPPType::get()`. Otherwise the caller would have to remove const. */ \ - template<> const blender::fn::CPPType &blender::fn::CPPType::get() \ + template<> const blender::fn::CPPType &blender::fn::CPPType::get_impl() \ { \ - return blender::fn::CPPType::get(); \ + static CPPType cpp_type{blender::fn::CPPTypeParam(), \ + STRINGIFY(IDENTIFIER)}; \ + return cpp_type; \ } diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc index 0f65d320f62..75c3583c5e5 100644 --- a/source/blender/functions/intern/multi_function_network_optimization.cc +++ b/source/blender/functions/intern/multi_function_network_optimization.cc @@ -263,7 +263,7 @@ static Array add_constant_folded_sockets(const MultiFunction & const CPPType &cpp_type = data_type.single_type(); GMutableSpan array = params.computed_array(param_index); void *buffer = array.data(); - scope.add(buffer, array.type().members().destruct, AT); + scope.add(buffer, array.type().destruct_fn(), AT); constant_fn = &scope.construct(AT, cpp_type, buffer); break; -- cgit v1.2.3 From d60a7a87445c140a42b6470ef2c54c411d8e4bf3 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 2 Aug 2021 15:09:15 +0200 Subject: WindowManager: Support Dynamic tooltips when dragging. Originally the operator name was drawn next to the dragging content. After that there was an option to add custom, static text with the dragging content. This patch allows dynamic text to be drawn. The custom text was implemented as out parameter of the poll function what made the code unclear. This patch introduces a tooltip function that separates tooltip generation from the poll function. NOTE: the text should always be returned in its own memory block. This block will be freed after it is copied in the drag struct. Reviewed By: Severin Differential Revision: https://developer.blender.org/D12104 --- source/blender/editors/include/UI_interface.h | 5 +- source/blender/editors/interface/interface_ops.c | 5 +- source/blender/editors/screen/screen_ops.c | 10 +- source/blender/editors/space_clip/space_clip.c | 7 +- .../blender/editors/space_console/space_console.c | 14 +- source/blender/editors/space_file/space_file.c | 7 +- source/blender/editors/space_image/space_image.c | 7 +- source/blender/editors/space_node/space_node.c | 46 +++---- .../editors/space_outliner/outliner_dragdrop.c | 146 +++++++++++---------- .../editors/space_sequencer/space_sequencer.c | 24 ++-- source/blender/editors/space_text/space_text.c | 14 +- source/blender/editors/space_view3d/space_view3d.c | 77 +++++------ source/blender/windowmanager/WM_api.h | 7 +- source/blender/windowmanager/WM_types.h | 16 ++- source/blender/windowmanager/intern/wm_dragdrop.c | 82 +++++++----- .../blender/windowmanager/intern/wm_event_system.c | 3 +- 16 files changed, 223 insertions(+), 247 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index a6e465d04e8..30be3588b5a 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2577,10 +2577,7 @@ void ED_keymap_ui(struct wmKeyConfig *keyconf); void ED_uilisttypes_ui(void); void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop); -bool UI_drop_color_poll(struct bContext *C, - struct wmDrag *drag, - const struct wmEvent *event, - const char **r_tooltip); +bool UI_drop_color_poll(struct bContext *C, struct wmDrag *drag, const struct wmEvent *event); bool UI_context_copy_to_selected_list(struct bContext *C, struct PointerRNA *ptr, diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 3ab49b8773b..20af3054882 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1759,10 +1759,7 @@ static void UI_OT_button_string_clear(wmOperatorType *ot) /** \name Drop Color Operator * \{ */ -bool UI_drop_color_poll(struct bContext *C, - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +bool UI_drop_color_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(event)) { /* should only return true for regions that include buttons, for now * return true always */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 8d7d742e44b..9534be3509b 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -5695,10 +5695,7 @@ static void keymap_modal_set(wmKeyConfig *keyconf) WM_modalkeymap_assign(keymap, "SCREEN_OT_area_move"); } -static bool blend_file_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool blend_file_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) { if (drag->icon == ICON_FILE_BLEND) { @@ -5728,8 +5725,9 @@ void ED_keymap_screen(wmKeyConfig *keyconf) /* dropbox for entire window */ ListBase *lb = WM_dropboxmap_find("Window", 0, 0); - WM_dropbox_add(lb, "WM_OT_drop_blend_file", blend_file_drop_poll, blend_file_drop_copy, NULL); - WM_dropbox_add(lb, "UI_OT_drop_color", UI_drop_color_poll, UI_drop_color_copy, NULL); + WM_dropbox_add( + lb, "WM_OT_drop_blend_file", blend_file_drop_poll, blend_file_drop_copy, NULL, NULL); + WM_dropbox_add(lb, "UI_OT_drop_color", UI_drop_color_poll, UI_drop_color_copy, NULL, NULL); keymap_modal_set(keyconf); } diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index aef3385f2dc..e2fbb4a5a59 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -605,10 +605,7 @@ static int /*eContextResult*/ clip_context(const bContext *C, } /* dropboxes */ -static bool clip_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool clip_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) { /* rule might not work? */ @@ -639,7 +636,7 @@ static void clip_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Clip", SPACE_CLIP, 0); - WM_dropbox_add(lb, "CLIP_OT_open", clip_drop_poll, clip_drop_copy, NULL); + WM_dropbox_add(lb, "CLIP_OT_open", clip_drop_poll, clip_drop_copy, NULL, NULL); } static void clip_refresh(const bContext *C, ScrArea *area) diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 3029eed1017..47d15efb6ca 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -158,10 +158,7 @@ static void console_cursor(wmWindow *win, ScrArea *UNUSED(area), ARegion *region /* ************* dropboxes ************* */ -static bool id_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(tooltip)) +static bool id_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return WM_drag_get_local_ID(drag, 0) != NULL; } @@ -176,10 +173,7 @@ static void id_drop_copy(wmDrag *drag, wmDropBox *drop) MEM_freeN(text); } -static bool path_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(tooltip)) +static bool path_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return (drag->type == WM_DRAG_PATH); } @@ -196,8 +190,8 @@ static void console_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Console", SPACE_CONSOLE, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, "CONSOLE_OT_insert", id_drop_poll, id_drop_copy, NULL); - WM_dropbox_add(lb, "CONSOLE_OT_insert", path_drop_poll, path_drop_copy, NULL); + WM_dropbox_add(lb, "CONSOLE_OT_insert", id_drop_poll, id_drop_copy, NULL, NULL); + WM_dropbox_add(lb, "CONSOLE_OT_insert", path_drop_poll, path_drop_copy, NULL, NULL); } /* ************* end drop *********** */ diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 46cc96ba0d4..d7a6523de26 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -815,10 +815,7 @@ static void file_ui_region_listener(const wmRegionListenerParams *listener_param } } -static bool filepath_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool filepath_drop_poll(bContext *C, wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) { SpaceFile *sfile = CTX_wm_space_file(C); @@ -839,7 +836,7 @@ static void file_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Window", SPACE_EMPTY, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy, NULL); + WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy, NULL, NULL); } static int file_space_subtype_get(ScrArea *area) diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 5a03b4f6ef0..4107fd619aa 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -253,10 +253,7 @@ static void image_keymap(struct wmKeyConfig *keyconf) } /* dropboxes */ -static bool image_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ScrArea *area = CTX_wm_area(C); if (ED_region_overlap_isect_any_xy(area, &event->x)) { @@ -282,7 +279,7 @@ static void image_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Image", SPACE_IMAGE, 0); - WM_dropbox_add(lb, "IMAGE_OT_open", image_drop_poll, image_drop_copy, NULL); + WM_dropbox_add(lb, "IMAGE_OT_open", image_drop_poll, image_drop_copy, NULL, NULL); } /** diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index ff848a7bb95..956fb3aa867 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -664,42 +664,29 @@ static void node_main_region_draw(const bContext *C, ARegion *region) /* ************* dropboxes ************* */ -static bool node_group_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool node_group_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return WM_drag_is_ID_type(drag, ID_NT); } -static bool node_object_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool node_object_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return WM_drag_is_ID_type(drag, ID_OB); } static bool node_collection_drop_poll(bContext *UNUSED(C), wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) + const wmEvent *UNUSED(event)) { return WM_drag_is_ID_type(drag, ID_GR); } -static bool node_texture_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool node_texture_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return WM_drag_is_ID_type(drag, ID_TE); } -static bool node_ima_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool node_ima_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) { /* rule might not work? */ @@ -708,10 +695,7 @@ static bool node_ima_drop_poll(bContext *UNUSED(C), return WM_drag_is_ID_type(drag, ID_IM); } -static bool node_mask_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool node_mask_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return WM_drag_is_ID_type(drag, ID_MSK); } @@ -753,32 +737,38 @@ static void node_dropboxes(void) "NODE_OT_add_object", node_object_drop_poll, node_id_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "NODE_OT_add_collection", node_collection_drop_poll, node_id_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "NODE_OT_add_texture", node_texture_drop_poll, node_id_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "NODE_OT_add_group", node_group_drop_poll, node_group_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "NODE_OT_add_file", node_ima_drop_poll, node_id_path_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "NODE_OT_add_mask", node_mask_drop_poll, node_id_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); } /* ************* end drop *********** */ diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index 86aab86db10..36b2966dc43 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -31,6 +31,7 @@ #include "DNA_space_types.h" #include "BLI_listbase.h" +#include "BLI_string.h" #include "BLT_translation.h" @@ -316,10 +317,7 @@ static bool allow_parenting_without_modifier_key(SpaceOutliner *space_outliner) } } -static bool parent_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -455,10 +453,7 @@ void OUTLINER_OT_parent_drop(wmOperatorType *ot) /* ******************** Parent Clear Operator *********************** */ -static bool parent_clear_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -541,10 +536,7 @@ void OUTLINER_OT_parent_clear(wmOperatorType *ot) /* ******************** Scene Drop Operator *********************** */ -static bool scene_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { /* Ensure item under cursor is valid drop target */ Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB); @@ -609,10 +601,7 @@ void OUTLINER_OT_scene_drop(wmOperatorType *ot) /* ******************** Material Drop Operator *********************** */ -static bool material_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool material_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { /* Ensure item under cursor is valid drop target */ Material *ma = (Material *)WM_drag_get_local_ID(drag, ID_MA); @@ -833,10 +822,7 @@ static bool datastack_drop_are_types_valid(StackDropData *drop_data) return true; } -static bool datastack_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **r_tooltip) +static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { if (drag->type != WM_DRAG_DATASTACK) { return false; @@ -873,33 +859,40 @@ static bool datastack_drop_poll(bContext *C, break; } + if (changed) { + ED_region_tag_redraw_no_rebuild(region); + } + + return true; +} + +static char *datastack_drop_tooltip(bContext *UNUSED(C), + wmDrag *drag, + const wmEvent *UNUSED(event)) +{ + StackDropData *drop_data = drag->poin; switch (drop_data->drop_action) { case DATA_STACK_DROP_REORDER: - *r_tooltip = TIP_("Reorder"); + return BLI_strdup(TIP_("Reorder")); break; case DATA_STACK_DROP_COPY: if (drop_data->pchan_parent) { - *r_tooltip = TIP_("Copy to bone"); + return BLI_strdup(TIP_("Copy to bone")); } else { - *r_tooltip = TIP_("Copy to object"); + return BLI_strdup(TIP_("Copy to object")); } break; case DATA_STACK_DROP_LINK: if (drop_data->pchan_parent) { - *r_tooltip = TIP_("Link all to bone"); + return BLI_strdup(TIP_("Link all to bone")); } else { - *r_tooltip = TIP_("Link all to object"); + return BLI_strdup(TIP_("Link all to object")); } break; } - - if (changed) { - ED_region_tag_redraw_no_rebuild(region); - } - - return true; + return NULL; } static void datastack_drop_link(bContext *C, StackDropData *drop_data) @@ -1155,10 +1148,7 @@ static bool collection_drop_init(bContext *C, return true; } -static bool collection_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **r_tooltip) +static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); ARegion *region = CTX_wm_region(C); @@ -1172,45 +1162,20 @@ static bool collection_drop_poll(bContext *C, if (!data.from || event->ctrl) { tselem->flag |= TSE_DRAG_INTO; changed = true; - *r_tooltip = TIP_("Link inside Collection"); } else { switch (data.insert_type) { case TE_INSERT_BEFORE: tselem->flag |= TSE_DRAG_BEFORE; changed = true; - if (te->prev && outliner_is_collection_tree_element(te->prev)) { - *r_tooltip = TIP_("Move between collections"); - } - else { - *r_tooltip = TIP_("Move before collection"); - } break; case TE_INSERT_AFTER: tselem->flag |= TSE_DRAG_AFTER; changed = true; - if (te->next && outliner_is_collection_tree_element(te->next)) { - *r_tooltip = TIP_("Move between collections"); - } - else { - *r_tooltip = TIP_("Move after collection"); - } break; case TE_INSERT_INTO: { tselem->flag |= TSE_DRAG_INTO; changed = true; - - /* Check the type of the drag IDs to avoid the incorrect "Shift to parent" - * for collections. Checking the type of the first ID works fine here since - * all drag IDs are the same type. */ - wmDragID *drag_id = (wmDragID *)drag->ids.first; - const bool is_object = (GS(drag_id->id->name) == ID_OB); - if (is_object) { - *r_tooltip = TIP_("Move inside collection (Ctrl to link, Shift to parent)"); - } - else { - *r_tooltip = TIP_("Move inside collection (Ctrl to link)"); - } break; } } @@ -1226,6 +1191,49 @@ static bool collection_drop_poll(bContext *C, return false; } +static char *collection_drop_tooltip(bContext *C, wmDrag *drag, const wmEvent *event) +{ + CollectionDrop data; + if (!event->shift && collection_drop_init(C, drag, event, &data)) { + TreeElement *te = data.te; + if (!data.from || event->ctrl) { + return BLI_strdup(TIP_("Link inside Collection")); + } + switch (data.insert_type) { + case TE_INSERT_BEFORE: + if (te->prev && outliner_is_collection_tree_element(te->prev)) { + return BLI_strdup(TIP_("Move between collections")); + } + else { + return BLI_strdup(TIP_("Move before collection")); + } + break; + case TE_INSERT_AFTER: + if (te->next && outliner_is_collection_tree_element(te->next)) { + return BLI_strdup(TIP_("Move between collections")); + } + else { + return BLI_strdup(TIP_("Move after collection")); + } + break; + case TE_INSERT_INTO: { + + /* Check the type of the drag IDs to avoid the incorrect "Shift to parent" + * for collections. Checking the type of the first ID works fine here since + * all drag IDs are the same type. */ + wmDragID *drag_id = (wmDragID *)drag->ids.first; + const bool is_object = (GS(drag_id->id->name) == ID_OB); + if (is_object) { + return BLI_strdup(TIP_("Move inside collection (Ctrl to link, Shift to parent)")); + } + return BLI_strdup(TIP_("Move inside collection (Ctrl to link)")); + break; + } + } + } + return NULL; +} + static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { Main *bmain = CTX_data_main(C); @@ -1499,10 +1507,16 @@ void outliner_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Outliner", SPACE_OUTLINER, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", parent_drop_poll, NULL, NULL); - WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", parent_clear_poll, NULL, NULL); - WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", scene_drop_poll, NULL, NULL); - WM_dropbox_add(lb, "OUTLINER_OT_material_drop", material_drop_poll, NULL, NULL); - WM_dropbox_add(lb, "OUTLINER_OT_datastack_drop", datastack_drop_poll, NULL, NULL); - WM_dropbox_add(lb, "OUTLINER_OT_collection_drop", collection_drop_poll, NULL, NULL); + WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", parent_drop_poll, NULL, NULL, NULL); + WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", parent_clear_poll, NULL, NULL, NULL); + WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", scene_drop_poll, NULL, NULL, NULL); + WM_dropbox_add(lb, "OUTLINER_OT_material_drop", material_drop_poll, NULL, NULL, NULL); + WM_dropbox_add( + lb, "OUTLINER_OT_datastack_drop", datastack_drop_poll, NULL, NULL, datastack_drop_tooltip); + WM_dropbox_add(lb, + "OUTLINER_OT_collection_drop", + collection_drop_poll, + NULL, + NULL, + collection_drop_tooltip); } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 6de95f0995a..00f3bf6ac72 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -364,10 +364,7 @@ static void sequencer_listener(const wmSpaceTypeListenerParams *params) /* ************* dropboxes ************* */ -static bool image_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ARegion *region = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); @@ -384,10 +381,7 @@ static bool image_drop_poll(bContext *C, return 0; } -static bool movie_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool movie_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ARegion *region = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); @@ -403,10 +397,7 @@ static bool movie_drop_poll(bContext *C, return 0; } -static bool sound_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool sound_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ARegion *region = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); @@ -448,9 +439,12 @@ static void sequencer_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Sequencer", SPACE_SEQ, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, "SEQUENCER_OT_image_strip_add", image_drop_poll, sequencer_drop_copy, NULL); - WM_dropbox_add(lb, "SEQUENCER_OT_movie_strip_add", movie_drop_poll, sequencer_drop_copy, NULL); - WM_dropbox_add(lb, "SEQUENCER_OT_sound_strip_add", sound_drop_poll, sequencer_drop_copy, NULL); + WM_dropbox_add( + lb, "SEQUENCER_OT_image_strip_add", image_drop_poll, sequencer_drop_copy, NULL, NULL); + WM_dropbox_add( + lb, "SEQUENCER_OT_movie_strip_add", movie_drop_poll, sequencer_drop_copy, NULL, NULL); + WM_dropbox_add( + lb, "SEQUENCER_OT_sound_strip_add", sound_drop_poll, sequencer_drop_copy, NULL, NULL); } /* ************* end drop *********** */ diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index af783051661..89e92231657 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -312,10 +312,7 @@ static void text_cursor(wmWindow *win, ScrArea *area, ARegion *region) /* ************* dropboxes ************* */ -static bool text_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool text_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) { /* rule might not work? */ @@ -332,10 +329,7 @@ static void text_drop_copy(wmDrag *drag, wmDropBox *drop) RNA_string_set(drop->ptr, "filepath", drag->path); } -static bool text_drop_paste_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool text_drop_paste_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return (drag->type == WM_DRAG_ID); } @@ -356,8 +350,8 @@ static void text_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Text", SPACE_TEXT, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, "TEXT_OT_open", text_drop_poll, text_drop_copy, NULL); - WM_dropbox_add(lb, "TEXT_OT_insert", text_drop_paste_poll, text_drop_paste, NULL); + WM_dropbox_add(lb, "TEXT_OT_open", text_drop_poll, text_drop_copy, NULL, NULL); + WM_dropbox_add(lb, "TEXT_OT_insert", text_drop_paste_poll, text_drop_paste, NULL, NULL); } /* ************* end drop *********** */ diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 54f10e259f9..a2564469c16 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -513,49 +513,38 @@ static bool view3d_drop_id_in_main_region_poll(bContext *C, return WM_drag_is_ID_type(drag, id_type); } -static bool view3d_ob_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool view3d_ob_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { return view3d_drop_id_in_main_region_poll(C, drag, event, ID_OB); } -static bool view3d_collection_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool view3d_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { return view3d_drop_id_in_main_region_poll(C, drag, event, ID_GR); } -static bool view3d_mat_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { return view3d_drop_id_in_main_region_poll(C, drag, event, ID_MA); } -static bool view3d_object_data_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **r_tooltip) +static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ID_Type id_type = view3d_drop_id_in_main_region_poll_get_id_type(C, drag, event); - if (id_type) { - if (OB_DATA_SUPPORT_ID(id_type)) { - *r_tooltip = TIP_("Create object instance from object-data"); - return true; - } + if (id_type && OB_DATA_SUPPORT_ID(id_type)) { + return true; } return false; } -static bool view3d_ima_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static char *view3d_object_data_drop_tooltip(bContext *UNUSED(C), + wmDrag *UNUSED(drag), + const wmEvent *UNUSED(event)) +{ + return BLI_strdup(TIP_("Create object instance from object-data")); +} + +static bool view3d_ima_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { if (ED_region_overlap_isect_any_xy(CTX_wm_area(C), &event->x)) { return false; @@ -580,12 +569,9 @@ static bool view3d_ima_bg_is_camera_view(bContext *C) return false; } -static bool view3d_ima_bg_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **r_tooltip) +static bool view3d_ima_bg_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { - if (!view3d_ima_drop_poll(C, drag, event, r_tooltip)) { + if (!view3d_ima_drop_poll(C, drag, event)) { return false; } @@ -596,12 +582,9 @@ static bool view3d_ima_bg_drop_poll(bContext *C, return view3d_ima_bg_is_camera_view(C); } -static bool view3d_ima_empty_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **r_tooltip) +static bool view3d_ima_empty_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { - if (!view3d_ima_drop_poll(C, drag, event, r_tooltip)) { + if (!view3d_ima_drop_poll(C, drag, event)) { return false; } @@ -620,8 +603,7 @@ static bool view3d_ima_empty_drop_poll(bContext *C, static bool view3d_volume_drop_poll(bContext *UNUSED(C), wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) + const wmEvent *UNUSED(event)) { return (drag->type == WM_DRAG_PATH) && (drag->icon == ICON_FILE_VOLUME); } @@ -700,37 +682,44 @@ static void view3d_dropboxes(void) "OBJECT_OT_add_named", view3d_ob_drop_poll, view3d_ob_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "OBJECT_OT_drop_named_material", view3d_mat_drop_poll, view3d_id_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "VIEW3D_OT_background_image_add", view3d_ima_bg_drop_poll, view3d_id_path_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "OBJECT_OT_drop_named_image", view3d_ima_empty_drop_poll, view3d_id_path_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "OBJECT_OT_volume_import", view3d_volume_drop_poll, view3d_id_path_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "OBJECT_OT_collection_instance_add", view3d_collection_drop_poll, view3d_collection_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "OBJECT_OT_data_instance_add", view3d_object_data_drop_poll, view3d_id_drop_copy_with_type, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + view3d_object_data_drop_tooltip); } static void view3d_widgets(void) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index c0d408be2e0..136c639caea 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -34,6 +34,7 @@ #include "BLI_sys_types.h" #include "DNA_windowmanager_types.h" #include "WM_keymap.h" +#include "WM_types.h" #ifdef __cplusplus extern "C" { @@ -705,13 +706,13 @@ void WM_event_drag_image(struct wmDrag *, struct ImBuf *, float scale, int sx, i void WM_drag_free(struct wmDrag *drag); void WM_drag_data_free(int dragtype, void *poin); void WM_drag_free_list(struct ListBase *lb); - struct wmDropBox *WM_dropbox_add( ListBase *lb, const char *idname, - bool (*poll)(struct bContext *, struct wmDrag *, const struct wmEvent *event, const char **), + bool (*poll)(struct bContext *, struct wmDrag *, const struct wmEvent *event), void (*copy)(struct wmDrag *, struct wmDropBox *), - void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *)); + void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *), + WMDropboxTooltipFunc tooltip); ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid); /* ID drag and drop */ diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 4ead0b2699c..4d6cb941347 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -114,6 +114,7 @@ struct bContext; struct wmEvent; struct wmOperator; struct wmWindowManager; +struct wmDrag; #include "BLI_compiler_attrs.h" #include "DNA_listBase.h" @@ -934,6 +935,10 @@ typedef struct wmDragAsset { int import_type; /* eFileAssetImportType */ } wmDragAsset; +typedef char *(*WMDropboxTooltipFunc)(struct bContext *, + struct wmDrag *, + const struct wmEvent *event); + typedef struct wmDrag { struct wmDrag *next, *prev; @@ -949,8 +954,8 @@ typedef struct wmDrag { float scale; int sx, sy; - /** If set, draws operator name. */ - char opname[200]; + /** If filled, draws operator tooltip/operator name. */ + char tooltip[200]; unsigned int flags; /** List of wmDragIDs, all are guaranteed to have the same ID type. */ @@ -964,8 +969,8 @@ typedef struct wmDrag { typedef struct wmDropBox { struct wmDropBox *next, *prev; - /** Test if the dropbox is active, then can print optype name. */ - bool (*poll)(struct bContext *, struct wmDrag *, const wmEvent *, const char **); + /** Test if the dropbox is active. */ + bool (*poll)(struct bContext *, struct wmDrag *, const wmEvent *); /** Before exec, this copies drag info to #wmDrop properties. */ void (*copy)(struct wmDrag *, struct wmDropBox *); @@ -976,6 +981,9 @@ typedef struct wmDropBox { */ void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *); + /** Custom tooltip shown during dragging. */ + WMDropboxTooltipFunc tooltip; + /** * If poll succeeds, operator is called. * Not saved in file, so can be pointer. diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index db72dd2a819..c58d3c53e03 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -96,14 +96,16 @@ ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid) wmDropBox *WM_dropbox_add(ListBase *lb, const char *idname, - bool (*poll)(bContext *, wmDrag *, const wmEvent *, const char **), + bool (*poll)(bContext *, wmDrag *, const wmEvent *), void (*copy)(wmDrag *, wmDropBox *), - void (*cancel)(struct Main *, wmDrag *, wmDropBox *)) + void (*cancel)(struct Main *, wmDrag *, wmDropBox *), + WMDropboxTooltipFunc tooltip) { wmDropBox *drop = MEM_callocN(sizeof(wmDropBox), "wmDropBox"); drop->poll = poll; drop->copy = copy; drop->cancel = cancel; + drop->tooltip = tooltip; drop->ot = WM_operatortype_find(idname, 0); drop->opcontext = WM_OP_INVOKE_DEFAULT; @@ -218,22 +220,36 @@ void WM_drag_free_list(struct ListBase *lb) } } -static const char *dropbox_active(bContext *C, - ListBase *handlers, - wmDrag *drag, - const wmEvent *event) +static char *dropbox_tooltip(bContext *C, + wmDrag *drag, + const wmEvent *event, + const wmDropBox *drop) +{ + char *tooltip = NULL; + if (drop->tooltip) { + tooltip = drop->tooltip(C, drag, event); + } + if (!tooltip) { + tooltip = BLI_strdup(WM_operatortype_name(drop->ot, drop->ptr)); + } + /* XXX Doing translation here might not be ideal, but later we have no more + * access to ot (and hence op context)... */ + return tooltip; +} + +static wmDropBox *dropbox_active(bContext *C, + ListBase *handlers, + wmDrag *drag, + const wmEvent *event) { LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) { wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base; if (handler->dropboxes) { LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) { - const char *tooltip = NULL; - if (drop->poll(C, drag, event, &tooltip) && + if (drop->poll(C, drag, event) && WM_operator_poll_context(C, drop->ot, drop->opcontext)) { - /* XXX Doing translation here might not be ideal, but later we have no more - * access to ot (and hence op context)... */ - return (tooltip) ? tooltip : WM_operatortype_name(drop->ot, drop->ptr); + return drop; } } } @@ -242,29 +258,22 @@ static const char *dropbox_active(bContext *C, return NULL; } -/* return active operator name when mouse is in box */ -static const char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) +/* return active operator tooltip/name when mouse is in box */ +static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); - ScrArea *area = CTX_wm_area(C); - ARegion *region = CTX_wm_region(C); - const char *name; - - name = dropbox_active(C, &win->handlers, drag, event); - if (name) { - return name; + wmDropBox *drop = dropbox_active(C, &win->handlers, drag, event); + if (!drop) { + ScrArea *area = CTX_wm_area(C); + drop = dropbox_active(C, &area->handlers, drag, event); } - - name = dropbox_active(C, &area->handlers, drag, event); - if (name) { - return name; + if (!drop) { + ARegion *region = CTX_wm_region(C); + drop = dropbox_active(C, ®ion->handlers, drag, event); } - - name = dropbox_active(C, ®ion->handlers, drag, event); - if (name) { - return name; + if (drop) { + return dropbox_tooltip(C, drag, event, drop); } - return NULL; } @@ -279,17 +288,18 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *e return; } - drag->opname[0] = 0; + drag->tooltip[0] = 0; /* check buttons (XXX todo rna and value) */ if (UI_but_active_drop_name(C)) { - BLI_strncpy(drag->opname, IFACE_("Paste name"), sizeof(drag->opname)); + BLI_strncpy(drag->tooltip, IFACE_("Paste name"), sizeof(drag->tooltip)); } else { - const char *opname = wm_dropbox_active(C, drag, event); + char *tooltip = wm_dropbox_active(C, drag, event); - if (opname) { - BLI_strncpy(drag->opname, opname, sizeof(drag->opname)); + if (tooltip) { + BLI_strncpy(drag->tooltip, tooltip, sizeof(drag->tooltip)); + MEM_freeN(tooltip); // WM_cursor_modal_set(win, WM_CURSOR_COPY); } // else @@ -584,7 +594,7 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) } /* operator name with roundbox */ - if (drag->opname[0]) { + if (drag->tooltip[0]) { if (drag->imb) { x = cursorx - drag->sx / 2; @@ -611,7 +621,7 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) drag_rect_minmax(rect, x, y, x + w, y + iconsize); } else { - wm_drop_operator_draw(drag->opname, x, y); + wm_drop_operator_draw(drag->tooltip, x, y); } } } diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 5e29a22304c..5cc361fc1bd 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -2851,8 +2851,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers if (event->custom == EVT_DATA_DRAGDROP) { ListBase *lb = (ListBase *)event->customdata; LISTBASE_FOREACH (wmDrag *, drag, lb) { - const char *tooltip = NULL; - if (drop->poll(C, drag, event, &tooltip)) { + if (drop->poll(C, drag, event)) { /* Optionally copy drag information to operator properties. Don't call it if the * operator fails anyway, it might do more than just set properties (e.g. * typically import an asset). */ -- cgit v1.2.3 From 11cfa6c718b7851a30f203c92e53c4dc6451ae1a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 2 Aug 2021 23:58:55 +1000 Subject: Fix T90332: Auto-smooth crashes in edit-mode Regression in 39b2a7bb7e815e051348bf5c5ec777d091324164 that meant non-manifold edges were not being tagged when they should have been. --- source/blender/bmesh/intern/bmesh_mesh_normals.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index 6dfaa0ca688..e62deb2dde5 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -50,6 +50,8 @@ static void bm_edge_tag_from_smooth(const float (*fnos)[3], BMEdge *e, const float split_angle_cos); +static void bm_edge_tag_clear(BMEdge *e); + /* -------------------------------------------------------------------- */ /** \name Update Vertex & Face Normals * \{ */ @@ -820,9 +822,10 @@ BLI_INLINE bool bm_edge_is_smooth_no_angle_test(const BMEdge *e, const BMLoop *l_a, const BMLoop *l_b) { + BLI_assert(l_a->radial_next == l_b); return ( /* The face is manifold. */ - (l_a->radial_next == l_b) && + (l_b->radial_next == l_a) && /* Faces have winding that faces the same way. */ (l_a->v != l_b->v) && /* The edge is smooth. */ @@ -863,6 +866,13 @@ static void bm_edge_tag_from_smooth(const float (*fnos)[3], BMEdge *e, const flo } } +static void bm_edge_tag_clear(BMEdge *e) +{ + /* No need for atomics here as this is a single byte. */ + char *hflag_p = &e->head.hflag; + *hflag_p = *hflag_p & ~BM_ELEM_TAG; +} + /** * A version of #bm_edge_tag_from_smooth that sets sharp edges * when they would be considered smooth but exceed the split angle . @@ -944,9 +954,13 @@ static void bm_mesh_loops_calc_normals_for_vert_with_clnors(BMesh *bm, continue; } + /* Always set as #bm_mesh_loops_calc_normals_for_loop checks the tag. */ if (do_edge_tag) { bm_edge_tag_from_smooth(fnos, e_curr_iter, split_angle_cos); } + else { + bm_edge_tag_clear(e_curr_iter); + } do { /* Radial loops. */ if (l_curr->v != v) { @@ -1052,9 +1066,13 @@ static void bm_mesh_loops_calc_normals_for_vert_without_clnors( continue; } + /* Always set as #bm_mesh_loops_calc_normals_for_loop checks the tag. */ if (do_edge_tag) { bm_edge_tag_from_smooth(fnos, e_curr_iter, split_angle_cos); } + else { + bm_edge_tag_clear(e_curr_iter); + } do { /* Radial loops. */ if (l_curr->v != v) { -- cgit v1.2.3 From 0e4f7b4a4b328d1d62c9c5e6a57e58c89a0e9aa2 Mon Sep 17 00:00:00 2001 From: James Monteath Date: Mon, 2 Aug 2021 16:57:29 +0200 Subject: Delete pipeline_config.json file. The yaml file is now used. Update README.md. --- build_files/config/README.md | 11 ++--- build_files/config/pipeline_config.json | 87 --------------------------------- 2 files changed, 5 insertions(+), 93 deletions(-) delete mode 100644 build_files/config/pipeline_config.json diff --git a/build_files/config/README.md b/build_files/config/README.md index 6ce601104af..efa3d4524ec 100644 --- a/build_files/config/README.md +++ b/build_files/config/README.md @@ -1,11 +1,10 @@ Pipeline Config =============== -This configuration file is used by buildbot new build pipeline for the `update-code` step. +The `yaml` configuration file is used by buildbot build pipeline `update-code` step. -It will also be used by the `../utils/make_update.py` script in the near future. +The file allows to set branches or specific commits for both git submodules and svn artifacts. Can also define various build package versions for use by build workers. Especially useful in experimental and release branches. -NOTES: -* Keep both `yaml` and `json` files in sync until deployment of build pipeline updates. -* The `json` file be removed once all branches are running with the `yaml` file. -* Expected buildbot pipeline update is *Friday, July 30th* or *Monday August, 2nd*. +NOTE: +* The configuration file is ```NOT``` used by the `../utils/make_update.py` script. +* That will implemented in the future. diff --git a/build_files/config/pipeline_config.json b/build_files/config/pipeline_config.json deleted file mode 100644 index d914cf85eae..00000000000 --- a/build_files/config/pipeline_config.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "update-code": - { - "git" : - { - "submodules": - [ - { "path": "release/scripts/addons", "branch": "master", "commit_id": "HEAD" }, - { "path": "release/scripts/addons_contrib", "branch": "master", "commit_id": "HEAD" }, - { "path": "release/datafiles/locale", "branch": "master", "commit_id": "HEAD" }, - { "path": "source/tools", "branch": "master", "commit_id": "HEAD" } - ] - }, - "svn": - { - "tests": { "path": "lib/tests", "branch": "trunk", "commit_id": "HEAD" }, - "libraries": - { - "darwin-x86_64": { "path": "lib/darwin", "branch": "trunk", "commit_id": "HEAD" }, - "darwin-arm64": { "path": "lib/darwin_arm64", "branch": "trunk", "commit_id": "HEAD" }, - "linux-x86_64": { "path": "lib/linux_centos7_x86_64", "branch": "trunk", "commit_id": "HEAD" }, - "windows-amd64": { "path": "lib/win64_vc15", "branch": "trunk", "commit_id": "HEAD" } - } - } - }, - "buildbot": - { - "gcc": - { - "version": "9.0" - }, - "sdks": - { - "optix": - { - "version": "7.1.0" - }, - "cuda10": - { - "version": "10.1" - }, - "cuda11": - { - "version": "11.4" - } - }, - "cmake": - { - "default": - { - "version": "any", - "overrides": - { - - } - }, - "darwin-x86_64": - { - "overrides": - { - - } - }, - "darwin-arm64": - { - "overrides": - { - - } - }, - "linux-x86_64": - { - "overrides": - { - - } - }, - "windows-amd64": - { - "overrides": - { - - } - } - } - } -} -- cgit v1.2.3 From d8bf332f86b3644b016950b596bb0814f245e995 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 2 Aug 2021 16:45:56 +0200 Subject: Asset Browser: Adjust header pulldowns to be Asset Browser specific So far the Asset Browser just showed the same menus as the File Browser. Not all of their entries made sense for the Asset Browser though. I decided to just give them entirely different classes to avoid confusing if-else checks everywhere. I think the code duplication this adds is a minor issue, it's better to keep things seperated clearly IMO. * View menu: Add "Asset Details" toggle for the sidebar region. * View menu: Remove recursion sub-menu * View menu: Remove "File Path" region toggle, which doesn't apply for the Asset Browser. Differential Revision: https://developer.blender.org/D12057 --- release/scripts/startup/bl_ui/space_filebrowser.py | 74 ++++++++++++++++++++-- source/blender/makesrna/intern/rna_space.c | 20 +++++- 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 8ca93d2406c..570d8b42821 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -84,12 +84,12 @@ class FILEBROWSER_HT_header(Header): if space_data.active_operator is None: layout.template_header() - FILEBROWSER_MT_editor_menus.draw_collapsible(context, layout) - if SpaceAssetInfo.is_asset_browser(space_data): + ASSETBROWSER_MT_editor_menus.draw_collapsible(context, layout) layout.separator() self.draw_asset_browser_buttons(context) else: + FILEBROWSER_MT_editor_menus.draw_collapsible(context, layout) layout.separator_spacer() if not context.screen.show_statusbar: @@ -477,7 +477,14 @@ class FILEBROWSER_PT_directory_path(Panel): ).region_type = 'TOOL_PROPS' -class FILEBROWSER_MT_editor_menus(Menu): +class FileBrowserMenu: + @classmethod + def poll(cls, context): + space_data = context.space_data + return space_data and space_data.type == 'FILE_BROWSER' and space_data.browse_mode == 'FILES' + + +class FILEBROWSER_MT_editor_menus(FileBrowserMenu, Menu): bl_idname = "FILEBROWSER_MT_editor_menus" bl_label = "" @@ -488,7 +495,7 @@ class FILEBROWSER_MT_editor_menus(Menu): layout.menu("FILEBROWSER_MT_select") -class FILEBROWSER_MT_view(Menu): +class FILEBROWSER_MT_view(FileBrowserMenu, Menu): bl_label = "View" def draw(self, context): @@ -510,7 +517,7 @@ class FILEBROWSER_MT_view(Menu): layout.menu("INFO_MT_area") -class FILEBROWSER_MT_select(Menu): +class FILEBROWSER_MT_select(FileBrowserMenu, Menu): bl_label = "Select" def draw(self, _context): @@ -572,6 +579,60 @@ class FILEBROWSER_MT_context_menu(Menu): layout.prop_menu_enum(params, "sort_method") +class AssetBrowserMenu: + @classmethod + def poll(cls, context): + from bpy_extras.asset_utils import SpaceAssetInfo + return SpaceAssetInfo.is_asset_browser_poll(context) + + +class ASSETBROWSER_MT_editor_menus(AssetBrowserMenu, Menu): + bl_idname = "ASSETBROWSER_MT_editor_menus" + bl_label = "" + + def draw(self, _context): + layout = self.layout + + layout.menu("ASSETBROWSER_MT_view") + layout.menu("ASSETBROWSER_MT_select") + + +class ASSETBROWSER_MT_view(AssetBrowserMenu, Menu): + bl_label = "View" + + def draw(self, context): + layout = self.layout + st = context.space_data + params = st.params + + layout.prop(st, "show_region_toolbar", text="Source List") + layout.prop(st, "show_region_tool_props", text="Asset Details") + layout.operator("file.view_selected") + + layout.separator() + + layout.prop_menu_enum(params, "display_size") + + layout.separator() + + layout.menu("INFO_MT_area") + + +class ASSETBROWSER_MT_select(AssetBrowserMenu, Menu): + bl_label = "Select" + + def draw(self, _context): + layout = self.layout + + layout.operator("file.select_all", text="All").action = 'SELECT' + layout.operator("file.select_all", text="None").action = 'DESELECT' + layout.operator("file.select_all", text="Inverse").action = 'INVERT' + + layout.separator() + + layout.operator("file.select_box") + + class ASSETBROWSER_PT_navigation_bar(asset_utils.AssetBrowserPanel, Panel): bl_label = "Asset Navigation" bl_region_type = 'TOOLS' @@ -694,6 +755,9 @@ classes = ( FILEBROWSER_MT_view, FILEBROWSER_MT_select, FILEBROWSER_MT_context_menu, + ASSETBROWSER_MT_editor_menus, + ASSETBROWSER_MT_view, + ASSETBROWSER_MT_select, ASSETBROWSER_PT_navigation_bar, ASSETBROWSER_PT_metadata, ASSETBROWSER_PT_metadata_preview, diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 1d4318602c2..796e62c8b2a 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -778,6 +778,19 @@ static void rna_Space_show_region_toolbar_update(bContext *C, PointerRNA *ptr) rna_Space_bool_from_region_flag_update_by_type(C, ptr, RGN_TYPE_TOOLS, RGN_FLAG_HIDDEN); } +static bool rna_Space_show_region_tool_props_get(PointerRNA *ptr) +{ + return !rna_Space_bool_from_region_flag_get_by_type(ptr, RGN_TYPE_TOOL_PROPS, RGN_FLAG_HIDDEN); +} +static void rna_Space_show_region_tool_props_set(PointerRNA *ptr, bool value) +{ + rna_Space_bool_from_region_flag_set_by_type(ptr, RGN_TYPE_TOOL_PROPS, RGN_FLAG_HIDDEN, !value); +} +static void rna_Space_show_region_tool_props_update(bContext *C, PointerRNA *ptr) +{ + rna_Space_bool_from_region_flag_update_by_type(C, ptr, RGN_TYPE_TOOL_PROPS, RGN_FLAG_HIDDEN); +} + /* Channels Region. */ static bool rna_Space_show_region_channels_get(PointerRNA *ptr) { @@ -3196,6 +3209,10 @@ static void rna_def_space_generic_show_region_toggles(StructRNA *srna, int regio region_type_mask &= ~(1 << RGN_TYPE_TOOLS); DEF_SHOW_REGION_PROPERTY(show_region_toolbar, "Toolbar", ""); } + if (region_type_mask & (1 << RGN_TYPE_TOOL_PROPS)) { + region_type_mask &= ~(1 << RGN_TYPE_TOOL_PROPS); + DEF_SHOW_REGION_PROPERTY(show_region_tool_props, "Toolbar", ""); + } if (region_type_mask & (1 << RGN_TYPE_CHANNELS)) { region_type_mask &= ~(1 << RGN_TYPE_CHANNELS); DEF_SHOW_REGION_PROPERTY(show_region_channels, "Channels", ""); @@ -6546,7 +6563,8 @@ static void rna_def_space_filebrowser(BlenderRNA *brna) RNA_def_struct_sdna(srna, "SpaceFile"); RNA_def_struct_ui_text(srna, "Space File Browser", "File browser space data"); - rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_TOOLS) | (1 << RGN_TYPE_UI)); + rna_def_space_generic_show_region_toggles( + srna, (1 << RGN_TYPE_TOOLS) | (1 << RGN_TYPE_UI) | (1 << RGN_TYPE_TOOL_PROPS)); prop = RNA_def_property(srna, "browse_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_space_file_browse_mode_items); -- cgit v1.2.3 From ceb049133c6fe7e66e67227c5f8fde311e69aa14 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 2 Aug 2021 16:50:04 +0200 Subject: Asset Browser: Don't show inapplicable filter and display settings So far the Asset Browser just showed the same popups and settings as the File Browser. Not all of them made sense for the Asset Browser though. * Don't show inapplicable recursion toggle. * Don't show sorting options, which don't work with assets anyway. * Don't show the Filter popover, there are currently no applicable items in there. Just like in D12057, I decided to add separate classes for the Asset Browser case. Differential Revision: https://developer.blender.org/D12059 --- release/scripts/startup/bl_ui/space_filebrowser.py | 59 ++++++++++++++-------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 570d8b42821..81a71ea0de2 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -52,18 +52,10 @@ class FILEBROWSER_HT_header(Header): layout.prop_with_popover( params, "display_type", - panel="FILEBROWSER_PT_display", + panel="ASSETBROWSER_PT_display", text="", icon_only=True, ) - layout.prop_with_popover( - params, - "display_type", - panel="FILEBROWSER_PT_filter", - text="", - icon='FILTER', - icon_only=True, - ) layout.prop(params, "filter_search", text="", icon='VIEWZOOM') @@ -96,16 +88,24 @@ class FILEBROWSER_HT_header(Header): layout.template_running_jobs() -class FILEBROWSER_PT_display(Panel): +class FileBrowserPanel: bl_space_type = 'FILE_BROWSER' - bl_region_type = 'HEADER' - bl_label = "Display Settings" # Shows as tooltip in popover - bl_ui_units_x = 10 @classmethod def poll(cls, context): + space_data = context.space_data + # can be None when save/reload with a file selector open - return context.space_data.params is not None + if space_data.params is None: + return False + + return space_data and space_data.type == 'FILE_BROWSER' and space_data.browse_mode == 'FILES' + + +class FILEBROWSER_PT_display(FileBrowserPanel, Panel): + bl_region_type = 'HEADER' + bl_label = "Display Settings" # Shows as tooltip in popover + bl_ui_units_x = 10 def draw(self, context): layout = self.layout @@ -129,17 +129,11 @@ class FILEBROWSER_PT_display(Panel): layout.prop(params, "use_sort_invert") -class FILEBROWSER_PT_filter(Panel): - bl_space_type = 'FILE_BROWSER' +class FILEBROWSER_PT_filter(FileBrowserPanel, Panel): bl_region_type = 'HEADER' bl_label = "Filter Settings" # Shows as tooltip in popover bl_ui_units_x = 8 - @classmethod - def poll(cls, context): - # can be None when save/reload with a file selector open - return context.space_data.params is not None - def draw(self, context): layout = self.layout @@ -579,6 +573,28 @@ class FILEBROWSER_MT_context_menu(Menu): layout.prop_menu_enum(params, "sort_method") +class ASSETBROWSER_PT_display(asset_utils.AssetBrowserPanel, Panel): + bl_region_type = 'HEADER' + bl_label = "Display Settings" # Shows as tooltip in popover + bl_ui_units_x = 10 + + def draw(self, context): + layout = self.layout + + space = context.space_data + params = space.params + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + if params.display_type == 'THUMBNAIL': + layout.prop(params, "display_size", text="Size") + else: + col = layout.column(heading="Columns", align=True) + col.prop(params, "show_details_size", text="Size") + col.prop(params, "show_details_datetime", text="Date") + + class AssetBrowserMenu: @classmethod def poll(cls, context): @@ -755,6 +771,7 @@ classes = ( FILEBROWSER_MT_view, FILEBROWSER_MT_select, FILEBROWSER_MT_context_menu, + ASSETBROWSER_PT_display, ASSETBROWSER_MT_editor_menus, ASSETBROWSER_MT_view, ASSETBROWSER_MT_select, -- cgit v1.2.3 From 3ff5d8f719f592c2ea17533532708e883a8baa96 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 2 Aug 2021 16:59:10 +0200 Subject: Asset Browser: Proper context menu for assets Add a context menu dedicated to asset operations to the Asset Browser. There are two separate context menus to keep things separated well and avoid confusing if-else logic (similar to D12057 & D12059). Their polls make sure they are displayed for the right contexts only. Also (to be committed as followup cleanup): Remove now unused special handling for assets in file delete operator. Differential Revision: https://developer.blender.org/D12062 --- .../keyconfig/keymap_data/blender_default.py | 1 + .../keymap_data/industry_compatible_data.py | 1 + release/scripts/startup/bl_ui/space_filebrowser.py | 35 ++++++++++++++++++---- source/blender/editors/space_file/file_ops.c | 18 +++-------- 4 files changed, 35 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 3527e993173..a37577e898b 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2000,6 +2000,7 @@ def km_file_browser(params): ("file.filenum", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, {"properties": [("increment", -100)]}), *_template_items_context_menu("FILEBROWSER_MT_context_menu", params.context_menu_event), + *_template_items_context_menu("ASSETBROWSER_MT_context_menu", params.context_menu_event), ]) 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 714126903d8..9d34b110afe 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -1249,6 +1249,7 @@ def km_file_browser(params): ("file.filenum", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, {"properties": [("increment", -100)]}), *_template_items_context_menu("FILEBROWSER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("ASSETBROWSER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ]) return keymap diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 81a71ea0de2..44631e368a3 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -296,7 +296,7 @@ class FILEBROWSER_MT_bookmarks_context_menu(Menu): text="Move to Bottom").direction = 'BOTTOM' -class FILEBROWSER_PT_bookmarks_favorites(Panel): +class FILEBROWSER_PT_bookmarks_favorites(FileBrowserPanel, Panel): bl_space_type = 'FILE_BROWSER' bl_region_type = 'TOOLS' bl_category = "Bookmarks" @@ -526,7 +526,7 @@ class FILEBROWSER_MT_select(FileBrowserMenu, Menu): layout.operator("file.select_box") -class FILEBROWSER_MT_context_menu(Menu): +class FILEBROWSER_MT_context_menu(FileBrowserMenu, Menu): bl_label = "Files Context Menu" def draw(self, context): @@ -553,10 +553,6 @@ class FILEBROWSER_MT_context_menu(Menu): sub.operator_context = 'EXEC_DEFAULT' sub.operator("file.delete", text="Delete") - active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context) - if active_asset: - layout.operator("asset.open_containing_blend_file") - layout.separator() sub = layout.row() @@ -755,6 +751,32 @@ class ASSETBROWSER_UL_metadata_tags(UIList): row.prop(tag, "name", text="", emboss=False, icon_value=icon) +class ASSETBROWSER_MT_context_menu(AssetBrowserMenu, Menu): + bl_label = "Assets Context Menu" + + def draw(self, context): + layout = self.layout + st = context.space_data + params = st.params + + layout.operator("file.refresh", text="Refresh") + + layout.separator() + + sub = layout.row() + sub.operator_context = 'EXEC_DEFAULT' + sub.operator("asset.clear", text="Clear Asset") + + layout.separator() + + layout.operator("asset.open_containing_blend_file") + + layout.separator() + + if params.display_type == 'THUMBNAIL': + layout.prop_menu_enum(params, "display_size") + + classes = ( FILEBROWSER_HT_header, FILEBROWSER_PT_display, @@ -781,6 +803,7 @@ classes = ( ASSETBROWSER_PT_metadata_details, ASSETBROWSER_PT_metadata_tags, ASSETBROWSER_UL_metadata_tags, + ASSETBROWSER_MT_context_menu, ) if __name__ == "__main__": # only for live edit. diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 616e7fe51db..944eb9988fa 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -2830,20 +2830,10 @@ static bool file_delete_single(const FileSelectParams *params, FileDirEntry *file, const char **r_error_message) { - if (file->typeflag & FILE_TYPE_ASSET) { - ID *id = filelist_file_get_id(file); - if (!id) { - *r_error_message = "File is not a local data-block asset."; - return false; - } - ED_asset_clear_id(id); - } - else { - char str[FILE_MAX]; - BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath); - if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) { - return false; - } + char str[FILE_MAX]; + BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath); + if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) { + return false; } return true; -- cgit v1.2.3 From 1062649b5e686a128c27552f40a912d1ab28e55d Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 22 Jul 2021 23:09:20 +0200 Subject: Fix T87041: Driver Editor not updated in realtime Caused by {rBbbb2e0614fc3} Since above commit only the playhead is updated as an overlay in animation playback (was moved out of drawing of the main region for perfomance reasons). The driver value "debug" visualization is very useful to have during playback though but was left in main region drawing as part of `draw_fcurve` (thus does not update in realtime anymore). Moving `graph_draw_driver_debug` into the overlay is not feasible because it requires animation filtering which has significant overhead which needs to be avoided in the overlay which is redrawn on every UI interaction. Now tag the whole main region for updates in the Driver Editor during playback instead (which will make the Drivers Editor as slow during playback as before rBbbb2e0614fc3 -- but with realtime updates of the debug visualization). Maniphest Tasks: T87041 Differential Revision: https://developer.blender.org/D12003 --- source/blender/editors/screen/screen_ops.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 9534be3509b..f172e22ea56 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4488,10 +4488,8 @@ static bool match_region_with_redraws(const ScrArea *area, return false; } -static void screen_animation_region_tag_redraw(ScrArea *area, - ARegion *region, - const Scene *scene, - eScreen_Redraws_Flag redraws) +static void screen_animation_region_tag_redraw( + bContext *C, ScrArea *area, ARegion *region, const Scene *scene, eScreen_Redraws_Flag redraws) { /* Do follow time here if editor type supports it */ if ((redraws & TIME_FOLLOW) && @@ -4515,10 +4513,29 @@ static void screen_animation_region_tag_redraw(ScrArea *area, * We do need to redraw when this area is in full screen as no other areas * will be tagged for redrawing. */ if (region->regiontype == RGN_TYPE_WINDOW && !area->full) { - if (ELEM(area->spacetype, SPACE_GRAPH, SPACE_NLA, SPACE_ACTION)) { + if (ELEM(area->spacetype, SPACE_NLA, SPACE_ACTION)) { return; } + /* Drivers Editor needs a full redraw on playback for graph_draw_driver_debug(). + * This will make it slower than regular graph editor during playback, but drawing this in + * graph_main_region_draw_overlay() is not feasible because it requires animation filtering + * which has significant overhead which needs to be avoided in the overlay which is redrawn on + * every UI interaction. */ + if (area->spacetype == SPACE_GRAPH) { + const SpaceGraph *sipo = area->spacedata.first; + if (sipo->mode != SIPO_MODE_DRIVERS) { + return; + } + bAnimContext ac; + if (ANIM_animdata_get_context(C, &ac) == false) { + return; + } + if (ac.datatype != ANIMCONT_DRIVERS) { + return; + } + } + if (area->spacetype == SPACE_SEQ) { const SpaceSeq *sseq = area->spacedata.first; if (!ED_space_sequencer_has_playback_animation(sseq, scene)) { @@ -4712,7 +4729,7 @@ static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), con } if (redraw) { - screen_animation_region_tag_redraw(area, region, scene, sad->redraws); + screen_animation_region_tag_redraw(C, area, region, scene, sad->redraws); } } } -- cgit v1.2.3 From eccd8af828ea9dea434905e0ed1c5348f77fbfd5 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 2 Aug 2021 17:15:53 +0200 Subject: Revert "GHOST/X11: enable EGL" This is causing issues for some users launching Blender, because EGL indirectly requires GLVND, which is not installed by default on e.g. Ubuntu. This reverts commit 0b18a618b88b22663e05eca0f4d976875710e7cc. Fixes T90374 Ref D12034 --- CMakeLists.txt | 31 ++++++++++---------------- build_files/build_environment/install_deps.sh | 13 +++++++++++ build_files/cmake/platform/platform_unix.cmake | 2 ++ 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5072977215d..6c6408bee2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -251,6 +251,16 @@ if(WITH_GHOST_X11) option(WITH_X11_ALPHA "Enable X11 transparent background" ON) endif() +if(UNIX AND NOT APPLE) + option(WITH_SYSTEM_GLEW "Use GLEW OpenGL wrapper library provided by the operating system" OFF) + option(WITH_SYSTEM_GLES "Use OpenGL ES library provided by the operating system" ON) +else() + # not an option for other OS's + set(WITH_SYSTEM_GLEW OFF) + set(WITH_SYSTEM_GLES OFF) +endif() + + if(UNIX AND NOT APPLE) option(WITH_SYSTEM_EIGEN3 "Use the systems Eigen3 library" OFF) endif() @@ -475,32 +485,15 @@ endif() # OpenGL -if(UNIX AND NOT APPLE) - # GLEW can only built with either GLX or EGL support and most binary - # distributions are built with GLX support. So we always compile GLEW - # with EGL support manually, and the options are no longer available. - set(WITH_SYSTEM_GLEW OFF) - set(WITH_SYSTEM_GLES ON) - - # Always use EGL instead of GLX, for X11, Wayland and headless. - set(WITH_GL_EGL ON) -else() - # System GLEW and GLES were never an option on other platforms. - set(WITH_SYSTEM_GLEW OFF) - set(WITH_SYSTEM_GLES OFF) - - # Experimental EGL option. - option(WITH_GL_EGL "Use the EGL OpenGL system library instead of the platform specific OpenGL system library (CGL or WGL)" OFF) - mark_as_advanced(WITH_GL_EGL) -endif() - option(WITH_OPENGL "When off limits visibility of the opengl headers to just bf_gpu and gawain (temporary option for development purposes)" ON) option(WITH_GLEW_ES "Switches to experimental copy of GLEW that has support for OpenGL ES. (temporary option for development purposes)" OFF) +option(WITH_GL_EGL "Use the EGL OpenGL system library instead of the platform specific OpenGL system library (CGL, glX, or WGL)" OFF) option(WITH_GL_PROFILE_ES20 "Support using OpenGL ES 2.0. (through either EGL or the AGL/WGL/XGL 'es20' profile)" OFF) mark_as_advanced( WITH_OPENGL WITH_GLEW_ES + WITH_GL_EGL WITH_GL_PROFILE_ES20 ) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index fd4f59fcda7..26281887e1f 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -603,6 +603,9 @@ MP3LAME_DEV="" OPENJPEG_USE=false OPENJPEG_DEV="" +# Whether to use system GLEW or not (OpenSubDiv needs recent glew to work). +NO_SYSTEM_GLEW=false + # Switch to english language, else some things (like check_package_DEB()) won't work! LANG_BACK=$LANG LANG="" @@ -3982,9 +3985,13 @@ install_DEB() { version_ge $_glew "1.7.0" if [ $? -eq 1 ]; then WARNING "OpenSubdiv disabled because GLEW-$_glew is not enough" + WARNING "Blender will not use system GLEW library" OSD_SKIP=true + NO_SYSTEM_GLEW=true else WARNING "OpenSubdiv will compile with GLEW-$_glew but with limited capability" + WARNING "Blender will not use system GLEW library" + NO_SYSTEM_GLEW=true fi fi @@ -5955,6 +5962,12 @@ print_info() { fi fi + if [ "$NO_SYSTEM_GLEW" = true ]; then + _1="-D WITH_SYSTEM_GLEW=OFF" + PRINT " $_1" + _buildargs="$_buildargs $_1" + fi + if [ "$FFMPEG_SKIP" = false ]; then _1="-D WITH_CODEC_FFMPEG=ON" _2="-D FFMPEG_LIBRARIES='avformat;avcodec;avutil;avdevice;swscale;swresample;lzma;rt;`print_info_ffmpeglink`'" diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index ffdbbc3f8c5..7f62399ac4f 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -581,6 +581,8 @@ if(WITH_GHOST_WAYLAND) pkg_check_modules(wayland-cursor REQUIRED wayland-cursor) pkg_check_modules(dbus REQUIRED dbus-1) + set(WITH_GL_EGL ON) + list(APPEND PLATFORM_LINKLIBS ${wayland-client_LINK_LIBRARIES} ${wayland-egl_LINK_LIBRARIES} -- cgit v1.2.3 From 77187718e4fb51ad8b1e486d0c09fcb0998afcd6 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 30 Jul 2021 08:34:24 +0200 Subject: Fix T78469: Output Metadata: Strip Name no longer accessible Caused by rB7fc60bff14a6. This has actually been reported and closed, but that was clearly a misunderstanding (above commit changed a checkbox to be an enum, but a second checkbox was simply removed) Maniphest Tasks: T78469 Differential Revision: https://developer.blender.org/D12084 --- release/scripts/startup/bl_ui/properties_output.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/release/scripts/startup/bl_ui/properties_output.py b/release/scripts/startup/bl_ui/properties_output.py index 75c1f69f84f..0c1a26ceec1 100644 --- a/release/scripts/startup/bl_ui/properties_output.py +++ b/release/scripts/startup/bl_ui/properties_output.py @@ -205,6 +205,9 @@ class RENDER_PT_stamp(RenderOutputButtonsPanel, Panel): col.prop(rd, "use_stamp_marker", text="Marker") col.prop(rd, "use_stamp_filename", text="Filename") + if rd.use_sequencer: + col.prop(rd, "use_stamp_sequencer_strip", text="Strip Name") + class RENDER_PT_stamp_note(RenderOutputButtonsPanel, Panel): bl_label = "Note" -- cgit v1.2.3 From 67d56eb71ef1b8da62d296290a87e7129f4c5ac6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 2 Aug 2021 12:26:28 -0400 Subject: Cleanup: Remove unused/unecessary OpenVDB C API This commit uses OpenVDB more directly for the voxel remesher, without the extra indirection of copying to a Blender API. This makes the code simpler, shorter, and easier to understand (though I didn't observe any performance improvement). This also removes the rest of the unused and undocumented OpenVDB C API, which was written when Blender's code didn't really use C++, and doesn't serve a purpose anymore. Those features will be implemented as nodes in the future anyway (see D12100). Differential Revision: https://developer.blender.org/D12097 --- intern/openvdb/CMakeLists.txt | 8 - intern/openvdb/intern/openvdb_level_set.cc | 173 --------------------- intern/openvdb/intern/openvdb_level_set.h | 60 ------- intern/openvdb/intern/openvdb_transform.cc | 43 ----- intern/openvdb/intern/openvdb_transform.h | 37 ----- intern/openvdb/openvdb_capi.cc | 144 +---------------- intern/openvdb/openvdb_capi.h | 116 -------------- intern/openvdb/openvdb_util.cc | 36 ----- intern/openvdb/openvdb_util.h | 51 ------ source/blender/blenkernel/BKE_mesh_remesh_voxel.h | 4 - .../blender/blenkernel/intern/mesh_remesh_voxel.cc | 126 +++++++-------- 11 files changed, 58 insertions(+), 740 deletions(-) delete mode 100644 intern/openvdb/intern/openvdb_level_set.cc delete mode 100644 intern/openvdb/intern/openvdb_level_set.h delete mode 100644 intern/openvdb/intern/openvdb_transform.cc delete mode 100644 intern/openvdb/intern/openvdb_transform.h delete mode 100644 intern/openvdb/openvdb_util.cc delete mode 100644 intern/openvdb/openvdb_util.h diff --git a/intern/openvdb/CMakeLists.txt b/intern/openvdb/CMakeLists.txt index d695e5533c0..9d37c6cd24c 100644 --- a/intern/openvdb/CMakeLists.txt +++ b/intern/openvdb/CMakeLists.txt @@ -20,7 +20,6 @@ set(INC . - intern ../guardedalloc ) @@ -42,14 +41,7 @@ if(WITH_OPENVDB) ) list(APPEND SRC - intern/openvdb_level_set.cc - intern/openvdb_transform.cc openvdb_capi.cc - openvdb_util.cc - - intern/openvdb_level_set.h - intern/openvdb_transform.h - openvdb_util.h ) list(APPEND LIB diff --git a/intern/openvdb/intern/openvdb_level_set.cc b/intern/openvdb/intern/openvdb_level_set.cc deleted file mode 100644 index 5b01c3b0cb7..00000000000 --- a/intern/openvdb/intern/openvdb_level_set.cc +++ /dev/null @@ -1,173 +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) 2015 Blender Foundation. - * All rights reserved. - */ - -#include "openvdb_level_set.h" -#include "MEM_guardedalloc.h" -#include "openvdb/tools/Composite.h" -#include "openvdb_capi.h" -#include "openvdb_util.h" - -OpenVDBLevelSet::OpenVDBLevelSet() -{ - openvdb::initialize(); -} - -OpenVDBLevelSet::~OpenVDBLevelSet() -{ -} - -void OpenVDBLevelSet::mesh_to_level_set(const float *vertices, - const int *faces, - const int totvertices, - const int totfaces, - const openvdb::math::Transform::Ptr &xform) -{ - std::vector points(totvertices); - std::vector triangles(totfaces); - std::vector quads; - - for (int i = 0; i < totvertices; i++) { - points[i] = openvdb::Vec3s(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]); - } - - for (int i = 0; i < totfaces; i++) { - triangles[i] = openvdb::Vec3I(faces[i * 3], faces[i * 3 + 1], faces[i * 3 + 2]); - } - - this->grid = openvdb::tools::meshToLevelSet( - *xform, points, triangles, quads, 1); -} - -void OpenVDBLevelSet::volume_to_mesh(OpenVDBVolumeToMeshData *mesh, - const double isovalue, - const double adaptivity, - const bool relax_disoriented_triangles) -{ - std::vector out_points; - std::vector out_quads; - std::vector out_tris; - openvdb::tools::volumeToMesh(*this->grid, - out_points, - out_tris, - out_quads, - isovalue, - adaptivity, - relax_disoriented_triangles); - mesh->vertices = (float *)MEM_malloc_arrayN(out_points.size(), sizeof(float[3]), __func__); - mesh->quads = (int *)MEM_malloc_arrayN(out_quads.size(), sizeof(int[4]), __func__); - mesh->triangles = NULL; - if (out_tris.size() > 0) { - mesh->triangles = (int *)MEM_malloc_arrayN(out_tris.size(), sizeof(int[3]), __func__); - } - - mesh->totvertices = out_points.size(); - mesh->tottriangles = out_tris.size(); - mesh->totquads = out_quads.size(); - - for (size_t i = 0; i < out_points.size(); i++) { - mesh->vertices[i * 3] = out_points[i].x(); - mesh->vertices[i * 3 + 1] = out_points[i].y(); - mesh->vertices[i * 3 + 2] = out_points[i].z(); - } - - for (size_t i = 0; i < out_quads.size(); i++) { - mesh->quads[i * 4] = out_quads[i].x(); - mesh->quads[i * 4 + 1] = out_quads[i].y(); - mesh->quads[i * 4 + 2] = out_quads[i].z(); - mesh->quads[i * 4 + 3] = out_quads[i].w(); - } - - for (size_t i = 0; i < out_tris.size(); i++) { - mesh->triangles[i * 3] = out_tris[i].x(); - mesh->triangles[i * 3 + 1] = out_tris[i].y(); - mesh->triangles[i * 3 + 2] = out_tris[i].z(); - } -} - -void OpenVDBLevelSet::filter(OpenVDBLevelSet_FilterType filter_type, - int width, - float distance, - OpenVDBLevelSet_FilterBias filter_bias) -{ - - if (!this->grid) { - return; - } - - if (this->grid->getGridClass() != openvdb::GRID_LEVEL_SET) { - return; - } - - openvdb::tools::LevelSetFilter filter(*this->grid); - filter.setSpatialScheme((openvdb::math::BiasedGradientScheme)filter_bias); - switch (filter_type) { - case OPENVDB_LEVELSET_FILTER_GAUSSIAN: - filter.gaussian(width); - break; - case OPENVDB_LEVELSET_FILTER_MEDIAN: - filter.median(width); - break; - case OPENVDB_LEVELSET_FILTER_MEAN: - filter.mean(width); - break; - case OPENVDB_LEVELSET_FILTER_MEAN_CURVATURE: - filter.meanCurvature(); - break; - case OPENVDB_LEVELSET_FILTER_LAPLACIAN: - filter.laplacian(); - break; - case OPENVDB_LEVELSET_FILTER_DILATE: - filter.offset(distance); - break; - case OPENVDB_LEVELSET_FILTER_ERODE: - filter.offset(distance); - break; - case OPENVDB_LEVELSET_FILTER_NONE: - break; - } -} -openvdb::FloatGrid::Ptr OpenVDBLevelSet::CSG_operation_apply( - const openvdb::FloatGrid::Ptr &gridA, - const openvdb::FloatGrid::Ptr &gridB, - OpenVDBLevelSet_CSGOperation operation) -{ - switch (operation) { - case OPENVDB_LEVELSET_CSG_UNION: - openvdb::tools::csgUnion(*gridA, *gridB); - break; - case OPENVDB_LEVELSET_CSG_DIFFERENCE: - openvdb::tools::csgDifference(*gridA, *gridB); - break; - case OPENVDB_LEVELSET_CSG_INTERSECTION: - openvdb::tools::csgIntersection(*gridA, *gridB); - break; - } - - return gridA; -} - -const openvdb::FloatGrid::Ptr &OpenVDBLevelSet::get_grid() -{ - return this->grid; -} - -void OpenVDBLevelSet::set_grid(const openvdb::FloatGrid::Ptr &grid) -{ - this->grid = grid; -} diff --git a/intern/openvdb/intern/openvdb_level_set.h b/intern/openvdb/intern/openvdb_level_set.h deleted file mode 100644 index 2c8f140c012..00000000000 --- a/intern/openvdb/intern/openvdb_level_set.h +++ /dev/null @@ -1,60 +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) 2015 Blender Foundation. - * All rights reserved. - */ - -#ifndef __OPENVDB_LEVEL_SET_H__ -#define __OPENVDB_LEVEL_SET_H__ - -#include "openvdb_capi.h" -#include -#include -#include -#include -#include -#include - -struct OpenVDBLevelSet { - private: - openvdb::FloatGrid::Ptr grid; - - public: - OpenVDBLevelSet(); - ~OpenVDBLevelSet(); - const openvdb::FloatGrid::Ptr &get_grid(); - void set_grid(const openvdb::FloatGrid::Ptr &grid); - - void mesh_to_level_set(const float *vertices, - const int *faces, - const int totvertices, - const int totfaces, - const openvdb::math::Transform::Ptr &transform); - - void volume_to_mesh(struct OpenVDBVolumeToMeshData *mesh, - const double isovalue, - const double adaptivity, - const bool relax_disoriented_triangles); - void filter(OpenVDBLevelSet_FilterType filter_type, - int width, - float distance, - OpenVDBLevelSet_FilterBias filter_bias); - openvdb::FloatGrid::Ptr CSG_operation_apply(const openvdb::FloatGrid::Ptr &gridA, - const openvdb::FloatGrid::Ptr &gridB, - OpenVDBLevelSet_CSGOperation operation); -}; - -#endif /* __OPENVDB_LEVEL_SET_H__ */ diff --git a/intern/openvdb/intern/openvdb_transform.cc b/intern/openvdb/intern/openvdb_transform.cc deleted file mode 100644 index 4bfcf43f81a..00000000000 --- a/intern/openvdb/intern/openvdb_transform.cc +++ /dev/null @@ -1,43 +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) 2015 Blender Foundation. - * All rights reserved. - */ - -#include "openvdb_transform.h" - -OpenVDBTransform::OpenVDBTransform() -{ -} - -OpenVDBTransform::~OpenVDBTransform() -{ -} - -void OpenVDBTransform::create_linear_transform(double voxel_size) -{ - this->transform = openvdb::math::Transform::createLinearTransform(voxel_size); -} - -const openvdb::math::Transform::Ptr &OpenVDBTransform::get_transform() -{ - return this->transform; -} - -void OpenVDBTransform::set_transform(const openvdb::math::Transform::Ptr &transform) -{ - this->transform = transform; -} diff --git a/intern/openvdb/intern/openvdb_transform.h b/intern/openvdb/intern/openvdb_transform.h deleted file mode 100644 index e2528cd0192..00000000000 --- a/intern/openvdb/intern/openvdb_transform.h +++ /dev/null @@ -1,37 +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) 2015 Blender Foundation. - * All rights reserved. - */ - -#ifndef OPENVDB_TRANSFORM_H -#define OPENVDB_TRANSFORM_H - -#include - -struct OpenVDBTransform { - private: - openvdb::math::Transform::Ptr transform; - - public: - OpenVDBTransform(); - ~OpenVDBTransform(); - void create_linear_transform(double voxel_size); - const openvdb::math::Transform::Ptr &get_transform(); - void set_transform(const openvdb::math::Transform::Ptr &transform); -}; - -#endif // OPENVDB_TRANSFORM_H diff --git a/intern/openvdb/openvdb_capi.cc b/intern/openvdb/openvdb_capi.cc index 674b394fa46..9c676e33ecc 100644 --- a/intern/openvdb/openvdb_capi.cc +++ b/intern/openvdb/openvdb_capi.cc @@ -18,151 +18,9 @@ */ #include "openvdb_capi.h" -#include "openvdb_level_set.h" -#include "openvdb_transform.h" -#include "openvdb_util.h" +#include int OpenVDB_getVersionHex() { return openvdb::OPENVDB_LIBRARY_VERSION; } - -OpenVDBLevelSet *OpenVDBLevelSet_create(bool initGrid, OpenVDBTransform *xform) -{ - OpenVDBLevelSet *level_set = new OpenVDBLevelSet(); - if (initGrid) { - openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(); - grid->setGridClass(openvdb::GRID_LEVEL_SET); - if (xform) { - grid->setTransform(xform->get_transform()); - } - level_set->set_grid(grid); - } - return level_set; -} - -OpenVDBTransform *OpenVDBTransform_create() -{ - return new OpenVDBTransform(); -} - -void OpenVDBTransform_free(OpenVDBTransform *transform) -{ - delete transform; -} - -void OpenVDBTransform_create_linear_transform(OpenVDBTransform *transform, double voxel_size) -{ - transform->create_linear_transform(voxel_size); -} - -void OpenVDBLevelSet_free(OpenVDBLevelSet *level_set) -{ - delete level_set; -} - -void OpenVDBLevelSet_mesh_to_level_set(struct OpenVDBLevelSet *level_set, - const float *vertices, - const int *faces, - const int totvertices, - const int totfaces, - OpenVDBTransform *xform) -{ - level_set->mesh_to_level_set(vertices, faces, totvertices, totfaces, xform->get_transform()); -} - -void OpenVDBLevelSet_mesh_to_level_set_transform(struct OpenVDBLevelSet *level_set, - const float *vertices, - const int *faces, - const int totvertices, - const int totfaces, - OpenVDBTransform *transform) -{ - level_set->mesh_to_level_set(vertices, faces, totvertices, totfaces, transform->get_transform()); -} - -void OpenVDBLevelSet_volume_to_mesh(struct OpenVDBLevelSet *level_set, - struct OpenVDBVolumeToMeshData *mesh, - const double isovalue, - const double adaptivity, - const bool relax_disoriented_triangles) -{ - level_set->volume_to_mesh(mesh, isovalue, adaptivity, relax_disoriented_triangles); -} - -void OpenVDBLevelSet_filter(struct OpenVDBLevelSet *level_set, - OpenVDBLevelSet_FilterType filter_type, - int width, - float distance, - OpenVDBLevelSet_FilterBias bias) -{ - level_set->filter(filter_type, width, distance, bias); -} - -void OpenVDBLevelSet_CSG_operation(struct OpenVDBLevelSet *out, - struct OpenVDBLevelSet *gridA, - struct OpenVDBLevelSet *gridB, - OpenVDBLevelSet_CSGOperation operation) -{ - openvdb::FloatGrid::Ptr grid = out->CSG_operation_apply( - gridA->get_grid(), gridB->get_grid(), operation); - out->set_grid(grid); -} - -OpenVDBLevelSet *OpenVDBLevelSet_transform_and_resample(struct OpenVDBLevelSet *level_setA, - struct OpenVDBLevelSet *level_setB, - char sampler, - float isolevel) -{ - openvdb::FloatGrid::Ptr sourceGrid = level_setA->get_grid(); - openvdb::FloatGrid::Ptr targetGrid = level_setB->get_grid()->deepCopy(); - - const openvdb::math::Transform &sourceXform = sourceGrid->transform(), - &targetXform = targetGrid->transform(); - - // Compute a source grid to target grid transform. - // (For this example, we assume that both grids' transforms are linear, - // so that they can be represented as 4 x 4 matrices.) - openvdb::Mat4R xform = sourceXform.baseMap()->getAffineMap()->getMat4() * - targetXform.baseMap()->getAffineMap()->getMat4().inverse(); - - // Create the transformer. - openvdb::tools::GridTransformer transformer(xform); - - switch (sampler) { - case OPENVDB_LEVELSET_GRIDSAMPLER_POINT: - // Resample using nearest-neighbor interpolation. - transformer.transformGrid(*sourceGrid, - *targetGrid); - // Prune the target tree for optimal sparsity. - targetGrid->tree().prune(); - break; - - case OPENVDB_LEVELSET_GRIDSAMPLER_BOX: - // Resample using trilinear interpolation. - transformer.transformGrid(*sourceGrid, - *targetGrid); - // Prune the target tree for optimal sparsity. - targetGrid->tree().prune(); - break; - - case OPENVDB_LEVELSET_GRIDSAMPLER_QUADRATIC: - // Resample using triquadratic interpolation. - transformer.transformGrid(*sourceGrid, - *targetGrid); - // Prune the target tree for optimal sparsity. - targetGrid->tree().prune(); - break; - - case OPENVDB_LEVELSET_GRIDSAMPLER_NONE: - break; - } - - targetGrid = openvdb::tools::levelSetRebuild(*targetGrid, isolevel, 1.0f); - openvdb::tools::pruneLevelSet(targetGrid->tree()); - - OpenVDBLevelSet *level_set = OpenVDBLevelSet_create(false, NULL); - level_set->set_grid(targetGrid); - - return level_set; -} diff --git a/intern/openvdb/openvdb_capi.h b/intern/openvdb/openvdb_capi.h index 9333413c2fe..3db1ef9484b 100644 --- a/intern/openvdb/openvdb_capi.h +++ b/intern/openvdb/openvdb_capi.h @@ -24,124 +24,8 @@ extern "C" { #endif -/* Level Set Filters */ -typedef enum OpenVDBLevelSet_FilterType { - OPENVDB_LEVELSET_FILTER_NONE = 0, - OPENVDB_LEVELSET_FILTER_GAUSSIAN = 1, - OPENVDB_LEVELSET_FILTER_MEAN = 2, - OPENVDB_LEVELSET_FILTER_MEDIAN = 3, - OPENVDB_LEVELSET_FILTER_MEAN_CURVATURE = 4, - OPENVDB_LEVELSET_FILTER_LAPLACIAN = 5, - OPENVDB_LEVELSET_FILTER_DILATE = 6, - OPENVDB_LEVELSET_FILTER_ERODE = 7, -} OpenVDBLevelSet_FilterType; - -typedef enum OpenVDBLevelSet_FilterBias { - OPENVDB_LEVELSET_FIRST_BIAS = 0, - OPENVDB_LEVELSET_SECOND_BIAS, - OPENVDB_LEVELSET_THIRD_BIAS, - OPENVDB_LEVELSET_WENO5_BIAS, - OPENVDB_LEVELSET_HJWENO5_BIAS, -} OpenVDBLevelSet_FilterBias; - -/* Level Set CSG Operations */ -typedef enum OpenVDBLevelSet_CSGOperation { - OPENVDB_LEVELSET_CSG_UNION = 0, - OPENVDB_LEVELSET_CSG_DIFFERENCE = 1, - OPENVDB_LEVELSET_CSG_INTERSECTION = 2, -} OpenVDBLevelSet_CSGOperation; - -typedef enum OpenVDBLevelSet_GridSampler { - OPENVDB_LEVELSET_GRIDSAMPLER_NONE = 0, - OPENVDB_LEVELSET_GRIDSAMPLER_POINT = 1, - OPENVDB_LEVELSET_GRIDSAMPLER_BOX = 2, - OPENVDB_LEVELSET_GRIDSAMPLER_QUADRATIC = 3, -} OpenVDBLevelSet_Gridsampler; - -struct OpenVDBTransform; -struct OpenVDBLevelSet; - -struct OpenVDBVolumeToMeshData { - int tottriangles; - int totquads; - int totvertices; - - float *vertices; - int *quads; - int *triangles; -}; - -struct OpenVDBRemeshData { - float *verts; - int *faces; - int totfaces; - int totverts; - - float *out_verts; - int *out_faces; - int *out_tris; - int out_totverts; - int out_totfaces; - int out_tottris; - int filter_type; - enum OpenVDBLevelSet_FilterType filter_bias; - enum OpenVDBLevelSet_FilterBias filter_width; /* Parameter for gaussian, median, mean. */ - - float voxel_size; - float isovalue; - float adaptivity; - int relax_disoriented_triangles; -}; - int OpenVDB_getVersionHex(void); -enum { - VEC_INVARIANT = 0, - VEC_COVARIANT = 1, - VEC_COVARIANT_NORMALIZE = 2, - VEC_CONTRAVARIANT_RELATIVE = 3, - VEC_CONTRAVARIANT_ABSOLUTE = 4, -}; - -struct OpenVDBTransform *OpenVDBTransform_create(void); -void OpenVDBTransform_free(struct OpenVDBTransform *transform); -void OpenVDBTransform_create_linear_transform(struct OpenVDBTransform *transform, - double voxel_size); - -struct OpenVDBLevelSet *OpenVDBLevelSet_create(bool initGrid, struct OpenVDBTransform *xform); -void OpenVDBLevelSet_free(struct OpenVDBLevelSet *level_set); -void OpenVDBLevelSet_mesh_to_level_set(struct OpenVDBLevelSet *level_set, - const float *vertices, - const int *faces, - const int totvertices, - const int totfaces, - struct OpenVDBTransform *xform); -void OpenVDBLevelSet_mesh_to_level_set_transform(struct OpenVDBLevelSet *level_set, - const float *vertices, - const int *faces, - const int totvertices, - const int totfaces, - struct OpenVDBTransform *transform); -void OpenVDBLevelSet_volume_to_mesh(struct OpenVDBLevelSet *level_set, - struct OpenVDBVolumeToMeshData *mesh, - const double isovalue, - const double adaptivity, - const bool relax_disoriented_triangles); -void OpenVDBLevelSet_filter(struct OpenVDBLevelSet *level_set, - OpenVDBLevelSet_FilterType filter_type, - int width, - float distance, - OpenVDBLevelSet_FilterBias bias); -void OpenVDBLevelSet_CSG_operation(struct OpenVDBLevelSet *out, - struct OpenVDBLevelSet *gridA, - struct OpenVDBLevelSet *gridB, - OpenVDBLevelSet_CSGOperation operation); - -struct OpenVDBLevelSet *OpenVDBLevelSet_transform_and_resample(struct OpenVDBLevelSet *level_setA, - struct OpenVDBLevelSet *level_setB, - char sampler, - float isolevel); - #ifdef __cplusplus } #endif diff --git a/intern/openvdb/openvdb_util.cc b/intern/openvdb/openvdb_util.cc deleted file mode 100644 index 899b41ff09b..00000000000 --- a/intern/openvdb/openvdb_util.cc +++ /dev/null @@ -1,36 +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) 2015 Blender Foundation. - * All rights reserved. - */ - -#include "openvdb_util.h" - -#include - -ScopeTimer::ScopeTimer(const std::string &message) : m_message(message), m_timer() -{ -} - -ScopeTimer::~ScopeTimer() -{ -#if OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER >= 7 - double delta = m_timer.milliseconds(); -#else - double delta = m_timer.delta(); /* Deprecated in OpenVDB 7. */ -#endif - std::printf("%s: %fms\n", m_message.c_str(), delta); -} diff --git a/intern/openvdb/openvdb_util.h b/intern/openvdb/openvdb_util.h deleted file mode 100644 index 7a19d588068..00000000000 --- a/intern/openvdb/openvdb_util.h +++ /dev/null @@ -1,51 +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) 2015 Blender Foundation. - * All rights reserved. - */ - -#ifndef __OPENVDB_UTIL_H__ -#define __OPENVDB_UTIL_H__ - -#include -#include - -#define CATCH_KEYERROR \ - catch (const openvdb::KeyError &e) \ - { \ - std::cerr << e.what() << '\n'; \ - } - -//#define DEBUG_TIME - -/* A utility class which prints the time elapsed during its lifetime, useful for - * e.g. timing the overall execution time of a function */ -class ScopeTimer { - std::string m_message; - openvdb::util::CpuTimer m_timer; - - public: - ScopeTimer(const std::string &message); - ~ScopeTimer(); -}; - -#ifdef DEBUG_TIME -# define Timer(x) ScopeTimer prof(x); -#else -# define Timer(x) -#endif - -#endif /* __OPENVDB_UTIL_H__ */ diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h index 5887db59ff2..80ced9b5f57 100644 --- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h +++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h @@ -23,10 +23,6 @@ * \ingroup bke */ -#ifdef WITH_OPENVDB -# include "openvdb_capi.h" -#endif - #ifdef __cplusplus extern "C" { #endif diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index 8364b0ec024..4484dd5bf09 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -33,6 +33,7 @@ #include "BLI_array.hh" #include "BLI_float3.hh" #include "BLI_index_range.hh" +#include "BLI_span.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -48,7 +49,9 @@ #include "bmesh_tools.h" #ifdef WITH_OPENVDB -# include "openvdb_capi.h" +# include +# include +# include #endif #ifdef WITH_QUADRIFLOW @@ -58,6 +61,8 @@ using blender::Array; using blender::float3; using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; #ifdef WITH_QUADRIFLOW static Mesh *remesh_quadriflow(const Mesh *input_mesh, @@ -192,92 +197,81 @@ Mesh *BKE_mesh_remesh_quadriflow(const Mesh *mesh, } #ifdef WITH_OPENVDB -static struct OpenVDBLevelSet *remesh_voxel_level_set_create(const Mesh *mesh, - struct OpenVDBTransform *transform) +static openvdb::FloatGrid::Ptr remesh_voxel_level_set_create(const Mesh *mesh, + const float voxel_size) { - const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh); - MVertTri *verttri = (MVertTri *)MEM_callocN( - sizeof(*verttri) * BKE_mesh_runtime_looptri_len(mesh), "remesh_looptri"); - BKE_mesh_runtime_verttri_from_looptri( - verttri, mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(mesh)); + Span mloop{mesh->mloop, mesh->totloop}; + Span looptris{BKE_mesh_runtime_looptri_ensure(mesh), + BKE_mesh_runtime_looptri_len(mesh)}; - const int totfaces = BKE_mesh_runtime_looptri_len(mesh); - const int totverts = mesh->totvert; - Array verts(totverts); - Array faces(totfaces * 3); + std::vector points(mesh->totvert); + std::vector triangles(looptris.size()); - for (const int i : IndexRange(totverts)) { - verts[i] = mesh->mvert[i].co; + for (const int i : IndexRange(mesh->totvert)) { + const float3 co = mesh->mvert[i].co; + points[i] = openvdb::Vec3s(co.x, co.y, co.z); } - for (const int i : IndexRange(totfaces)) { - MVertTri &vt = verttri[i]; - faces[i * 3] = vt.tri[0]; - faces[i * 3 + 1] = vt.tri[1]; - faces[i * 3 + 2] = vt.tri[2]; + for (const int i : IndexRange(looptris.size())) { + const MLoopTri &loop_tri = looptris[i]; + triangles[i] = openvdb::Vec3I( + mloop[loop_tri.tri[0]].v, mloop[loop_tri.tri[1]].v, mloop[loop_tri.tri[2]].v); } - struct OpenVDBLevelSet *level_set = OpenVDBLevelSet_create(false, nullptr); - OpenVDBLevelSet_mesh_to_level_set( - level_set, (const float *)verts.data(), faces.data(), totverts, totfaces, transform); - - MEM_freeN(verttri); + openvdb::math::Transform::Ptr transform = openvdb::math::Transform::createLinearTransform( + voxel_size); + openvdb::FloatGrid::Ptr grid = openvdb::tools::meshToLevelSet( + *transform, points, triangles, 1.0f); - return level_set; + return grid; } -static Mesh *remesh_voxel_volume_to_mesh(struct OpenVDBLevelSet *level_set, - double isovalue, - double adaptivity, - bool relax_disoriented_triangles) +static Mesh *remesh_voxel_volume_to_mesh(const openvdb::FloatGrid::Ptr level_set_grid, + const float isovalue, + const float adaptivity, + const bool relax_disoriented_triangles) { - struct OpenVDBVolumeToMeshData output_mesh; - OpenVDBLevelSet_volume_to_mesh( - level_set, &output_mesh, isovalue, adaptivity, relax_disoriented_triangles); - - Mesh *mesh = BKE_mesh_new_nomain(output_mesh.totvertices, - 0, - 0, - (output_mesh.totquads * 4) + (output_mesh.tottriangles * 3), - output_mesh.totquads + output_mesh.tottriangles); - - for (const int i : IndexRange(output_mesh.totvertices)) { - copy_v3_v3(mesh->mvert[i].co, &output_mesh.vertices[i * 3]); + std::vector vertices; + std::vector quads; + std::vector tris; + openvdb::tools::volumeToMesh( + *level_set_grid, vertices, tris, quads, isovalue, adaptivity, relax_disoriented_triangles); + + Mesh *mesh = BKE_mesh_new_nomain( + vertices.size(), 0, 0, quads.size() * 4 + tris.size() * 3, quads.size() + tris.size()); + MutableSpan mverts{mesh->mvert, mesh->totvert}; + MutableSpan mloops{mesh->mloop, mesh->totloop}; + MutableSpan mpolys{mesh->mpoly, mesh->totpoly}; + + for (const int i : mverts.index_range()) { + copy_v3_v3(mverts[i].co, float3(vertices[i].x(), vertices[i].y(), vertices[i].z())); } - for (const int i : IndexRange(output_mesh.totquads)) { - MPoly &poly = mesh->mpoly[i]; + for (const int i : IndexRange(quads.size())) { + MPoly &poly = mpolys[i]; const int loopstart = i * 4; poly.loopstart = loopstart; poly.totloop = 4; - mesh->mloop[loopstart].v = output_mesh.quads[loopstart]; - mesh->mloop[loopstart + 1].v = output_mesh.quads[loopstart + 1]; - mesh->mloop[loopstart + 2].v = output_mesh.quads[loopstart + 2]; - mesh->mloop[loopstart + 3].v = output_mesh.quads[loopstart + 3]; + mloops[loopstart].v = quads[i][0]; + mloops[loopstart + 1].v = quads[i][3]; + mloops[loopstart + 2].v = quads[i][2]; + mloops[loopstart + 3].v = quads[i][1]; } - const int triangle_poly_start = output_mesh.totquads; - const int triangle_loop_start = output_mesh.totquads * 4; - for (const int i : IndexRange(output_mesh.tottriangles)) { - MPoly &poly = mesh->mpoly[triangle_poly_start + i]; + const int triangle_loop_start = quads.size() * 4; + for (const int i : IndexRange(tris.size())) { + MPoly &poly = mpolys[quads.size() + i]; const int loopstart = triangle_loop_start + i * 3; poly.loopstart = loopstart; poly.totloop = 3; - mesh->mloop[loopstart].v = output_mesh.triangles[i * 3 + 2]; - mesh->mloop[loopstart + 1].v = output_mesh.triangles[i * 3 + 1]; - mesh->mloop[loopstart + 2].v = output_mesh.triangles[i * 3]; + mloops[loopstart].v = tris[i][2]; + mloops[loopstart + 1].v = tris[i][1]; + mloops[loopstart + 2].v = tris[i][0]; } BKE_mesh_calc_edges(mesh, false, false); BKE_mesh_calc_normals(mesh); - MEM_freeN(output_mesh.quads); - MEM_freeN(output_mesh.vertices); - - if (output_mesh.tottriangles > 0) { - MEM_freeN(output_mesh.triangles); - } - return mesh; } #endif @@ -288,14 +282,8 @@ Mesh *BKE_mesh_remesh_voxel(const Mesh *mesh, const float isovalue) { #ifdef WITH_OPENVDB - struct OpenVDBTransform *xform = OpenVDBTransform_create(); - OpenVDBTransform_create_linear_transform(xform, (double)voxel_size); - struct OpenVDBLevelSet *level_set = remesh_voxel_level_set_create(mesh, xform); - Mesh *new_mesh = remesh_voxel_volume_to_mesh( - level_set, (double)isovalue, (double)adaptivity, false); - OpenVDBLevelSet_free(level_set); - OpenVDBTransform_free(xform); - return new_mesh; + openvdb::FloatGrid::Ptr level_set = remesh_voxel_level_set_create(mesh, voxel_size); + return remesh_voxel_volume_to_mesh(level_set, isovalue, adaptivity, false); #else UNUSED_VARS(mesh, voxel_size, adaptivity, isovalue); return nullptr; -- cgit v1.2.3 From a2203a27d9298f7e51948e825e6aea992b3ac5b2 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 2 Aug 2021 18:43:16 +0200 Subject: Fix T90356: Frame selected includes active strip Don't include active strip in this operator. This was confusing due to name of operator and inconsistent with other editors. --- source/blender/editors/space_sequencer/sequencer_view.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 337ac2e0009..c5385e24d2c 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -275,7 +275,6 @@ static int sequencer_view_selected_exec(bContext *C, wmOperator *op) View2D *v2d = UI_view2d_fromcontext(C); ARegion *region = CTX_wm_region(C); Editing *ed = SEQ_editing_get(scene, false); - Sequence *last_seq = SEQ_select_active_get(scene); Sequence *seq; rctf cur_new = v2d->cur; @@ -293,7 +292,7 @@ static int sequencer_view_selected_exec(bContext *C, wmOperator *op) } for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if ((seq->flag & SELECT) || (seq == last_seq)) { + if ((seq->flag & SELECT)) { xmin = min_ii(xmin, seq->startdisp); xmax = max_ii(xmax, seq->enddisp); -- cgit v1.2.3 From 8b93265c19feeed2b758046359ac102a1f21278a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 2 Aug 2021 13:47:32 -0400 Subject: Mesh: Tag normals dirty instead of calculating Because mesh vertex and face normals are just derived data, they can be calculated lazily instead of eagerly. Often normal calculation is a relatively expensive task, and the calculation is often redundant if the mesh is deformed afterwards anyway. Instead, normals should be calculated only when they are needed. This commit moves in that direction by adding a new function to tag a mesh's normals dirty and replacing normal calculation with it in some places. Differential Revision: https://developer.blender.org/D12107 --- source/blender/blenkernel/BKE_mesh.h | 1 + source/blender/blenkernel/intern/mesh_normals.cc | 6 ++++++ source/blender/blenkernel/intern/mesh_remesh_voxel.cc | 2 +- source/blender/blenkernel/intern/volume_to_mesh.cc | 2 +- source/blender/editors/object/object_remesh.cc | 3 ++- source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc | 6 ++---- source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc | 2 +- 9 files changed, 16 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index e3be9cd8ef8..ef1384c804b 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -280,6 +280,7 @@ 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, diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index f496d6eada1..8740baa5aef 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -63,6 +63,12 @@ static CLG_LogRef LOG = {"bke.mesh_normals"}; /** \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. */ diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index 4484dd5bf09..63b0403dcea 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -270,7 +270,7 @@ static Mesh *remesh_voxel_volume_to_mesh(const openvdb::FloatGrid::Ptr level_set } BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_calc_normals(mesh); + BKE_mesh_normals_tag_dirty(mesh); return mesh; } diff --git a/source/blender/blenkernel/intern/volume_to_mesh.cc b/source/blender/blenkernel/intern/volume_to_mesh.cc index 7ab67516242..e9d6eea4614 100644 --- a/source/blender/blenkernel/intern/volume_to_mesh.cc +++ b/source/blender/blenkernel/intern/volume_to_mesh.cc @@ -159,7 +159,7 @@ static Mesh *new_mesh_from_openvdb_data(Span verts, } BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_calc_normals(mesh); + BKE_mesh_normals_tag_dirty(mesh); return mesh; } diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index 82dbc9aaf38..d56cb3c7548 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -166,9 +166,10 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) Mesh *mesh_fixed_poles = BKE_mesh_remesh_voxel_fix_poles(new_mesh); BKE_id_free(nullptr, new_mesh); new_mesh = mesh_fixed_poles; - BKE_mesh_calc_normals(new_mesh); } + BKE_mesh_calc_normals(new_mesh); + if (mesh->flag & ME_REMESH_REPROJECT_VOLUME || mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK || mesh->flag & ME_REMESH_REPROJECT_SCULPT_FACE_SETS) { BKE_mesh_runtime_clear_geometry(mesh); 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 4286db52115..91d569282c3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -150,7 +150,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span coords) plConvexHullDelete(hull); - BKE_mesh_calc_normals(result); + BKE_mesh_normals_tag_dirty(result); return result; } 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 925ed0f8da8..d46ea2d2050 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 @@ -299,7 +299,7 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, mesh->medge[0].v1 = 0; mesh->medge[0].v2 = 1; mesh->medge[0].flag |= ME_LOOSEEDGE; - BKE_mesh_calc_normals(mesh); + BKE_mesh_normals_tag_dirty(mesh); return mesh; } @@ -534,12 +534,10 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, } } - BKE_mesh_calc_normals(mesh); + BKE_mesh_normals_tag_dirty(mesh); calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type); - BLI_assert(BKE_mesh_is_valid(mesh)); - return mesh; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index 245d7800621..165da8ec9f2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -88,7 +88,7 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) } Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); - BKE_mesh_calc_normals(mesh_out); + BKE_mesh_normals_tag_dirty(mesh_out); MeshComponent &mesh_component = geometry_set.get_component_for_write(); mesh_component.replace(mesh_out); diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index f0f032ed8f4..a2c10af9c4d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -93,7 +93,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) } Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); - BKE_mesh_calc_normals(mesh_out); + BKE_mesh_normals_tag_dirty(mesh_out); MeshComponent &mesh_component = geometry_set.get_component_for_write(); mesh_component.replace(mesh_out); -- cgit v1.2.3 From 4f6f445120bb534a36e2ee5ddd89be6793a4c35d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 2 Aug 2021 14:21:19 -0400 Subject: Cleanup: Make spline settings copy function public It will be useful in the spline type conversion node. Theoretically it could become protected again if that conversion moves out of a node, which might be a nice improvement after an initial version. --- source/blender/blenkernel/BKE_spline.hh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 53485ecbd6b..fc145f1ddf1 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -109,6 +109,7 @@ class Spline { SplinePtr copy() const; SplinePtr copy_only_settings() const; SplinePtr copy_without_attributes() const; + static void copy_base_settings(const Spline &src, Spline &dst); Spline::Type type() const; @@ -209,8 +210,6 @@ class Spline { virtual void correct_end_tangents() const = 0; virtual void copy_settings(Spline &dst) const = 0; virtual void copy_data(Spline &dst) const = 0; - - static void copy_base_settings(const Spline &src, Spline &dst); }; /** -- cgit v1.2.3 From a4813379f9ce26862d826cfc98d1f28e0bbe11d7 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 2 Aug 2021 15:43:37 -0400 Subject: Fix T90042: Spline tangent calculation assert with coincident points --- source/blender/blenkernel/intern/spline_base.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index a7caae967f6..987152f34bc 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -189,7 +189,11 @@ static float3 direction_bisect(const float3 &prev, const float3 &middle, const f const float3 dir_prev = (middle - prev).normalized(); const float3 dir_next = (next - middle).normalized(); - return (dir_prev + dir_next).normalized(); + const float3 result = (dir_prev + dir_next).normalized(); + if (UNLIKELY(result.is_zero())) { + return float3(0.0f, 0.0f, 1.0f); + } + return result; } static void calculate_tangents(Span positions, -- cgit v1.2.3 From efd7c95bb27ccee31a34de03f15ad9542d19f7da Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 2 Aug 2021 15:45:25 -0400 Subject: Cleanup: Remove duplicate special case check --- source/blender/blenkernel/intern/spline_base.cc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 987152f34bc..dda7abea0fc 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -201,6 +201,7 @@ static void calculate_tangents(Span positions, MutableSpan tangents) { if (positions.size() == 1) { + tangents.first() = float3(0.0f, 0.0f, 1.0f); return; } @@ -241,13 +242,8 @@ Span Spline::evaluated_tangents() const Span positions = this->evaluated_positions(); - if (eval_size == 1) { - evaluated_tangents_cache_.first() = float3(1.0f, 0.0f, 0.0f); - } - else { - calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_); - this->correct_end_tangents(); - } + calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_); + this->correct_end_tangents(); tangent_cache_dirty_ = false; return evaluated_tangents_cache_; -- cgit v1.2.3 From 223f0481382cc6019c79568f6136c4d59d4ec543 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Mon, 2 Aug 2021 20:14:41 -0400 Subject: Fix annotations placement option in the Movie Clip editor In rB6ee14c966d05362228511756c4906e043b87e346 the enum items were renamed/removed, this change was not propagated to this setting --- release/scripts/startup/bl_ui/properties_grease_pencil_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index de743033036..8de6925c051 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -54,7 +54,7 @@ class AnnotationDrawingToolsPanel: col.label(text="Stroke Placement:") row = col.row(align=True) row.prop_enum(tool_settings, "annotation_stroke_placement_view2d", 'VIEW') - row.prop_enum(tool_settings, "annotation_stroke_placement_view2d", 'CURSOR', text="Cursor") + row.prop_enum(tool_settings, "annotation_stroke_placement_view2d", 'IMAGE', text="Image") class GreasePencilSculptOptionsPanel: -- cgit v1.2.3 From 04c75c5ce7699a1502a7c2212d4aa57166465514 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 3 Aug 2021 12:19:31 +1000 Subject: Edit Mesh: Correct normal calculation for "Set From Faces" Setting normals from faces wasn't weighting the faces contribution by the corner angle, giving lop-sided results in some cases. This removes the epsilon check for CLNORS_VALID_VEC_LEN, in favor of matching the behavior of vertex normals exactly. --- source/blender/editors/mesh/editmesh_tools.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 41a9f426798..7e94383390e 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -9538,18 +9538,11 @@ static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op) BKE_editmesh_ensure_autosmooth(em, obedit->data); BKE_editmesh_lnorspace_update(em, obedit->data); - float(*vnors)[3] = MEM_callocN(sizeof(*vnors) * bm->totvert, __func__); - BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - BM_ITER_ELEM (v, &viter, f, BM_VERTS_OF_FACE) { - const int v_index = BM_elem_index_get(v); - add_v3_v3(vnors[v_index], f->no); - } - } - } - for (int i = 0; i < bm->totvert; i++) { - if (!is_zero_v3(vnors[i]) && normalize_v3(vnors[i]) < CLNORS_VALID_VEC_LEN) { - zero_v3(vnors[i]); + float(*vnors)[3] = MEM_mallocN(sizeof(*vnors) * bm->totvert, __func__); + { + int v_index; + BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, v_index) { + BM_vert_calc_normal_ex(v, BM_ELEM_SELECT, vnors[v_index]); } } -- cgit v1.2.3 From 1973fd89dff7e33d76e1aa71c87444505450c058 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 3 Aug 2021 15:01:39 +1000 Subject: Fix crash adding custom normals Caused by error converting this file to C++ eccdced972f42a451d0c73dfb7ce19a43c120d7f. --- source/blender/blenkernel/intern/mesh_normals.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 8740baa5aef..1438d737b79 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -987,7 +987,7 @@ void BKE_edges_sharp_from_angle_set(const struct MVert *mverts, /* 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; + LoopSplitTaskDataCommon common_data = {}; common_data.mverts = mverts; common_data.medges = medges; common_data.mloops = mloops; -- cgit v1.2.3 From c1a477b497c8d57ff9b402fdd285ea0f8d91d221 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 3 Aug 2021 15:27:20 +1000 Subject: Cleanup: use C++ comments or 'if 0' for commented code --- intern/ghost/intern/GHOST_DisplayManagerCocoa.mm | 26 +++++++++++--------- intern/ghost/intern/GHOST_SystemSDL.cpp | 2 +- intern/ghost/intern/GHOST_WindowX11.cpp | 2 +- source/blender/blenkernel/intern/armature.c | 2 +- source/blender/blenkernel/intern/mask_rasterize.c | 4 ++-- source/blender/blenkernel/intern/mesh_normals.cc | 13 +++++----- source/blender/blenkernel/intern/object.c | 2 +- source/blender/blenkernel/intern/particle_system.c | 2 +- source/blender/blenkernel/intern/screen.c | 2 +- source/blender/blenkernel/intern/softbody.c | 4 ++-- source/blender/blenlib/intern/boxpack_2d.c | 4 ++-- source/blender/blenlib/intern/path_util.c | 2 +- source/blender/blenlib/intern/scanfill.c | 22 ++++++++--------- source/blender/bmesh/intern/bmesh_mesh_normals.c | 6 ++--- source/blender/bmesh/tools/bmesh_bevel.c | 4 ++-- source/blender/editors/gpencil/annotate_paint.c | 28 +++++++++++----------- source/blender/editors/interface/interface_ops.c | 2 +- .../editors/sculpt_paint/paint_image_proj.c | 6 ++--- source/blender/gpu/intern/gpu_immediate.cc | 12 +++++----- source/blender/io/collada/SceneExporter.cpp | 2 +- source/blender/makesrna/intern/rna_access.c | 4 ++-- .../blender/python/intern/bpy_app_translations.c | 2 +- source/blender/windowmanager/intern/wm_window.c | 2 +- 23 files changed, 79 insertions(+), 76 deletions(-) diff --git a/intern/ghost/intern/GHOST_DisplayManagerCocoa.mm b/intern/ghost/intern/GHOST_DisplayManagerCocoa.mm index b983f5a9a4d..96898a15f62 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerCocoa.mm +++ b/intern/ghost/intern/GHOST_DisplayManagerCocoa.mm @@ -141,22 +141,26 @@ GHOST_TSuccess GHOST_DisplayManagerCocoa::setCurrentDisplaySetting( printf(" setting.frequency=%d\n", setting.frequency); #endif // GHOST_DEBUG - // Display configuration is no more available in 10.6 - - /* CFDictionaryRef displayModeValues = ::CGDisplayBestModeForParametersAndRefreshRate( - m_displayIDs[display], - (size_t)setting.bpp, - (size_t)setting.xPixels, - (size_t)setting.yPixels, - (CGRefreshRate)setting.frequency, - NULL);*/ + /* Display configuration is no more available in 10.6. */ + +#if 0 + CFDictionaryRef displayModeValues = ::CGDisplayBestModeForParametersAndRefreshRate( + m_displayIDs[display], + (size_t)setting.bpp, + (size_t)setting.xPixels, + (size_t)setting.yPixels, + (CGRefreshRate)setting.frequency, + NULL); +#endif #ifdef GHOST_DEBUG -/* printf("GHOST_DisplayManagerCocoa::setCurrentDisplaySetting(): switching to:\n"); +# if 0 + printf("GHOST_DisplayManagerCocoa::setCurrentDisplaySetting(): switching to:\n"); printf(" setting.xPixels=%d\n", getValue(displayModeValues, kCGDisplayWidth)); printf(" setting.yPixels=%d\n", getValue(displayModeValues, kCGDisplayHeight)); printf(" setting.bpp=%d\n", getValue(displayModeValues, kCGDisplayBitsPerPixel)); - printf(" setting.frequency=%d\n", getValue(displayModeValues, kCGDisplayRefreshRate)); */ + printf(" setting.frequency=%d\n", getValue(displayModeValues, kCGDisplayRefreshRate)); +# endif #endif // GHOST_DEBUG // CGDisplayErr err = ::CGDisplaySwitchToMode(m_displayIDs[display], displayModeValues); diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 0309a4f9c52..35c7a7ef463 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -473,7 +473,7 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) GHOST_TKey gkey = convertSDLKey(sdl_sub_evt.keysym.scancode); /* NOTE: the `sdl_sub_evt.keysym.sym` is truncated, * for unicode support ghost has to be modified. */ - /* printf("%d\n", sym); */ + // printf("%d\n", sym); if (sym > 127) { switch (sym) { case SDLK_KP_DIVIDE: diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index 0ade7a52891..185d12717e7 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -403,7 +403,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, } if (natom) { - /* printf("Register atoms: %d\n", natom); */ + // printf("Register atoms: %d\n", natom); XSetWMProtocols(m_display, m_window, atoms, natom); } } diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index b8ed519e8d1..434d696f9bd 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2663,7 +2663,7 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_ } } - /* printf("rebuild pose %s, %d bones\n", ob->id.name, counter); */ + // printf("rebuild pose %s, %d bones\n", ob->id.name, counter); /* synchronize protected layers with proxy */ /* HACK! To preserve 2.7x behavior that you always can pose even locked bones, diff --git a/source/blender/blenkernel/intern/mask_rasterize.c b/source/blender/blenkernel/intern/mask_rasterize.c index 81c161a4a7d..00ed7d86975 100644 --- a/source/blender/blenkernel/intern/mask_rasterize.c +++ b/source/blender/blenkernel/intern/mask_rasterize.c @@ -106,7 +106,7 @@ /* for debugging add... */ #ifndef NDEBUG -/* printf("%u %u %u %u\n", _t[0], _t[1], _t[2], _t[3]); \ */ +// printf("%u %u %u %u\n", _t[0], _t[1], _t[2], _t[3]); # define FACE_ASSERT(face, vert_max) \ { \ unsigned int *_t = face; \ @@ -1213,7 +1213,7 @@ void BKE_maskrasterize_handle_init(MaskRasterHandle *mr_handle, layer->falloff = masklay->falloff; } - /* printf("tris %d, feather tris %d\n", sf_tri_tot, tot_feather_quads); */ + // printf("tris %d, feather tris %d\n", sf_tri_tot, tot_feather_quads); } /* add trianges */ diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 1438d737b79..87b11904f90 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -1195,7 +1195,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli } } - // printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); + // printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); while (true) { const MEdge *me_curr = &medges[mlfan_curr->e]; @@ -1212,7 +1212,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli normalize_v3(vec_curr); } - // printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); + // printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); { /* Code similar to accumulate_vertex_normals_poly_v3. */ @@ -1252,9 +1252,8 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli 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"); + * or this vert is smooth, and we have completed a full turn around it. */ + // printf("FAN: Finished!\n"); break; } @@ -1537,12 +1536,12 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common ml_curr_index, ml_prev_index, mp_index))) { - // printf("SKIPPING!\n"); + // printf("SKIPPING!\n"); } else { LoopSplitTaskData *data, data_local; - // printf("PROCESSING!\n"); + // printf("PROCESSING!\n"); if (pool) { if (data_idx == 0) { diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 89de37d6e4b..a6796d0513c 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -922,7 +922,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) const short *totcol_data = BKE_object_material_len_p(ob); /* Only expand so as not to lose any object materials that might be set. */ if (totcol_data && (*totcol_data > ob->totcol)) { - /* printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); */ + // printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); BKE_object_material_resize(BLO_read_lib_get_main(reader), ob, *totcol_data, false); } } diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 06d3daaf4d6..9c0c5639777 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -3112,7 +3112,7 @@ static void collision_fail(ParticleData *pa, ParticleCollision *col) copy_v3_v3(pa->state.vel, col->pce.vel); mul_v3_fl(pa->state.vel, col->inv_timestep); - /* printf("max iterations\n"); */ + // printf("max iterations\n"); } /* Particle - Mesh collision detection and response diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index c3885b5dcf7..e44c5a6b40e 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -766,7 +766,7 @@ void BKE_screen_remove_double_scrverts(bScreen *screen) while (v1) { if (v1->newv == NULL) { /* !?! */ if (v1->vec.x == verg->vec.x && v1->vec.y == verg->vec.y) { - /* printf("doublevert\n"); */ + // printf("doublevert\n"); v1->newv = verg; } } diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index e4e2ed94b41..1a408aceeb2 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -2225,7 +2225,7 @@ static void sb_cf_threads_run(Scene *scene, totthread--; } - /* printf("sb_cf_threads_run spawning %d threads\n", totthread); */ + // printf("sb_cf_threads_run spawning %d threads\n", totthread); sb_threads = MEM_callocN(sizeof(SB_thread_context) * totthread, "SBThread"); memset(sb_threads, 0, sizeof(SB_thread_context) * totthread); @@ -2812,7 +2812,7 @@ static void reference_to_scratch(Object *ob) } mul_v3_fl(accu_pos, 1.0f / accu_mass); copy_v3_v3(sb->scratch->Ref.com, accu_pos); - /* printf("reference_to_scratch\n"); */ + // printf("reference_to_scratch\n"); } /* diff --git a/source/blender/blenlib/intern/boxpack_2d.c b/source/blender/blenlib/intern/boxpack_2d.c index ea6c2d8d498..4a07f1134d0 100644 --- a/source/blender/blenlib/intern/boxpack_2d.c +++ b/source/blender/blenlib/intern/boxpack_2d.c @@ -409,8 +409,8 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r for (i = 0; i < verts_pack_len && isect; i++) { vert = &vs_ctx.vertarray[vertex_pack_indices[i]]; - /* printf("\ttesting vert %i %i %i %f %f\n", i, - * vert->free, verts_pack_len, vert->x, vert->y); */ + // printf("\ttesting vert %i %i %i %f %f\n", i, + // vert->free, verts_pack_len, vert->x, vert->y); /* This vert has a free quadrant * Test if we can place the box here diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 4d0dc43ed1e..99fae1f1616 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1897,7 +1897,7 @@ bool BLI_path_name_at_index(const char *__restrict path, if (index_step == index) { *r_offset = prev; *r_len = i - prev; - /* printf("!!! %d %d\n", start, end); */ + // printf("!!! %d %d\n", start, end); return true; } index_step += 1; diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c index b0d00007580..f0cf19bf508 100644 --- a/source/blender/blenlib/intern/scanfill.c +++ b/source/blender/blenlib/intern/scanfill.c @@ -430,7 +430,7 @@ static void testvertexnearedge(ScanFillContext *sf_ctx) /* new edge */ ed1 = BLI_scanfill_edge_add(sf_ctx, eed->v1, eve); - /* printf("fill: vertex near edge %x\n", eve); */ + // printf("fill: vertex near edge %x\n", eve); ed1->poly_nr = eed->poly_nr; eed->v1 = eve; eve->edge_tot = 3; @@ -608,7 +608,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl sc = scdata; for (a = 0; a < verts; a++) { - /* printf("VERTEX %d index %d\n", a, sc->vert->tmp.u); */ + // printf("VERTEX %d index %d\n", a, sc->vert->tmp.u); /* Set connect-flags. */ for (ed1 = sc->edge_first; ed1; ed1 = eed_next) { eed_next = ed1->next; @@ -634,13 +634,13 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl * (and doesn't work during grab). */ /* if (callLocalInterruptCallBack()) break; */ if (totface >= maxface) { - /* printf("Fill error: endless loop. Escaped at vert %d, tot: %d.\n", a, verts); */ + // printf("Fill error: endless loop. Escaped at vert %d, tot: %d.\n", a, verts); a = verts; break; } if (ed2 == NULL) { sc->edge_first = sc->edge_last = NULL; - /* printf("just 1 edge to vert\n"); */ + // printf("just 1 edge to vert\n"); BLI_addtail(&sf_ctx->filledgebase, ed1); ed1->v2->f = SF_VERT_NEW; ed1->v1->edge_tot--; @@ -662,7 +662,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl break; } - /* printf("test verts %d %d %d\n", v1->tmp.u, v2->tmp.u, v3->tmp.u); */ + // printf("test verts %d %d %d\n", v1->tmp.u, v2->tmp.u, v3->tmp.u); miny = min_ff(v1->xy[1], v3->xy[1]); sc1 = sc + 1; @@ -705,7 +705,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl if (best_sc) { /* make new edge, and start over */ - /* printf("add new edge %d %d and start again\n", v2->tmp.u, best_sc->vert->tmp.u); */ + // printf("add new edge %d %d and start again\n", v2->tmp.u, best_sc->vert->tmp.u); ed3 = BLI_scanfill_edge_add(sf_ctx, v2, best_sc->vert); BLI_remlink(&sf_ctx->filledgebase, ed3); @@ -717,7 +717,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl } else { /* new triangle */ - /* printf("add face %d %d %d\n", v1->tmp.u, v2->tmp.u, v3->tmp.u); */ + // printf("add face %d %d %d\n", v1->tmp.u, v2->tmp.u, v3->tmp.u); addfillface(sf_ctx, v1, v2, v3); totface++; BLI_remlink((ListBase *)&(sc->edge_first), ed1); @@ -741,11 +741,11 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl ed3->v1->edge_tot++; ed3->v2->edge_tot++; - /* printf("add new edge %x %x\n", v1, v3); */ + // printf("add new edge %x %x\n", v1, v3); sc1 = addedgetoscanlist(scdata, ed3, verts); if (sc1) { /* ed3 already exists: remove if a boundary */ - /* printf("Edge exists\n"); */ + // printf("Edge exists\n"); ed3->v1->edge_tot--; ed3->v2->edge_tot--; @@ -954,7 +954,7 @@ unsigned int BLI_scanfill_calc_ex(ScanFillContext *sf_ctx, const int flag, const poly++; } } - /* printf("amount of poly's: %d\n", poly); */ + // printf("amount of poly's: %d\n", poly); } else if (poly) { /* we pre-calculated poly_nr */ @@ -1020,7 +1020,7 @@ unsigned int BLI_scanfill_calc_ex(ScanFillContext *sf_ctx, const int flag, const } } if (BLI_listbase_is_empty(&sf_ctx->filledgebase)) { - /* printf("All edges removed\n"); */ + // printf("All edges removed\n"); return 0; } } diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index e62deb2dde5..746f094aed6 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -756,7 +756,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm, /* Fix/update all clnors of this fan with computed average value. */ /* Prints continuously when merge custom normals, so commenting. */ - /* printf("Invalid clnors in this fan!\n"); */ + // printf("Invalid clnors in this fan!\n"); while ((clnor = BLI_SMALLSTACK_POP(clnors))) { // print_v2("org clnor", clnor); @@ -1834,8 +1834,8 @@ void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all) BM_mesh_elem_index_ensure(bm, BM_VERT); /* When we affect a given vertex, we may affect following smooth fans: - * - all smooth fans of said vertex; - * - all smooth fans of all immediate loop-neighbors vertices; + * - all smooth fans of said vertex; + * - all smooth fans of all immediate loop-neighbors vertices; * This can be simplified as 'all loops of selected vertices and their immediate neighbors' * need to be tagged for update. */ diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 0f99f04ad57..e306fe47770 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -2511,7 +2511,7 @@ static void bevel_harden_normals(BevelParams *bp, BMesh *bm) pnorm = lnext->f->no; } else { - /* printf("unexpected harden case (edge)\n"); */ + // printf("unexpected harden case (edge)\n"); } } else if (fkind == F_VERT) { @@ -2554,7 +2554,7 @@ static void bevel_harden_normals(BevelParams *bp, BMesh *bm) pnorm = norm; } else { - /* printf("unexpected harden case (vert)\n"); */ + // printf("unexpected harden case (vert)\n"); } } } diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 9bf44370c80..31ef094afa6 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -1507,7 +1507,7 @@ static void annotation_session_cleanup(tGPsdata *p) /* free stroke buffer */ if (gpd->runtime.sbuffer) { - /* printf("\t\tGP - free sbuffer\n"); */ + // printf("\t\tGP - free sbuffer\n"); MEM_freeN(gpd->runtime.sbuffer); gpd->runtime.sbuffer = NULL; } @@ -2221,20 +2221,20 @@ static int annotation_draw_exec(bContext *C, wmOperator *op) tGPsdata *p = NULL; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - /* printf("GPencil - Starting Re-Drawing\n"); */ + // printf("GPencil - Starting Re-Drawing\n"); /* try to initialize context data needed while drawing */ if (!annotation_draw_init(C, op, NULL)) { if (op->customdata) { MEM_freeN(op->customdata); } - /* printf("\tGP - no valid data\n"); */ + // printf("\tGP - no valid data\n"); return OPERATOR_CANCELLED; } p = op->customdata; - /* printf("\tGP - Start redrawing stroke\n"); */ + // printf("\tGP - Start redrawing stroke\n"); /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement), * setting the relevant values in context at each step, then applying @@ -2242,7 +2242,7 @@ static int annotation_draw_exec(bContext *C, wmOperator *op) RNA_BEGIN (op->ptr, itemptr, "stroke") { float mousef[2]; - /* printf("\t\tGP - stroke elem\n"); */ + // printf("\t\tGP - stroke elem\n"); /* get relevant data for this point from stroke */ RNA_float_get_array(&itemptr, "mouse", mousef); @@ -2277,7 +2277,7 @@ static int annotation_draw_exec(bContext *C, wmOperator *op) } RNA_END; - /* printf("\tGP - done\n"); */ + // printf("\tGP - done\n"); /* cleanup */ annotation_draw_exit(C, op); @@ -2361,7 +2361,7 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev /* only start drawing immediately if we're allowed to do so... */ if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { /* hotkey invoked - start drawing */ - /* printf("\tGP - set first spot\n"); */ + // printf("\tGP - set first spot\n"); p->status = GP_STATUS_PAINTING; /* handle the initial drawing - i.e. for just doing a simple dot */ @@ -2370,7 +2370,7 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev } else { /* toolbar invoked - don't start drawing yet... */ - /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ + // printf("\tGP - hotkey invoked... waiting for click-drag\n"); op->flag |= OP_IS_MODAL_CURSOR_REGION; } @@ -2399,7 +2399,7 @@ static tGPsdata *annotation_stroke_begin(bContext *C, wmOperator *op) p->status = GP_STATUS_ERROR; } - /* printf("\t\tGP - start stroke\n"); */ + // printf("\t\tGP - start stroke\n"); /* we may need to set up paint env again if we're resuming */ /* XXX: watch it with the paintmode! in future, @@ -2547,7 +2547,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve if (event->val == KM_PRESS && ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY, EVT_EKEY)) { /* exit() ends the current stroke before cleaning up */ - /* printf("\t\tGP - end of paint op + end of stroke\n"); */ + // printf("\t\tGP - end of paint op + end of stroke\n"); p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2571,7 +2571,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve if (sketch) { /* end stroke only, and then wait to resume painting soon */ - /* printf("\t\tGP - end stroke only\n"); */ + // printf("\t\tGP - end stroke only\n"); annotation_stroke_end(op); /* If eraser mode is on, turn it off after the stroke finishes @@ -2602,7 +2602,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } else { - /* printf("\t\tGP - end of stroke + op\n"); */ + // printf("\t\tGP - end of stroke + op\n"); p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2719,7 +2719,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve } else { /* event handled, so just tag as running modal */ - /* printf("\t\t\t\tGP - add point handled!\n"); */ + // printf("\t\t\t\tGP - add point handled!\n"); estate = OPERATOR_RUNNING_MODAL; } } @@ -2729,7 +2729,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve /* just resize the brush (local version) * TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys */ - /* printf("\t\tGP - resize eraser\n"); */ + // printf("\t\tGP - resize eraser\n"); switch (event->type) { case WHEELDOWNMOUSE: /* larger */ case EVT_PADPLUSKEY: diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 20af3054882..a75cc839ae4 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1557,7 +1557,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op) } /* Try to find a valid po file for current language... */ edittranslation_find_po_file(root, uilng, popath, FILE_MAX); - /* printf("po path: %s\n", popath); */ + // printf("po path: %s\n", popath); if (popath[0] == '\0') { BKE_reportf( op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index a8ad6ab1b74..a58b1947b0c 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -4512,7 +4512,7 @@ static void project_paint_begin(const bContext *C, ps->buckets_x = (int)(ps->screen_width / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); ps->buckets_y = (int)(ps->screen_height / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); - /* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */ + // printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); if (ps->buckets_x > PROJ_BUCKET_RECT_MAX || ps->buckets_y > PROJ_BUCKET_RECT_MAX) { reset_threads = true; @@ -5194,8 +5194,8 @@ static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), void *ph_v softenArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint soften arena"); } - /* printf("brush bounds %d %d %d %d\n", - * bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); */ + // printf("brush bounds %d %d %d %d\n", + // bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); while (project_bucket_iter_next(ps, &bucket_index, &bucket_bounds, pos)) { diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc index 062741a6270..cdd56a117db 100644 --- a/source/blender/gpu/intern/gpu_immediate.cc +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -313,7 +313,7 @@ void immAttr1f(uint attr_id, float x) setAttrValueBit(attr_id); float *data = (float *)(imm->vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ + // printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); data[0] = x; } @@ -329,7 +329,7 @@ void immAttr2f(uint attr_id, float x, float y) setAttrValueBit(attr_id); float *data = (float *)(imm->vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ + // printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); data[0] = x; data[1] = y; @@ -346,7 +346,7 @@ void immAttr3f(uint attr_id, float x, float y, float z) setAttrValueBit(attr_id); float *data = (float *)(imm->vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ + // printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); data[0] = x; data[1] = y; @@ -364,7 +364,7 @@ void immAttr4f(uint attr_id, float x, float y, float z, float w) setAttrValueBit(attr_id); float *data = (float *)(imm->vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ + // printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); data[0] = x; data[1] = y; @@ -445,7 +445,7 @@ void immAttr3ub(uint attr_id, uchar r, uchar g, uchar b) setAttrValueBit(attr_id); uchar *data = imm->vertex_data + attr->offset; - /* printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); */ + // printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); data[0] = r; data[1] = g; @@ -463,7 +463,7 @@ void immAttr4ub(uint attr_id, uchar r, uchar g, uchar b, uchar a) setAttrValueBit(attr_id); uchar *data = imm->vertex_data + attr->offset; - /* printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); */ + // printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); data[0] = r; data[1] = g; diff --git a/source/blender/io/collada/SceneExporter.cpp b/source/blender/io/collada/SceneExporter.cpp index 5bbd22b8275..09da2288cfc 100644 --- a/source/blender/io/collada/SceneExporter.cpp +++ b/source/blender/io/collada/SceneExporter.cpp @@ -172,7 +172,7 @@ void SceneExporter::writeNode(Object *ob) else if (ob->type == OB_EMPTY) { /* TODO: handle groups (OB_DUPLICOLLECTION */ if ((ob->transflag & OB_DUPLICOLLECTION) == OB_DUPLICOLLECTION && ob->instance_collection) { Collection *collection = ob->instance_collection; - /* printf("group detected '%s'\n", group->id.name + 2); */ + // printf("group detected '%s'\n", group->id.name + 2); FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, object) { printf("\t%s\n", object->id.name); } diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index feacd47c98c..46d60bf0da9 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -6723,7 +6723,7 @@ bool RNA_struct_property_is_set_ex(PointerRNA *ptr, const char *identifier, bool return RNA_property_is_set_ex(ptr, prop, use_ghost); } /* python raises an error */ - /* printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); */ + // printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); return 0; } @@ -6735,7 +6735,7 @@ bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier) return RNA_property_is_set(ptr, prop); } /* python raises an error */ - /* printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); */ + // printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); return 0; } diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c index 7437598582f..de70035eb2b 100644 --- a/source/blender/python/intern/bpy_app_translations.c +++ b/source/blender/python/intern/bpy_app_translations.c @@ -746,7 +746,7 @@ static PyObject *app_translations_new(PyTypeObject *type, PyObject *UNUSED(args), PyObject *UNUSED(kw)) { - /* printf("%s (%p)\n", __func__, _translations); */ + // printf("%s (%p)\n", __func__, _translations); if (!_translations) { _translations = (BlenderAppTranslations *)type->tp_alloc(type, 0); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 1b08b8dad92..8f37e7f34a9 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1416,7 +1416,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr wm_event_add(win, &event); - /* printf("Drop detected\n"); */ + // printf("Drop detected\n"); /* add drag data to wm for paths: */ -- cgit v1.2.3 From a53feb0aff5227fe0728275285ad35b6eba57508 Mon Sep 17 00:00:00 2001 From: Miguel G Date: Tue, 3 Aug 2021 15:55:35 +1000 Subject: Fix T89691: Solidify modifier simple/complex inconsistency Maintain the sign when clamping non zero offset. Reviewed By: campbellbarton, weasel Ref D11832 --- source/blender/modifiers/intern/MOD_solidify_nonmanifold.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index b872f04b60f..18d308e5f02 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -71,6 +71,15 @@ static float angle_signed_on_axis_normalized_v3v3_v3(const float n[3], return angle; } +static float clamp_nonzero(const float value, const float epsilon) +{ + BLI_assert(!(epsilon < 0.0f)); + if (value < 0.0f) { + return min_ff(value, -epsilon); + } + return max_ff(value, epsilon); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -164,8 +173,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, const float ofs_front = (smd->offset_fac + 1.0f) * 0.5f * smd->offset; const float ofs_back = ofs_front - smd->offset * smd->offset_fac; - const float ofs_front_clamped = max_ff(1e-5f, fabsf(smd->offset > 0 ? ofs_front : ofs_back)); - const float ofs_back_clamped = max_ff(1e-5f, fabsf(smd->offset > 0 ? ofs_back : ofs_front)); + const float ofs_front_clamped = clamp_nonzero(smd->offset > 0 ? ofs_front : ofs_back, 1e-5f); + const float ofs_back_clamped = clamp_nonzero(smd->offset > 0 ? ofs_back : ofs_front, 1e-5f); const float offset_fac_vg = smd->offset_fac_vg; const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg; const float offset = fabsf(smd->offset) * smd->offset_clamp; -- cgit v1.2.3 From ebd55b4acdc47ba2a412fd769aa6ab657abe97ca Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 3 Aug 2021 08:06:26 +0200 Subject: T90372: Assets: When dropping material use active material slot. Currently when dropping an asset the first material slot is always updated. This patch changes that logic to update the active material slot. In future the behavior will be updated to use the material slot of the face under the cursor. That requires better feedback tot he user. Reviewed By: Severin Maniphest Tasks: T90372 Differential Revision: https://developer.blender.org/D12056 --- source/blender/editors/object/object_relations.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index c61965b3e23..f576c0c8517 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2736,7 +2736,11 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } - BKE_object_material_assign(CTX_data_main(C), base->object, ma, 1, BKE_MAT_ASSIGN_USERPREF); + 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); DEG_id_tag_update(&base->object->id, ID_RECALC_TRANSFORM); -- cgit v1.2.3 From 6f50969406a751ac69d41e792d859d74410ebef6 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 3 Aug 2021 08:10:07 +0200 Subject: Cleanup: Hide implementation details for ED_keyframe_keylist. For T78995 we want to change the data structure of keylists to improve performance. (Probably a Vector with bin-search capabilities). This patch hides the internal structure of the keylists behind `AnimKeylist` structure. This allows us to change the internals without 'breaking' where it is being used. The change adds functions to create, free, find and walk over the keylist. Reviewed By: sybren Maniphest Tasks: T78995 Differential Revision: https://developer.blender.org/D11974 --- source/blender/blenlib/BLI_range.h | 34 +++++ source/blender/blenlib/CMakeLists.txt | 1 + source/blender/editors/animation/anim_draw.c | 21 ++- .../blender/editors/animation/anim_motion_paths.c | 63 ++++---- source/blender/editors/animation/keyframes_draw.c | 98 +++++------- .../blender/editors/animation/keyframes_keylist.c | 168 +++++++++++++++------ source/blender/editors/armature/pose_lib.c | 10 +- source/blender/editors/armature/pose_slide.c | 108 ++++++------- .../blender/editors/include/ED_keyframes_keylist.h | 35 +++-- source/blender/editors/screen/screen_ops.c | 17 +-- .../blender/editors/space_action/action_select.c | 48 +++--- source/blender/editors/space_nla/nla_draw.c | 26 ++-- 12 files changed, 364 insertions(+), 265 deletions(-) create mode 100644 source/blender/blenlib/BLI_range.h diff --git a/source/blender/blenlib/BLI_range.h b/source/blender/blenlib/BLI_range.h new file mode 100644 index 00000000000..ad4cd6c162e --- /dev/null +++ b/source/blender/blenlib/BLI_range.h @@ -0,0 +1,34 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Range2f { + float min; + float max; +} Range2f; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index ea5572f1c8a..b6603dce378 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -265,6 +265,7 @@ set(SRC BLI_quadric.h BLI_rand.h BLI_rand.hh + BLI_range.h BLI_rect.h BLI_resource_scope.hh BLI_scanfill.h diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index baf8adf28d0..6469c47ab11 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -494,7 +494,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ Object *ob = CTX_data_active_object(C); Mask *mask = CTX_data_edit_mask(C); bDopeSheet ads = {NULL}; - DLRBT_Tree keys; + struct AnimKeylist *keylist = ED_keylist_create(); ActKeyColumn *aknext, *akprev; float cfranext, cfraprev; bool donenext = false, doneprev = false; @@ -502,9 +502,6 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ cfranext = cfraprev = (float)(CFRA); - /* init binarytree-list for getting keyframes */ - BLI_dlrbTree_init(&keys); - /* seed up dummy dopesheet context with flags to perform necessary filtering */ if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) { /* only selected channels are included */ @@ -512,22 +509,22 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ } /* populate tree with keyframe nodes */ - scene_to_keylist(&ads, scene, &keys, 0); - gpencil_to_keylist(&ads, scene->gpd, &keys, false); + scene_to_keylist(&ads, scene, keylist, 0); + gpencil_to_keylist(&ads, scene->gpd, keylist, false); if (ob) { - ob_to_keylist(&ads, ob, &keys, 0); - gpencil_to_keylist(&ads, ob->data, &keys, false); + ob_to_keylist(&ads, ob, keylist, 0); + gpencil_to_keylist(&ads, ob->data, keylist, false); } if (mask) { MaskLayer *masklay = BKE_mask_layer_active(mask); - mask_to_keylist(&ads, masklay, &keys); + mask_to_keylist(&ads, masklay, keylist); } /* find matching keyframe in the right direction */ do { - aknext = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfranext); + aknext = ED_keylist_find_next(keylist, cfranext); if (aknext) { if (CFRA == (int)aknext->cfra) { @@ -545,7 +542,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ } while ((aknext != NULL) && (donenext == false)); do { - akprev = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfraprev); + akprev = ED_keylist_find_prev(keylist, cfraprev); if (akprev) { if (CFRA == (int)akprev->cfra) { @@ -562,7 +559,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ } while ((akprev != NULL) && (doneprev == false)); /* free temp stuff */ - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); /* any success? */ if (doneprev || donenext) { diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index bddd5dbff55..2a3ae35aab0 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -55,7 +55,7 @@ typedef struct MPathTarget { bMotionPath *mpath; /* motion path in question */ - DLRBT_Tree keys; /* temp, to know where the keyframes are */ + struct AnimKeylist *keylist; /* temp, to know where the keyframes are */ /* Original (Source Objects) */ Object *ob; /* source object */ @@ -187,7 +187,7 @@ static void motionpaths_calc_bake_targets(ListBase *targets, int cframe) float mframe = (float)(cframe); /* Tag if it's a keyframe */ - if (BLI_dlrbTree_search_exact(&mpt->keys, compare_ak_cfraPtr, &mframe)) { + if (ED_keylist_find_exact(mpt->keylist, mframe)) { mpv->flag |= MOTIONPATH_VERT_KEY; } else { @@ -234,52 +234,54 @@ static void motionpath_get_global_framerange(ListBase *targets, int *r_sfra, int } } -static int motionpath_get_prev_keyframe(MPathTarget *mpt, DLRBT_Tree *fcu_keys, int current_frame) +static int motionpath_get_prev_keyframe(MPathTarget *mpt, + struct AnimKeylist *keylist, + int current_frame) { if (current_frame <= mpt->mpath->start_frame) { return mpt->mpath->start_frame; } float current_frame_float = current_frame; - DLRBT_Node *node = BLI_dlrbTree_search_prev(fcu_keys, compare_ak_cfraPtr, ¤t_frame_float); - if (node == NULL) { + const ActKeyColumn *ak = ED_keylist_find_prev(keylist, current_frame_float); + if (ak == NULL) { return mpt->mpath->start_frame; } - ActKeyColumn *key_data = (ActKeyColumn *)node; - return key_data->cfra; + return ak->cfra; } static int motionpath_get_prev_prev_keyframe(MPathTarget *mpt, - DLRBT_Tree *fcu_keys, + struct AnimKeylist *keylist, int current_frame) { - int frame = motionpath_get_prev_keyframe(mpt, fcu_keys, current_frame); - return motionpath_get_prev_keyframe(mpt, fcu_keys, frame); + int frame = motionpath_get_prev_keyframe(mpt, keylist, current_frame); + return motionpath_get_prev_keyframe(mpt, keylist, frame); } -static int motionpath_get_next_keyframe(MPathTarget *mpt, DLRBT_Tree *fcu_keys, int current_frame) +static int motionpath_get_next_keyframe(MPathTarget *mpt, + struct AnimKeylist *keylist, + int current_frame) { if (current_frame >= mpt->mpath->end_frame) { return mpt->mpath->end_frame; } float current_frame_float = current_frame; - DLRBT_Node *node = BLI_dlrbTree_search_next(fcu_keys, compare_ak_cfraPtr, ¤t_frame_float); - if (node == NULL) { + const ActKeyColumn *ak = ED_keylist_find_next(keylist, current_frame_float); + if (ak == NULL) { return mpt->mpath->end_frame; } - ActKeyColumn *key_data = (ActKeyColumn *)node; - return key_data->cfra; + return ak->cfra; } static int motionpath_get_next_next_keyframe(MPathTarget *mpt, - DLRBT_Tree *fcu_keys, + struct AnimKeylist *keylist, int current_frame) { - int frame = motionpath_get_next_keyframe(mpt, fcu_keys, current_frame); - return motionpath_get_next_keyframe(mpt, fcu_keys, frame); + int frame = motionpath_get_next_keyframe(mpt, keylist, current_frame); + return motionpath_get_next_keyframe(mpt, keylist, frame); } static bool motionpath_check_can_use_keyframe_range(MPathTarget *UNUSED(mpt), @@ -324,17 +326,16 @@ static void motionpath_calculate_update_range(MPathTarget *mpt, * Could be optimized further by storing some flags about which channels has been modified so * we ignore all others (which can potentially make an update range unnecessary wide). */ for (FCurve *fcu = fcurve_list->first; fcu != NULL; fcu = fcu->next) { - DLRBT_Tree fcu_keys; - BLI_dlrbTree_init(&fcu_keys); - fcurve_to_keylist(adt, fcu, &fcu_keys, 0); + struct AnimKeylist *keylist = ED_keylist_create(); + fcurve_to_keylist(adt, fcu, keylist, 0); - int fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, &fcu_keys, current_frame); - int fcu_efra = motionpath_get_next_next_keyframe(mpt, &fcu_keys, current_frame); + int fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, keylist, current_frame); + int fcu_efra = motionpath_get_next_next_keyframe(mpt, keylist, current_frame); /* Extend range further, since acceleration compensation propagates even further away. */ if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) { - fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, &fcu_keys, fcu_sfra); - fcu_efra = motionpath_get_next_next_keyframe(mpt, &fcu_keys, fcu_efra); + fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, keylist, fcu_sfra); + fcu_efra = motionpath_get_next_next_keyframe(mpt, keylist, fcu_efra); } if (fcu_sfra <= fcu_efra) { @@ -342,14 +343,14 @@ static void motionpath_calculate_update_range(MPathTarget *mpt, *r_efra = max_ii(*r_efra, fcu_efra); } - BLI_dlrbTree_free(&fcu_keys); + ED_keylist_free(keylist); } } static void motionpath_free_free_tree_data(ListBase *targets) { LISTBASE_FOREACH (MPathTarget *, mpt, targets) { - BLI_dlrbTree_free(&mpt->keys); + ED_keylist_free(mpt->keylist); } } @@ -418,7 +419,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, AnimData *adt = BKE_animdata_from_id(&mpt->ob_eval->id); /* build list of all keyframes in active action for object or pchan */ - BLI_dlrbTree_init(&mpt->keys); + mpt->keylist = ED_keylist_create(); ListBase *fcurve_list = NULL; if (adt) { @@ -433,12 +434,12 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, if (agrp) { fcurve_list = &agrp->channels; - agroup_to_keylist(adt, agrp, &mpt->keys, 0); + agroup_to_keylist(adt, agrp, mpt->keylist, 0); } } else { fcurve_list = &adt->action->curves; - action_to_keylist(adt, adt->action, &mpt->keys, 0); + action_to_keylist(adt, adt->action, mpt->keylist, 0); } } @@ -502,7 +503,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, avs->recalc &= ~ANIMVIZ_RECALC_PATHS; /* Clean temp data */ - BLI_dlrbTree_free(&mpt->keys); + ED_keylist_free(mpt->keylist); /* Free previous batches to force update. */ GPU_VERTBUF_DISCARD_SAFE(mpath->points_vbo); diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 4f512c9d7ca..deed79942ac 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -184,16 +184,12 @@ void draw_keyframe_shape(float x, } static void draw_keylist(View2D *v2d, - DLRBT_Tree *keys, + const struct AnimKeylist *keylist, float ypos, float yscale_fac, bool channelLocked, int saction_flag) { - if (keys == NULL) { - return; - } - 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; @@ -228,6 +224,8 @@ static void draw_keylist(View2D *v2d, 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)) { @@ -378,134 +376,118 @@ static void draw_keylist(View2D *v2d, void draw_summary_channel( View2D *v2d, bAnimContext *ac, float ypos, float yscale_fac, int saction_flag) { - DLRBT_Tree keys; + struct AnimKeylist *keylist = ED_keylist_create(); saction_flag &= ~SACTION_SHOW_EXTREMES; - BLI_dlrbTree_init(&keys); + summary_to_keylist(ac, keylist, saction_flag); - summary_to_keylist(ac, &keys, saction_flag); + draw_keylist(v2d, keylist, ypos, yscale_fac, false, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag); - - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); } void draw_scene_channel( View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos, float yscale_fac, int saction_flag) { - DLRBT_Tree keys; + struct AnimKeylist *keylist = ED_keylist_create(); saction_flag &= ~SACTION_SHOW_EXTREMES; - BLI_dlrbTree_init(&keys); - - scene_to_keylist(ads, sce, &keys, saction_flag); + scene_to_keylist(ads, sce, keylist, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag); + draw_keylist(v2d, keylist, ypos, yscale_fac, false, saction_flag); - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); } void draw_object_channel( View2D *v2d, bDopeSheet *ads, Object *ob, float ypos, float yscale_fac, int saction_flag) { - DLRBT_Tree keys; + struct AnimKeylist *keylist = ED_keylist_create(); saction_flag &= ~SACTION_SHOW_EXTREMES; - BLI_dlrbTree_init(&keys); - - ob_to_keylist(ads, ob, &keys, saction_flag); + ob_to_keylist(ads, ob, keylist, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag); + draw_keylist(v2d, keylist, ypos, yscale_fac, false, saction_flag); - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); } void draw_fcurve_channel( View2D *v2d, AnimData *adt, FCurve *fcu, float ypos, float yscale_fac, int saction_flag) { - DLRBT_Tree keys; + 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)); - BLI_dlrbTree_init(&keys); + fcurve_to_keylist(adt, fcu, keylist, saction_flag); - fcurve_to_keylist(adt, fcu, &keys, saction_flag); + draw_keylist(v2d, keylist, ypos, yscale_fac, locked, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); - - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); } void draw_agroup_channel( View2D *v2d, AnimData *adt, bActionGroup *agrp, float ypos, float yscale_fac, int saction_flag) { - DLRBT_Tree keys; + struct AnimKeylist *keylist = ED_keylist_create(); bool locked = (agrp->flag & AGRP_PROTECTED) || ((adt && adt->action) && ID_IS_LINKED(adt->action)); - BLI_dlrbTree_init(&keys); - - agroup_to_keylist(adt, agrp, &keys, saction_flag); + agroup_to_keylist(adt, agrp, keylist, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); + draw_keylist(v2d, keylist, ypos, yscale_fac, locked, saction_flag); - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); } void draw_action_channel( View2D *v2d, AnimData *adt, bAction *act, float ypos, float yscale_fac, int saction_flag) { - DLRBT_Tree keys; + struct AnimKeylist *keylist = ED_keylist_create(); bool locked = (act && ID_IS_LINKED(act)); saction_flag &= ~SACTION_SHOW_EXTREMES; - BLI_dlrbTree_init(&keys); - - action_to_keylist(adt, act, &keys, saction_flag); + action_to_keylist(adt, act, keylist, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); + draw_keylist(v2d, keylist, ypos, yscale_fac, locked, saction_flag); - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); } void draw_gpencil_channel( View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos, float yscale_fac, int saction_flag) { - DLRBT_Tree keys; + struct AnimKeylist *keylist = ED_keylist_create(); saction_flag &= ~SACTION_SHOW_EXTREMES; - BLI_dlrbTree_init(&keys); + gpencil_to_keylist(ads, gpd, keylist, false); - gpencil_to_keylist(ads, gpd, &keys, false); + draw_keylist(v2d, keylist, ypos, yscale_fac, false, saction_flag); - draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag); - - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); } void draw_gpl_channel( View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos, float yscale_fac, int saction_flag) { - DLRBT_Tree keys; + struct AnimKeylist *keylist = ED_keylist_create(); bool locked = (gpl->flag & GP_LAYER_LOCKED) != 0; - BLI_dlrbTree_init(&keys); - - gpl_to_keylist(ads, gpl, &keys); + gpl_to_keylist(ads, gpl, keylist); - draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); + draw_keylist(v2d, keylist, ypos, yscale_fac, locked, saction_flag); - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); } void draw_masklay_channel(View2D *v2d, @@ -515,15 +497,13 @@ void draw_masklay_channel(View2D *v2d, float yscale_fac, int saction_flag) { - DLRBT_Tree keys; + struct AnimKeylist *keylist = ED_keylist_create(); bool locked = (masklay->flag & MASK_LAYERFLAG_LOCKED) != 0; - BLI_dlrbTree_init(&keys); - - mask_to_keylist(ads, masklay, &keys); + mask_to_keylist(ads, masklay, keylist); - draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); + draw_keylist(v2d, keylist, ypos, yscale_fac, locked, saction_flag); - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); } diff --git a/source/blender/editors/animation/keyframes_keylist.c b/source/blender/editors/animation/keyframes_keylist.c index 47ed2b56300..8f0f6d753be 100644 --- a/source/blender/editors/animation/keyframes_keylist.c +++ b/source/blender/editors/animation/keyframes_keylist.c @@ -32,6 +32,7 @@ #include "BLI_dlrbTree.h" #include "BLI_listbase.h" +#include "BLI_range.h" #include "BLI_utildefines.h" #include "DNA_anim_types.h" @@ -48,6 +49,83 @@ /* *************************** 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); +} + +ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, float cfra) +{ + return (ActKeyColumn *)BLI_dlrbTree_search_exact(&keylist->keys, compare_ak_cfraPtr, &cfra); +} + +ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, float cfra) +{ + return (ActKeyColumn *)BLI_dlrbTree_search_next(&keylist->keys, compare_ak_cfraPtr, &cfra); +} + +ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, float cfra) +{ + return (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, float min_fra, float max_fra) +{ + 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)) { + 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(float a, float b) @@ -323,33 +401,33 @@ static void nupdate_ak_masklayshape(void *node, void *data) /* --------------- */ /* Add the given BezTriple to the given 'list' of Keyframes */ -static void add_bezt_to_keycolumns_list(DLRBT_Tree *keys, BezTripleChain *bezt) +static void add_bezt_to_keycolumns_list(AnimKeylist *keylist, BezTripleChain *bezt) { - if (ELEM(NULL, keys, bezt)) { + if (ELEM(NULL, keylist, bezt)) { return; } - BLI_dlrbTree_add(keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt); + 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(DLRBT_Tree *keys, bGPDframe *gpf) +static void add_gpframe_to_keycolumns_list(AnimKeylist *keylist, bGPDframe *gpf) { - if (ELEM(NULL, keys, gpf)) { + if (ELEM(NULL, keylist, gpf)) { return; } - BLI_dlrbTree_add(keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf); + 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(DLRBT_Tree *keys, MaskLayerShape *masklay_shape) +static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape *masklay_shape) { - if (ELEM(NULL, keys, masklay_shape)) { + if (ELEM(NULL, keylist, masklay_shape)) { return; } - BLI_dlrbTree_add(keys, + BLI_dlrbTree_add(&keylist->keys, compare_ak_masklayshape, nalloc_ak_masklayshape, nupdate_ak_masklayshape, @@ -423,9 +501,9 @@ static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block) } } -static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len) +static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, int bezt_len) { - ActKeyColumn *col = keys->first; + ActKeyColumn *col = keylist->keys.first; if (bezt && bezt_len >= 2) { ActKeyBlockInfo block; @@ -444,7 +522,7 @@ static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int be /* Backtrack to find the right location. */ if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) { ActKeyColumn *newcol = (ActKeyColumn *)BLI_dlrbTree_search_exact( - keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]); + &keylist->keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]); if (newcol != NULL) { col = newcol; @@ -486,22 +564,22 @@ static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int be * 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(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len) +static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, int bezt_len) { /* Recompute the prev/next linked list. */ - BLI_dlrbTree_linkedlist_sync(keys); + BLI_dlrbTree_linkedlist_sync(&keylist->keys); /* Find the curve count */ int max_curve = 0; - LISTBASE_FOREACH (ActKeyColumn *, col, keys) { + 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, keys) { + LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) { /* Pre-existing column. */ if (col->totcurve > 0) { prev_ready = col; @@ -516,7 +594,7 @@ static void update_keyblocks(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len) } /* Add blocks on top */ - add_bezt_to_keyblocks_list(keys, bezt, bezt_len); + add_bezt_to_keyblocks_list(keylist, bezt, bezt_len); } /* --------- */ @@ -540,7 +618,7 @@ int actkeyblock_get_valid_hold(ActKeyColumn *ac) /* *************************** Keyframe List Conversions *************************** */ -void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, int saction_flag) +void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, int saction_flag) { if (ac) { ListBase anim_data = {NULL, NULL}; @@ -561,13 +639,13 @@ void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, int saction_flag) switch (ale->datatype) { case ALE_FCURVE: - fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); + fcurve_to_keylist(ale->adt, ale->data, keylist, saction_flag); break; case ALE_MASKLAY: - mask_to_keylist(ac->ads, ale->data, keys); + mask_to_keylist(ac->ads, ale->data, keylist); break; case ALE_GPFRAME: - gpl_to_keylist(ac->ads, ale->data, keys); + gpl_to_keylist(ac->ads, ale->data, keylist); break; default: // printf("%s: datatype %d unhandled\n", __func__, ale->datatype); @@ -579,7 +657,7 @@ void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, int saction_flag) } } -void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, int saction_flag) +void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, int saction_flag) { bAnimContext ac = {NULL}; ListBase anim_data = {NULL, NULL}; @@ -608,13 +686,13 @@ void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, int saction /* loop through each F-Curve, grabbing the keyframes */ for (ale = anim_data.first; ale; ale = ale->next) { - fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); + fcurve_to_keylist(ale->adt, ale->data, keylist, saction_flag); } ANIM_animdata_freelist(&anim_data); } -void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_flag) +void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, int saction_flag) { bAnimContext ac = {NULL}; ListBase anim_data = {NULL, NULL}; @@ -646,7 +724,7 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_fl /* loop through each F-Curve, grabbing the keyframes */ for (ale = anim_data.first; ale; ale = ale->next) { - fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); + fcurve_to_keylist(ale->adt, ale->data, keylist, saction_flag); } ANIM_animdata_freelist(&anim_data); @@ -654,7 +732,7 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_fl void cachefile_to_keylist(bDopeSheet *ads, CacheFile *cache_file, - DLRBT_Tree *keys, + AnimKeylist *keylist, int saction_flag) { if (cache_file == NULL) { @@ -680,13 +758,13 @@ void cachefile_to_keylist(bDopeSheet *ads, /* loop through each F-Curve, grabbing the keyframes */ LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { - fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); + fcurve_to_keylist(ale->adt, ale->data, keylist, saction_flag); } ANIM_animdata_freelist(&anim_data); } -void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, int saction_flag) +void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, int saction_flag) { if (fcu && fcu->totvert && fcu->bezt) { /* apply NLA-mapping (if applicable) */ @@ -710,11 +788,11 @@ void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, int saction chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] : is_cyclic ? &fcu->bezt[1] : NULL; } - add_bezt_to_keycolumns_list(keys, &chain); + add_bezt_to_keycolumns_list(keylist, &chain); } /* Update keyblocks. */ - update_keyblocks(keys, fcu->bezt, fcu->totvert); + update_keyblocks(keylist, fcu->bezt, fcu->totvert); /* unapply NLA-mapping if applicable */ if (adt) { @@ -723,71 +801,71 @@ void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, int saction } } -void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, DLRBT_Tree *keys, int saction_flag) +void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, AnimKeylist *keylist, 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, keys, saction_flag); + fcurve_to_keylist(adt, fcu, keylist, saction_flag); } } } -void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys, int saction_flag) +void action_to_keylist(AnimData *adt, bAction *act, AnimKeylist *keylist, 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, keys, saction_flag); + fcurve_to_keylist(adt, fcu, keylist, saction_flag); } } } -void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys, const bool active) +void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, AnimKeylist *keylist, const bool active) { bGPDlayer *gpl; - if (gpd && keys) { + 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, keys); + gpl_to_keylist(ads, gpl, keylist); } } } } } -void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, DLRBT_Tree *keys) +void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, AnimKeylist *keylist) { bGPDframe *gpf; - if (gpl && keys) { + 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(keys, gpf); + add_gpframe_to_keycolumns_list(keylist, gpf); } - update_keyblocks(keys, NULL, 0); + update_keyblocks(keylist, NULL, 0); } } -void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, DLRBT_Tree *keys) +void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, AnimKeylist *keylist) { MaskLayerShape *masklay_shape; - if (masklay && keys) { + if (masklay && keylist) { for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { - add_masklay_to_keycolumns_list(keys, masklay_shape); + add_masklay_to_keycolumns_list(keylist, masklay_shape); } - update_keyblocks(keys, NULL, 0); + update_keyblocks(keylist, NULL, 0); } } diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index cb70b2810d1..646356e7a45 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -304,8 +304,6 @@ static int poselib_sanitize_exec(bContext *C, wmOperator *op) { Object *ob = get_poselib_object(C); bAction *act = (ob) ? ob->poselib : NULL; - DLRBT_Tree keys; - ActKeyColumn *ak; TimeMarker *marker, *markern; /* validate action */ @@ -315,11 +313,11 @@ static int poselib_sanitize_exec(bContext *C, wmOperator *op) } /* determine which frames have keys */ - BLI_dlrbTree_init(&keys); - action_to_keylist(NULL, act, &keys, 0); + struct AnimKeylist *keylist = ED_keylist_create(); + action_to_keylist(NULL, act, keylist, 0); /* for each key, make sure there is a corresponding pose */ - for (ak = keys.first; ak; ak = ak->next) { + LISTBASE_FOREACH (const ActKeyColumn *, ak, ED_keylist_listbase(keylist)) { /* check if any pose matches this */ /* TODO: don't go looking through the list like this every time... */ for (marker = act->markers.first; marker; marker = marker->next) { @@ -356,7 +354,7 @@ static int poselib_sanitize_exec(bContext *C, wmOperator *op) } /* free temp memory */ - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); /* send notifiers for this - using keyframe editing notifiers, since action * may be being shown in anim editors as active action diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 38f562ebf25..238799650a0 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -146,7 +146,7 @@ typedef struct tPoseSlideOp { /** links between posechannels and f-curves for all the pose objects. */ ListBase pfLinks; /** binary tree for quicker searching for keyframes (when applicable) */ - DLRBT_Tree keys; + struct AnimKeylist *keylist; /** current frame number - global time */ int cframe; @@ -277,7 +277,7 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) /* Do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up * to the caller of this (usually only invoke() will do it, to make things more efficient). */ - BLI_dlrbTree_init(&pso->keys); + pso->keylist = ED_keylist_create(); /* Initialize numeric input. */ initNumInput(&pso->num); @@ -306,7 +306,7 @@ static void pose_slide_exit(bContext *C, wmOperator *op) poseAnim_mapping_free(&pso->pfLinks); /* Free RB-BST for keyframes (if it contained data). */ - BLI_dlrbTree_free(&pso->keys); + ED_keylist_free(pso->keylist); if (pso->ob_data_array != NULL) { MEM_freeN(pso->ob_data_array); @@ -971,60 +971,56 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, const wmEvent * /* Do this for each F-Curve. */ for (ld = pfl->fcurves.first; ld; ld = ld->next) { FCurve *fcu = (FCurve *)ld->data; - fcurve_to_keylist(pfl->ob->adt, fcu, &pso->keys, 0); + fcurve_to_keylist(pfl->ob->adt, fcu, pso->keylist, 0); } } /* Cancel if no keyframes found. */ - if (pso->keys.root) { - ActKeyColumn *ak; - float cframe = (float)pso->cframe; - - /* Firstly, check if the current frame is a keyframe. */ - ak = (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe); - - if (ak == NULL) { - /* Current frame is not a keyframe, so search. */ - ActKeyColumn *pk = (ActKeyColumn *)BLI_dlrbTree_search_prev( - &pso->keys, compare_ak_cfraPtr, &cframe); - ActKeyColumn *nk = (ActKeyColumn *)BLI_dlrbTree_search_next( - &pso->keys, compare_ak_cfraPtr, &cframe); - - /* New set the frames. */ - /* Prev frame. */ - pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1); - RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); - /* Next frame. */ - pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1); - RNA_int_set(op->ptr, "next_frame", pso->nextFrame); - } - else { - /* Current frame itself is a keyframe, so just take keyframes on either side. */ - /* Prev frame. */ - pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1); - RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); - /* Next frame. */ - pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1); - RNA_int_set(op->ptr, "next_frame", pso->nextFrame); - } - - /* Apply NLA mapping corrections so the frame look-ups work. */ - for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { - tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; - if (ob_data->valid) { - ob_data->prevFrameF = BKE_nla_tweakedit_remap( - ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); - ob_data->nextFrameF = BKE_nla_tweakedit_remap( - ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP); - } - } - } - else { + if (ED_keylist_is_empty(pso->keylist)) { BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between"); pose_slide_exit(C, op); return OPERATOR_CANCELLED; } + float cframe = (float)pso->cframe; + + /* Firstly, check if the current frame is a keyframe. */ + const ActKeyColumn *ak = ED_keylist_find_exact(pso->keylist, cframe); + + if (ak == NULL) { + /* Current frame is not a keyframe, so search. */ + const ActKeyColumn *pk = ED_keylist_find_prev(pso->keylist, cframe); + const ActKeyColumn *nk = ED_keylist_find_next(pso->keylist, cframe); + + /* New set the frames. */ + /* Prev frame. */ + pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1); + RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); + /* Next frame. */ + pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1); + RNA_int_set(op->ptr, "next_frame", pso->nextFrame); + } + else { + /* Current frame itself is a keyframe, so just take keyframes on either side. */ + /* Prev frame. */ + pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1); + RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); + /* Next frame. */ + pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1); + RNA_int_set(op->ptr, "next_frame", pso->nextFrame); + } + + /* Apply NLA mapping corrections so the frame look-ups work. */ + for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { + tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; + if (ob_data->valid) { + ob_data->prevFrameF = BKE_nla_tweakedit_remap( + ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); + ob_data->nextFrameF = BKE_nla_tweakedit_remap( + ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP); + } + } + /* Initial apply for operator. */ /* TODO: need to calculate factor for initial round too. */ if (!ELEM(pso->mode, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST)) { @@ -1705,26 +1701,22 @@ typedef union tPosePropagate_ModeData { */ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float startFrame) { - DLRBT_Tree keys; + struct AnimKeylist *keylist = ED_keylist_create(); Object *ob = pfl->ob; AnimData *adt = ob->adt; LinkData *ld; float endFrame = startFrame; - /* Set up optimized data-structures for searching for relevant keyframes + holds. */ - BLI_dlrbTree_init(&keys); - for (ld = pfl->fcurves.first; ld; ld = ld->next) { FCurve *fcu = (FCurve *)ld->data; - fcurve_to_keylist(adt, fcu, &keys, 0); + fcurve_to_keylist(adt, fcu, keylist, 0); } /* 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 = (ActKeyColumn *)BLI_dlrbTree_search_exact( - &keys, compare_ak_cfraPtr, &startFrame); + 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 @@ -1735,11 +1727,11 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st */ if (ab == NULL) { /* We've got case 1, so try the one after. */ - ab = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &startFrame); + ab = ED_keylist_find_next(keylist, startFrame); if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { /* Try the block before this frame then as last resort. */ - ab = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &startFrame); + ab = ED_keylist_find_prev(keylist, startFrame); } } @@ -1774,7 +1766,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st } /* Free temp memory. */ - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); /* Return the end frame we've found. */ return endFrame; diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h index be3eac66771..92b57b89cf8 100644 --- a/source/blender/editors/include/ED_keyframes_keylist.h +++ b/source/blender/editors/include/ED_keyframes_keylist.h @@ -37,9 +37,12 @@ struct Scene; struct bAnimContext; struct bDopeSheet; struct bGPDlayer; +struct Range2f; /* ****************************** Base Structs ****************************** */ +struct AnimKeylist; + /* Information about the stretch of time from current to the next column */ typedef struct ActKeyBlockInfo { /* Combination of flags from all curves. */ @@ -133,49 +136,61 @@ typedef enum eKeyframeExtremeDrawOpts { /* ******************************* Methods ****************************** */ +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, + float min_fra, + float max_fra); +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); + /* Key-data Generation --------------- */ /* F-Curve */ void fcurve_to_keylist(struct AnimData *adt, struct FCurve *fcu, - struct DLRBT_Tree *keys, + struct AnimKeylist *keys, int saction_flag); /* Action Group */ void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, - struct DLRBT_Tree *keys, + struct AnimKeylist *keys, int saction_flag); /* Action */ void action_to_keylist(struct AnimData *adt, struct bAction *act, - struct DLRBT_Tree *keys, + struct AnimKeylist *keys, int saction_flag); /* Object */ void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, - struct DLRBT_Tree *keys, + struct AnimKeylist *keys, int saction_flag); /* Cache File */ void cachefile_to_keylist(struct bDopeSheet *ads, struct CacheFile *cache_file, - struct DLRBT_Tree *keys, + struct AnimKeylist *keys, int saction_flag); /* Scene */ void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, - struct DLRBT_Tree *keys, + struct AnimKeylist *keys, int saction_flag); /* DopeSheet Summary */ -void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys, int saction_flag); +void summary_to_keylist(struct bAnimContext *ac, struct AnimKeylist *keys, int saction_flag); /* Grease Pencil datablock summary */ void gpencil_to_keylist(struct bDopeSheet *ads, struct bGPdata *gpd, - struct DLRBT_Tree *keys, + struct AnimKeylist *keys, const bool active); /* Grease Pencil Layer */ -void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct DLRBT_Tree *keys); +void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct AnimKeylist *keys); /* Mask */ -void mask_to_keylist(struct bDopeSheet *ads, struct MaskLayer *masklay, struct DLRBT_Tree *keys); +void mask_to_keylist(struct bDopeSheet *ads, struct MaskLayer *masklay, struct AnimKeylist *keys); /* ActKeyColumn API ---------------- */ /* Comparator callback used for ActKeyColumns and cframe float-value pointer */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index f172e22ea56..107466a8a0b 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -3057,8 +3057,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) float cfra = (float)(CFRA); /* init binarytree-list for getting keyframes */ - DLRBT_Tree keys; - BLI_dlrbTree_init(&keys); + struct AnimKeylist *keylist = ED_keylist_create(); /* seed up dummy dopesheet context with flags to perform necessary filtering */ if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) { @@ -3067,14 +3066,14 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) } /* populate tree with keyframe nodes */ - scene_to_keylist(&ads, scene, &keys, 0); + scene_to_keylist(&ads, scene, keylist, 0); if (ob) { - ob_to_keylist(&ads, ob, &keys, 0); + ob_to_keylist(&ads, ob, keylist, 0); if (ob->type == OB_GPENCIL) { const bool active = !(scene->flag & SCE_KEYS_NO_SELONLY); - gpencil_to_keylist(&ads, ob->data, &keys, active); + gpencil_to_keylist(&ads, ob->data, keylist, active); } } @@ -3082,17 +3081,17 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) Mask *mask = CTX_data_edit_mask(C); if (mask) { MaskLayer *masklay = BKE_mask_layer_active(mask); - mask_to_keylist(&ads, masklay, &keys); + mask_to_keylist(&ads, masklay, keylist); } } /* find matching keyframe in the right direction */ ActKeyColumn *ak; if (next) { - ak = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra); + ak = ED_keylist_find_next(keylist, cfra); } else { - ak = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra); + ak = ED_keylist_find_prev(keylist, cfra); } while ((ak != NULL) && (done == false)) { @@ -3113,7 +3112,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) } /* free temp stuff */ - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); /* any success? */ if (done == false) { diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 59d2063ea84..64ccca2c907 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -93,7 +93,7 @@ static bAnimListElem *actkeys_find_list_element_at_position(bAnimContext *ac, } static void actkeys_list_element_to_keylist(bAnimContext *ac, - DLRBT_Tree *anim_keys, + struct AnimKeylist *keylist, bAnimListElem *ale) { AnimData *adt = ANIM_nla_mapping_get(ac, ale); @@ -107,44 +107,44 @@ static void actkeys_list_element_to_keylist(bAnimContext *ac, switch (ale->datatype) { case ALE_SCE: { Scene *scene = (Scene *)ale->key_data; - scene_to_keylist(ads, scene, anim_keys, 0); + scene_to_keylist(ads, scene, keylist, 0); break; } case ALE_OB: { Object *ob = (Object *)ale->key_data; - ob_to_keylist(ads, ob, anim_keys, 0); + ob_to_keylist(ads, ob, keylist, 0); break; } case ALE_ACT: { bAction *act = (bAction *)ale->key_data; - action_to_keylist(adt, act, anim_keys, 0); + action_to_keylist(adt, act, keylist, 0); break; } case ALE_FCURVE: { FCurve *fcu = (FCurve *)ale->key_data; - fcurve_to_keylist(adt, fcu, anim_keys, 0); + fcurve_to_keylist(adt, fcu, keylist, 0); break; } } } else if (ale->type == ANIMTYPE_SUMMARY) { /* dopesheet summary covers everything */ - summary_to_keylist(ac, anim_keys, 0); + summary_to_keylist(ac, keylist, 0); } else if (ale->type == ANIMTYPE_GROUP) { /* TODO: why don't we just give groups key_data too? */ bActionGroup *agrp = (bActionGroup *)ale->data; - agroup_to_keylist(adt, agrp, anim_keys, 0); + agroup_to_keylist(adt, agrp, keylist, 0); } else if (ale->type == ANIMTYPE_GPLAYER) { /* TODO: why don't we just give gplayers key_data too? */ bGPDlayer *gpl = (bGPDlayer *)ale->data; - gpl_to_keylist(ads, gpl, anim_keys); + gpl_to_keylist(ads, gpl, keylist); } else if (ale->type == ANIMTYPE_MASKLAYER) { /* TODO: why don't we just give masklayers key_data too? */ MaskLayer *masklay = (MaskLayer *)ale->data; - mask_to_keylist(ads, masklay, anim_keys); + mask_to_keylist(ads, masklay, keylist); } } @@ -160,9 +160,8 @@ static void actkeys_find_key_in_list_element(bAnimContext *ac, View2D *v2d = &ac->region->v2d; - DLRBT_Tree anim_keys; - BLI_dlrbTree_init(&anim_keys); - actkeys_list_element_to_keylist(ac, &anim_keys, ale); + struct AnimKeylist *keylist = ED_keylist_create(); + actkeys_list_element_to_keylist(ac, keylist, ale); AnimData *adt = ANIM_nla_mapping_get(ac, ale); @@ -174,22 +173,21 @@ static void actkeys_find_key_in_list_element(bAnimContext *ac, 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); - for (ActKeyColumn *ak = anim_keys.root; ak; ak = (ak->cfra < xmin) ? ak->right : ak->left) { - if (IN_RANGE(ak->cfra, xmin, xmax)) { - /* set the frame to use, and apply inverse-correction for NLA-mapping - * so that the frame will get selected by the selection functions without - * requiring to map each frame once again... - */ - *r_selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP); - *r_frame = ak->cfra; - *r_found = true; - *r_is_selected = (ak->sel & SELECT) != 0; - break; - } + const ActKeyColumn *ak = ED_keylist_find_any_between(keylist, xmin, xmax); + if (ak) { + + /* set the frame to use, and apply inverse-correction for NLA-mapping + * so that the frame will get selected by the selection functions without + * requiring to map each frame once again... + */ + *r_selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP); + *r_frame = ak->cfra; + *r_found = true; + *r_is_selected = (ak->sel & SELECT) != 0; } /* cleanup temporary lists */ - BLI_dlrbTree_free(&anim_keys); + ED_keylist_free(keylist); } static void actkeys_find_key_at_position(bAnimContext *ac, diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index c96047da0c8..2bf4c7d4344 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -35,6 +35,7 @@ #include "BLI_blenlib.h" #include "BLI_dlrbTree.h" +#include "BLI_range.h" #include "BLI_utildefines.h" #include "BKE_context.h" @@ -95,12 +96,16 @@ void nla_action_get_color(AnimData *adt, bAction *act, float color[4]) static void nla_action_draw_keyframes( View2D *v2d, AnimData *adt, bAction *act, float y, float ymin, float ymax) { + if (act == NULL) { + return; + } + /* get a list of the keyframes with NLA-scaling applied */ - DLRBT_Tree keys; - BLI_dlrbTree_init(&keys); - action_to_keylist(adt, act, &keys, 0); + struct AnimKeylist *keylist = ED_keylist_create(); + action_to_keylist(adt, act, keylist, 0); - if (ELEM(NULL, act, keys.first)) { + if (ED_keylist_is_empty(keylist)) { + ED_keylist_free(keylist); return; } @@ -122,15 +127,16 @@ static void nla_action_draw_keyframes( /* - draw a rect from the first to the last frame (no extra overlaps for now) * that is slightly stumpier than the track background (hardcoded 2-units here) */ - float f1 = ((ActKeyColumn *)keys.first)->cfra; - float f2 = ((ActKeyColumn *)keys.last)->cfra; - immRectf(pos_id, f1, ymin + 2, f2, ymax - 2); + Range2f frame_range; + ED_keylist_frame_range(keylist, &frame_range); + immRectf(pos_id, frame_range.min, ymin + 2, frame_range.max, ymax - 2); immUnbindProgram(); /* Count keys before drawing. */ /* NOTE: It's safe to cast #DLRBT_Tree, as it's designed to degrade down to a #ListBase. */ - uint key_len = BLI_listbase_count((ListBase *)&keys); + const ListBase *keys = ED_keylist_listbase(keylist); + uint key_len = BLI_listbase_count(keys); if (key_len > 0) { format = immVertexFormat(); @@ -151,7 +157,7 @@ static void nla_action_draw_keyframes( /* - disregard the selection status of keyframes so they draw a certain way * - size is 6.0f which is smaller than the editable keyframes, so that there is a distinction */ - LISTBASE_FOREACH (ActKeyColumn *, ak, &keys) { + LISTBASE_FOREACH (const ActKeyColumn *, ak, keys) { draw_keyframe_shape(ak->cfra, y, 6.0f, @@ -174,7 +180,7 @@ static void nla_action_draw_keyframes( } /* free icons */ - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); } /* Strip Markers ------------------------ */ -- cgit v1.2.3 From 00b57136e4167ef5a303b1215574bb52a22e9194 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 3 Aug 2021 16:06:42 +1000 Subject: Revert "Fix spin-gizmo not allowing click events to select vertices" This reverts commit 0b903755a9908344e9fcb4a33b4f0340abeb9386. This caused T86030, left-mouse selection override clicking, which is used for creating a full revolution. --- source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c index 38d530ba911..d01331bd497 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c @@ -468,7 +468,9 @@ void MESH_GGT_spin(struct wmGizmoGroupType *gzgt) gzgt->poll = ED_gizmo_poll_or_unlink_delayed_from_tool; gzgt->setup = gizmo_mesh_spin_init_setup; - gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; + /* This works well with right click selection but overrides left-mouse selection + * when clicking which is needed to create a full 360 degree revolution, see: T86030. */ + // gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; gzgt->refresh = gizmo_mesh_spin_init_refresh; gzgt->message_subscribe = gizmo_mesh_spin_init_message_subscribe; gzgt->draw_prepare = gizmo_mesh_spin_init_draw_prepare; -- cgit v1.2.3 From b54e741a8b5655208ae666653639885afe15c5e8 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 3 Aug 2021 09:41:08 +0200 Subject: UI: Add light count to viewport statistics Even though lights were part of `SceneStats`, they were not used when rBfd10ac9acaa0 was committed. This patch adds the light count back into the statistics. When a light is the active object, it will display the total number of lights in the scene, as well as how many lights are currently selected. {F10141354} Reviewed By: #user_interface, Severin, Blendify, harley Maniphest Tasks: T88512 Differential Revision: https://developer.blender.org/D11387 --- source/blender/editors/space_info/info_stats.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index d7671a372c6..983e04127e0 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -764,6 +764,7 @@ void ED_info_draw_stats( FRAMES, STROKES, POINTS, + LIGHTS, MAX_LABELS_COUNT }; char labels[MAX_LABELS_COUNT][64]; @@ -779,6 +780,7 @@ void ED_info_draw_stats( STRNCPY(labels[FRAMES], IFACE_("Frames")); STRNCPY(labels[STROKES], IFACE_("Strokes")); STRNCPY(labels[POINTS], IFACE_("Points")); + STRNCPY(labels[LIGHTS], IFACE_("Lights")); int longest_label = 0; int i; @@ -832,6 +834,9 @@ void ED_info_draw_stats( stats_row(col1, labels[FACES], col2, stats_fmt.totfacesculpt, stats_fmt.totface, y, height); } } + else if ((ob) && (ob->type == OB_LAMP)) { + stats_row(col1, labels[LIGHTS], col2, stats_fmt.totlampsel, stats_fmt.totlamp, y, height); + } else { stats_row(col1, labels[VERTS], col2, stats_fmt.totvert, NULL, y, height); stats_row(col1, labels[EDGES], col2, stats_fmt.totedge, NULL, y, height); -- cgit v1.2.3 From d6e97a53abfbf1ba28c6c30a4bb3faa8561ebd47 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 3 Aug 2021 18:06:42 +1000 Subject: Correct task ID in 00b57136e4167ef5a303b1215574bb52a22e9194 --- source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c index d01331bd497..1af489b60ce 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c @@ -469,7 +469,7 @@ void MESH_GGT_spin(struct wmGizmoGroupType *gzgt) gzgt->poll = ED_gizmo_poll_or_unlink_delayed_from_tool; gzgt->setup = gizmo_mesh_spin_init_setup; /* This works well with right click selection but overrides left-mouse selection - * when clicking which is needed to create a full 360 degree revolution, see: T86030. */ + * when clicking which is needed to create a full 360 degree revolution, see: T89912. */ // gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; gzgt->refresh = gizmo_mesh_spin_init_refresh; gzgt->message_subscribe = gizmo_mesh_spin_init_message_subscribe; -- cgit v1.2.3 From f8abc3fb2fd3ec883a13ff5e692550b38479f81f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 3 Aug 2021 18:08:10 +1000 Subject: Fix T85436: Separate by loose parts doesn't show new objects Only the "changed" state from the last edit-object was used, this meant the operator would not perform the necessary update with multi-object edit-mode. Use "changed" & "changed_multi" naming convention. --- source/blender/editors/mesh/editmesh_tools.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 7e94383390e..215ce0185f1 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4666,7 +4666,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const int type = RNA_enum_get(op->ptr, "type"); - int retval = 0; + bool changed_multi = false; if (ED_operator_editmesh(C)) { uint bases_len = 0; @@ -4676,6 +4676,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) for (uint bs_index = 0; bs_index < bases_len; bs_index++) { Base *base = bases[bs_index]; BMEditMesh *em = BKE_editmesh_from_object(base->object); + bool changed = false; if (type == 0) { if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) { @@ -4690,20 +4691,20 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) /* editmode separate */ switch (type) { case MESH_SEPARATE_SELECTED: - retval = mesh_separate_selected(bmain, scene, view_layer, base, em->bm); + changed = mesh_separate_selected(bmain, scene, view_layer, base, em->bm); break; case MESH_SEPARATE_MATERIAL: - retval = mesh_separate_material(bmain, scene, view_layer, base, em->bm); + changed = mesh_separate_material(bmain, scene, view_layer, base, em->bm); break; case MESH_SEPARATE_LOOSE: - retval = mesh_separate_loose(bmain, scene, view_layer, base, em->bm); + changed = mesh_separate_loose(bmain, scene, view_layer, base, em->bm); break; default: BLI_assert(0); break; } - if (retval) { + if (changed) { EDBM_update(base->object->data, &(const struct EDBMUpdate_Params){ .calc_looptri = true, @@ -4711,6 +4712,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) .is_destructive = true, }); } + changed_multi |= changed; } MEM_freeN(bases); } @@ -4727,7 +4729,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) Mesh *me = ob->data; if (!ID_IS_LINKED(me)) { BMesh *bm_old = NULL; - int retval_iter = 0; + bool changed = false; bm_old = BM_mesh_create(&bm_mesh_allocsize_default, &((struct BMeshCreateParams){ @@ -4738,17 +4740,17 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) switch (type) { case MESH_SEPARATE_MATERIAL: - retval_iter = mesh_separate_material(bmain, scene, view_layer, base_iter, bm_old); + changed = mesh_separate_material(bmain, scene, view_layer, base_iter, bm_old); break; case MESH_SEPARATE_LOOSE: - retval_iter = mesh_separate_loose(bmain, scene, view_layer, base_iter, bm_old); + changed = mesh_separate_loose(bmain, scene, view_layer, base_iter, bm_old); break; default: BLI_assert(0); break; } - if (retval_iter) { + if (changed) { BM_mesh_bm_to_me(bmain, bm_old, me, @@ -4762,14 +4764,14 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) BM_mesh_free(bm_old); - retval |= retval_iter; + changed_multi |= changed; } } } CTX_DATA_END; } - if (retval) { + if (changed_multi) { /* delay depsgraph recalc until all objects are duplicated */ DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); -- cgit v1.2.3 From b1a607ce04fecae7af4474f7b087993ec2cf8e5b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 3 Aug 2021 10:38:12 +0200 Subject: Cleanup: deduplicate type conversion logic --- .../modifiers/intern/MOD_nodes_evaluator.cc | 42 ++++++++++++---------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 2157092d639..1391587fa7d 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -1245,14 +1245,8 @@ class GeometryNodesEvaluator { void *buffer = allocator.allocate(to_type.size(), to_type.alignment()); GMutablePointer value{to_type, buffer}; - if (conversions_.is_convertible(from_type, to_type)) { - /* Do the conversion if possible. */ - conversions_.convert_to_uninitialized(from_type, to_type, value_to_forward.get(), buffer); - } - else { - /* Cannot convert, use default value instead. */ - to_type.copy_construct(to_type.default_value(), buffer); - } + this->convert_value(from_type, to_type, value_to_forward.get(), buffer); + /* Multi input socket values are logged once all values are available. */ if (!to_socket->is_multi_input_socket()) { this->log_socket_value({to_socket}, value); @@ -1380,17 +1374,29 @@ class GeometryNodesEvaluator { if (type == required_type) { return {type, buffer}; } - if (conversions_.is_convertible(type, required_type)) { - /* Convert the loaded value to the required type if possible. */ - void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment()); - conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer); - type.destruct(buffer); - return {required_type, converted_buffer}; + void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment()); + this->convert_value(type, required_type, buffer, converted_buffer); + return {required_type, converted_buffer}; + } + + void convert_value(const CPPType &from_type, + const CPPType &to_type, + const void *from_value, + void *to_value) + { + if (from_type == to_type) { + from_type.copy_construct(from_value, to_value); + return; + } + + if (conversions_.is_convertible(from_type, to_type)) { + /* Do the conversion if possible. */ + conversions_.convert_to_uninitialized(from_type, to_type, from_value, to_value); + } + else { + /* Cannot convert, use default value instead. */ + to_type.copy_construct(to_type.default_value(), to_value); } - /* Use a default fallback value when the loaded type is not compatible. */ - void *default_buffer = allocator.allocate(required_type.size(), required_type.alignment()); - required_type.copy_construct(required_type.default_value(), default_buffer); - return {required_type, default_buffer}; } NodeState &get_node_state(const DNode node) -- cgit v1.2.3 From c0900a64ceeaa46a1dfd1fa19d5b57449fb7788a Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 3 Aug 2021 12:10:26 +0200 Subject: Fix T90341: Crash opening 2.4 file with File Browser editor open The file selection parameters (e.g. `context.space_data.params`) are expected to be unset in certain cases. Reading 2.4 files seems to be one of them. Hence, code using it should check it's set first. Also added an assert to a File Browser UI template printing a message when the parameters are unset, to help debugging the issue. --- release/scripts/startup/bl_ui/space_filebrowser.py | 5 +++++ source/blender/editors/space_file/file_draw.c | 3 +++ 2 files changed, 8 insertions(+) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 44631e368a3..3189efeb939 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -373,6 +373,7 @@ class FILEBROWSER_PT_advanced_filter(Panel): def poll(cls, context): # only useful in append/link (library) context currently... return ( + context.space_data.params and context.space_data.params.use_library_browsing and panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) @@ -423,6 +424,10 @@ class FILEBROWSER_PT_directory_path(Panel): return True + @classmethod + def poll(cls, context): + return context.space_data.params + def draw(self, context): layout = self.layout space = context.space_data diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 37a56816677..7d9b8583838 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -82,6 +82,9 @@ void ED_file_path_button(bScreen *screen, PointerRNA params_rna_ptr; uiBut *but; + BLI_assert_msg(params != NULL, + "File select parameters not set. The caller is expected to check this."); + RNA_pointer_create(&screen->id, &RNA_FileSelectParams, params, ¶ms_rna_ptr); /* callbacks for operator check functions */ -- cgit v1.2.3 From 28b9dd7b1f0a8d4ae56eafca188796936a6e3723 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 3 Aug 2021 12:24:48 +0200 Subject: Cleanup: Remove redundant checks in File Browser UI script Was already checking these preconditions in the poll method. --- release/scripts/startup/bl_ui/space_filebrowser.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 3189efeb939..9f70d26c71b 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -384,19 +384,17 @@ class FILEBROWSER_PT_advanced_filter(Panel): space = context.space_data params = space.params - if params and params.use_library_browsing: - layout.prop(params, "use_filter_blendid") - if params.use_filter_blendid: - layout.separator() - col = layout.column(align=True) + layout.prop(params, "use_filter_blendid") + if params.use_filter_blendid: + layout.separator() + col = layout.column(align=True) - if context.preferences.experimental.use_asset_browser: - col.prop(params, "use_filter_asset_only") + col.prop(params, "use_filter_asset_only") - filter_id = params.filter_id - for identifier in dir(filter_id): - if identifier.startswith("filter_"): - col.prop(filter_id, identifier, toggle=True) + filter_id = params.filter_id + for identifier in dir(filter_id): + if identifier.startswith("filter_"): + col.prop(filter_id, identifier, toggle=True) def is_option_region_visible(context, space): -- cgit v1.2.3 From ea54cbe1b42efb3107285c89685f555c06997062 Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Tue, 3 Aug 2021 11:55:53 +0200 Subject: USD: add USD importer This is an initial implementation of a USD importer. This work is comprised of Tangent Animation's open source USD importer, combined with features @makowalski had implemented. The design is very similar to the approach taken in the Alembic importer. The core functionality resides in a collection of "reader" classes, each of which is responsible for converting an instance of a USD prim to the corresponding Blender Object representation. The flow of control for the conversion can be followed in the `import_startjob()` and `import_endjob()` functions in `usd_capi.cc`. The `USDStageReader` class is responsible for traversing the USD stage and instantiating the appropriate readers. Reviewed By: sybren, HooglyBoogly Differential Revision: https://developer.blender.org/D10700 --- release/scripts/startup/bl_ui/space_topbar.py | 3 + source/blender/blenkernel/CMakeLists.txt | 7 + source/blender/blenkernel/intern/cachefile.c | 93 ++- source/blender/blenkernel/intern/constraint.c | 21 +- source/blender/editors/io/io_ops.c | 1 + source/blender/editors/io/io_usd.c | 279 +++++++ source/blender/editors/io/io_usd.h | 2 + source/blender/io/alembic/ABC_alembic.h | 17 +- source/blender/io/alembic/intern/abc_util.h | 9 - source/blender/io/alembic/intern/alembic_capi.cc | 35 +- source/blender/io/common/CMakeLists.txt | 1 + source/blender/io/common/IO_types.h | 34 + source/blender/io/usd/CMakeLists.txt | 30 +- source/blender/io/usd/intern/usd_capi.cc | 252 ------ source/blender/io/usd/intern/usd_capi_export.cc | 238 ++++++ source/blender/io/usd/intern/usd_capi_import.cc | 578 ++++++++++++++ source/blender/io/usd/intern/usd_common.cc | 43 ++ source/blender/io/usd/intern/usd_common.h | 25 + source/blender/io/usd/intern/usd_reader_camera.cc | 100 +++ source/blender/io/usd/intern/usd_reader_camera.h | 42 + source/blender/io/usd/intern/usd_reader_curve.cc | 256 +++++++ source/blender/io/usd/intern/usd_reader_curve.h | 62 ++ source/blender/io/usd/intern/usd_reader_geom.cc | 59 ++ source/blender/io/usd/intern/usd_reader_geom.h | 52 ++ .../blender/io/usd/intern/usd_reader_instance.cc | 64 ++ source/blender/io/usd/intern/usd_reader_instance.h | 47 ++ source/blender/io/usd/intern/usd_reader_light.cc | 252 ++++++ source/blender/io/usd/intern/usd_reader_light.h | 41 + .../blender/io/usd/intern/usd_reader_material.cc | 703 +++++++++++++++++ source/blender/io/usd/intern/usd_reader_material.h | 131 ++++ source/blender/io/usd/intern/usd_reader_mesh.cc | 853 +++++++++++++++++++++ source/blender/io/usd/intern/usd_reader_mesh.h | 95 +++ source/blender/io/usd/intern/usd_reader_nurbs.cc | 256 +++++++ source/blender/io/usd/intern/usd_reader_nurbs.h | 61 ++ source/blender/io/usd/intern/usd_reader_prim.cc | 80 ++ source/blender/io/usd/intern/usd_reader_prim.h | 131 ++++ source/blender/io/usd/intern/usd_reader_stage.cc | 324 ++++++++ source/blender/io/usd/intern/usd_reader_stage.h | 90 +++ source/blender/io/usd/intern/usd_reader_volume.cc | 114 +++ source/blender/io/usd/intern/usd_reader_volume.h | 49 ++ source/blender/io/usd/intern/usd_reader_xform.cc | 184 +++++ source/blender/io/usd/intern/usd_reader_xform.h | 68 ++ source/blender/io/usd/usd.h | 69 ++ source/blender/makesdna/DNA_cachefile_defaults.h | 1 + source/blender/makesdna/DNA_cachefile_types.h | 26 +- source/blender/makesrna/intern/rna_cachefile.c | 12 +- source/blender/modifiers/CMakeLists.txt | 10 + .../modifiers/intern/MOD_meshsequencecache.c | 63 +- 48 files changed, 5637 insertions(+), 326 deletions(-) create mode 100644 source/blender/io/common/IO_types.h delete mode 100644 source/blender/io/usd/intern/usd_capi.cc create mode 100644 source/blender/io/usd/intern/usd_capi_export.cc create mode 100644 source/blender/io/usd/intern/usd_capi_import.cc create mode 100644 source/blender/io/usd/intern/usd_common.cc create mode 100644 source/blender/io/usd/intern/usd_common.h create mode 100644 source/blender/io/usd/intern/usd_reader_camera.cc create mode 100644 source/blender/io/usd/intern/usd_reader_camera.h create mode 100644 source/blender/io/usd/intern/usd_reader_curve.cc create mode 100644 source/blender/io/usd/intern/usd_reader_curve.h create mode 100644 source/blender/io/usd/intern/usd_reader_geom.cc create mode 100644 source/blender/io/usd/intern/usd_reader_geom.h create mode 100644 source/blender/io/usd/intern/usd_reader_instance.cc create mode 100644 source/blender/io/usd/intern/usd_reader_instance.h create mode 100644 source/blender/io/usd/intern/usd_reader_light.cc create mode 100644 source/blender/io/usd/intern/usd_reader_light.h create mode 100644 source/blender/io/usd/intern/usd_reader_material.cc create mode 100644 source/blender/io/usd/intern/usd_reader_material.h create mode 100644 source/blender/io/usd/intern/usd_reader_mesh.cc create mode 100644 source/blender/io/usd/intern/usd_reader_mesh.h create mode 100644 source/blender/io/usd/intern/usd_reader_nurbs.cc create mode 100644 source/blender/io/usd/intern/usd_reader_nurbs.h create mode 100644 source/blender/io/usd/intern/usd_reader_prim.cc create mode 100644 source/blender/io/usd/intern/usd_reader_prim.h create mode 100644 source/blender/io/usd/intern/usd_reader_stage.cc create mode 100644 source/blender/io/usd/intern/usd_reader_stage.h create mode 100644 source/blender/io/usd/intern/usd_reader_volume.cc create mode 100644 source/blender/io/usd/intern/usd_reader_volume.h create mode 100644 source/blender/io/usd/intern/usd_reader_xform.cc create mode 100644 source/blender/io/usd/intern/usd_reader_xform.h diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 5e68896a2a7..bcf579208a0 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -468,6 +468,9 @@ class TOPBAR_MT_file_import(Menu): text="Collada (Default) (.dae)") if bpy.app.build_options.alembic: self.layout.operator("wm.alembic_import", text="Alembic (.abc)") + if bpy.app.build_options.usd: + self.layout.operator( + "wm.usd_import", text="Universal Scene Description (.usd, .usdc, .usda)") self.layout.operator("wm.gpencil_import_svg", text="SVG as Grease Pencil") diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 78bfe8c9afb..adaef22d5bc 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -698,6 +698,13 @@ if(WITH_ALEMBIC) add_definitions(-DWITH_ALEMBIC) endif() +if(WITH_USD) + list(APPEND INC + ../io/usd + ) + add_definitions(-DWITH_USD) +endif() + if(WITH_OPENSUBDIV) list(APPEND INC_SYS ${OPENSUBDIV_INCLUDE_DIRS} diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index eaba5d33a20..75180de94d8 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -55,6 +55,10 @@ # include "ABC_alembic.h" #endif +#ifdef WITH_USD +# include "usd.h" +#endif + static void cachefile_handle_free(CacheFile *cache_file); static void cache_file_init_data(ID *id) @@ -166,15 +170,30 @@ void BKE_cachefile_reader_open(CacheFile *cache_file, Object *object, const char *object_path) { -#ifdef WITH_ALEMBIC +#if defined(WITH_ALEMBIC) || defined(WITH_USD) + BLI_assert(cache_file->id.tag & LIB_TAG_COPIED_ON_WRITE); if (cache_file->handle == NULL) { return; } - /* Open Alembic cache reader. */ - *reader = CacheReader_open_alembic_object(cache_file->handle, *reader, object, object_path); + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + /* Open Alembic cache reader. */ + *reader = CacheReader_open_alembic_object(cache_file->handle, *reader, object, object_path); +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + /* Open USD cache reader. */ + *reader = CacheReader_open_usd_object(cache_file->handle, *reader, object, object_path); +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } /* Multiple modifiers and constraints can call this function concurrently. */ BLI_spin_lock(&spin); @@ -197,16 +216,30 @@ void BKE_cachefile_reader_open(CacheFile *cache_file, void BKE_cachefile_reader_free(CacheFile *cache_file, struct CacheReader **reader) { -#ifdef WITH_ALEMBIC +#if defined(WITH_ALEMBIC) || defined(WITH_USD) /* Multiple modifiers and constraints can call this function concurrently, and * cachefile_handle_free() can also be called at the same time. */ BLI_spin_lock(&spin); if (*reader != NULL) { if (cache_file) { BLI_assert(cache_file->id.tag & LIB_TAG_COPIED_ON_WRITE); + + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + ABC_CacheReader_free(*reader); +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + USD_CacheReader_free(*reader); +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } } - CacheReader_free(*reader); *reader = NULL; if (cache_file && cache_file->handle_readers) { @@ -221,7 +254,8 @@ void BKE_cachefile_reader_free(CacheFile *cache_file, struct CacheReader **reade static void cachefile_handle_free(CacheFile *cache_file) { -#ifdef WITH_ALEMBIC +#if defined(WITH_ALEMBIC) || defined(WITH_USD) + /* Free readers in all modifiers and constraints that use the handle, before * we free the handle itself. */ BLI_spin_lock(&spin); @@ -230,7 +264,21 @@ static void cachefile_handle_free(CacheFile *cache_file) GSET_ITER (gs_iter, cache_file->handle_readers) { struct CacheReader **reader = BLI_gsetIterator_getKey(&gs_iter); if (*reader != NULL) { - CacheReader_free(*reader); + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + ABC_CacheReader_free(*reader); +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + USD_CacheReader_free(*reader); +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } + *reader = NULL; } } @@ -242,7 +290,22 @@ static void cachefile_handle_free(CacheFile *cache_file) /* Free handle. */ if (cache_file->handle) { - ABC_free_handle(cache_file->handle); + + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + ABC_free_handle(cache_file->handle); +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + USD_free_handle(cache_file->handle); +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } + cache_file->handle = NULL; } @@ -289,8 +352,18 @@ void BKE_cachefile_eval(Main *bmain, Depsgraph *depsgraph, CacheFile *cache_file BLI_freelistN(&cache_file->object_paths); #ifdef WITH_ALEMBIC - cache_file->handle = ABC_create_handle(bmain, filepath, &cache_file->object_paths); - BLI_strncpy(cache_file->handle_filepath, filepath, FILE_MAX); + if (BLI_path_extension_check_glob(filepath, "*abc")) { + cache_file->type = CACHEFILE_TYPE_ALEMBIC; + cache_file->handle = ABC_create_handle(bmain, filepath, &cache_file->object_paths); + BLI_strncpy(cache_file->handle_filepath, filepath, FILE_MAX); + } +#endif +#ifdef WITH_USD + if (BLI_path_extension_check_glob(filepath, "*.usd;*.usda;*.usdc")) { + cache_file->type = CACHEFILE_TYPE_USD; + cache_file->handle = USD_create_handle(bmain, filepath, &cache_file->object_paths); + BLI_strncpy(cache_file->handle_filepath, filepath, FILE_MAX); + } #endif if (DEG_is_active(depsgraph)) { diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 47df31e3a2c..022073b0f12 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -95,6 +95,10 @@ # include "ABC_alembic.h" #endif +#ifdef WITH_USD +# include "usd.h" +#endif + /* ---------------------------------------------------------------------------- */ /* Useful macros for testing various common flag combinations */ @@ -5403,7 +5407,7 @@ static void transformcache_id_looper(bConstraint *con, ConstraintIDFunc func, vo static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets) { -#ifdef WITH_ALEMBIC +#if defined(WITH_ALEMBIC) || defined(WITH_USD) bTransformCacheConstraint *data = con->data; Scene *scene = cob->scene; @@ -5421,7 +5425,20 @@ static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBa BKE_cachefile_reader_open(cache_file, &data->reader, cob->ob, data->object_path); } - ABC_get_transform(data->reader, cob->matrix, time, cache_file->scale); + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + ABC_get_transform(data->reader, cob->matrix, time, cache_file->scale); +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + USD_get_transform(data->reader, cob->matrix, time * FPS, cache_file->scale); +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } #else UNUSED_VARS(con, cob); #endif diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c index 9fa34a1c55d..b2788ee49a2 100644 --- a/source/blender/editors/io/io_ops.c +++ b/source/blender/editors/io/io_ops.c @@ -52,6 +52,7 @@ void ED_operatortypes_io(void) WM_operatortype_append(WM_OT_alembic_export); #endif #ifdef WITH_USD + WM_operatortype_append(WM_OT_usd_import); WM_operatortype_append(WM_OT_usd_export); #endif diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index 0eadb38abb5..d0007d9e5be 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -22,23 +22,30 @@ */ #ifdef WITH_USD +# include "DNA_modifier_types.h" # include "DNA_space_types.h" +# include # include "BKE_context.h" # include "BKE_main.h" # include "BKE_report.h" +# include "BLI_blenlib.h" # include "BLI_path_util.h" # include "BLI_string.h" # include "BLI_utildefines.h" # include "BLT_translation.h" +# include "ED_object.h" + # include "MEM_guardedalloc.h" # include "RNA_access.h" # include "RNA_define.h" +# include "RNA_enum_types.h" + # include "UI_interface.h" # include "UI_resources.h" @@ -50,6 +57,8 @@ # include "io_usd.h" # include "usd.h" +# include "stdio.h" + const EnumPropertyItem rna_enum_usd_export_evaluation_mode_items[] = { {DAG_EVAL_RENDER, "RENDER", @@ -242,4 +251,274 @@ void WM_OT_usd_export(struct wmOperatorType *ot) "are different settings for viewport and rendering"); } +/* ====== USD Import ====== */ + +static int wm_usd_import_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + eUSDOperatorOptions *options = MEM_callocN(sizeof(eUSDOperatorOptions), "eUSDOperatorOptions"); + options->as_background_job = true; + op->customdata = options; + + return WM_operator_filesel(C, op, event); +} + +static int wm_usd_import_exec(bContext *C, wmOperator *op) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + BKE_report(op->reports, RPT_ERROR, "No filename given"); + return OPERATOR_CANCELLED; + } + + char filename[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filename); + + eUSDOperatorOptions *options = (eUSDOperatorOptions *)op->customdata; + const bool as_background_job = (options != NULL && options->as_background_job); + MEM_SAFE_FREE(op->customdata); + + const float scale = RNA_float_get(op->ptr, "scale"); + + const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range"); + + const bool read_mesh_uvs = RNA_boolean_get(op->ptr, "read_mesh_uvs"); + const bool read_mesh_colors = RNA_boolean_get(op->ptr, "read_mesh_colors"); + + char mesh_read_flag = MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY; + if (read_mesh_uvs) { + mesh_read_flag |= MOD_MESHSEQ_READ_UV; + } + if (read_mesh_colors) { + mesh_read_flag |= MOD_MESHSEQ_READ_COLOR; + } + + const bool import_cameras = RNA_boolean_get(op->ptr, "import_cameras"); + const bool import_curves = RNA_boolean_get(op->ptr, "import_curves"); + const bool import_lights = RNA_boolean_get(op->ptr, "import_lights"); + const bool import_materials = RNA_boolean_get(op->ptr, "import_materials"); + const bool import_meshes = RNA_boolean_get(op->ptr, "import_meshes"); + const bool import_volumes = RNA_boolean_get(op->ptr, "import_volumes"); + + const bool import_subdiv = RNA_boolean_get(op->ptr, "import_subdiv"); + + const bool import_instance_proxies = RNA_boolean_get(op->ptr, "import_instance_proxies"); + + const bool import_visible_only = RNA_boolean_get(op->ptr, "import_visible_only"); + + const bool create_collection = RNA_boolean_get(op->ptr, "create_collection"); + + char *prim_path_mask = malloc(1024); + RNA_string_get(op->ptr, "prim_path_mask", prim_path_mask); + + const bool import_guide = RNA_boolean_get(op->ptr, "import_guide"); + const bool import_proxy = RNA_boolean_get(op->ptr, "import_proxy"); + const bool import_render = RNA_boolean_get(op->ptr, "import_render"); + + const bool import_usd_preview = RNA_boolean_get(op->ptr, "import_usd_preview"); + const bool set_material_blend = RNA_boolean_get(op->ptr, "set_material_blend"); + + const float light_intensity_scale = RNA_float_get(op->ptr, "light_intensity_scale"); + + /* TODO(makowalski): Add support for sequences. */ + const bool is_sequence = false; + int offset = 0; + int sequence_len = 1; + + /* Switch out of edit mode to avoid being stuck in it (T54326). */ + Object *obedit = CTX_data_edit_object(C); + if (obedit) { + ED_object_mode_set(C, OB_MODE_EDIT); + } + + const bool validate_meshes = false; + const bool use_instancing = false; + + struct USDImportParams params = {.scale = scale, + .is_sequence = is_sequence, + .set_frame_range = set_frame_range, + .sequence_len = sequence_len, + .offset = offset, + .validate_meshes = validate_meshes, + .mesh_read_flag = mesh_read_flag, + .import_cameras = import_cameras, + .import_curves = import_curves, + .import_lights = import_lights, + .import_materials = import_materials, + .import_meshes = import_meshes, + .import_volumes = import_volumes, + .prim_path_mask = prim_path_mask, + .import_subdiv = import_subdiv, + .import_instance_proxies = import_instance_proxies, + .create_collection = create_collection, + .import_guide = import_guide, + .import_proxy = import_proxy, + .import_render = import_render, + .import_visible_only = import_visible_only, + .use_instancing = use_instancing, + .import_usd_preview = import_usd_preview, + .set_material_blend = set_material_blend, + .light_intensity_scale = light_intensity_scale}; + + const bool ok = USD_import(C, filename, ¶ms, as_background_job); + + return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + +static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + struct PointerRNA *ptr = op->ptr; + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + + uiLayout *box = uiLayoutBox(layout); + uiLayout *col = uiLayoutColumnWithHeading(box, true, IFACE_("Data Types")); + uiItemR(col, ptr, "import_cameras", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_curves", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_lights", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_materials", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_meshes", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_volumes", 0, NULL, ICON_NONE); + uiItemR(box, ptr, "prim_path_mask", 0, NULL, ICON_NONE); + uiItemR(box, ptr, "scale", 0, NULL, ICON_NONE); + + box = uiLayoutBox(layout); + col = uiLayoutColumnWithHeading(box, true, IFACE_("Mesh Data")); + uiItemR(col, ptr, "read_mesh_uvs", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "read_mesh_colors", 0, NULL, ICON_NONE); + col = uiLayoutColumnWithHeading(box, true, IFACE_("Include")); + uiItemR(col, ptr, "import_subdiv", 0, IFACE_("Subdivision"), ICON_NONE); + uiItemR(col, ptr, "import_instance_proxies", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_visible_only", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_guide", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_proxy", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_render", 0, NULL, ICON_NONE); + + col = uiLayoutColumnWithHeading(box, true, IFACE_("Options")); + uiItemR(col, ptr, "set_frame_range", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "relative_path", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "create_collection", 0, NULL, ICON_NONE); + uiItemR(box, ptr, "light_intensity_scale", 0, NULL, ICON_NONE); + + box = uiLayoutBox(layout); + col = uiLayoutColumnWithHeading(box, true, IFACE_("Experimental")); + uiItemR(col, ptr, "import_usd_preview", 0, NULL, ICON_NONE); + uiLayoutSetEnabled(col, RNA_boolean_get(ptr, "import_materials")); + uiLayout *row = uiLayoutRow(col, true); + uiItemR(row, ptr, "set_material_blend", 0, NULL, ICON_NONE); + uiLayoutSetEnabled(row, RNA_boolean_get(ptr, "import_usd_preview")); +} + +void WM_OT_usd_import(struct wmOperatorType *ot) +{ + ot->name = "Import USD"; + ot->description = "Import USD stage into current scene"; + ot->idname = "WM_OT_usd_import"; + + ot->invoke = wm_usd_import_invoke; + ot->exec = wm_usd_import_exec; + ot->poll = WM_operator_winactive; + ot->ui = wm_usd_import_draw; + + WM_operator_properties_filesel(ot, + FILE_TYPE_FOLDER | FILE_TYPE_USD, + FILE_BLENDER, + FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS, + FILE_DEFAULTDISPLAY, + FILE_SORT_ALPHA); + + RNA_def_float( + ot->srna, + "scale", + 1.0f, + 0.0001f, + 1000.0f, + "Scale", + "Value by which to enlarge or shrink the objects with respect to the world's origin", + 0.0001f, + 1000.0f); + + RNA_def_boolean(ot->srna, + "set_frame_range", + true, + "Set Frame Range", + "Update the scene's start and end frame to match those of the USD archive"); + + RNA_def_boolean(ot->srna, "import_cameras", true, "Cameras", ""); + RNA_def_boolean(ot->srna, "import_curves", true, "Curves", ""); + RNA_def_boolean(ot->srna, "import_lights", true, "Lights", ""); + RNA_def_boolean(ot->srna, "import_materials", true, "Materials", ""); + RNA_def_boolean(ot->srna, "import_meshes", true, "Meshes", ""); + RNA_def_boolean(ot->srna, "import_volumes", true, "Volumes", ""); + + RNA_def_boolean(ot->srna, + "import_subdiv", + false, + "Import Subdivision Scheme", + "Create subdivision surface modifiers based on the USD " + "SubdivisionScheme attribute"); + + RNA_def_boolean(ot->srna, + "import_instance_proxies", + true, + "Import Instance Proxies", + "Create unique Blender objects for USD instances"); + + RNA_def_boolean(ot->srna, + "import_visible_only", + true, + "Visible Primitives Only", + "Do not import invisible USD primitives. " + "Only applies to primitives with a non-animated visibility attribute. " + "Primitives with animated visibility will always be imported"); + + RNA_def_boolean(ot->srna, + "create_collection", + false, + "Create Collection", + "Add all imported objects to a new collection"); + + RNA_def_boolean(ot->srna, "read_mesh_uvs", true, "UV Coordinates", "Read mesh UV coordinates"); + + RNA_def_boolean(ot->srna, "read_mesh_colors", false, "Vertex Colors", "Read mesh vertex colors"); + + RNA_def_string(ot->srna, + "prim_path_mask", + NULL, + 1024, + "Path Mask", + "Import only the subset of the USD scene rooted at the given primitive"); + + RNA_def_boolean(ot->srna, "import_guide", false, "Guide", "Import guide geometry"); + + RNA_def_boolean(ot->srna, "import_proxy", true, "Proxy", "Import proxy geometry"); + + RNA_def_boolean(ot->srna, "import_render", true, "Render", "Import final render geometry"); + + RNA_def_boolean(ot->srna, + "import_usd_preview", + false, + "Import USD Preview", + "Convert UsdPreviewSurface shaders to Principled BSDF shader networks"); + + RNA_def_boolean(ot->srna, + "set_material_blend", + true, + "Set Material Blend", + "If the Import USD Preview option is enabled, " + "the material blend method will automatically be set based on the " + "shader's opacity and opacityThreshold inputs"); + + RNA_def_float(ot->srna, + "light_intensity_scale", + 1.0f, + 0.0001f, + 10000.0f, + "Light Intensity Scale", + "Scale for the intensity of imported lights", + 0.0001f, + 1000.0f); +} + #endif /* WITH_USD */ diff --git a/source/blender/editors/io/io_usd.h b/source/blender/editors/io/io_usd.h index 671984b6f34..7424cc0df32 100644 --- a/source/blender/editors/io/io_usd.h +++ b/source/blender/editors/io/io_usd.h @@ -26,3 +26,5 @@ struct wmOperatorType; void WM_OT_usd_export(struct wmOperatorType *ot); + +void WM_OT_usd_import(struct wmOperatorType *ot); diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h index 3d1391ac2a4..0dbebb1e4c4 100644 --- a/source/blender/io/alembic/ABC_alembic.h +++ b/source/blender/io/alembic/ABC_alembic.h @@ -25,6 +25,7 @@ extern "C" { #endif +struct CacheArchiveHandle; struct CacheReader; struct ListBase; struct Main; @@ -33,8 +34,6 @@ struct Object; struct Scene; struct bContext; -typedef struct AbcArchiveHandle AbcArchiveHandle; - int ABC_get_version(void); struct AlembicExportParams { @@ -100,11 +99,11 @@ bool ABC_import(struct bContext *C, bool validate_meshes, bool as_background_job); -AbcArchiveHandle *ABC_create_handle(struct Main *bmain, - const char *filename, - struct ListBase *object_paths); +struct CacheArchiveHandle *ABC_create_handle(struct Main *bmain, + const char *filename, + struct ListBase *object_paths); -void ABC_free_handle(AbcArchiveHandle *handle); +void ABC_free_handle(struct CacheArchiveHandle *handle); void ABC_get_transform(struct CacheReader *reader, float r_mat_world[4][4], @@ -125,10 +124,10 @@ bool ABC_mesh_topology_changed(struct CacheReader *reader, const float time, const char **err_str); -void CacheReader_incref(struct CacheReader *reader); -void CacheReader_free(struct CacheReader *reader); +void ABC_CacheReader_incref(struct CacheReader *reader); +void ABC_CacheReader_free(struct CacheReader *reader); -struct CacheReader *CacheReader_open_alembic_object(struct AbcArchiveHandle *handle, +struct CacheReader *CacheReader_open_alembic_object(struct CacheArchiveHandle *handle, struct CacheReader *reader, struct Object *object, const char *object_path); diff --git a/source/blender/io/alembic/intern/abc_util.h b/source/blender/io/alembic/intern/abc_util.h index 98f4b0376a7..ced9fde0f85 100644 --- a/source/blender/io/alembic/intern/abc_util.h +++ b/source/blender/io/alembic/intern/abc_util.h @@ -22,15 +22,6 @@ #include #include -/** - * \brief The CacheReader struct is only used for anonymous pointers, - * to interface between C and C++ code. This library only creates - * pointers to AbcObjectReader (or subclasses thereof). - */ -struct CacheReader { - int unused; -}; - using Alembic::Abc::chrono_t; struct ID; diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index e8d70bf3edb..b94b75b2216 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -19,6 +19,7 @@ */ #include "../ABC_alembic.h" +#include "IO_types.h" #include @@ -89,18 +90,14 @@ using Alembic::AbcMaterial::IMaterial; using namespace blender::io::alembic; -struct AbcArchiveHandle { - int unused; -}; - -BLI_INLINE ArchiveReader *archive_from_handle(AbcArchiveHandle *handle) +BLI_INLINE ArchiveReader *archive_from_handle(CacheArchiveHandle *handle) { return reinterpret_cast(handle); } -BLI_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive) +BLI_INLINE CacheArchiveHandle *handle_from_archive(ArchiveReader *archive) { - return reinterpret_cast(archive); + return reinterpret_cast(archive); } //#define USE_NURBS @@ -150,8 +147,8 @@ static bool gather_objects_paths(const IObject &object, ListBase *object_paths) } if (get_path) { - void *abc_path_void = MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"); - AlembicObjectPath *abc_path = static_cast(abc_path_void); + void *abc_path_void = MEM_callocN(sizeof(CacheObjectPath), "CacheObjectPath"); + CacheObjectPath *abc_path = static_cast(abc_path_void); BLI_strncpy(abc_path->path, object.getFullName().c_str(), sizeof(abc_path->path)); BLI_addtail(object_paths, abc_path); @@ -160,9 +157,9 @@ static bool gather_objects_paths(const IObject &object, ListBase *object_paths) return parent_is_part_of_this_object; } -AbcArchiveHandle *ABC_create_handle(struct Main *bmain, - const char *filename, - ListBase *object_paths) +CacheArchiveHandle *ABC_create_handle(struct Main *bmain, + const char *filename, + ListBase *object_paths) { ArchiveReader *archive = new ArchiveReader(bmain, filename); @@ -178,7 +175,7 @@ AbcArchiveHandle *ABC_create_handle(struct Main *bmain, return handle_from_archive(archive); } -void ABC_free_handle(AbcArchiveHandle *handle) +void ABC_free_handle(CacheArchiveHandle *handle) { delete archive_from_handle(handle); } @@ -359,8 +356,8 @@ static std::pair visit_object( readers.push_back(reader); reader->incref(); - AlembicObjectPath *abc_path = static_cast( - MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath")); + CacheObjectPath *abc_path = static_cast( + MEM_callocN(sizeof(CacheObjectPath), "CacheObjectPath")); BLI_strncpy(abc_path->path, full_name.c_str(), sizeof(abc_path->path)); BLI_addtail(&settings.cache_file->object_paths, abc_path); @@ -812,7 +809,7 @@ bool ABC_mesh_topology_changed( /* ************************************************************************** */ -void CacheReader_free(CacheReader *reader) +void ABC_CacheReader_free(CacheReader *reader) { AbcObjectReader *abc_reader = reinterpret_cast(reader); abc_reader->decref(); @@ -822,13 +819,13 @@ void CacheReader_free(CacheReader *reader) } } -void CacheReader_incref(CacheReader *reader) +void ABC_CacheReader_incref(CacheReader *reader) { AbcObjectReader *abc_reader = reinterpret_cast(reader); abc_reader->incref(); } -CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle, +CacheReader *CacheReader_open_alembic_object(CacheArchiveHandle *handle, CacheReader *reader, Object *object, const char *object_path) @@ -847,7 +844,7 @@ CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle, find_iobject(archive->getTop(), iobject, object_path); if (reader) { - CacheReader_free(reader); + ABC_CacheReader_free(reader); } ImportSettings settings; diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index 7e39af32f11..2aaf5d57fd6 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -37,6 +37,7 @@ set(SRC IO_abstract_hierarchy_iterator.h IO_dupli_persistent_id.hh + IO_types.h intern/dupli_parent_finder.hh ) diff --git a/source/blender/io/common/IO_types.h b/source/blender/io/common/IO_types.h new file mode 100644 index 00000000000..4570e29f6ed --- /dev/null +++ b/source/blender/io/common/IO_types.h @@ -0,0 +1,34 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ +#pragma once + +/* The CacheArchiveHandle struct is only used for anonymous pointers, + * to interface between C and C++ code. This is currently used + * to hide pointers to alembic ArchiveReader and USDStageReader. */ +struct CacheArchiveHandle { + int unused; +}; + +/* The CacheReader struct is only used for anonymous pointers, + * to interface between C and C++ code. This is currently used + * to hide pointers to AbcObjectReader and USDPrimReader + * (or subclasses thereof). */ +struct CacheReader { + int unused; +}; diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt index 6ea30f48a13..5499fe36898 100644 --- a/source/blender/io/usd/CMakeLists.txt +++ b/source/blender/io/usd/CMakeLists.txt @@ -56,7 +56,9 @@ set(INC_SYS ) set(SRC - intern/usd_capi.cc + intern/usd_capi_export.cc + intern/usd_capi_import.cc + intern/usd_common.cc intern/usd_hierarchy_iterator.cc intern/usd_writer_abstract.cc intern/usd_writer_camera.cc @@ -66,7 +68,21 @@ set(SRC intern/usd_writer_metaball.cc intern/usd_writer_transform.cc + intern/usd_reader_camera.cc + intern/usd_reader_curve.cc + intern/usd_reader_geom.cc + intern/usd_reader_light.cc + intern/usd_reader_material.cc + intern/usd_reader_mesh.cc + intern/usd_reader_nurbs.cc + intern/usd_reader_prim.cc + intern/usd_reader_stage.cc + intern/usd_reader_xform.cc + intern/usd_reader_volume.cc + usd.h + + intern/usd_common.h intern/usd_exporter_context.h intern/usd_hierarchy_iterator.h intern/usd_writer_abstract.h @@ -76,6 +92,18 @@ set(SRC intern/usd_writer_mesh.h intern/usd_writer_metaball.h intern/usd_writer_transform.h + + intern/usd_reader_camera.h + intern/usd_reader_curve.h + intern/usd_reader_geom.h + intern/usd_reader_light.h + intern/usd_reader_material.h + intern/usd_reader_mesh.h + intern/usd_reader_nurbs.h + intern/usd_reader_prim.h + intern/usd_reader_stage.h + intern/usd_reader_xform.h + intern/usd_reader_volume.h ) set(LIB diff --git a/source/blender/io/usd/intern/usd_capi.cc b/source/blender/io/usd/intern/usd_capi.cc deleted file mode 100644 index dc2b46e5cea..00000000000 --- a/source/blender/io/usd/intern/usd_capi.cc +++ /dev/null @@ -1,252 +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) 2019 Blender Foundation. - * All rights reserved. - */ - -#include "usd.h" -#include "usd_hierarchy_iterator.h" - -#include -#include -#include -#include - -#include "MEM_guardedalloc.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_build.h" -#include "DEG_depsgraph_query.h" - -#include "DNA_scene_types.h" - -#include "BKE_appdir.h" -#include "BKE_blender_version.h" -#include "BKE_context.h" -#include "BKE_global.h" -#include "BKE_scene.h" - -#include "BLI_fileops.h" -#include "BLI_path_util.h" -#include "BLI_string.h" - -#include "WM_api.h" -#include "WM_types.h" - -namespace blender::io::usd { - -struct ExportJobData { - Main *bmain; - Depsgraph *depsgraph; - wmWindowManager *wm; - - char filename[FILE_MAX]; - USDExportParams params; - - bool export_ok; -}; - -static void ensure_usd_plugin_path_registered() -{ - static bool plugin_path_registered = false; - if (plugin_path_registered) { - return; - } - plugin_path_registered = true; - - /* Tell USD which directory to search for its JSON files. If 'datafiles/usd' - * does not exist, the USD library will not be able to read or write any files. */ - const std::string blender_usd_datafiles = BKE_appdir_folder_id(BLENDER_DATAFILES, "usd"); - /* The trailing slash indicates to the USD library that the path is a directory. */ - pxr::PlugRegistry::GetInstance().RegisterPlugins(blender_usd_datafiles + "/"); -} - -static void export_startjob(void *customdata, - /* Cannot be const, this function implements wm_jobs_start_callback. - * NOLINTNEXTLINE: readability-non-const-parameter. */ - short *stop, - short *do_update, - float *progress) -{ - ExportJobData *data = static_cast(customdata); - data->export_ok = false; - - G.is_rendering = true; - WM_set_locked_interface(data->wm, true); - G.is_break = false; - - /* Construct the depsgraph for exporting. */ - Scene *scene = DEG_get_input_scene(data->depsgraph); - if (data->params.visible_objects_only) { - DEG_graph_build_from_view_layer(data->depsgraph); - } - else { - DEG_graph_build_for_all_objects(data->depsgraph); - } - BKE_scene_graph_update_tagged(data->depsgraph, data->bmain); - - *progress = 0.0f; - *do_update = true; - - /* For restoring the current frame after exporting animation is done. */ - const int orig_frame = CFRA; - - pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->filename); - if (!usd_stage) { - /* This happens when the USD JSON files cannot be found. When that happens, - * the USD library doesn't know it has the functionality to write USDA and - * USDC files, and creating a new UsdStage fails. */ - WM_reportf( - RPT_ERROR, "USD Export: unable to find suitable USD plugin to write %s", data->filename); - return; - } - - usd_stage->SetMetadata(pxr::UsdGeomTokens->upAxis, pxr::VtValue(pxr::UsdGeomTokens->z)); - usd_stage->SetMetadata(pxr::UsdGeomTokens->metersPerUnit, - pxr::VtValue(scene->unit.scale_length)); - usd_stage->GetRootLayer()->SetDocumentation(std::string("Blender v") + - BKE_blender_version_string()); - - /* Set up the stage for animated data. */ - if (data->params.export_animation) { - usd_stage->SetTimeCodesPerSecond(FPS); - usd_stage->SetStartTimeCode(scene->r.sfra); - usd_stage->SetEndTimeCode(scene->r.efra); - } - - USDHierarchyIterator iter(data->depsgraph, usd_stage, data->params); - - if (data->params.export_animation) { - /* Writing the animated frames is not 100% of the work, but it's our best guess. */ - float progress_per_frame = 1.0f / std::max(1, (scene->r.efra - scene->r.sfra + 1)); - - for (float frame = scene->r.sfra; frame <= scene->r.efra; frame++) { - if (G.is_break || (stop != nullptr && *stop)) { - break; - } - - /* Update the scene for the next frame to render. */ - scene->r.cfra = static_cast(frame); - scene->r.subframe = frame - scene->r.cfra; - BKE_scene_graph_update_for_newframe(data->depsgraph); - - iter.set_export_frame(frame); - iter.iterate_and_write(); - - *progress += progress_per_frame; - *do_update = true; - } - } - else { - /* If we're not animating, a single iteration over all objects is enough. */ - iter.iterate_and_write(); - } - - iter.release_writers(); - usd_stage->GetRootLayer()->Save(); - - /* Finish up by going back to the keyframe that was current before we started. */ - if (CFRA != orig_frame) { - CFRA = orig_frame; - BKE_scene_graph_update_for_newframe(data->depsgraph); - } - - data->export_ok = true; - *progress = 1.0f; - *do_update = true; -} - -static void export_endjob(void *customdata) -{ - ExportJobData *data = static_cast(customdata); - - DEG_graph_free(data->depsgraph); - - if (!data->export_ok && BLI_exists(data->filename)) { - BLI_delete(data->filename, false, false); - } - - G.is_rendering = false; - WM_set_locked_interface(data->wm, false); -} - -} // namespace blender::io::usd - -bool USD_export(bContext *C, - const char *filepath, - const USDExportParams *params, - bool as_background_job) -{ - ViewLayer *view_layer = CTX_data_view_layer(C); - Scene *scene = CTX_data_scene(C); - - blender::io::usd::ensure_usd_plugin_path_registered(); - - blender::io::usd::ExportJobData *job = static_cast( - MEM_mallocN(sizeof(blender::io::usd::ExportJobData), "ExportJobData")); - - job->bmain = CTX_data_main(C); - job->wm = CTX_wm_manager(C); - job->export_ok = false; - BLI_strncpy(job->filename, filepath, sizeof(job->filename)); - - job->depsgraph = DEG_graph_new(job->bmain, scene, view_layer, params->evaluation_mode); - job->params = *params; - - bool export_ok = false; - if (as_background_job) { - wmJob *wm_job = WM_jobs_get( - job->wm, CTX_wm_window(C), scene, "USD Export", WM_JOB_PROGRESS, WM_JOB_TYPE_ALEMBIC); - - /* setup job */ - WM_jobs_customdata_set(wm_job, job, MEM_freeN); - WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); - WM_jobs_callbacks(wm_job, - blender::io::usd::export_startjob, - nullptr, - nullptr, - blender::io::usd::export_endjob); - - WM_jobs_start(CTX_wm_manager(C), wm_job); - } - else { - /* Fake a job context, so that we don't need NULL pointer checks while exporting. */ - short stop = 0, do_update = 0; - float progress = 0.0f; - - blender::io::usd::export_startjob(job, &stop, &do_update, &progress); - blender::io::usd::export_endjob(job); - export_ok = job->export_ok; - - MEM_freeN(job); - } - - return export_ok; -} - -int USD_get_version(void) -{ - /* USD 19.11 defines: - * - * #define PXR_MAJOR_VERSION 0 - * #define PXR_MINOR_VERSION 19 - * #define PXR_PATCH_VERSION 11 - * #define PXR_VERSION 1911 - * - * So the major version is implicit/invisible in the public version number. - */ - return PXR_VERSION; -} diff --git a/source/blender/io/usd/intern/usd_capi_export.cc b/source/blender/io/usd/intern/usd_capi_export.cc new file mode 100644 index 00000000000..25f12e683cf --- /dev/null +++ b/source/blender/io/usd/intern/usd_capi_export.cc @@ -0,0 +1,238 @@ +/* + * 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) 2019 Blender Foundation. + * All rights reserved. + */ + +#include "usd.h" +#include "usd_common.h" +#include "usd_hierarchy_iterator.h" + +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "DNA_scene_types.h" + +#include "BKE_appdir.h" +#include "BKE_blender_version.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_scene.h" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "WM_api.h" +#include "WM_types.h" + +namespace blender::io::usd { + +struct ExportJobData { + Main *bmain; + Depsgraph *depsgraph; + wmWindowManager *wm; + + char filename[FILE_MAX]; + USDExportParams params; + + bool export_ok; +}; + +static void export_startjob(void *customdata, + /* Cannot be const, this function implements wm_jobs_start_callback. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + short *stop, + short *do_update, + float *progress) +{ + ExportJobData *data = static_cast(customdata); + data->export_ok = false; + + G.is_rendering = true; + WM_set_locked_interface(data->wm, true); + G.is_break = false; + + /* Construct the depsgraph for exporting. */ + Scene *scene = DEG_get_input_scene(data->depsgraph); + if (data->params.visible_objects_only) { + DEG_graph_build_from_view_layer(data->depsgraph); + } + else { + DEG_graph_build_for_all_objects(data->depsgraph); + } + BKE_scene_graph_update_tagged(data->depsgraph, data->bmain); + + *progress = 0.0f; + *do_update = true; + + /* For restoring the current frame after exporting animation is done. */ + const int orig_frame = CFRA; + + pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->filename); + if (!usd_stage) { + /* This happens when the USD JSON files cannot be found. When that happens, + * the USD library doesn't know it has the functionality to write USDA and + * USDC files, and creating a new UsdStage fails. */ + WM_reportf( + RPT_ERROR, "USD Export: unable to find suitable USD plugin to write %s", data->filename); + return; + } + + usd_stage->SetMetadata(pxr::UsdGeomTokens->upAxis, pxr::VtValue(pxr::UsdGeomTokens->z)); + usd_stage->SetMetadata(pxr::UsdGeomTokens->metersPerUnit, + pxr::VtValue(scene->unit.scale_length)); + usd_stage->GetRootLayer()->SetDocumentation(std::string("Blender v") + + BKE_blender_version_string()); + + /* Set up the stage for animated data. */ + if (data->params.export_animation) { + usd_stage->SetTimeCodesPerSecond(FPS); + usd_stage->SetStartTimeCode(scene->r.sfra); + usd_stage->SetEndTimeCode(scene->r.efra); + } + + USDHierarchyIterator iter(data->depsgraph, usd_stage, data->params); + + if (data->params.export_animation) { + /* Writing the animated frames is not 100% of the work, but it's our best guess. */ + float progress_per_frame = 1.0f / std::max(1, (scene->r.efra - scene->r.sfra + 1)); + + for (float frame = scene->r.sfra; frame <= scene->r.efra; frame++) { + if (G.is_break || (stop != nullptr && *stop)) { + break; + } + + /* Update the scene for the next frame to render. */ + scene->r.cfra = static_cast(frame); + scene->r.subframe = frame - scene->r.cfra; + BKE_scene_graph_update_for_newframe(data->depsgraph); + + iter.set_export_frame(frame); + iter.iterate_and_write(); + + *progress += progress_per_frame; + *do_update = true; + } + } + else { + /* If we're not animating, a single iteration over all objects is enough. */ + iter.iterate_and_write(); + } + + iter.release_writers(); + usd_stage->GetRootLayer()->Save(); + + /* Finish up by going back to the keyframe that was current before we started. */ + if (CFRA != orig_frame) { + CFRA = orig_frame; + BKE_scene_graph_update_for_newframe(data->depsgraph); + } + + data->export_ok = true; + *progress = 1.0f; + *do_update = true; +} + +static void export_endjob(void *customdata) +{ + ExportJobData *data = static_cast(customdata); + + DEG_graph_free(data->depsgraph); + + if (!data->export_ok && BLI_exists(data->filename)) { + BLI_delete(data->filename, false, false); + } + + G.is_rendering = false; + WM_set_locked_interface(data->wm, false); +} + +} // namespace blender::io::usd + +bool USD_export(bContext *C, + const char *filepath, + const USDExportParams *params, + bool as_background_job) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + Scene *scene = CTX_data_scene(C); + + blender::io::usd::ensure_usd_plugin_path_registered(); + + blender::io::usd::ExportJobData *job = static_cast( + MEM_mallocN(sizeof(blender::io::usd::ExportJobData), "ExportJobData")); + + job->bmain = CTX_data_main(C); + job->wm = CTX_wm_manager(C); + job->export_ok = false; + BLI_strncpy(job->filename, filepath, sizeof(job->filename)); + + job->depsgraph = DEG_graph_new(job->bmain, scene, view_layer, params->evaluation_mode); + job->params = *params; + + bool export_ok = false; + if (as_background_job) { + wmJob *wm_job = WM_jobs_get( + job->wm, CTX_wm_window(C), scene, "USD Export", WM_JOB_PROGRESS, WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, MEM_freeN); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); + WM_jobs_callbacks(wm_job, + blender::io::usd::export_startjob, + nullptr, + nullptr, + blender::io::usd::export_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + /* Fake a job context, so that we don't need NULL pointer checks while exporting. */ + short stop = 0, do_update = 0; + float progress = 0.0f; + + blender::io::usd::export_startjob(job, &stop, &do_update, &progress); + blender::io::usd::export_endjob(job); + export_ok = job->export_ok; + + MEM_freeN(job); + } + + return export_ok; +} + +int USD_get_version(void) +{ + /* USD 19.11 defines: + * + * #define PXR_MAJOR_VERSION 0 + * #define PXR_MINOR_VERSION 19 + * #define PXR_PATCH_VERSION 11 + * #define PXR_VERSION 1911 + * + * So the major version is implicit/invisible in the public version number. + */ + return PXR_VERSION; +} diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc new file mode 100644 index 00000000000..8255fca284c --- /dev/null +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -0,0 +1,578 @@ +/* + * 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) 2019 Blender Foundation. + * All rights reserved. + */ + +#include "IO_types.h" +#include "usd.h" +#include "usd_common.h" +#include "usd_hierarchy_iterator.h" +#include "usd_reader_geom.h" +#include "usd_reader_prim.h" +#include "usd_reader_stage.h" + +#include "BKE_appdir.h" +#include "BKE_blender_version.h" +#include "BKE_cachefile.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_layer.h" +#include "BKE_lib_id.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_scene.h" +#include "BKE_world.h" + +#include "BLI_fileops.h" +#include "BLI_listbase.h" +#include "BLI_math_matrix.h" +#include "BLI_math_rotation.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "DNA_cachefile_types.h" +#include "DNA_collection_types.h" +#include "DNA_node_types.h" +#include "DNA_scene_types.h" +#include "DNA_world_types.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include +#include +#include +#include +#include + +#include + +namespace blender::io::usd { + +static CacheArchiveHandle *handle_from_stage_reader(USDStageReader *reader) +{ + return reinterpret_cast(reader); +} + +static USDStageReader *stage_reader_from_handle(CacheArchiveHandle *handle) +{ + return reinterpret_cast(handle); +} + +static bool gather_objects_paths(const pxr::UsdPrim &object, ListBase *object_paths) +{ + if (!object.IsValid()) { + return false; + } + + for (const pxr::UsdPrim &childPrim : object.GetChildren()) { + gather_objects_paths(childPrim, object_paths); + } + + void *usd_path_void = MEM_callocN(sizeof(CacheObjectPath), "CacheObjectPath"); + CacheObjectPath *usd_path = static_cast(usd_path_void); + + BLI_strncpy(usd_path->path, object.GetPrimPath().GetString().c_str(), sizeof(usd_path->path)); + BLI_addtail(object_paths, usd_path); + + return true; +} + +/* Update the given import settings with the global rotation matrix to orient + * imported objects with Z-up, if necessary */ +static void convert_to_z_up(pxr::UsdStageRefPtr stage, ImportSettings *r_settings) +{ + if (!stage || pxr::UsdGeomGetStageUpAxis(stage) == pxr::UsdGeomTokens->z) { + return; + } + + if (!r_settings) { + return; + } + + r_settings->do_convert_mat = true; + + /* Rotate 90 degrees about the X-axis. */ + float rmat[3][3]; + float axis[3] = {1.0f, 0.0f, 0.0f}; + axis_angle_normalized_to_mat3(rmat, axis, M_PI / 2.0f); + + unit_m4(r_settings->conversion_mat); + copy_m4_m3(r_settings->conversion_mat, rmat); +} + +enum { + USD_NO_ERROR = 0, + USD_ARCHIVE_FAIL, +}; + +struct ImportJobData { + Main *bmain; + Scene *scene; + ViewLayer *view_layer; + wmWindowManager *wm; + + char filename[1024]; + USDImportParams params; + ImportSettings settings; + + USDStageReader *archive; + + short *stop; + short *do_update; + float *progress; + + char error_code; + bool was_canceled; + bool import_ok; +}; + +static void import_startjob(void *customdata, short *stop, short *do_update, float *progress) +{ + ImportJobData *data = static_cast(customdata); + + data->stop = stop; + data->do_update = do_update; + data->progress = progress; + data->was_canceled = false; + data->archive = nullptr; + + WM_set_locked_interface(data->wm, true); + G.is_break = false; + + if (data->params.create_collection) { + char display_name[1024]; + BLI_path_to_display_name( + display_name, strlen(data->filename), BLI_path_basename(data->filename)); + Collection *import_collection = BKE_collection_add( + data->bmain, data->scene->master_collection, display_name); + id_fake_user_set(&import_collection->id); + + DEG_id_tag_update(&import_collection->id, ID_RECALC_COPY_ON_WRITE); + DEG_relations_tag_update(data->bmain); + + WM_main_add_notifier(NC_SCENE | ND_LAYER, nullptr); + + data->view_layer->active_collection = BKE_layer_collection_first_from_scene_collection( + data->view_layer, import_collection); + } + + BLI_path_abs(data->filename, BKE_main_blendfile_path_from_global()); + + CacheFile *cache_file = static_cast( + BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename))); + + /* Decrement the ID ref-count because it is going to be incremented for each + * modifier and constraint that it will be attached to, so since currently + * it is not used by anyone, its use count will off by one. */ + id_us_min(&cache_file->id); + + cache_file->is_sequence = data->params.is_sequence; + cache_file->scale = data->params.scale; + STRNCPY(cache_file->filepath, data->filename); + + data->settings.cache_file = cache_file; + + *data->do_update = true; + *data->progress = 0.05f; + + if (G.is_break) { + data->was_canceled = true; + return; + } + + *data->do_update = true; + *data->progress = 0.1f; + + pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(data->filename); + + if (!stage) { + WM_reportf(RPT_ERROR, "USD Import: unable to open stage to read %s", data->filename); + data->import_ok = false; + return; + } + + convert_to_z_up(stage, &data->settings); + + /* Set up the stage for animated data. */ + if (data->params.set_frame_range) { + data->scene->r.sfra = stage->GetStartTimeCode(); + data->scene->r.efra = stage->GetEndTimeCode(); + } + + *data->progress = 0.15f; + + USDStageReader *archive = new USDStageReader(stage, data->params, data->settings); + + data->archive = archive; + + archive->collect_readers(data->bmain); + + *data->progress = 0.2f; + + const float size = static_cast(archive->readers().size()); + size_t i = 0; + + /* Setup parenthood */ + + for (USDPrimReader *reader : archive->readers()) { + + if (!reader) { + continue; + } + + Object *ob = reader->object(); + + reader->read_object_data(data->bmain, 0.0); + + USDPrimReader *parent = reader->parent(); + + if (parent == nullptr) { + ob->parent = nullptr; + } + else { + ob->parent = parent->object(); + } + + *data->progress = 0.2f + 0.8f * (++i / size); + *data->do_update = true; + + if (G.is_break) { + data->was_canceled = true; + return; + } + } + + data->import_ok = !data->was_canceled; + + *progress = 1.0f; + *do_update = true; +} + +static void import_endjob(void *customdata) +{ + ImportJobData *data = static_cast(customdata); + + /* Delete objects on cancellation. */ + if (data->was_canceled && data->archive) { + + for (USDPrimReader *reader : data->archive->readers()) { + + if (!reader) { + continue; + } + + /* It's possible that cancellation occurred between the creation of + * the reader and the creation of the Blender object. */ + if (Object *ob = reader->object()) { + BKE_id_free_us(data->bmain, ob); + } + } + } + else if (data->archive) { + /* Add object to scene. */ + Base *base; + LayerCollection *lc; + ViewLayer *view_layer = data->view_layer; + + BKE_view_layer_base_deselect_all(view_layer); + + lc = BKE_layer_collection_get_active(view_layer); + + for (USDPrimReader *reader : data->archive->readers()) { + + if (!reader) { + continue; + } + + Object *ob = reader->object(); + + if (!ob) { + continue; + } + + BKE_collection_object_add(data->bmain, lc->collection, ob); + + base = BKE_view_layer_base_find(view_layer, ob); + /* TODO: is setting active needed? */ + BKE_view_layer_base_select_and_set_active(view_layer, base); + + DEG_id_tag_update(&lc->collection->id, ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update_ex(data->bmain, + &ob->id, + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION | + ID_RECALC_BASE_FLAGS); + } + + DEG_id_tag_update(&data->scene->id, ID_RECALC_BASE_FLAGS); + DEG_relations_tag_update(data->bmain); + } + + WM_set_locked_interface(data->wm, false); + + switch (data->error_code) { + default: + case USD_NO_ERROR: + data->import_ok = !data->was_canceled; + break; + case USD_ARCHIVE_FAIL: + WM_report(RPT_ERROR, "Could not open USD archive for reading! See console for detail."); + break; + } + + WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene); +} + +static void import_freejob(void *user_data) +{ + ImportJobData *data = static_cast(user_data); + + delete data->archive; + delete data; +} + +} // namespace blender::io::usd + +using namespace blender::io::usd; + +bool USD_import(struct bContext *C, + const char *filepath, + const USDImportParams *params, + bool as_background_job) +{ + blender::io::usd::ensure_usd_plugin_path_registered(); + + /* Using new here since MEM_* funcs do not call ctor to properly initialize + * data. */ + ImportJobData *job = new ImportJobData(); + job->bmain = CTX_data_main(C); + job->scene = CTX_data_scene(C); + job->view_layer = CTX_data_view_layer(C); + job->wm = CTX_wm_manager(C); + job->import_ok = false; + BLI_strncpy(job->filename, filepath, 1024); + + job->settings.scale = params->scale; + job->settings.sequence_offset = params->offset; + job->settings.is_sequence = params->is_sequence; + job->settings.sequence_len = params->sequence_len; + job->settings.validate_meshes = params->validate_meshes; + job->settings.sequence_len = params->sequence_len; + job->error_code = USD_NO_ERROR; + job->was_canceled = false; + job->archive = nullptr; + + job->params = *params; + + G.is_break = false; + + bool import_ok = false; + if (as_background_job) { + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + job->scene, + "USD Import", + WM_JOB_PROGRESS, + WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, import_freejob); + WM_jobs_timer(wm_job, 0.1, NC_SCENE, NC_SCENE); + WM_jobs_callbacks(wm_job, import_startjob, nullptr, nullptr, import_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + /* Fake a job context, so that we don't need NULL pointer checks while importing. */ + short stop = 0, do_update = 0; + float progress = 0.f; + + import_startjob(job, &stop, &do_update, &progress); + import_endjob(job); + import_ok = job->import_ok; + + import_freejob(job); + } + + return import_ok; +} + +/* TODO(makowalski): Extend this function with basic validation that the + * USD reader is compatible with the type of the given (currently unused) 'ob' + * Object parameter, similar to the logic in get_abc_reader() in the + * Alembic importer code. */ +static USDPrimReader *get_usd_reader(CacheReader *reader, Object * /* ob */, const char **err_str) +{ + USDPrimReader *usd_reader = reinterpret_cast(reader); + pxr::UsdPrim iobject = usd_reader->prim(); + + if (!iobject.IsValid()) { + *err_str = "Invalid object: verify object path"; + return nullptr; + } + + return usd_reader; +} + +struct Mesh *USD_read_mesh(struct CacheReader *reader, + struct Object *ob, + struct Mesh *existing_mesh, + const float time, + const char **err_str, + const int read_flag) +{ + USDGeomReader *usd_reader = dynamic_cast(get_usd_reader(reader, ob, err_str)); + + if (usd_reader == nullptr) { + return nullptr; + } + + return usd_reader->read_mesh(existing_mesh, time, read_flag, err_str); +} + +bool USD_mesh_topology_changed( + CacheReader *reader, Object *ob, Mesh *existing_mesh, const float time, const char **err_str) +{ + USDGeomReader *usd_reader = dynamic_cast(get_usd_reader(reader, ob, err_str)); + + if (usd_reader == nullptr) { + return false; + } + + return usd_reader->topology_changed(existing_mesh, time); +} + +void USD_CacheReader_incref(CacheReader *reader) +{ + USDPrimReader *usd_reader = reinterpret_cast(reader); + usd_reader->incref(); +} + +CacheReader *CacheReader_open_usd_object(CacheArchiveHandle *handle, + CacheReader *reader, + Object *object, + const char *object_path) +{ + if (object_path[0] == '\0') { + return reader; + } + + USDStageReader *archive = stage_reader_from_handle(handle); + + if (!archive || !archive->valid()) { + return reader; + } + + pxr::UsdPrim prim = archive->stage()->GetPrimAtPath(pxr::SdfPath(object_path)); + + if (reader) { + USD_CacheReader_free(reader); + } + + /* TODO(makowalski): The handle does not have the proper import params or settings. */ + USDPrimReader *usd_reader = archive->create_reader(prim); + + if (usd_reader == nullptr) { + /* This object is not supported */ + return nullptr; + } + usd_reader->object(object); + usd_reader->incref(); + + return reinterpret_cast(usd_reader); +} + +void USD_CacheReader_free(CacheReader *reader) +{ + USDPrimReader *usd_reader = reinterpret_cast(reader); + usd_reader->decref(); + + if (usd_reader->refcount() == 0) { + delete usd_reader; + } +} + +CacheArchiveHandle *USD_create_handle(struct Main * /*bmain*/, + const char *filename, + ListBase *object_paths) +{ + pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(filename); + + if (!stage) { + return nullptr; + } + + USDImportParams params{}; + + blender::io::usd::ImportSettings settings{}; + convert_to_z_up(stage, &settings); + + USDStageReader *stage_reader = new USDStageReader(stage, params, settings); + + if (object_paths) { + gather_objects_paths(stage->GetPseudoRoot(), object_paths); + } + + return handle_from_stage_reader(stage_reader); +} + +void USD_free_handle(CacheArchiveHandle *handle) +{ + USDStageReader *stage_reader = stage_reader_from_handle(handle); + delete stage_reader; +} + +void USD_get_transform(struct CacheReader *reader, + float r_mat_world[4][4], + float time, + float scale) +{ + if (!reader) { + return; + } + USDXformReader *usd_reader = reinterpret_cast(reader); + + bool is_constant = false; + + /* Convert from the local matrix we obtain from USD to world coordinates + * for Blender. This conversion is done here rather than by Blender due to + * work around the non-standard interpretation of CONSTRAINT_SPACE_LOCAL in + * BKE_constraint_mat_convertspace(). */ + Object *object = usd_reader->object(); + if (object->parent == nullptr) { + /* No parent, so local space is the same as world space. */ + usd_reader->read_matrix(r_mat_world, time, scale, &is_constant); + return; + } + + float mat_parent[4][4]; + BKE_object_get_parent_matrix(object, object->parent, mat_parent); + + float mat_local[4][4]; + usd_reader->read_matrix(mat_local, time, scale, &is_constant); + mul_m4_m4m4(r_mat_world, mat_parent, object->parentinv); + mul_m4_m4m4(r_mat_world, r_mat_world, mat_local); +} diff --git a/source/blender/io/usd/intern/usd_common.cc b/source/blender/io/usd/intern/usd_common.cc new file mode 100644 index 00000000000..0cd9c3019ef --- /dev/null +++ b/source/blender/io/usd/intern/usd_common.cc @@ -0,0 +1,43 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +#include "usd_common.h" + +#include + +#include "BKE_appdir.h" + +namespace blender::io::usd { + +void ensure_usd_plugin_path_registered() +{ + static bool plugin_path_registered = false; + if (plugin_path_registered) { + return; + } + plugin_path_registered = true; + + /* Tell USD which directory to search for its JSON files. If 'datafiles/usd' + * does not exist, the USD library will not be able to read or write any files. */ + const std::string blender_usd_datafiles = BKE_appdir_folder_id(BLENDER_DATAFILES, "usd"); + /* The trailing slash indicates to the USD library that the path is a directory. */ + pxr::PlugRegistry::GetInstance().RegisterPlugins(blender_usd_datafiles + "/"); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_common.h b/source/blender/io/usd/intern/usd_common.h new file mode 100644 index 00000000000..36667bbc6b1 --- /dev/null +++ b/source/blender/io/usd/intern/usd_common.h @@ -0,0 +1,25 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ +#pragma once + +namespace blender::io::usd { + +void ensure_usd_plugin_path_registered(); + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_camera.cc b/source/blender/io/usd/intern/usd_reader_camera.cc new file mode 100644 index 00000000000..2732ed5770d --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_camera.cc @@ -0,0 +1,100 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_camera.h" + +#include "DNA_camera_types.h" +#include "DNA_object_types.h" + +#include "BKE_camera.h" +#include "BKE_object.h" + +#include "BLI_math.h" + +#include +#include + +namespace blender::io::usd { + +void USDCameraReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + Camera *bcam = static_cast(BKE_camera_add(bmain, name_.c_str())); + + object_ = BKE_object_add_only_object(bmain, OB_CAMERA, name_.c_str()); + object_->data = bcam; +} + +void USDCameraReader::read_object_data(Main *bmain, const double motionSampleTime) +{ + Camera *bcam = (Camera *)object_->data; + + pxr::UsdGeomCamera cam_prim(prim_); + + if (!cam_prim) { + return; + } + + pxr::VtValue val; + cam_prim.GetFocalLengthAttr().Get(&val, motionSampleTime); + pxr::VtValue verApOffset; + cam_prim.GetVerticalApertureOffsetAttr().Get(&verApOffset, motionSampleTime); + pxr::VtValue horApOffset; + cam_prim.GetHorizontalApertureOffsetAttr().Get(&horApOffset, motionSampleTime); + pxr::VtValue clippingRangeVal; + cam_prim.GetClippingRangeAttr().Get(&clippingRangeVal, motionSampleTime); + pxr::VtValue focalDistanceVal; + cam_prim.GetFocusDistanceAttr().Get(&focalDistanceVal, motionSampleTime); + pxr::VtValue fstopVal; + cam_prim.GetFStopAttr().Get(&fstopVal, motionSampleTime); + pxr::VtValue projectionVal; + cam_prim.GetProjectionAttr().Get(&projectionVal, motionSampleTime); + pxr::VtValue verAp; + cam_prim.GetVerticalApertureAttr().Get(&verAp, motionSampleTime); + pxr::VtValue horAp; + cam_prim.GetHorizontalApertureAttr().Get(&horAp, motionSampleTime); + + bcam->lens = val.Get(); + /* TODO(makowalski) */ +#if 0 + bcam->sensor_x = 0.0f; + bcam->sensor_y = 0.0f; +#endif + bcam->shiftx = verApOffset.Get(); + bcam->shifty = horApOffset.Get(); + + bcam->type = (projectionVal.Get().GetString() == "perspective") ? CAM_PERSP : + CAM_ORTHO; + + /* Calling UncheckedGet() to silence compiler warnings. */ + bcam->clip_start = max_ff(0.1f, clippingRangeVal.UncheckedGet()[0]); + bcam->clip_end = clippingRangeVal.UncheckedGet()[1]; + + bcam->dof.focus_distance = focalDistanceVal.Get(); + bcam->dof.aperture_fstop = static_cast(fstopVal.Get()); + + if (bcam->type == CAM_ORTHO) { + bcam->ortho_scale = max_ff(verAp.Get(), horAp.Get()); + } + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_camera.h b/source/blender/io/usd/intern/usd_reader_camera.h new file mode 100644 index 00000000000..a4156aa8be2 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_camera.h @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_xform.h" + +namespace blender::io::usd { + +class USDCameraReader : public USDXformReader { + + public: + USDCameraReader(const pxr::UsdPrim &object, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDXformReader(object, import_params, settings) + { + } + + void create_object(Main *bmain, double motionSampleTime) override; + void read_object_data(Main *bmain, double motionSampleTime) override; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc new file mode 100644 index 00000000000..31ecf27cf7e --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_curve.cc @@ -0,0 +1,256 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation, + * Copyright (C) 2016 Kévin Dietrich. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_curve.h" + +#include "BKE_curve.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +#include "BLI_listbase.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "MEM_guardedalloc.h" + +#include +#include +#include + +#include +#include + +namespace blender::io::usd { + +void USDCurvesReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); + + curve_->flag |= CU_DEFORM_FILL | CU_3D; + curve_->actvert = CU_ACT_NONE; + curve_->resolu = 2; + + object_ = BKE_object_add_only_object(bmain, OB_CURVE, name_.c_str()); + object_->data = curve_; +} + +void USDCurvesReader::read_object_data(Main *bmain, double motionSampleTime) +{ + Curve *cu = (Curve *)object_->data; + read_curve_sample(cu, motionSampleTime); + + if (curve_prim_.GetPointsAttr().ValueMightBeTimeVarying()) { + add_cache_modifier(); + } + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +void USDCurvesReader::read_curve_sample(Curve *cu, const double motionSampleTime) +{ + curve_prim_ = pxr::UsdGeomBasisCurves(prim_); + + if (!curve_prim_) { + return; + } + + pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr(); + pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr(); + pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr(); + + pxr::VtIntArray usdCounts; + + vertexAttr.Get(&usdCounts, motionSampleTime); + int num_subcurves = usdCounts.size(); + + pxr::VtVec3fArray usdPoints; + pointsAttr.Get(&usdPoints, motionSampleTime); + + pxr::VtFloatArray usdWidths; + widthsAttr.Get(&usdWidths, motionSampleTime); + + pxr::UsdAttribute basisAttr = curve_prim_.GetBasisAttr(); + pxr::TfToken basis; + basisAttr.Get(&basis, motionSampleTime); + + pxr::UsdAttribute typeAttr = curve_prim_.GetTypeAttr(); + pxr::TfToken type; + typeAttr.Get(&type, motionSampleTime); + + pxr::UsdAttribute wrapAttr = curve_prim_.GetWrapAttr(); + pxr::TfToken wrap; + wrapAttr.Get(&wrap, motionSampleTime); + + pxr::VtVec3fArray usdNormals; + curve_prim_.GetNormalsAttr().Get(&usdNormals, motionSampleTime); + + /* If normals, extrude, else bevel. + * Perhaps to be replaced by Blender/USD Schema. */ + if (!usdNormals.empty()) { + /* Set extrusion to 1.0f. */ + curve_->ext1 = 1.0f; + } + else { + /* Set bevel depth to 1.0f. */ + curve_->ext2 = 1.0f; + } + + size_t idx = 0; + for (size_t i = 0; i < num_subcurves; i++) { + const int num_verts = usdCounts[i]; + Nurb *nu = static_cast(MEM_callocN(sizeof(Nurb), __func__)); + + if (basis == pxr::UsdGeomTokens->bspline) { + nu->flag = CU_SMOOTH; + nu->type = CU_NURBS; + } + else if (basis == pxr::UsdGeomTokens->bezier) { + /* TODO(makowalski): Beziers are not properly imported as beziers. */ + nu->type = CU_POLY; + } + else if (basis.IsEmpty()) { + nu->type = CU_POLY; + } + nu->resolu = cu->resolu; + nu->resolv = cu->resolv; + + nu->pntsu = num_verts; + nu->pntsv = 1; + + if (type == pxr::UsdGeomTokens->cubic) { + nu->orderu = 4; + } + else if (type == pxr::UsdGeomTokens->linear) { + nu->orderu = 2; + } + + if (wrap == pxr::UsdGeomTokens->periodic) { + nu->flagu |= CU_NURB_CYCLIC; + } + else if (wrap == pxr::UsdGeomTokens->pinned) { + nu->flagu |= CU_NURB_ENDPOINT; + } + + float weight = 1.0f; + + nu->bp = static_cast(MEM_callocN(sizeof(BPoint) * nu->pntsu, __func__)); + BPoint *bp = nu->bp; + + for (int j = 0; j < nu->pntsu; j++, bp++, idx++) { + bp->vec[0] = (float)usdPoints[idx][0]; + bp->vec[1] = (float)usdPoints[idx][1]; + bp->vec[2] = (float)usdPoints[idx][2]; + bp->vec[3] = weight; + bp->f1 = SELECT; + bp->weight = weight; + + float radius = curve_->width; + if (idx < usdWidths.size()) { + radius = usdWidths[idx]; + } + + bp->radius = radius; + } + + BKE_nurb_knot_calc_u(nu); + BKE_nurb_knot_calc_v(nu); + + BLI_addtail(BKE_curve_nurbs_get(cu), nu); + } +} + +Mesh *USDCurvesReader::read_mesh(struct Mesh *existing_mesh, + const double motionSampleTime, + const int /* read_flag */, + const char ** /* err_str */) +{ + if (!curve_prim_) { + return existing_mesh; + } + + pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr(); + pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr(); + pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr(); + + pxr::VtIntArray usdCounts; + + vertexAttr.Get(&usdCounts, motionSampleTime); + int num_subcurves = usdCounts.size(); + + pxr::VtVec3fArray usdPoints; + pointsAttr.Get(&usdPoints, motionSampleTime); + + int vertex_idx = 0; + int curve_idx; + Curve *curve = static_cast(object_->data); + + const int curve_count = BLI_listbase_count(&curve->nurb); + bool same_topology = curve_count == num_subcurves; + + if (same_topology) { + Nurb *nurbs = static_cast(curve->nurb.first); + for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { + const int num_in_usd = usdCounts[curve_idx]; + const int num_in_blender = nurbs->pntsu; + + if (num_in_usd != num_in_blender) { + same_topology = false; + break; + } + } + } + + if (!same_topology) { + BKE_nurbList_free(&curve->nurb); + read_curve_sample(curve, motionSampleTime); + } + else { + Nurb *nurbs = static_cast(curve->nurb.first); + for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { + const int totpoint = usdCounts[curve_idx]; + + if (nurbs->bp) { + BPoint *point = nurbs->bp; + + for (int i = 0; i < totpoint; i++, point++, vertex_idx++) { + point->vec[0] = usdPoints[vertex_idx][0]; + point->vec[1] = usdPoints[vertex_idx][1]; + point->vec[2] = usdPoints[vertex_idx][2]; + } + } + else if (nurbs->bezt) { + BezTriple *bezier = nurbs->bezt; + + for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) { + bezier->vec[1][0] = usdPoints[vertex_idx][0]; + bezier->vec[1][1] = usdPoints[vertex_idx][1]; + bezier->vec[1][2] = usdPoints[vertex_idx][2]; + } + } + } + } + + return BKE_mesh_new_nomain_from_curve(object_); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_curve.h b/source/blender/io/usd/intern/usd_reader_curve.h new file mode 100644 index 00000000000..1e676bbbd02 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_curve.h @@ -0,0 +1,62 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation, + * Copyright (C) 2016 Kévin Dietrich. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_geom.h" + +#include "pxr/usd/usdGeom/basisCurves.h" + +struct Curve; + +namespace blender::io::usd { + +class USDCurvesReader : public USDGeomReader { + protected: + pxr::UsdGeomBasisCurves curve_prim_; + Curve *curve_; + + public: + USDCurvesReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDGeomReader(prim, import_params, settings), curve_prim_(prim), curve_(nullptr) + { + } + + bool valid() const override + { + return static_cast(curve_prim_); + } + + void create_object(Main *bmain, double motionSampleTime) override; + void read_object_data(Main *bmain, double motionSampleTime) override; + + void read_curve_sample(Curve *cu, double motionSampleTime); + + Mesh *read_mesh(struct Mesh *existing_mesh, + double motionSampleTime, + int read_flag, + const char **err_str) override; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_geom.cc b/source/blender/io/usd/intern/usd_reader_geom.cc new file mode 100644 index 00000000000..23c5f57120c --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_geom.cc @@ -0,0 +1,59 @@ +/* + * 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) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_geom.h" + +#include "BKE_lib_id.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_math_geom.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "DNA_cachefile_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" /* for FILE_MAX */ + +namespace blender::io::usd { + +void USDGeomReader::add_cache_modifier() +{ + ModifierData *md = BKE_modifier_new(eModifierType_MeshSequenceCache); + BLI_addtail(&object_->modifiers, md); + + MeshSeqCacheModifierData *mcmd = reinterpret_cast(md); + + mcmd->cache_file = settings_->cache_file; + id_us_plus(&mcmd->cache_file->id); + mcmd->read_flag = import_params_.mesh_read_flag; + + BLI_strncpy(mcmd->object_path, prim_.GetPath().GetString().c_str(), FILE_MAX); +} + +void USDGeomReader::add_subdiv_modifier() +{ + ModifierData *md = BKE_modifier_new(eModifierType_Subsurf); + BLI_addtail(&object_->modifiers, md); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_geom.h b/source/blender/io/usd/intern/usd_reader_geom.h new file mode 100644 index 00000000000..99e22248f2b --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_geom.h @@ -0,0 +1,52 @@ +/* + * 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) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_xform.h" + +struct Mesh; + +namespace blender::io::usd { + +class USDGeomReader : public USDXformReader { + + public: + USDGeomReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDXformReader(prim, import_params, settings) + { + } + + virtual Mesh *read_mesh(struct Mesh *existing_mesh, + double motionSampleTime, + int read_flag, + const char **err_str) = 0; + + virtual bool topology_changed(Mesh * /* existing_mesh */, double /* motionSampleTime */) + { + return true; + } + + void add_cache_modifier(); + void add_subdiv_modifier(); +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_instance.cc b/source/blender/io/usd/intern/usd_reader_instance.cc new file mode 100644 index 00000000000..e645b0237b9 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_instance.cc @@ -0,0 +1,64 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ + +#include "usd_reader_instance.h" + +#include "BKE_object.h" +#include "DNA_object_types.h" + +#include + +namespace blender::io::usd { + +USDInstanceReader::USDInstanceReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDXformReader(prim, import_params, settings) +{ +} + +bool USDInstanceReader::valid() const +{ + return prim_.IsValid() && prim_.IsInstance(); +} + +void USDInstanceReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + this->object_ = BKE_object_add_only_object(bmain, OB_EMPTY, name_.c_str()); + this->object_->data = nullptr; + this->object_->transflag |= OB_DUPLICOLLECTION; +} + +void USDInstanceReader::set_instance_collection(Collection *coll) +{ + if (this->object_) { + this->object_->instance_collection = coll; + } +} + +pxr::SdfPath USDInstanceReader::proto_path() const +{ + if (pxr::UsdPrim master = prim_.GetMaster()) { + return master.GetPath(); + } + + return pxr::SdfPath(); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_instance.h b/source/blender/io/usd/intern/usd_reader_instance.h new file mode 100644 index 00000000000..efc1c69a7dd --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_instance.h @@ -0,0 +1,47 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ +#pragma once + +#include "usd_reader_xform.h" + +#include + +struct Collection; + +namespace blender::io::usd { + +/* Wraps the UsdGeomXform schema. Creates a Blender Empty object. */ + +class USDInstanceReader : public USDXformReader { + + public: + USDInstanceReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings); + + bool valid() const override; + + void create_object(Main *bmain, double motionSampleTime) override; + + void set_instance_collection(Collection *coll); + + pxr::SdfPath proto_path() const; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_light.cc b/source/blender/io/usd/intern/usd_reader_light.cc new file mode 100644 index 00000000000..fda0c17968a --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_light.cc @@ -0,0 +1,252 @@ +/* + * 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) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_light.h" + +#include "BKE_light.h" +#include "BKE_object.h" + +#include "DNA_light_types.h" +#include "DNA_object_types.h" + +#include + +#include +#include +#include +#include +#include + +#include + +namespace blender::io::usd { + +void USDLightReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + Light *blight = static_cast(BKE_light_add(bmain, name_.c_str())); + + object_ = BKE_object_add_only_object(bmain, OB_LAMP, name_.c_str()); + object_->data = blight; +} + +void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime) +{ + Light *blight = (Light *)object_->data; + + if (blight == nullptr) { + return; + } + + if (!prim_) { + return; + } + + pxr::UsdLuxLight light_prim(prim_); + + if (!light_prim) { + return; + } + + pxr::UsdLuxShapingAPI shaping_api(light_prim); + + /* Set light type. */ + + if (prim_.IsA()) { + blight->type = LA_AREA; + blight->area_shape = LA_AREA_DISK; + /* Ellipse lights are not currently supported */ + } + else if (prim_.IsA()) { + blight->type = LA_AREA; + blight->area_shape = LA_AREA_RECT; + } + else if (prim_.IsA()) { + blight->type = LA_LOCAL; + + if (shaping_api && shaping_api.GetShapingConeAngleAttr().IsAuthored()) { + blight->type = LA_SPOT; + } + } + else if (prim_.IsA()) { + blight->type = LA_SUN; + } + + /* Set light values. */ + + if (pxr::UsdAttribute intensity_attr = light_prim.GetIntensityAttr()) { + float intensity = 0.0f; + if (intensity_attr.Get(&intensity, motionSampleTime)) { + blight->energy = intensity * this->import_params_.light_intensity_scale; + } + } + + /* TODO(makowalsk): Not currently supported. */ +#if 0 + pxr::VtValue exposure; + light_prim.GetExposureAttr().Get(&exposure, motionSampleTime); +#endif + + /* TODO(makowalsk): Not currently supported */ +#if 0 + pxr::VtValue diffuse; + light_prim.GetDiffuseAttr().Get(&diffuse, motionSampleTime); +#endif + + if (pxr::UsdAttribute spec_attr = light_prim.GetSpecularAttr()) { + float spec = 0.0f; + if (spec_attr.Get(&spec, motionSampleTime)) { + blight->spec_fac = spec; + } + } + + if (pxr::UsdAttribute color_attr = light_prim.GetColorAttr()) { + pxr::GfVec3f color; + if (color_attr.Get(&color, motionSampleTime)) { + blight->r = color[0]; + blight->g = color[1]; + blight->b = color[2]; + } + } + + /* TODO(makowalski): Not currently supported. */ +#if 0 + pxr::VtValue use_color_temp; + light_prim.GetEnableColorTemperatureAttr().Get(&use_color_temp, motionSampleTime); +#endif + + /* TODO(makowalski): Not currently supported. */ +#if 0 + pxr::VtValue color_temp; + light_prim.GetColorTemperatureAttr().Get(&color_temp, motionSampleTime); +#endif + + switch (blight->type) { + case LA_AREA: + if (blight->area_shape == LA_AREA_RECT && prim_.IsA()) { + + pxr::UsdLuxRectLight rect_light(prim_); + + if (!rect_light) { + break; + } + + if (pxr::UsdAttribute width_attr = rect_light.GetWidthAttr()) { + float width = 0.0f; + if (width_attr.Get(&width, motionSampleTime)) { + blight->area_size = width; + } + } + + if (pxr::UsdAttribute height_attr = rect_light.GetHeightAttr()) { + float height = 0.0f; + if (height_attr.Get(&height, motionSampleTime)) { + blight->area_sizey = height; + } + } + } + else if (blight->area_shape == LA_AREA_DISK && prim_.IsA()) { + + pxr::UsdLuxDiskLight disk_light(prim_); + + if (!disk_light) { + break; + } + + if (pxr::UsdAttribute radius_attr = disk_light.GetRadiusAttr()) { + float radius = 0.0f; + if (radius_attr.Get(&radius, motionSampleTime)) { + blight->area_size = radius * 2.0f; + } + } + } + break; + case LA_LOCAL: + if (prim_.IsA()) { + + pxr::UsdLuxSphereLight sphere_light(prim_); + + if (!sphere_light) { + break; + } + + if (pxr::UsdAttribute radius_attr = sphere_light.GetRadiusAttr()) { + float radius = 0.0f; + if (radius_attr.Get(&radius, motionSampleTime)) { + blight->area_size = radius; + } + } + } + break; + case LA_SPOT: + if (prim_.IsA()) { + + pxr::UsdLuxSphereLight sphere_light(prim_); + + if (!sphere_light) { + break; + } + + if (pxr::UsdAttribute radius_attr = sphere_light.GetRadiusAttr()) { + float radius = 0.0f; + if (radius_attr.Get(&radius, motionSampleTime)) { + blight->area_size = radius; + } + } + + if (!shaping_api) { + break; + } + + if (pxr::UsdAttribute cone_angle_attr = shaping_api.GetShapingConeAngleAttr()) { + float cone_angle = 0.0f; + if (cone_angle_attr.Get(&cone_angle, motionSampleTime)) { + blight->spotsize = cone_angle * ((float)M_PI / 180.0f) * 2.0f; + } + } + + if (pxr::UsdAttribute cone_softness_attr = shaping_api.GetShapingConeSoftnessAttr()) { + float cone_softness = 0.0f; + if (cone_softness_attr.Get(&cone_softness, motionSampleTime)) { + blight->spotblend = cone_softness; + } + } + } + break; + case LA_SUN: + if (prim_.IsA()) { + pxr::UsdLuxDistantLight distant_light(prim_); + + if (!distant_light) { + break; + } + + if (pxr::UsdAttribute angle_attr = distant_light.GetAngleAttr()) { + float angle = 0.0f; + if (angle_attr.Get(&angle, motionSampleTime)) { + blight->sun_angle = angle * (float)M_PI / 180.0f; + } + } + } + break; + } + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_light.h b/source/blender/io/usd/intern/usd_reader_light.h new file mode 100644 index 00000000000..e7860fd2c80 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_light.h @@ -0,0 +1,41 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_xform.h" + +namespace blender::io::usd { + +class USDLightReader : public USDXformReader { + + public: + USDLightReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDXformReader(prim, import_params, settings) + { + } + + void create_object(Main *bmain, double motionSampleTime) override; + + void read_object_data(Main *bmain, double motionSampleTime) override; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc new file mode 100644 index 00000000000..02ed7c35e57 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_material.cc @@ -0,0 +1,703 @@ +/* + * 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) 2021 NVIDIA Corporation. + * All rights reserved. + */ + +#include "usd_reader_material.h" + +#include "BKE_image.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_node.h" + +#include "BLI_math_vector.h" +#include "BLI_string.h" + +#include "DNA_material_types.h" + +#include +#include +#include + +#include +#include + +namespace usdtokens { + +/* Parameter names. */ +static const pxr::TfToken a("a", pxr::TfToken::Immortal); +static const pxr::TfToken b("b", pxr::TfToken::Immortal); +static const pxr::TfToken clearcoat("clearcoat", pxr::TfToken::Immortal); +static const pxr::TfToken clearcoatRoughness("clearcoatRoughness", pxr::TfToken::Immortal); +static const pxr::TfToken diffuseColor("diffuseColor", pxr::TfToken::Immortal); +static const pxr::TfToken emissiveColor("emissiveColor", pxr::TfToken::Immortal); +static const pxr::TfToken file("file", pxr::TfToken::Immortal); +static const pxr::TfToken g("g", pxr::TfToken::Immortal); +static const pxr::TfToken ior("ior", pxr::TfToken::Immortal); +static const pxr::TfToken metallic("metallic", pxr::TfToken::Immortal); +static const pxr::TfToken normal("normal", pxr::TfToken::Immortal); +static const pxr::TfToken occlusion("occlusion", pxr::TfToken::Immortal); +static const pxr::TfToken opacity("opacity", pxr::TfToken::Immortal); +static const pxr::TfToken opacityThreshold("opacityThreshold", pxr::TfToken::Immortal); +static const pxr::TfToken r("r", pxr::TfToken::Immortal); +static const pxr::TfToken result("result", pxr::TfToken::Immortal); +static const pxr::TfToken rgb("rgb", pxr::TfToken::Immortal); +static const pxr::TfToken rgba("rgba", pxr::TfToken::Immortal); +static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal); +static const pxr::TfToken sourceColorSpace("sourceColorSpace", pxr::TfToken::Immortal); +static const pxr::TfToken specularColor("specularColor", pxr::TfToken::Immortal); +static const pxr::TfToken st("st", pxr::TfToken::Immortal); +static const pxr::TfToken varname("varname", pxr::TfToken::Immortal); + +/* Color space names. */ +static const pxr::TfToken raw("raw", pxr::TfToken::Immortal); +static const pxr::TfToken RAW("RAW", pxr::TfToken::Immortal); + +/* USD shader names. */ +static const pxr::TfToken UsdPreviewSurface("UsdPreviewSurface", pxr::TfToken::Immortal); +static const pxr::TfToken UsdPrimvarReader_float2("UsdPrimvarReader_float2", + pxr::TfToken::Immortal); +static const pxr::TfToken UsdUVTexture("UsdUVTexture", pxr::TfToken::Immortal); +} // namespace usdtokens + +/* Add a node of the given type at the given location coordinates. */ +static bNode *add_node( + const bContext *C, bNodeTree *ntree, const int type, const float locx, const float locy) +{ + bNode *new_node = nodeAddStaticNode(C, ntree, type); + + if (new_node) { + new_node->locx = locx; + new_node->locy = locy; + } + + return new_node; +} + +/* Connect the output socket of node 'source' to the input socket of node 'dest'. */ +static void link_nodes( + bNodeTree *ntree, bNode *source, const char *sock_out, bNode *dest, const char *sock_in) +{ + bNodeSocket *source_socket = nodeFindSocket(source, SOCK_OUT, sock_out); + + if (!source_socket) { + std::cerr << "PROGRAMMER ERROR: Couldn't find output socket " << sock_out << std::endl; + return; + } + + bNodeSocket *dest_socket = nodeFindSocket(dest, SOCK_IN, sock_in); + + if (!dest_socket) { + std::cerr << "PROGRAMMER ERROR: Couldn't find input socket " << sock_in << std::endl; + return; + } + + nodeAddLink(ntree, source, source_socket, dest, dest_socket); +} + +/* Returns true if the given shader may have opacity < 1.0, based + * on heuristics. */ +static bool needs_blend(const pxr::UsdShadeShader &usd_shader) +{ + if (!usd_shader) { + return false; + } + + bool needs_blend = false; + + if (pxr::UsdShadeInput opacity_input = usd_shader.GetInput(usdtokens::opacity)) { + + if (opacity_input.HasConnectedSource()) { + needs_blend = true; + } + else { + pxr::VtValue val; + if (opacity_input.GetAttr().HasAuthoredValue() && opacity_input.GetAttr().Get(&val)) { + float opacity = val.Get(); + needs_blend = opacity < 1.0f; + } + } + } + + return needs_blend; +} + +/* Returns the given shader's opacityThreshold input value, if this input has an + * authored value. Otherwise, returns the given default value. */ +static float get_opacity_threshold(const pxr::UsdShadeShader &usd_shader, + float default_value = 0.0f) +{ + if (!usd_shader) { + return default_value; + } + + pxr::UsdShadeInput opacity_threshold_input = usd_shader.GetInput(usdtokens::opacityThreshold); + + if (!opacity_threshold_input) { + return default_value; + } + + pxr::VtValue val; + if (opacity_threshold_input.GetAttr().HasAuthoredValue() && + opacity_threshold_input.GetAttr().Get(&val)) { + return val.Get(); + } + + return default_value; +} + +static pxr::TfToken get_source_color_space(const pxr::UsdShadeShader &usd_shader) +{ + if (!usd_shader) { + return pxr::TfToken(); + } + + pxr::UsdShadeInput color_space_input = usd_shader.GetInput(usdtokens::sourceColorSpace); + + if (!color_space_input) { + return pxr::TfToken(); + } + + pxr::VtValue color_space_val; + if (color_space_input.Get(&color_space_val) && color_space_val.IsHolding()) { + return color_space_val.Get(); + } + + return pxr::TfToken(); +} + +/* Attempts to return in r_preview_surface the UsdPreviewSurface shader source + * of the given material. Returns true if a UsdPreviewSurface source was found + * and returns false otherwise. */ +static bool get_usd_preview_surface(const pxr::UsdShadeMaterial &usd_material, + pxr::UsdShadeShader &r_preview_surface) +{ + if (!usd_material) { + return false; + } + + if (pxr::UsdShadeShader surf_shader = usd_material.ComputeSurfaceSource()) { + /* Check if we have a UsdPreviewSurface shader. */ + pxr::TfToken shader_id; + if (surf_shader.GetShaderId(&shader_id) && shader_id == usdtokens::UsdPreviewSurface) { + r_preview_surface = surf_shader; + return true; + } + } + + return false; +} + +/* Set the Blender material's viewport display color, metallic and roughness + * properties from the given USD preview surface shader's inputs. */ +static void set_viewport_material_props(Material *mtl, const pxr::UsdShadeShader &usd_preview) +{ + if (!(mtl && usd_preview)) { + return; + } + + if (pxr::UsdShadeInput diffuse_color_input = usd_preview.GetInput(usdtokens::diffuseColor)) { + pxr::VtValue val; + if (diffuse_color_input.GetAttr().HasAuthoredValue() && + diffuse_color_input.GetAttr().Get(&val) && val.IsHolding()) { + pxr::GfVec3f color = val.UncheckedGet(); + mtl->r = color[0]; + mtl->g = color[1]; + mtl->b = color[2]; + } + } + + if (pxr::UsdShadeInput metallic_input = usd_preview.GetInput(usdtokens::metallic)) { + pxr::VtValue val; + if (metallic_input.GetAttr().HasAuthoredValue() && metallic_input.GetAttr().Get(&val) && + val.IsHolding()) { + mtl->metallic = val.Get(); + } + } + + if (pxr::UsdShadeInput roughness_input = usd_preview.GetInput(usdtokens::roughness)) { + pxr::VtValue val; + if (roughness_input.GetAttr().HasAuthoredValue() && roughness_input.GetAttr().Get(&val) && + val.IsHolding()) { + mtl->roughness = val.Get(); + } + } +} + +namespace blender::io::usd { + +namespace { + +/* Compute the x- and y-coordinates for placing a new node in an unoccupied region of + * the column with the given index. Returns the coordinates in r_locx and r_locy and + * updates the column-occupancy information in r_ctx. */ +void compute_node_loc(const int column, float *r_locx, float *r_locy, NodePlacementContext *r_ctx) +{ + if (!(r_locx && r_locy && r_ctx)) { + return; + } + + (*r_locx) = r_ctx->origx - column * r_ctx->horizontal_step; + + if (column >= r_ctx->column_offsets.size()) { + r_ctx->column_offsets.push_back(0.0f); + } + + (*r_locy) = r_ctx->origy - r_ctx->column_offsets[column]; + + /* Record the y-offset of the occupied region in + * the column, including padding. */ + r_ctx->column_offsets[column] += r_ctx->vertical_step + 10.0f; +} + +} // End anonymous namespace. + +USDMaterialReader::USDMaterialReader(const USDImportParams ¶ms, Main *bmain) + : params_(params), bmain_(bmain) +{ +} + +Material *USDMaterialReader::add_material(const pxr::UsdShadeMaterial &usd_material) const +{ + if (!(bmain_ && usd_material)) { + return nullptr; + } + + std::string mtl_name = usd_material.GetPrim().GetName().GetString(); + + /* Create the material. */ + Material *mtl = BKE_material_add(bmain_, mtl_name.c_str()); + + /* Get the UsdPreviewSurface shader source for the material, + * if there is one. */ + pxr::UsdShadeShader usd_preview; + if (get_usd_preview_surface(usd_material, usd_preview)) { + + set_viewport_material_props(mtl, usd_preview); + + /* Optionally, create shader nodes to represent a UsdPreviewSurface. */ + if (params_.import_usd_preview) { + import_usd_preview(mtl, usd_preview); + } + } + + return mtl; +} + +/* Create the Principled BSDF shader node network. */ +void USDMaterialReader::import_usd_preview(Material *mtl, + const pxr::UsdShadeShader &usd_shader) const +{ + if (!(bmain_ && mtl && usd_shader)) { + return; + } + + /* Create the Material's node tree containing the principled BSDF + * and output shaders. */ + + /* Add the node tree. */ + bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", "ShaderNodeTree"); + mtl->nodetree = ntree; + mtl->use_nodes = true; + + /* Create the Principled BSDF shader node. */ + bNode *principled = add_node(nullptr, ntree, SH_NODE_BSDF_PRINCIPLED, 0.0f, 300.0f); + + if (!principled) { + std::cerr << "ERROR: Couldn't create SH_NODE_BSDF_PRINCIPLED node for USD shader " + << usd_shader.GetPath() << std::endl; + return; + } + + /* Create the material output node. */ + bNode *output = add_node(nullptr, ntree, SH_NODE_OUTPUT_MATERIAL, 300.0f, 300.0f); + + if (!output) { + std::cerr << "ERROR: Couldn't create SH_NODE_OUTPUT_MATERIAL node for USD shader " + << usd_shader.GetPath() << std::endl; + return; + } + + /* Connect the Principled BSDF node to the output node. */ + link_nodes(ntree, principled, "BSDF", output, "Surface"); + + /* Recursively create the principled shader input networks. */ + set_principled_node_inputs(principled, ntree, usd_shader); + + nodeSetActive(ntree, output); + + ntreeUpdateTree(bmain_, ntree); + + /* Optionally, set the material blend mode. */ + + if (params_.set_material_blend) { + if (needs_blend(usd_shader)) { + float opacity_threshold = get_opacity_threshold(usd_shader, 0.0f); + if (opacity_threshold > 0.0f) { + mtl->blend_method = MA_BM_CLIP; + mtl->alpha_threshold = opacity_threshold; + } + else { + mtl->blend_method = MA_BM_BLEND; + } + } + } +} + +void USDMaterialReader::set_principled_node_inputs(bNode *principled, + bNodeTree *ntree, + const pxr::UsdShadeShader &usd_shader) const +{ + /* The context struct keeps track of the locations for adding + * input nodes. */ + NodePlacementContext context(0.0f, 300.0); + + /* The column index (from right to left relative to the principled + * node) where we're adding the nodes. */ + int column = 0; + + /* Recursively set the principled shader inputs. */ + + if (pxr::UsdShadeInput diffuse_input = usd_shader.GetInput(usdtokens::diffuseColor)) { + set_node_input(diffuse_input, principled, "Base Color", ntree, column, &context); + } + + if (pxr::UsdShadeInput emissive_input = usd_shader.GetInput(usdtokens::emissiveColor)) { + set_node_input(emissive_input, principled, "Emission", ntree, column, &context); + } + + if (pxr::UsdShadeInput specular_input = usd_shader.GetInput(usdtokens::specularColor)) { + set_node_input(specular_input, principled, "Specular", ntree, column, &context); + } + + if (pxr::UsdShadeInput metallic_input = usd_shader.GetInput(usdtokens::metallic)) { + ; + set_node_input(metallic_input, principled, "Metallic", ntree, column, &context); + } + + if (pxr::UsdShadeInput roughness_input = usd_shader.GetInput(usdtokens::roughness)) { + set_node_input(roughness_input, principled, "Roughness", ntree, column, &context); + } + + if (pxr::UsdShadeInput clearcoat_input = usd_shader.GetInput(usdtokens::clearcoat)) { + set_node_input(clearcoat_input, principled, "Clearcoat", ntree, column, &context); + } + + if (pxr::UsdShadeInput clearcoat_roughness_input = usd_shader.GetInput( + usdtokens::clearcoatRoughness)) { + set_node_input( + clearcoat_roughness_input, principled, "Clearcoat Roughness", ntree, column, &context); + } + + if (pxr::UsdShadeInput opacity_input = usd_shader.GetInput(usdtokens::opacity)) { + set_node_input(opacity_input, principled, "Alpha", ntree, column, &context); + } + + if (pxr::UsdShadeInput ior_input = usd_shader.GetInput(usdtokens::ior)) { + set_node_input(ior_input, principled, "IOR", ntree, column, &context); + } + + if (pxr::UsdShadeInput normal_input = usd_shader.GetInput(usdtokens::normal)) { + set_node_input(normal_input, principled, "Normal", ntree, column, &context); + } +} + +/* Convert the given USD shader input to an input on the given Blender node. */ +void USDMaterialReader::set_node_input(const pxr::UsdShadeInput &usd_input, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + const int column, + NodePlacementContext *r_ctx) const +{ + if (!(usd_input && dest_node && r_ctx)) { + return; + } + + if (usd_input.HasConnectedSource()) { + /* The USD shader input has a connected source shader. Follow the connection + * and attempt to convert the connected USD shader to a Blender node. */ + follow_connection(usd_input, dest_node, dest_socket_name, ntree, column, r_ctx); + } + else { + /* Set the destination node socket value from the USD shader input value. */ + + bNodeSocket *sock = nodeFindSocket(dest_node, SOCK_IN, dest_socket_name); + if (!sock) { + std::cerr << "ERROR: couldn't get destination node socket " << dest_socket_name << std::endl; + return; + } + + pxr::VtValue val; + if (!usd_input.Get(&val)) { + std::cerr << "ERROR: couldn't get value for usd shader input " + << usd_input.GetPrim().GetPath() << std::endl; + return; + } + + switch (sock->type) { + case SOCK_FLOAT: + if (val.IsHolding()) { + ((bNodeSocketValueFloat *)sock->default_value)->value = val.UncheckedGet(); + } + else if (val.IsHolding()) { + pxr::GfVec3f v3f = val.UncheckedGet(); + float average = (v3f[0] + v3f[1] + v3f[2]) / 3.0f; + ((bNodeSocketValueFloat *)sock->default_value)->value = average; + } + break; + case SOCK_RGBA: + if (val.IsHolding()) { + pxr::GfVec3f v3f = val.UncheckedGet(); + copy_v3_v3(((bNodeSocketValueRGBA *)sock->default_value)->value, v3f.data()); + } + break; + case SOCK_VECTOR: + if (val.IsHolding()) { + pxr::GfVec3f v3f = val.UncheckedGet(); + copy_v3_v3(((bNodeSocketValueVector *)sock->default_value)->value, v3f.data()); + } + else if (val.IsHolding()) { + pxr::GfVec2f v2f = val.UncheckedGet(); + copy_v2_v2(((bNodeSocketValueVector *)sock->default_value)->value, v2f.data()); + } + break; + default: + std::cerr << "WARNING: unexpected type " << sock->idname << " for destination node socket " + << dest_socket_name << std::endl; + break; + } + } +} + +/* Follow the connected source of the USD input to create corresponding inputs + * for the given Blender node. */ +void USDMaterialReader::follow_connection(const pxr::UsdShadeInput &usd_input, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + int column, + NodePlacementContext *r_ctx) const +{ + if (!(usd_input && dest_node && dest_socket_name && ntree && r_ctx)) { + return; + } + + pxr::UsdShadeConnectableAPI source; + pxr::TfToken source_name; + pxr::UsdShadeAttributeType source_type; + + usd_input.GetConnectedSource(&source, &source_name, &source_type); + + if (!(source && source.GetPrim().IsA())) { + return; + } + + pxr::UsdShadeShader source_shader(source.GetPrim()); + + if (!source_shader) { + return; + } + + pxr::TfToken shader_id; + if (!source_shader.GetShaderId(&shader_id)) { + std::cerr << "ERROR: couldn't get shader id for source shader " + << source_shader.GetPrim().GetPath() << std::endl; + return; + } + + /* For now, only convert UsdUVTexture and UsdPrimvarReader_float2 inputs. */ + if (shader_id == usdtokens::UsdUVTexture) { + + if (strcmp(dest_socket_name, "Normal") == 0) { + + /* The normal texture input requires creating a normal map node. */ + float locx = 0.0f; + float locy = 0.0f; + compute_node_loc(column + 1, &locx, &locy, r_ctx); + + bNode *normal_map = add_node(nullptr, ntree, SH_NODE_NORMAL_MAP, locx, locy); + + /* Currently, the Normal Map node has Tangent Space as the default, + * which is what we need, so we don't need to explicitly set it. */ + + /* Connect the Normal Map to the Normal input. */ + link_nodes(ntree, normal_map, "Normal", dest_node, "Normal"); + + /* Now, create the Texture Image node input to the Normal Map "Color" input. */ + convert_usd_uv_texture( + source_shader, source_name, normal_map, "Color", ntree, column + 2, r_ctx); + } + else { + convert_usd_uv_texture( + source_shader, source_name, dest_node, dest_socket_name, ntree, column + 1, r_ctx); + } + } + else if (shader_id == usdtokens::UsdPrimvarReader_float2) { + convert_usd_primvar_reader_float2( + source_shader, source_name, dest_node, dest_socket_name, ntree, column + 1, r_ctx); + } +} + +void USDMaterialReader::convert_usd_uv_texture(const pxr::UsdShadeShader &usd_shader, + const pxr::TfToken &usd_source_name, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + const int column, + NodePlacementContext *r_ctx) const +{ + if (!usd_shader || !dest_node || !ntree || !dest_socket_name || !bmain_ || !r_ctx) { + return; + } + + float locx = 0.0f; + float locy = 0.0f; + compute_node_loc(column, &locx, &locy, r_ctx); + + /* Create the Texture Image node. */ + bNode *tex_image = add_node(nullptr, ntree, SH_NODE_TEX_IMAGE, locx, locy); + + if (!tex_image) { + std::cerr << "ERROR: Couldn't create SH_NODE_TEX_IMAGE for node input " << dest_socket_name + << std::endl; + return; + } + + /* Load the texture image. */ + load_tex_image(usd_shader, tex_image); + + /* Connect to destination node input. */ + + /* Get the source socket name. */ + std::string source_socket_name = usd_source_name == usdtokens::a ? "Alpha" : "Color"; + + link_nodes(ntree, tex_image, source_socket_name.c_str(), dest_node, dest_socket_name); + + /* Connect the texture image node "Vector" input. */ + if (pxr::UsdShadeInput st_input = usd_shader.GetInput(usdtokens::st)) { + set_node_input(st_input, tex_image, "Vector", ntree, column, r_ctx); + } +} + +/* Load the texture image node's texture from the path given by the USD shader's + * file input value. */ +void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader, + bNode *tex_image) const +{ + if (!(usd_shader && tex_image && tex_image->type == SH_NODE_TEX_IMAGE)) { + return; + } + + /* Try to load the texture image. */ + pxr::UsdShadeInput file_input = usd_shader.GetInput(usdtokens::file); + + if (!file_input) { + std::cerr << "WARNING: Couldn't get file input for USD shader " << usd_shader.GetPath() + << std::endl; + return; + } + + pxr::VtValue file_val; + if (!file_input.Get(&file_val) || !file_val.IsHolding()) { + std::cerr << "WARNING: Couldn't get file input value for USD shader " << usd_shader.GetPath() + << std::endl; + return; + } + + const pxr::SdfAssetPath &asset_path = file_val.Get(); + std::string file_path = asset_path.GetResolvedPath(); + if (file_path.empty()) { + std::cerr << "WARNING: Couldn't resolve image asset '" << asset_path + << "' for Texture Image node." << std::endl; + return; + } + + const char *im_file = file_path.c_str(); + Image *image = BKE_image_load_exists(bmain_, im_file); + if (!image) { + std::cerr << "WARNING: Couldn't open image file '" << im_file << "' for Texture Image node." + << std::endl; + return; + } + + tex_image->id = &image->id; + + /* Set texture color space. + * TODO(makowalski): For now, just checking for RAW color space, + * assuming sRGB otherwise, but more complex logic might be + * required if the color space is "auto". */ + + pxr::TfToken color_space = get_source_color_space(usd_shader); + + if (color_space.IsEmpty()) { + color_space = file_input.GetAttr().GetColorSpace(); + } + + if (color_space == usdtokens::RAW || color_space == usdtokens::raw) { + STRNCPY(image->colorspace_settings.name, "Raw"); + } +} + +/* This function creates a Blender UV Map node, under the simplifying assumption that + * UsdPrimvarReader_float2 shaders output UV coordinates. + * TODO(makowalski): investigate supporting conversion to other Blender node types + * (e.g., Attribute Nodes) if needed. */ +void USDMaterialReader::convert_usd_primvar_reader_float2( + const pxr::UsdShadeShader &usd_shader, + const pxr::TfToken & /* usd_source_name */, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + const int column, + NodePlacementContext *r_ctx) const +{ + if (!usd_shader || !dest_node || !ntree || !dest_socket_name || !bmain_ || !r_ctx) { + return; + } + + float locx = 0.0f; + float locy = 0.0f; + compute_node_loc(column, &locx, &locy, r_ctx); + + /* Create the UV Map node. */ + bNode *uv_map = add_node(nullptr, ntree, SH_NODE_UVMAP, locx, locy); + + if (!uv_map) { + std::cerr << "ERROR: Couldn't create SH_NODE_UVMAP for node input " << dest_socket_name + << std::endl; + return; + } + + /* Set the texmap name. */ + pxr::UsdShadeInput varname_input = usd_shader.GetInput(usdtokens::varname); + if (varname_input) { + pxr::VtValue varname_val; + if (varname_input.Get(&varname_val) && varname_val.IsHolding()) { + std::string varname = varname_val.Get().GetString(); + if (!varname.empty()) { + NodeShaderUVMap *storage = (NodeShaderUVMap *)uv_map->storage; + BLI_strncpy(storage->uv_map, varname.c_str(), sizeof(storage->uv_map)); + } + } + } + + /* Connect to destination node input. */ + link_nodes(ntree, uv_map, "UV", dest_node, dest_socket_name); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_material.h b/source/blender/io/usd/intern/usd_reader_material.h new file mode 100644 index 00000000000..3e8fc675931 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_material.h @@ -0,0 +1,131 @@ +/* + * 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) 2021 NVIDIA Corporation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" + +#include + +struct Main; +struct Material; +struct bNode; +struct bNodeTree; + +namespace blender::io::usd { + +/* Helper struct used when arranging nodes in columns, keeping track the + * occupancy information for a given column. I.e., for column n, + * column_offsets[n] is the y-offset (from top to bottom) of the occupied + * region in that column. */ +struct NodePlacementContext { + float origx; + float origy; + std::vector column_offsets; + const float horizontal_step; + const float vertical_step; + + NodePlacementContext(float in_origx, + float in_origy, + float in_horizontal_step = 300.0f, + float in_vertical_step = 300.0f) + : origx(in_origx), + origy(in_origy), + column_offsets(64, 0.0f), + horizontal_step(in_horizontal_step), + vertical_step(in_vertical_step) + { + } +}; + +/* Converts USD materials to Blender representation. */ + +/* By default, the USDMaterialReader creates a Blender material with + * the same name as the USD material. If the USD material has a + * UsdPreviewSurface source, the Blender material's viewport display + * color, roughness and metallic properties are set to the corresponding + * UsdPreoviewSurface inputs. + * + * If the Import USD Preview option is enabled, the current implementation + * converts UsdPreviewSurface to Blender nodes as follows: + * + * UsdPreviewSurface -> Pricipled BSDF + * UsdUVTexture -> Texture Image + Normal Map + * UsdPrimvarReader_float2 -> UV Map + * + * Limitations: arbitrary primvar readers or UsdTransform2d not yet + * supported. For UsdUVTexture, only the file, st and sourceColorSpace + * inputs are handled. + * + * TODO(makowalski): Investigate adding support for converting additional + * shaders and inputs. Supporting certain types of inputs, such as texture + * scale and bias, will probably require creating Blender Group nodes with + * the corresponding inputs. */ + +class USDMaterialReader { + protected: + USDImportParams params_; + + Main *bmain_; + + public: + USDMaterialReader(const USDImportParams ¶ms, Main *bmain); + + Material *add_material(const pxr::UsdShadeMaterial &usd_material) const; + + protected: + void import_usd_preview(Material *mtl, const pxr::UsdShadeShader &usd_shader) const; + + void set_principled_node_inputs(bNode *principled_node, + bNodeTree *ntree, + const pxr::UsdShadeShader &usd_shader) const; + + void set_node_input(const pxr::UsdShadeInput &usd_input, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + int column, + NodePlacementContext *r_ctx) const; + + void follow_connection(const pxr::UsdShadeInput &usd_input, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + int column, + NodePlacementContext *r_ctx) const; + + void convert_usd_uv_texture(const pxr::UsdShadeShader &usd_shader, + const pxr::TfToken &usd_source_name, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + int column, + NodePlacementContext *r_ctx) const; + + void load_tex_image(const pxr::UsdShadeShader &usd_shader, bNode *tex_image) const; + + void convert_usd_primvar_reader_float2(const pxr::UsdShadeShader &usd_shader, + const pxr::TfToken &usd_source_name, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + int column, + NodePlacementContext *r_ctx) const; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc new file mode 100644 index 00000000000..f13da4680e2 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -0,0 +1,853 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation and + * NVIDIA Corporation. All rights reserved. + */ + +#include "usd_reader_mesh.h" +#include "usd_reader_material.h" + +#include "BKE_customdata.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +#include "BLI_math.h" +#include "BLI_math_geom.h" +#include "BLI_string.h" + +#include "DNA_customdata_types.h" +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" + +#include "MEM_guardedalloc.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace usdtokens { +/* Materials */ +static const pxr::TfToken st("st", pxr::TfToken::Immortal); +static const pxr::TfToken UVMap("UVMap", pxr::TfToken::Immortal); +static const pxr::TfToken Cd("Cd", pxr::TfToken::Immortal); +static const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal); +static const pxr::TfToken normalsPrimvar("normals", pxr::TfToken::Immortal); +} // namespace usdtokens + +namespace utils { +/* Very similar to abc mesh utils. */ +static void build_mat_map(const Main *bmain, std::map *r_mat_map) +{ + if (r_mat_map == nullptr) { + return; + } + + Material *material = static_cast(bmain->materials.first); + + for (; material; material = static_cast(material->id.next)) { + /* We have to do this because the stored material name is coming directly from usd. */ + (*r_mat_map)[pxr::TfMakeValidIdentifier(material->id.name + 2)] = material; + } +} + +static void assign_materials(Main *bmain, + Object *ob, + const std::map &mat_index_map, + const USDImportParams ¶ms, + pxr::UsdStageRefPtr stage) +{ + if (!(stage && bmain && ob)) { + return; + } + + bool can_assign = true; + std::map::const_iterator it = mat_index_map.begin(); + + int matcount = 0; + for (; it != mat_index_map.end(); ++it, matcount++) { + if (!BKE_object_material_slot_add(bmain, ob)) { + can_assign = false; + break; + } + } + + if (!can_assign) { + return; + } + + /* TODO(kevin): use global map? */ + std::map mat_map; + build_mat_map(bmain, &mat_map); + + blender::io::usd::USDMaterialReader mat_reader(params, bmain); + + for (it = mat_index_map.begin(); it != mat_index_map.end(); ++it) { + std::string mat_name = it->first.GetName(); + + std::map::iterator mat_iter = mat_map.find(mat_name); + + Material *assigned_mat = nullptr; + + if (mat_iter == mat_map.end()) { + /* Blender material doesn't exist, so create it now. */ + + /* Look up the USD material. */ + pxr::UsdPrim prim = stage->GetPrimAtPath(it->first); + pxr::UsdShadeMaterial usd_mat(prim); + + if (!usd_mat) { + std::cout << "WARNING: Couldn't construct USD material from prim " << it->first + << std::endl; + continue; + } + + /* Add the Blender material. */ + assigned_mat = mat_reader.add_material(usd_mat); + + if (!assigned_mat) { + std::cout << "WARNING: Couldn't create Blender material from USD material " << it->first + << std::endl; + continue; + } + + mat_map[mat_name] = assigned_mat; + } + else { + /* We found an existing Blender material. */ + assigned_mat = mat_iter->second; + } + + if (assigned_mat) { + BKE_object_material_assign(bmain, ob, assigned_mat, it->second, BKE_MAT_ASSIGN_OBDATA); + } + else { + /* This shouldn't happen. */ + std::cout << "WARNING: Couldn't assign material " << mat_name << std::endl; + } + } +} + +} // namespace utils + +static void *add_customdata_cb(Mesh *mesh, const char *name, const int data_type) +{ + CustomDataType cd_data_type = static_cast(data_type); + void *cd_ptr; + CustomData *loopdata; + int numloops; + + /* unsupported custom data type -- don't do anything. */ + if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) { + return nullptr; + } + + loopdata = &mesh->ldata; + cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name); + if (cd_ptr != nullptr) { + /* layer already exists, so just return it. */ + return cd_ptr; + } + + /* Create a new layer. */ + numloops = mesh->totloop; + cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, nullptr, numloops, name); + return cd_ptr; +} + +namespace blender::io::usd { + +USDMeshReader::USDMeshReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDGeomReader(prim, import_params, settings), + mesh_prim_(prim), + is_left_handed_(false), + has_uvs_(false), + is_time_varying_(false), + is_initial_load_(false) +{ +} + +void USDMeshReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + Mesh *mesh = BKE_mesh_add(bmain, name_.c_str()); + + object_ = BKE_object_add_only_object(bmain, OB_MESH, name_.c_str()); + object_->data = mesh; +} + +void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime) +{ + Mesh *mesh = (Mesh *)object_->data; + + is_initial_load_ = true; + Mesh *read_mesh = this->read_mesh( + mesh, motionSampleTime, import_params_.mesh_read_flag, nullptr); + + is_initial_load_ = false; + if (read_mesh != mesh) { + /* XXX fixme after 2.80; mesh->flag isn't copied by BKE_mesh_nomain_to_mesh() */ + /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ + short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); + BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_, &CD_MASK_MESH, true); + mesh->flag |= autosmooth; + } + + readFaceSetsSample(bmain, mesh, motionSampleTime); + + if (mesh_prim_.GetPointsAttr().ValueMightBeTimeVarying()) { + is_time_varying_ = true; + } + + if (is_time_varying_) { + add_cache_modifier(); + } + + if (import_params_.import_subdiv) { + pxr::TfToken subdivScheme; + mesh_prim_.GetSubdivisionSchemeAttr().Get(&subdivScheme, motionSampleTime); + + if (subdivScheme == pxr::UsdGeomTokens->catmullClark) { + add_subdiv_modifier(); + } + } + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +bool USDMeshReader::valid() const +{ + return static_cast(mesh_prim_); +} + +bool USDMeshReader::topology_changed(Mesh *existing_mesh, const double motionSampleTime) +{ + /* TODO(makowalski): Is it the best strategy to cache the mesh + * geometry in this function? This needs to be revisited. */ + + mesh_prim_.GetFaceVertexIndicesAttr().Get(&face_indices_, motionSampleTime); + mesh_prim_.GetFaceVertexCountsAttr().Get(&face_counts_, motionSampleTime); + mesh_prim_.GetPointsAttr().Get(&positions_, motionSampleTime); + + /* TODO(makowalski): Reading normals probably doesn't belong in this function, + * as this is not required to determine if the topology has changed. */ + + /* If 'normals' and 'primvars:normals' are both specified, the latter has precedence. */ + pxr::UsdGeomPrimvar primvar = mesh_prim_.GetPrimvar(usdtokens::normalsPrimvar); + if (primvar.HasValue()) { + primvar.ComputeFlattened(&normals_, motionSampleTime); + normal_interpolation_ = primvar.GetInterpolation(); + } + else { + mesh_prim_.GetNormalsAttr().Get(&normals_, motionSampleTime); + normal_interpolation_ = mesh_prim_.GetNormalsInterpolation(); + } + + return positions_.size() != existing_mesh->totvert || + face_counts_.size() != existing_mesh->totpoly || + face_indices_.size() != existing_mesh->totloop; +} + +void USDMeshReader::read_mpolys(Mesh *mesh) +{ + MPoly *mpolys = mesh->mpoly; + MLoop *mloops = mesh->mloop; + + int loop_index = 0; + + for (int i = 0; i < face_counts_.size(); i++) { + const int face_size = face_counts_[i]; + + MPoly &poly = mpolys[i]; + poly.loopstart = loop_index; + poly.totloop = face_size; + poly.mat_nr = 0; + + /* Polygons are always assumed to be smooth-shaded. If the mesh should be flat-shaded, + * this is encoded in custom loop normals. */ + poly.flag |= ME_SMOOTH; + + if (is_left_handed_) { + int loop_end_index = loop_index + (face_size - 1); + for (int f = 0; f < face_size; ++f, ++loop_index) { + mloops[loop_index].v = face_indices_[loop_end_index - f]; + } + } + else { + for (int f = 0; f < face_size; ++f, ++loop_index) { + mloops[loop_index].v = face_indices_[loop_index]; + } + } + } + + BKE_mesh_calc_edges(mesh, false, false); +} + +void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bool load_uvs) +{ + unsigned int loop_index = 0; + unsigned int rev_loop_index = 0; + unsigned int uv_index = 0; + + const CustomData *ldata = &mesh->ldata; + + struct UVSample { + pxr::VtVec2fArray uvs; + pxr::TfToken interpolation; + }; + + std::vector uv_primvars(ldata->totlayer); + + if (has_uvs_) { + for (int layer_idx = 0; layer_idx < ldata->totlayer; layer_idx++) { + const CustomDataLayer *layer = &ldata->layers[layer_idx]; + std::string layer_name = std::string(layer->name); + if (layer->type != CD_MLOOPUV) { + continue; + } + + pxr::TfToken uv_token; + + /* If first time seeing uv token, store in map of uid, TfToken> */ + if (uv_token_map_.find(layer_name) == uv_token_map_.end()) { + uv_token = pxr::TfToken(layer_name); + uv_token_map_.insert(std::make_pair(layer_name, uv_token)); + } + else { + uv_token = uv_token_map_.at(layer_name); + } + + /* Early out if no token found, this should never happen */ + if (uv_token.IsEmpty()) { + continue; + } + /* Early out if not first load and uvs arent animated. */ + if (!load_uvs && primvar_varying_map_.find(uv_token) != primvar_varying_map_.end() && + !primvar_varying_map_.at(uv_token)) { + continue; + } + + /* Early out if mesh doesn't have primvar. */ + if (!mesh_prim_.HasPrimvar(uv_token)) { + continue; + } + + if (pxr::UsdGeomPrimvar uv_primvar = mesh_prim_.GetPrimvar(uv_token)) { + uv_primvar.ComputeFlattened(&uv_primvars[layer_idx].uvs, motionSampleTime); + uv_primvars[layer_idx].interpolation = uv_primvar.GetInterpolation(); + } + } + } + + for (int i = 0; i < face_counts_.size(); i++) { + const int face_size = face_counts_[i]; + + rev_loop_index = loop_index + (face_size - 1); + + for (int f = 0; f < face_size; f++, loop_index++, rev_loop_index--) { + + for (int layer_idx = 0; layer_idx < ldata->totlayer; layer_idx++) { + const CustomDataLayer *layer = &ldata->layers[layer_idx]; + if (layer->type != CD_MLOOPUV) { + continue; + } + + /* Early out if mismatched layer sizes. */ + if (layer_idx > uv_primvars.size()) { + continue; + } + + /* Early out if no uvs loaded. */ + if (uv_primvars[layer_idx].uvs.empty()) { + continue; + } + + const UVSample &sample = uv_primvars[layer_idx]; + + if (!(sample.interpolation == pxr::UsdGeomTokens->faceVarying || + sample.interpolation == pxr::UsdGeomTokens->vertex)) { + std::cerr << "WARNING: unexpected interpolation type " << sample.interpolation + << " for uv " << layer->name << std::endl; + continue; + } + + /* For Vertex interpolation, use the vertex index. */ + int usd_uv_index = sample.interpolation == pxr::UsdGeomTokens->vertex ? + mesh->mloop[loop_index].v : + loop_index; + + if (usd_uv_index >= sample.uvs.size()) { + std::cerr << "WARNING: out of bounds uv index " << usd_uv_index << " for uv " + << layer->name << " of size " << sample.uvs.size() << std::endl; + continue; + } + + MLoopUV *mloopuv = static_cast(layer->data); + if (is_left_handed_) { + uv_index = rev_loop_index; + } + else { + uv_index = loop_index; + } + mloopuv[uv_index].uv[0] = sample.uvs[usd_uv_index][0]; + mloopuv[uv_index].uv[1] = sample.uvs[usd_uv_index][1]; + } + } + } +} + +void USDMeshReader::read_colors(Mesh *mesh, const double motionSampleTime) +{ + if (!(mesh && mesh_prim_ && mesh->totloop > 0)) { + return; + } + + /* Early out if we read the display color before and if this attribute isn't animated. */ + if (primvar_varying_map_.find(usdtokens::displayColor) != primvar_varying_map_.end() && + !primvar_varying_map_.at(usdtokens::displayColor)) { + return; + } + + pxr::UsdGeomPrimvar color_primvar = mesh_prim_.GetDisplayColorPrimvar(); + + if (!color_primvar.HasValue()) { + return; + } + + pxr::TfToken interp = color_primvar.GetInterpolation(); + + if (interp == pxr::UsdGeomTokens->varying) { + std::cerr << "WARNING: Unsupported varying interpolation for display colors\n" << std::endl; + return; + } + + if (primvar_varying_map_.find(usdtokens::displayColor) == primvar_varying_map_.end()) { + bool might_be_time_varying = color_primvar.ValueMightBeTimeVarying(); + primvar_varying_map_.insert(std::make_pair(usdtokens::displayColor, might_be_time_varying)); + if (might_be_time_varying) { + is_time_varying_ = true; + } + } + + pxr::VtArray display_colors; + + if (!color_primvar.ComputeFlattened(&display_colors, motionSampleTime)) { + std::cerr << "WARNING: Couldn't compute display colors\n" << std::endl; + return; + } + + if ((interp == pxr::UsdGeomTokens->faceVarying && display_colors.size() != mesh->totloop) || + (interp == pxr::UsdGeomTokens->vertex && display_colors.size() != mesh->totvert) || + (interp == pxr::UsdGeomTokens->constant && display_colors.size() != 1) || + (interp == pxr::UsdGeomTokens->uniform && display_colors.size() != mesh->totpoly)) { + std::cerr << "WARNING: display colors count mismatch\n" << std::endl; + return; + } + + void *cd_ptr = add_customdata_cb(mesh, "displayColors", CD_MLOOPCOL); + + if (!cd_ptr) { + std::cerr << "WARNING: Couldn't add displayColors custom data.\n"; + return; + } + + MLoopCol *colors = static_cast(cd_ptr); + + mesh->mloopcol = colors; + + MPoly *poly = mesh->mpoly; + + for (int i = 0, e = mesh->totpoly; i < e; ++i, ++poly) { + for (int j = 0; j < poly->totloop; ++j) { + int loop_index = poly->loopstart + j; + + /* Default for constant varying interpolation. */ + int usd_index = 0; + + if (interp == pxr::UsdGeomTokens->vertex) { + usd_index = mesh->mloop[loop_index].v; + } + else if (interp == pxr::UsdGeomTokens->faceVarying) { + usd_index = poly->loopstart; + if (is_left_handed_) { + usd_index += poly->totloop - 1 - j; + } + else { + usd_index += j; + } + } + else if (interp == pxr::UsdGeomTokens->uniform) { + /* Uniform varying uses the poly index. */ + usd_index = i; + } + + if (usd_index >= display_colors.size()) { + continue; + } + + colors[loop_index].r = unit_float_to_uchar_clamp(display_colors[usd_index][0]); + colors[loop_index].g = unit_float_to_uchar_clamp(display_colors[usd_index][1]); + colors[loop_index].b = unit_float_to_uchar_clamp(display_colors[usd_index][2]); + colors[loop_index].a = unit_float_to_uchar_clamp(1.0); + } + } +} + +void USDMeshReader::process_normals_vertex_varying(Mesh *mesh) +{ + if (!mesh) { + return; + } + + if (normals_.empty()) { + BKE_mesh_calc_normals(mesh); + return; + } + + if (normals_.size() != mesh->totvert) { + std::cerr << "WARNING: vertex varying normals count mismatch for mesh " << prim_path_ + << std::endl; + BKE_mesh_calc_normals(mesh); + return; + } + + for (int i = 0; i < normals_.size(); i++) { + MVert &mvert = mesh->mvert[i]; + normal_float_to_short_v3(mvert.no, normals_[i].data()); + } +} + +void USDMeshReader::process_normals_face_varying(Mesh *mesh) +{ + if (normals_.empty()) { + BKE_mesh_calc_normals(mesh); + return; + } + + /* Check for normals count mismatches to prevent crashes. */ + if (normals_.size() != mesh->totloop) { + std::cerr << "WARNING: loop normal count mismatch for mesh " << mesh->id.name << std::endl; + BKE_mesh_calc_normals(mesh); + return; + } + + mesh->flag |= ME_AUTOSMOOTH; + + long int loop_count = normals_.size(); + + float(*lnors)[3] = static_cast( + MEM_malloc_arrayN(loop_count, sizeof(float[3]), "USD::FaceNormals")); + + MPoly *mpoly = mesh->mpoly; + + for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mpoly) { + for (int j = 0; j < mpoly->totloop; j++) { + int blender_index = mpoly->loopstart + j; + + int usd_index = mpoly->loopstart; + if (is_left_handed_) { + usd_index += mpoly->totloop - 1 - j; + } + else { + usd_index += j; + } + + lnors[blender_index][0] = normals_[usd_index][0]; + lnors[blender_index][1] = normals_[usd_index][1]; + lnors[blender_index][2] = normals_[usd_index][2]; + } + } + BKE_mesh_set_custom_normals(mesh, lnors); + + MEM_freeN(lnors); +} + +/* Set USD uniform (per-face) normals as Blender loop normals. */ +void USDMeshReader::process_normals_uniform(Mesh *mesh) +{ + if (normals_.empty()) { + BKE_mesh_calc_normals(mesh); + return; + } + + /* Check for normals count mismatches to prevent crashes. */ + if (normals_.size() != mesh->totpoly) { + std::cerr << "WARNING: uniform normal count mismatch for mesh " << mesh->id.name << std::endl; + BKE_mesh_calc_normals(mesh); + return; + } + + float(*lnors)[3] = static_cast( + MEM_malloc_arrayN(mesh->totloop, sizeof(float[3]), "USD::FaceNormals")); + + MPoly *mpoly = mesh->mpoly; + + for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mpoly) { + + for (int j = 0; j < mpoly->totloop; j++) { + int loop_index = mpoly->loopstart + j; + lnors[loop_index][0] = normals_[i][0]; + lnors[loop_index][1] = normals_[i][1]; + lnors[loop_index][2] = normals_[i][2]; + } + } + + mesh->flag |= ME_AUTOSMOOTH; + BKE_mesh_set_custom_normals(mesh, lnors); + + MEM_freeN(lnors); +} + +void USDMeshReader::read_mesh_sample(ImportSettings *settings, + Mesh *mesh, + const double motionSampleTime, + const bool new_mesh) +{ + /* Note that for new meshes we always want to read verts and polys, + * regradless of the value of the read_flag, to avoid a crash downstream + * in code that expect this data to be there. */ + + if (new_mesh || (settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) { + for (int i = 0; i < positions_.size(); i++) { + MVert &mvert = mesh->mvert[i]; + mvert.co[0] = positions_[i][0]; + mvert.co[1] = positions_[i][1]; + mvert.co[2] = positions_[i][2]; + } + } + + if (new_mesh || (settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) { + read_mpolys(mesh); + if (normal_interpolation_ == pxr::UsdGeomTokens->faceVarying) { + process_normals_face_varying(mesh); + } + else if (normal_interpolation_ == pxr::UsdGeomTokens->uniform) { + process_normals_uniform(mesh); + } + else { + /* Default */ + BKE_mesh_calc_normals(mesh); + } + } + + /* Process point normals after reading polys. This + * is important in the case where the normals are empty + * and we invoke BKE_mesh_calc_normals(mesh), which requires + * edges to be defined. */ + if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0 && + normal_interpolation_ == pxr::UsdGeomTokens->vertex) { + process_normals_vertex_varying(mesh); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) { + read_uvs(mesh, motionSampleTime, new_mesh); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_COLOR) != 0) { + read_colors(mesh, motionSampleTime); + } +} + +void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime, + MPoly *mpoly, + const int /* totpoly */, + std::map *r_mat_map) +{ + if (r_mat_map == nullptr) { + return; + } + + /* Find the geom subsets that have bound materials. + * We don't call pxr::UsdShadeMaterialBindingAPI::GetMaterialBindSubsets() + * because this function returns only those subsets that are in the 'materialBind' + * family, but, in practice, applications (like Houdini) might export subsets + * in different families that are bound to materials. + * TODO(makowalski): Reassess if the above is the best approach. */ + const std::vector subsets = pxr::UsdGeomSubset::GetAllGeomSubsets( + mesh_prim_); + + int current_mat = 0; + if (!subsets.empty()) { + for (const pxr::UsdGeomSubset &subset : subsets) { + pxr::UsdShadeMaterialBindingAPI subset_api = pxr::UsdShadeMaterialBindingAPI( + subset.GetPrim()); + + pxr::UsdShadeMaterial subset_mtl = subset_api.ComputeBoundMaterial(); + + if (!subset_mtl) { + continue; + } + + pxr::SdfPath subset_mtl_path = subset_mtl.GetPath(); + + if (subset_mtl_path.IsEmpty()) { + continue; + } + + if (r_mat_map->find(subset_mtl_path) == r_mat_map->end()) { + (*r_mat_map)[subset_mtl_path] = 1 + current_mat++; + } + + const int mat_idx = (*r_mat_map)[subset_mtl_path] - 1; + + pxr::UsdAttribute indicesAttribute = subset.GetIndicesAttr(); + pxr::VtIntArray indices; + indicesAttribute.Get(&indices, motionSampleTime); + + for (int i = 0; i < indices.size(); i++) { + MPoly &poly = mpoly[indices[i]]; + poly.mat_nr = mat_idx; + } + } + } + + if (r_mat_map->empty()) { + pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(prim_); + + if (pxr::UsdShadeMaterial mtl = api.ComputeBoundMaterial()) { + + pxr::SdfPath mtl_path = mtl.GetPath(); + + if (!mtl_path.IsEmpty()) { + r_mat_map->insert(std::make_pair(mtl.GetPath(), 1)); + } + } + } +} + +void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double motionSampleTime) +{ + if (!import_params_.import_materials) { + return; + } + + std::map mat_map; + assign_facesets_to_mpoly(motionSampleTime, mesh->mpoly, mesh->totpoly, &mat_map); + utils::assign_materials(bmain, object_, mat_map, this->import_params_, this->prim_.GetStage()); +} + +Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, + const double motionSampleTime, + const int read_flag, + const char ** /* err_str */) +{ + if (!mesh_prim_) { + return existing_mesh; + } + + mesh_prim_.GetOrientationAttr().Get(&orientation_); + if (orientation_ == pxr::UsdGeomTokens->leftHanded) { + is_left_handed_ = true; + } + + std::vector uv_tokens; + + /* Currently we only handle UV primvars. */ + if (read_flag & MOD_MESHSEQ_READ_UV) { + + std::vector primvars = mesh_prim_.GetPrimvars(); + + for (pxr::UsdGeomPrimvar p : primvars) { + + pxr::TfToken name = p.GetPrimvarName(); + pxr::SdfValueTypeName type = p.GetTypeName(); + + bool is_uv = false; + + /* Assume all uvs are stored in one of these primvar types */ + if (type == pxr::SdfValueTypeNames->TexCoord2hArray || + type == pxr::SdfValueTypeNames->TexCoord2fArray || + type == pxr::SdfValueTypeNames->TexCoord2dArray) { + is_uv = true; + } + /* In some cases, the st primvar is stored as float2 values. */ + else if (name == usdtokens::st && type == pxr::SdfValueTypeNames->Float2Array) { + is_uv = true; + } + + if (is_uv) { + + pxr::TfToken interp = p.GetInterpolation(); + + if (!(interp == pxr::UsdGeomTokens->faceVarying || interp == pxr::UsdGeomTokens->vertex)) { + continue; + } + + uv_tokens.push_back(p.GetBaseName()); + has_uvs_ = true; + + /* Record whether the UVs might be time varying. */ + if (primvar_varying_map_.find(name) == primvar_varying_map_.end()) { + bool might_be_time_varying = p.ValueMightBeTimeVarying(); + primvar_varying_map_.insert(std::make_pair(name, might_be_time_varying)); + if (might_be_time_varying) { + is_time_varying_ = true; + } + } + } + } + } + + Mesh *active_mesh = existing_mesh; + bool new_mesh = false; + + /* TODO(makowalski): inmplement the optimization of only updating the mesh points when + * the topology is consistent, as in the Alembic importer. */ + + ImportSettings settings; + settings.read_flag |= read_flag; + + if (topology_changed(existing_mesh, motionSampleTime)) { + new_mesh = true; + active_mesh = BKE_mesh_new_nomain_from_template( + existing_mesh, positions_.size(), 0, 0, face_indices_.size(), face_counts_.size()); + + for (pxr::TfToken token : uv_tokens) { + void *cd_ptr = add_customdata_cb(active_mesh, token.GetText(), CD_MLOOPUV); + active_mesh->mloopuv = static_cast(cd_ptr); + } + } + + read_mesh_sample(&settings, active_mesh, motionSampleTime, new_mesh || is_initial_load_); + + if (new_mesh) { + /* Here we assume that the number of materials doesn't change, i.e. that + * the material slots that were created when the object was loaded from + * USD are still valid now. */ + size_t num_polys = active_mesh->totpoly; + if (num_polys > 0 && import_params_.import_materials) { + std::map mat_map; + assign_facesets_to_mpoly(motionSampleTime, active_mesh->mpoly, num_polys, &mat_map); + } + } + + return active_mesh; +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_mesh.h b/source/blender/io/usd/intern/usd_reader_mesh.h new file mode 100644 index 00000000000..54ad144d191 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_mesh.h @@ -0,0 +1,95 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation and + * NVIDIA Corporation. All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_geom.h" + +#include "pxr/usd/usdGeom/mesh.h" + +struct MPoly; + +namespace blender::io::usd { + +class USDMeshReader : public USDGeomReader { + private: + pxr::UsdGeomMesh mesh_prim_; + + std::unordered_map uv_token_map_; + std::map primvar_varying_map_; + + /* TODO(makowalski): Is it the best strategy to cache the + * mesh geometry in the following members? It appears these + * arrays are never cleared, so this might bloat memory. */ + pxr::VtIntArray face_indices_; + pxr::VtIntArray face_counts_; + pxr::VtVec3fArray positions_; + pxr::VtVec3fArray normals_; + + pxr::TfToken normal_interpolation_; + pxr::TfToken orientation_; + bool is_left_handed_; + bool has_uvs_; + bool is_time_varying_; + + /* This is to ensure we load all data once, because we reuse the read_mesh function + * in the mesh seq modifier, and in initial load. Ideally, a better fix would be + * implemented. Note this will break if faces or positions vary. */ + bool is_initial_load_; + + public: + USDMeshReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings); + + bool valid() const override; + + void create_object(Main *bmain, double motionSampleTime) override; + void read_object_data(Main *bmain, double motionSampleTime) override; + + struct Mesh *read_mesh(struct Mesh *existing_mesh, + double motionSampleTime, + int read_flag, + const char **err_str) override; + + bool topology_changed(Mesh *existing_mesh, double motionSampleTime) override; + + private: + void process_normals_vertex_varying(Mesh *mesh); + void process_normals_face_varying(Mesh *mesh); + void process_normals_uniform(Mesh *mesh); + void readFaceSetsSample(Main *bmain, Mesh *mesh, double motionSampleTime); + void assign_facesets_to_mpoly(double motionSampleTime, + struct MPoly *mpoly, + int totpoly, + std::map *r_mat_map); + + void read_mpolys(Mesh *mesh); + void read_uvs(Mesh *mesh, double motionSampleTime, bool load_uvs = false); + void read_colors(Mesh *mesh, double motionSampleTime); + + void read_mesh_sample(ImportSettings *settings, + Mesh *mesh, + double motionSampleTime, + bool new_mesh); +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.cc b/source/blender/io/usd/intern/usd_reader_nurbs.cc new file mode 100644 index 00000000000..9b30b524729 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_nurbs.cc @@ -0,0 +1,256 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_nurbs.h" + +#include "BKE_curve.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +#include "BLI_listbase.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "MEM_guardedalloc.h" + +#include +#include +#include +#include + +#include + +static bool set_knots(const pxr::VtDoubleArray &knots, float *&nu_knots) +{ + if (knots.empty()) { + return false; + } + + /* Skip first and last knots, as they are used for padding. */ + const size_t num_knots = knots.size(); + nu_knots = static_cast(MEM_callocN(num_knots * sizeof(float), __func__)); + + for (size_t i = 0; i < num_knots; i++) { + nu_knots[i] = (float)knots[i]; + } + + return true; +} + +namespace blender::io::usd { + +void USDNurbsReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); + + curve_->flag |= CU_DEFORM_FILL | CU_3D; + curve_->actvert = CU_ACT_NONE; + curve_->resolu = 2; + + object_ = BKE_object_add_only_object(bmain, OB_CURVE, name_.c_str()); + object_->data = curve_; +} + +void USDNurbsReader::read_object_data(Main *bmain, const double motionSampleTime) +{ + Curve *cu = (Curve *)object_->data; + read_curve_sample(cu, motionSampleTime); + + if (curve_prim_.GetPointsAttr().ValueMightBeTimeVarying()) { + add_cache_modifier(); + } + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +void USDNurbsReader::read_curve_sample(Curve *cu, const double motionSampleTime) +{ + curve_prim_ = pxr::UsdGeomNurbsCurves(prim_); + + pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr(); + pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr(); + pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr(); + + pxr::VtIntArray usdCounts; + vertexAttr.Get(&usdCounts, motionSampleTime); + + pxr::VtVec3fArray usdPoints; + pointsAttr.Get(&usdPoints, motionSampleTime); + + pxr::VtFloatArray usdWidths; + widthsAttr.Get(&usdWidths, motionSampleTime); + + pxr::VtIntArray orders; + curve_prim_.GetOrderAttr().Get(&orders, motionSampleTime); + + pxr::VtDoubleArray knots; + curve_prim_.GetKnotsAttr().Get(&knots, motionSampleTime); + + pxr::VtVec3fArray usdNormals; + curve_prim_.GetNormalsAttr().Get(&usdNormals, motionSampleTime); + + /* If normals, extrude, else bevel. + * Perhaps to be replaced by Blender USD Schema. */ + if (!usdNormals.empty()) { + /* Set extrusion to 1. */ + curve_->ext1 = 1.0f; + } + else { + /* Set bevel depth to 1. */ + curve_->ext2 = 1.0f; + } + + size_t idx = 0; + for (size_t i = 0; i < usdCounts.size(); i++) { + const int num_verts = usdCounts[i]; + + Nurb *nu = static_cast(MEM_callocN(sizeof(Nurb), __func__)); + nu->flag = CU_SMOOTH; + nu->type = CU_NURBS; + + nu->resolu = cu->resolu; + nu->resolv = cu->resolv; + + nu->pntsu = num_verts; + nu->pntsv = 1; + + if (i < orders.size()) { + nu->orderu = static_cast(orders[i]); + } + else { + nu->orderu = 4; + nu->orderv = 4; + } + + /* TODO(makowalski): investigate setting Cyclic U and Endpoint U options. */ +#if 0 + if (knots.size() > 3) { + if ((knots[0] == knots[1]) && (knots[knots.size()] == knots[knots.size() - 1])) { + nu->flagu |= CU_NURB_ENDPOINT; + } else { + nu->flagu |= CU_NURB_CYCLIC; + } + } +#endif + + float weight = 1.0f; + + nu->bp = static_cast(MEM_callocN(sizeof(BPoint) * nu->pntsu, __func__)); + BPoint *bp = nu->bp; + + for (int j = 0; j < nu->pntsu; j++, bp++, idx++) { + bp->vec[0] = (float)usdPoints[idx][0]; + bp->vec[1] = (float)usdPoints[idx][1]; + bp->vec[2] = (float)usdPoints[idx][2]; + bp->vec[3] = weight; + bp->f1 = SELECT; + bp->weight = weight; + + float radius = 0.1f; + if (idx < usdWidths.size()) { + radius = usdWidths[idx]; + } + + bp->radius = radius; + } + + if (!set_knots(knots, nu->knotsu)) { + BKE_nurb_knot_calc_u(nu); + } + + BLI_addtail(BKE_curve_nurbs_get(cu), nu); + } +} + +Mesh *USDNurbsReader::read_mesh(struct Mesh * /* existing_mesh */, + const double motionSampleTime, + const int /* read_flag */, + const char ** /* err_str */) +{ + pxr::UsdGeomCurves curve_prim_(prim_); + + pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr(); + pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr(); + pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr(); + + pxr::VtIntArray usdCounts; + + vertexAttr.Get(&usdCounts, motionSampleTime); + int num_subcurves = usdCounts.size(); + + pxr::VtVec3fArray usdPoints; + pointsAttr.Get(&usdPoints, motionSampleTime); + + int vertex_idx = 0; + int curve_idx; + Curve *curve = static_cast(object_->data); + + const int curve_count = BLI_listbase_count(&curve->nurb); + bool same_topology = curve_count == num_subcurves; + + if (same_topology) { + Nurb *nurbs = static_cast(curve->nurb.first); + for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { + const int num_in_usd = usdCounts[curve_idx]; + const int num_in_blender = nurbs->pntsu; + + if (num_in_usd != num_in_blender) { + same_topology = false; + break; + } + } + } + + if (!same_topology) { + BKE_nurbList_free(&curve->nurb); + read_curve_sample(curve, motionSampleTime); + } + else { + Nurb *nurbs = static_cast(curve->nurb.first); + for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { + const int totpoint = usdCounts[curve_idx]; + + if (nurbs->bp) { + BPoint *point = nurbs->bp; + + for (int i = 0; i < totpoint; i++, point++, vertex_idx++) { + point->vec[0] = usdPoints[vertex_idx][0]; + point->vec[1] = usdPoints[vertex_idx][1]; + point->vec[2] = usdPoints[vertex_idx][2]; + } + } + else if (nurbs->bezt) { + BezTriple *bezier = nurbs->bezt; + + for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) { + bezier->vec[1][0] = usdPoints[vertex_idx][0]; + bezier->vec[1][1] = usdPoints[vertex_idx][1]; + bezier->vec[1][2] = usdPoints[vertex_idx][2]; + } + } + } + } + + return BKE_mesh_new_nomain_from_curve(object_); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.h b/source/blender/io/usd/intern/usd_reader_nurbs.h new file mode 100644 index 00000000000..33a4acf503e --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_nurbs.h @@ -0,0 +1,61 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_geom.h" + +#include "pxr/usd/usdGeom/nurbsCurves.h" + +struct Curve; + +namespace blender::io::usd { + +class USDNurbsReader : public USDGeomReader { + protected: + pxr::UsdGeomNurbsCurves curve_prim_; + Curve *curve_; + + public: + USDNurbsReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDGeomReader(prim, import_params, settings), curve_prim_(prim), curve_(nullptr) + { + } + + bool valid() const override + { + return static_cast(curve_prim_); + } + + void create_object(Main *bmain, double motionSampleTime) override; + void read_object_data(Main *bmain, double motionSampleTime) override; + + void read_curve_sample(Curve *cu, double motionSampleTime); + + Mesh *read_mesh(struct Mesh *existing_mesh, + double motionSampleTime, + int read_flag, + const char **err_str) override; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_prim.cc b/source/blender/io/usd/intern/usd_reader_prim.cc new file mode 100644 index 00000000000..abd70f49f23 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_prim.cc @@ -0,0 +1,80 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_prim.h" + +#include "BLI_utildefines.h" + +namespace blender::io::usd { + +USDPrimReader::USDPrimReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : name_(prim.GetName().GetString()), + prim_path_(prim.GetPrimPath().GetString()), + object_(nullptr), + prim_(prim), + import_params_(import_params), + parent_reader_(nullptr), + settings_(&settings), + refcount_(0) +{ +} + +USDPrimReader::~USDPrimReader() = default; + +const pxr::UsdPrim &USDPrimReader::prim() const +{ + return prim_; +} + +Object *USDPrimReader::object() const +{ + return object_; +} + +void USDPrimReader::object(Object *ob) +{ + object_ = ob; +} + +bool USDPrimReader::valid() const +{ + return prim_.IsValid(); +} + +int USDPrimReader::refcount() const +{ + return refcount_; +} + +void USDPrimReader::incref() +{ + refcount_++; +} + +void USDPrimReader::decref() +{ + refcount_--; + BLI_assert(refcount_ >= 0); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_prim.h b/source/blender/io/usd/intern/usd_reader_prim.h new file mode 100644 index 00000000000..5aff52f011f --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_prim.h @@ -0,0 +1,131 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" + +#include + +struct Main; +struct Object; + +namespace blender::io::usd { + +struct ImportSettings { + bool do_convert_mat; + float conversion_mat[4][4]; + + int from_up; + int from_forward; + float scale; + bool is_sequence; + bool set_frame_range; + + /* Length and frame offset of file sequences. */ + int sequence_len; + int sequence_offset; + + /* From MeshSeqCacheModifierData.read_flag */ + int read_flag; + + bool validate_meshes; + + CacheFile *cache_file; + + ImportSettings() + : do_convert_mat(false), + from_up(0), + from_forward(0), + scale(1.0f), + is_sequence(false), + set_frame_range(false), + sequence_len(1), + sequence_offset(0), + read_flag(0), + validate_meshes(false), + cache_file(NULL) + { + } +}; + +/* Most generic USD Reader. */ + +class USDPrimReader { + + protected: + std::string name_; + std::string prim_path_; + Object *object_; + pxr::UsdPrim prim_; + const USDImportParams &import_params_; + USDPrimReader *parent_reader_; + const ImportSettings *settings_; + int refcount_; + + public: + USDPrimReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings); + virtual ~USDPrimReader(); + + const pxr::UsdPrim &prim() const; + + virtual bool valid() const; + + virtual void create_object(Main *bmain, double motionSampleTime) = 0; + virtual void read_object_data(Main * /* bmain */, double /* motionSampleTime */){}; + + Object *object() const; + void object(Object *ob); + + USDPrimReader *parent() const + { + return parent_reader_; + } + void parent(USDPrimReader *parent) + { + parent_reader_ = parent; + } + + /* Since readers might be referenced through handles + * maintained by modifiers and constraints, we provide + * a reference count to facilitate managing the object + * lifetime. + * TODO(makowalski): investigate transitioning to using + * smart pointers for readers, or, alternatively look into + * making the lifetime management more robust, e.g., by + * making the destructors protected and implementing deletion + * in decref(), etc. */ + int refcount() const; + void incref(); + void decref(); + + const std::string &name() const + { + return name_; + } + const std::string &prim_path() const + { + return prim_path_; + } +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc new file mode 100644 index 00000000000..d3693f783ec --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -0,0 +1,324 @@ +/* + * 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) 2021 Tangent Animation and + * NVIDIA Corporation. All rights reserved. + */ + +#include "usd_reader_stage.h" +#include "usd_reader_camera.h" +#include "usd_reader_curve.h" +#include "usd_reader_instance.h" +#include "usd_reader_light.h" +#include "usd_reader_mesh.h" +#include "usd_reader_nurbs.h" +#include "usd_reader_prim.h" +#include "usd_reader_volume.h" +#include "usd_reader_xform.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace blender::io::usd { + +USDStageReader::USDStageReader(pxr::UsdStageRefPtr stage, + const USDImportParams ¶ms, + const ImportSettings &settings) + : stage_(stage), params_(params), settings_(settings) +{ +} + +USDStageReader::~USDStageReader() +{ + clear_readers(); +} + +bool USDStageReader::valid() const +{ + return stage_; +} + +USDPrimReader *USDStageReader::create_reader_if_allowed(const pxr::UsdPrim &prim) +{ + if (params_.import_cameras && prim.IsA()) { + return new USDCameraReader(prim, params_, settings_); + } + if (params_.import_curves && prim.IsA()) { + return new USDCurvesReader(prim, params_, settings_); + } + if (params_.import_curves && prim.IsA()) { + return new USDNurbsReader(prim, params_, settings_); + } + if (params_.import_meshes && prim.IsA()) { + return new USDMeshReader(prim, params_, settings_); + } + if (params_.import_lights && prim.IsA()) { + return new USDLightReader(prim, params_, settings_); + } + if (params_.import_volumes && prim.IsA()) { + return new USDVolumeReader(prim, params_, settings_); + } + if (prim.IsA()) { + return new USDXformReader(prim, params_, settings_); + } + + return nullptr; +} + +USDPrimReader *USDStageReader::create_reader(const pxr::UsdPrim &prim) +{ + if (prim.IsA()) { + return new USDCameraReader(prim, params_, settings_); + } + if (prim.IsA()) { + return new USDCurvesReader(prim, params_, settings_); + } + if (prim.IsA()) { + return new USDNurbsReader(prim, params_, settings_); + } + if (prim.IsA()) { + return new USDMeshReader(prim, params_, settings_); + } + if (prim.IsA()) { + return new USDLightReader(prim, params_, settings_); + } + if (prim.IsA()) { + return new USDVolumeReader(prim, params_, settings_); + } + if (prim.IsA()) { + return new USDXformReader(prim, params_, settings_); + } + return nullptr; +} + +/* Returns true if the given prim should be included in the + * traversal based on the import options and the prim's visibility + * attribute. Note that the prim will be trivially included + * if it has no visibility attribute or if the visibility + * is inherited. */ +bool USDStageReader::include_by_visibility(const pxr::UsdGeomImageable &imageable) const +{ + if (!params_.import_visible_only) { + /* Invisible prims are allowed. */ + return true; + } + + pxr::UsdAttribute visibility_attr = imageable.GetVisibilityAttr(); + + if (!visibility_attr) { + /* No visibility attribute, so allow. */ + return true; + } + + /* Include if the prim has an animating visibility attribute or is not invisible. */ + + if (visibility_attr.ValueMightBeTimeVarying()) { + return true; + } + + pxr::TfToken visibility; + visibility_attr.Get(&visibility); + return visibility != pxr::UsdGeomTokens->invisible; +} + +/* Returns true if the given prim should be included in the + * traversal based on the import options and the prim's purpose + * attribute. E.g., return false (to exclude the prim) if the prim + * represents guide geometry and the 'Import Guide' option is + * toggled off. */ +bool USDStageReader::include_by_purpose(const pxr::UsdGeomImageable &imageable) const +{ + if (params_.import_guide && params_.import_proxy && params_.import_render) { + /* The options allow any purpose, so we trivially include the prim. */ + return true; + } + + pxr::UsdAttribute purpose_attr = imageable.GetPurposeAttr(); + + if (!purpose_attr) { + /* No purpose attribute, so trivially include the prim. */ + return true; + } + + pxr::TfToken purpose; + purpose_attr.Get(&purpose); + + if (purpose == pxr::UsdGeomTokens->guide) { + return params_.import_guide; + } + if (purpose == pxr::UsdGeomTokens->proxy) { + return params_.import_proxy; + } + if (purpose == pxr::UsdGeomTokens->render) { + return params_.import_render; + } + + return true; +} + +/* Determine if the given reader can use the parent of the encapsulated USD prim + * to compute the Blender object's transform. If so, the reader is appropriately + * flagged and the function returns true. Otherwise, the function returns false. */ +static bool merge_with_parent(USDPrimReader *reader) +{ + USDXformReader *xform_reader = dynamic_cast(reader); + + if (!xform_reader) { + return false; + } + + /* Check if the Xform reader is already merged. */ + if (xform_reader->use_parent_xform()) { + return false; + } + + /* Only merge if the parent is an Xform. */ + if (!xform_reader->prim().GetParent().IsA()) { + return false; + } + + /* Don't merge Xform and Scope prims. */ + if (xform_reader->prim().IsA() || + xform_reader->prim().IsA()) { + return false; + } + + /* Don't merge if the prim has authored transform ops. */ + if (xform_reader->prim_has_xform_ops()) { + return false; + } + + /* Flag the Xform reader as merged. */ + xform_reader->set_use_parent_xform(true); + + return true; +} + +USDPrimReader *USDStageReader::collect_readers(Main *bmain, const pxr::UsdPrim &prim) +{ + if (prim.IsA()) { + pxr::UsdGeomImageable imageable(prim); + + if (!include_by_purpose(imageable)) { + return nullptr; + } + + if (!include_by_visibility(imageable)) { + return nullptr; + } + } + + pxr::Usd_PrimFlagsPredicate filter_predicate = pxr::UsdPrimDefaultPredicate; + + if (params_.import_instance_proxies) { + filter_predicate = pxr::UsdTraverseInstanceProxies(filter_predicate); + } + + pxr::UsdPrimSiblingRange children = prim.GetFilteredChildren(filter_predicate); + + std::vector child_readers; + + for (const auto &childPrim : children) { + if (USDPrimReader *child_reader = collect_readers(bmain, childPrim)) { + child_readers.push_back(child_reader); + } + } + + if (prim.IsPseudoRoot()) { + return nullptr; + } + + /* Check if we can merge an Xform with its child prim. */ + if (child_readers.size() == 1) { + + USDPrimReader *child_reader = child_readers.front(); + + if (merge_with_parent(child_reader)) { + return child_reader; + } + } + + USDPrimReader *reader = create_reader_if_allowed(prim); + + if (!reader) { + return nullptr; + } + + reader->create_object(bmain, 0.0); + + readers_.push_back(reader); + reader->incref(); + + /* Set each child reader's parent. */ + for (USDPrimReader *child_reader : child_readers) { + child_reader->parent(reader); + } + + return reader; +} + +void USDStageReader::collect_readers(Main *bmain) +{ + if (!valid()) { + return; + } + + clear_readers(); + + /* Iterate through the stage. */ + pxr::UsdPrim root = stage_->GetPseudoRoot(); + + std::string prim_path_mask(params_.prim_path_mask); + + if (!prim_path_mask.empty()) { + pxr::UsdPrim prim = stage_->GetPrimAtPath(pxr::SdfPath(prim_path_mask)); + if (prim.IsValid()) { + root = prim; + } + else { + std::cerr << "WARNING: Prim Path Mask " << prim_path_mask + << " does not specify a valid prim.\n"; + } + } + + stage_->SetInterpolationType(pxr::UsdInterpolationType::UsdInterpolationTypeHeld); + collect_readers(bmain, root); +} + +void USDStageReader::clear_readers() +{ + for (USDPrimReader *reader : readers_) { + if (!reader) { + continue; + } + + reader->decref(); + + if (reader->refcount() == 0) { + delete reader; + } + } + + readers_.clear(); +} + +} // Namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_stage.h b/source/blender/io/usd/intern/usd_reader_stage.h new file mode 100644 index 00000000000..7cc557f7802 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_stage.h @@ -0,0 +1,90 @@ +/* + * 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) 2021 Tangent Animation and + * NVIDIA Corporation. All rights reserved. + */ +#pragma once + +struct Main; + +#include "usd.h" +#include "usd_reader_prim.h" + +#include +#include + +#include + +struct ImportSettings; + +namespace blender::io::usd { + +typedef std::map> ProtoReaderMap; + +class USDStageReader { + + protected: + pxr::UsdStageRefPtr stage_; + USDImportParams params_; + ImportSettings settings_; + + std::vector readers_; + + public: + USDStageReader(pxr::UsdStageRefPtr stage, + const USDImportParams ¶ms, + const ImportSettings &settings); + + ~USDStageReader(); + + USDPrimReader *create_reader_if_allowed(const pxr::UsdPrim &prim); + + USDPrimReader *create_reader(const pxr::UsdPrim &prim); + + void collect_readers(struct Main *bmain); + + bool valid() const; + + pxr::UsdStageRefPtr stage() + { + return stage_; + } + const USDImportParams ¶ms() const + { + return params_; + } + + const ImportSettings &settings() const + { + return settings_; + } + + void clear_readers(); + + const std::vector &readers() const + { + return readers_; + }; + + private: + USDPrimReader *collect_readers(Main *bmain, const pxr::UsdPrim &prim); + + bool include_by_visibility(const pxr::UsdGeomImageable &imageable) const; + + bool include_by_purpose(const pxr::UsdGeomImageable &imageable) const; +}; + +}; // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_volume.cc b/source/blender/io/usd/intern/usd_reader_volume.cc new file mode 100644 index 00000000000..871f791c1dd --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_volume.cc @@ -0,0 +1,114 @@ +/* + * 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) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_volume.h" + +#include "BKE_object.h" +#include "BKE_volume.h" + +#include "DNA_object_types.h" +#include "DNA_volume_types.h" + +#include +#include + +#include + +namespace usdtokens { + +static const pxr::TfToken density("density", pxr::TfToken::Immortal); + +} + +namespace blender::io::usd { + +void USDVolumeReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + Volume *volume = (Volume *)BKE_volume_add(bmain, name_.c_str()); + + object_ = BKE_object_add_only_object(bmain, OB_VOLUME, name_.c_str()); + object_->data = volume; +} + +void USDVolumeReader::read_object_data(Main *bmain, const double motionSampleTime) +{ + if (!volume_) { + return; + } + + Volume *volume = static_cast(object_->data); + + if (!volume) { + return; + } + + pxr::UsdVolVolume::FieldMap fields = volume_.GetFieldPaths(); + + for (pxr::UsdVolVolume::FieldMap::const_iterator it = fields.begin(); it != fields.end(); ++it) { + + pxr::UsdPrim fieldPrim = prim_.GetStage()->GetPrimAtPath(it->second); + + if (!fieldPrim.IsA()) { + continue; + } + + pxr::UsdVolOpenVDBAsset fieldBase(fieldPrim); + + pxr::UsdAttribute fieldNameAttr = fieldBase.GetFieldNameAttr(); + + if (fieldNameAttr.IsAuthored()) { + pxr::TfToken fieldName; + fieldNameAttr.Get(&fieldName, motionSampleTime); + + /* A Blender volume creates density by default. */ + if (fieldName != usdtokens::density) { + BKE_volume_grid_add(volume, fieldName.GetString().c_str(), VOLUME_GRID_FLOAT); + } + } + + pxr::UsdAttribute filepathAttr = fieldBase.GetFilePathAttr(); + + if (filepathAttr.IsAuthored()) { + pxr::SdfAssetPath fp; + filepathAttr.Get(&fp, motionSampleTime); + + if (filepathAttr.ValueMightBeTimeVarying()) { + std::vector filePathTimes; + filepathAttr.GetTimeSamples(&filePathTimes); + + if (!filePathTimes.empty()) { + int start = static_cast(filePathTimes.front()); + int end = static_cast(filePathTimes.back()); + + volume->is_sequence = static_cast(true); + volume->frame_start = start; + volume->frame_duration = (end - start) + 1; + } + } + + std::string filepath = fp.GetResolvedPath(); + + strcpy(volume->filepath, filepath.c_str()); + } + } + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_volume.h b/source/blender/io/usd/intern/usd_reader_volume.h new file mode 100644 index 00000000000..ca2fddb5531 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_volume.h @@ -0,0 +1,49 @@ +/* + * 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) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_xform.h" + +#include "pxr/usd/usdVol/volume.h" + +namespace blender::io::usd { + +class USDVolumeReader : public USDXformReader { + private: + pxr::UsdVolVolume volume_; + + public: + USDVolumeReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDXformReader(prim, import_params, settings), volume_(prim) + { + } + + bool valid() const override + { + return static_cast(volume_); + } + + void create_object(Main *bmain, double motionSampleTime) override; + void read_object_data(Main *bmain, double motionSampleTime) override; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_xform.cc b/source/blender/io/usd/intern/usd_reader_xform.cc new file mode 100644 index 00000000000..eebcc5eb3d5 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_xform.cc @@ -0,0 +1,184 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_xform.h" + +#include "BKE_constraint.h" +#include "BKE_lib_id.h" +#include "BKE_library.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "BLI_math_geom.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "DNA_cachefile_types.h" +#include "DNA_constraint_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" /* for FILE_MAX */ + +#include +#include + +#include + +namespace blender::io::usd { + +void USDXformReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + object_ = BKE_object_add_only_object(bmain, OB_EMPTY, name_.c_str()); + object_->empty_drawsize = 0.1f; + object_->data = nullptr; +} + +void USDXformReader::read_object_data(Main * /* bmain */, const double motionSampleTime) +{ + bool is_constant; + float transform_from_usd[4][4]; + + read_matrix(transform_from_usd, motionSampleTime, import_params_.scale, &is_constant); + + if (!is_constant) { + bConstraint *con = BKE_constraint_add_for_object( + object_, nullptr, CONSTRAINT_TYPE_TRANSFORM_CACHE); + bTransformCacheConstraint *data = static_cast(con->data); + + std::string prim_path = use_parent_xform_ ? prim_.GetParent().GetPath().GetAsString() : + prim_path_; + + BLI_strncpy(data->object_path, prim_path.c_str(), FILE_MAX); + + data->cache_file = settings_->cache_file; + id_us_plus(&data->cache_file->id); + } + + BKE_object_apply_mat4(object_, transform_from_usd, true, false); +} + +void USDXformReader::read_matrix(float r_mat[4][4] /* local matrix */, + const float time, + const float scale, + bool *r_is_constant) +{ + if (r_is_constant) { + *r_is_constant = true; + } + + unit_m4(r_mat); + + pxr::UsdGeomXformable xformable; + + if (use_parent_xform_) { + xformable = pxr::UsdGeomXformable(prim_.GetParent()); + } + else { + xformable = pxr::UsdGeomXformable(prim_); + } + + if (!xformable) { + /* This might happen if the prim is a Scope. */ + return; + } + + if (r_is_constant) { + *r_is_constant = !xformable.TransformMightBeTimeVarying(); + } + + pxr::GfMatrix4d usd_local_xf; + bool reset_xform_stack; + xformable.GetLocalTransformation(&usd_local_xf, &reset_xform_stack, time); + + /* Convert the result to a float matrix. */ + pxr::GfMatrix4f mat4f = pxr::GfMatrix4f(usd_local_xf); + mat4f.Get(r_mat); + + /* Apply global scaling and rotation only to root objects, parenting + * will propagate it. */ + if ((scale != 1.0 || settings_->do_convert_mat) && is_root_xform_) { + + if (scale != 1.0f) { + float scale_mat[4][4]; + scale_m4_fl(scale_mat, scale); + mul_m4_m4m4(r_mat, scale_mat, r_mat); + } + + if (settings_->do_convert_mat) { + mul_m4_m4m4(r_mat, settings_->conversion_mat, r_mat); + } + } +} + +bool USDXformReader::prim_has_xform_ops() const +{ + pxr::UsdGeomXformable xformable(prim_); + + if (!xformable) { + /* This might happen if the prim is a Scope. */ + return false; + } + + bool reset_xform_stack = false; + + return !xformable.GetOrderedXformOps(&reset_xform_stack).empty(); +} + +bool USDXformReader::is_root_xform_prim() const +{ + if (!prim_.IsValid()) { + return false; + } + + if (prim_.IsInMaster()) { + /* We don't consider prototypes to be root prims, + * because we never want to apply global scaling + * or rotations to the prototypes themselves. */ + return false; + } + + if (prim_.IsA()) { + /* If this prim doesn't have an ancestor that's a + * UsdGeomXformable, then it's a root prim. Note + * that it's not sufficient to only check the immediate + * parent prim, since the immediate parent could be a + * UsdGeomScope that has an xformable ancestor. */ + pxr::UsdPrim cur_parent = prim_.GetParent(); + + if (use_parent_xform_) { + cur_parent = cur_parent.GetParent(); + } + + while (cur_parent && !cur_parent.IsPseudoRoot()) { + if (cur_parent.IsA()) { + return false; + } + cur_parent = cur_parent.GetParent(); + } + + /* We didn't find an xformable ancestor. */ + return true; + } + + return false; +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_xform.h b/source/blender/io/usd/intern/usd_reader_xform.h new file mode 100644 index 00000000000..587ac373a4f --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_xform.h @@ -0,0 +1,68 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_prim.h" + +namespace blender::io::usd { + +class USDXformReader : public USDPrimReader { + private: + bool use_parent_xform_; + + /* Indicates if the created object is the root of a + * transform hierarchy. */ + bool is_root_xform_; + + public: + USDXformReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDPrimReader(prim, import_params, settings), + use_parent_xform_(false), + is_root_xform_(is_root_xform_prim()) + { + } + + void create_object(Main *bmain, double motionSampleTime) override; + void read_object_data(Main *bmain, double motionSampleTime) override; + + void read_matrix(float r_mat[4][4], const float time, const float scale, bool *r_is_constant); + + bool use_parent_xform() const + { + return use_parent_xform_; + } + void set_use_parent_xform(bool flag) + { + use_parent_xform_ = flag; + is_root_xform_ = is_root_xform_prim(); + } + + bool prim_has_xform_ops() const; + + protected: + /* Returns true if the contained USD prim is the root of a transform hierarchy. */ + bool is_root_xform_prim() const; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index 40e2d0d8674..7a2d1b58c4d 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -26,6 +26,10 @@ extern "C" { #endif struct bContext; +struct Object; +struct CacheArchiveHandle; +struct CacheReader; +struct CacheFile; struct USDExportParams { bool export_animation; @@ -39,6 +43,34 @@ struct USDExportParams { enum eEvaluationMode evaluation_mode; }; +struct USDImportParams { + float scale; + bool is_sequence; + bool set_frame_range; + int sequence_len; + int offset; + bool validate_meshes; + char mesh_read_flag; + bool import_cameras; + bool import_curves; + bool import_lights; + bool import_materials; + bool import_meshes; + bool import_volumes; + char *prim_path_mask; + bool import_subdiv; + bool import_instance_proxies; + bool create_collection; + bool import_guide; + bool import_proxy; + bool import_render; + bool import_visible_only; + bool use_instancing; + bool import_usd_preview; + bool set_material_blend; + float light_intensity_scale; +}; + /* The USD_export takes a as_background_job parameter, and returns a boolean. * * When as_background_job=true, returns false immediately after scheduling @@ -53,8 +85,45 @@ bool USD_export(struct bContext *C, const struct USDExportParams *params, bool as_background_job); +bool USD_import(struct bContext *C, + const char *filepath, + const struct USDImportParams *params, + bool as_background_job); + int USD_get_version(void); +/* USD Import and Mesh Cache interface. */ + +struct CacheArchiveHandle *USD_create_handle(struct Main *bmain, + const char *filename, + struct ListBase *object_paths); + +void USD_free_handle(struct CacheArchiveHandle *handle); + +void USD_get_transform(struct CacheReader *reader, float r_mat[4][4], float time, float scale); + +/* Either modifies current_mesh in-place or constructs a new mesh. */ +struct Mesh *USD_read_mesh(struct CacheReader *reader, + struct Object *ob, + struct Mesh *existing_mesh, + const float time, + const char **err_str, + int flags); + +bool USD_mesh_topology_changed(struct CacheReader *reader, + struct Object *ob, + struct Mesh *existing_mesh, + const float time, + const char **err_str); + +struct CacheReader *CacheReader_open_usd_object(struct CacheArchiveHandle *handle, + struct CacheReader *reader, + struct Object *object, + const char *object_path); + +void USD_CacheReader_incref(struct CacheReader *reader); +void USD_CacheReader_free(struct CacheReader *reader); + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_cachefile_defaults.h b/source/blender/makesdna/DNA_cachefile_defaults.h index d37994bb488..521b72567d4 100644 --- a/source/blender/makesdna/DNA_cachefile_defaults.h +++ b/source/blender/makesdna/DNA_cachefile_defaults.h @@ -36,6 +36,7 @@ .scale = 1.0f, \ .object_paths ={NULL, NULL}, \ \ + .type = 0, \ .handle = NULL, \ .handle_filepath[0] = '\0', \ .handle_readers = NULL, \ diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h index 04c99c6c4b1..b38c7827ea5 100644 --- a/source/blender/makesdna/DNA_cachefile_types.h +++ b/source/blender/makesdna/DNA_cachefile_types.h @@ -31,6 +31,13 @@ extern "C" { struct GSet; +/* CacheFile::type */ +typedef enum { + CACHEFILE_TYPE_ALEMBIC = 1, + CACHEFILE_TYPE_USD = 2, + CACHE_FILE_TYPE_INVALID = 0, +} eCacheFileType; + /* CacheFile::flag */ enum { CACHEFILE_DS_EXPAND = (1 << 0), @@ -44,13 +51,13 @@ enum { }; #endif -/* Representation of an object's path inside the Alembic file. +/* Representation of an object's path inside the archive. * Note that this is not a file path. */ -typedef struct AlembicObjectPath { - struct AlembicObjectPath *next, *prev; +typedef struct CacheObjectPath { + struct CacheObjectPath *next, *prev; char path[4096]; -} AlembicObjectPath; +} CacheObjectPath; /* CacheFile::velocity_unit * Determines what temporal unit is used to interpret velocity vectors for motion blur effects. */ @@ -63,7 +70,7 @@ typedef struct CacheFile { ID id; struct AnimData *adt; - /** Paths of the objects inside of the Alembic archive referenced by this CacheFile. */ + /** Paths of the objects inside of the archive referenced by this CacheFile. */ ListBase object_paths; /** 1024 = FILE_MAX. */ @@ -84,14 +91,17 @@ typedef struct CacheFile { short flag; short draw_flag; /* UNUSED */ - char _pad[3]; + /* eCacheFileType enum. */ + char type; + + char _pad[2]; char velocity_unit; - /* Name of the velocity property in the Alembic file. */ + /* Name of the velocity property in the archive. */ char velocity_name[64]; /* Runtime */ - struct AbcArchiveHandle *handle; + struct CacheArchiveHandle *handle; char handle_filepath[1024]; struct GSet *handle_readers; } CacheFile; diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c index c25cea1b4b3..b93f494072c 100644 --- a/source/blender/makesrna/intern/rna_cachefile.c +++ b/source/blender/makesrna/intern/rna_cachefile.c @@ -64,8 +64,8 @@ static void rna_CacheFile_object_paths_begin(CollectionPropertyIterator *iter, P /* cachefile.object_paths */ static void rna_def_alembic_object_path(BlenderRNA *brna) { - StructRNA *srna = RNA_def_struct(brna, "AlembicObjectPath", NULL); - RNA_def_struct_sdna(srna, "AlembicObjectPath"); + StructRNA *srna = RNA_def_struct(brna, "CacheObjectPath", NULL); + RNA_def_struct_sdna(srna, "CacheObjectPath"); RNA_def_struct_ui_text(srna, "Object Path", "Path of an object inside of an Alembic archive"); RNA_def_struct_ui_icon(srna, ICON_NONE); @@ -81,8 +81,8 @@ static void rna_def_alembic_object_path(BlenderRNA *brna) /* cachefile.object_paths */ static void rna_def_cachefile_object_paths(BlenderRNA *brna, PropertyRNA *cprop) { - RNA_def_property_srna(cprop, "AlembicObjectPaths"); - StructRNA *srna = RNA_def_struct(brna, "AlembicObjectPaths", NULL); + RNA_def_property_srna(cprop, "CacheObjectPaths"); + StructRNA *srna = RNA_def_struct(brna, "CacheObjectPaths", NULL); RNA_def_struct_sdna(srna, "CacheFile"); RNA_def_struct_ui_text(srna, "Object Paths", "Collection of object paths"); } @@ -169,8 +169,8 @@ static void rna_def_cachefile(BlenderRNA *brna) NULL, NULL, NULL); - RNA_def_property_struct_type(prop, "AlembicObjectPath"); - RNA_def_property_srna(prop, "AlembicObjectPaths"); + RNA_def_property_struct_type(prop, "CacheObjectPath"); + RNA_def_property_srna(prop, "CacheObjectPaths"); RNA_def_property_ui_text( prop, "Object Paths", "Paths of the objects inside the Alembic archive"); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 0138dd0c3ad..d9b9fa96d04 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -141,6 +141,16 @@ if(WITH_ALEMBIC) ) endif() +if(WITH_USD) + add_definitions(-DWITH_USD) + list(APPEND INC + ../io/usd + ) + list(APPEND LIB + bf_usd + ) +endif() + if(WITH_MOD_REMESH) list(APPEND INC ../../../intern/dualcon diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index c2f9cd8c867..3e6081e0a18 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -55,18 +55,29 @@ #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" -#ifdef WITH_ALEMBIC -# include "ABC_alembic.h" +#if defined(WITH_USD) || defined(WITH_ALEMBIC) # include "BKE_global.h" # include "BKE_lib_id.h" #endif +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + +#ifdef WITH_USD +# include "usd.h" +#endif + static void initData(ModifierData *md) { MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(mcmd, modifier)); + mcmd->cache_file = NULL; + mcmd->object_path[0] = '\0'; + mcmd->read_flag = MOD_MESHSEQ_READ_ALL; + MEMCPY_STRUCT_AFTER(mcmd, DNA_struct_default_get(MeshSeqCacheModifierData), modifier); } @@ -109,7 +120,7 @@ static bool isDisabled(const struct Scene *UNUSED(scene), static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { -#ifdef WITH_ALEMBIC +#if defined(WITH_USD) || defined(WITH_ALEMBIC) MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; /* Only used to check whether we are operating on org data or not... */ @@ -127,16 +138,32 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * BKE_cachefile_reader_open(cache_file, &mcmd->reader, ctx->object, mcmd->object_path); if (!mcmd->reader) { BKE_modifier_set_error( - ctx->object, md, "Could not create Alembic reader for file %s", cache_file->filepath); + ctx->object, md, "Could not create reader for file %s", cache_file->filepath); return mesh; } } - /* If this invocation is for the ORCO mesh, and the mesh in Alembic hasn't changed topology, we + /* If this invocation is for the ORCO mesh, and the mesh hasn't changed topology, we * must return the mesh as-is instead of deforming it. */ - if (ctx->flag & MOD_APPLY_ORCO && - !ABC_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, &err_str)) { - return mesh; + if (ctx->flag & MOD_APPLY_ORCO) { + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + if (!ABC_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, &err_str)) { + return mesh; + } +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + if (!USD_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, &err_str)) { + return mesh; + } +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } } if (me != NULL) { @@ -156,7 +183,23 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } } - Mesh *result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, time, &err_str, mcmd->read_flag); + Mesh *result = NULL; + + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, time, &err_str, mcmd->read_flag); +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + result = USD_read_mesh( + mcmd->reader, ctx->object, mesh, time * FPS, &err_str, mcmd->read_flag); +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } mcmd->velocity_delta = 1.0f; if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_SECOND) { @@ -187,7 +230,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * static bool dependsOnTime(ModifierData *md) { -#ifdef WITH_ALEMBIC +#if defined(WITH_USD) || defined(WITH_ALEMBIC) MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; return (mcmd->cache_file != NULL); #else -- cgit v1.2.3 From c6f64d46ededba44bff48e18db5a2bbb2cb3d27d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 3 Aug 2021 12:44:16 +0200 Subject: Cleanup: USD importer, consistent naming of function parameter Rename function parameter `flags` to `read_flag` in the declaration, to be consistent with the definition. No functional changes. --- source/blender/io/usd/usd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index 7a2d1b58c4d..6b6b2d37162 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -108,7 +108,7 @@ struct Mesh *USD_read_mesh(struct CacheReader *reader, struct Mesh *existing_mesh, const float time, const char **err_str, - int flags); + int read_flag); bool USD_mesh_topology_changed(struct CacheReader *reader, struct Object *ob, -- cgit v1.2.3 From 20d5d7b8ece6f5e24f75a486e24922e943131dde Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 3 Aug 2021 12:54:05 +0200 Subject: Blender Readfile: Fix annoying useless Object reading error messages. Extend the 'reading error' container to produce the generic short message in the popup directly visible by the user, and move all detailed info the `INFO` reports that only show up in the console and Info editor. --- source/blender/blenkernel/intern/object.c | 41 +++++++++++++++----------- source/blender/blenloader/BLO_readfile.h | 3 ++ source/blender/windowmanager/intern/wm_files.c | 15 ++++++++-- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index a6796d0513c..6a309e040c0 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -144,6 +144,7 @@ #include "DRW_engine.h" #include "BLO_read_write.h" +#include "BLO_readfile.h" #include "SEQ_sequencer.h" @@ -833,7 +834,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) { Object *ob = (Object *)id; - bool warn = false; + BlendFileReadReport *reports = BLO_read_lib_reports(reader); /* XXX deprecated - old animation system <<< */ BLO_read_id_address(reader, ob->id.lib, &ob->ipo); @@ -851,8 +852,8 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) else { if (ob->instance_collection != NULL) { ID *new_id = BLO_read_get_new_id_address(reader, ob->id.lib, &ob->instance_collection->id); - BLO_reportf_wrap(BLO_read_lib_reports(reader), - RPT_WARNING, + BLO_reportf_wrap(reports, + RPT_INFO, TIP_("Non-Empty object '%s' cannot duplicate collection '%s' " "anymore in Blender 2.80, removed instancing"), ob->id.name + 2, @@ -870,11 +871,17 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) ob->proxy = NULL; if (ob->id.lib) { - printf("Proxy lost from object %s lib %s\n", ob->id.name + 2, ob->id.lib->filepath); + BLO_reportf_wrap(reports, + RPT_INFO, + TIP_("Proxy lost from object %s lib %s\n"), + ob->id.name + 2, + ob->id.lib->filepath); } else { - printf("Proxy lost from object %s lib \n", ob->id.name + 2); + BLO_reportf_wrap( + reports, RPT_INFO, TIP_("Proxy lost from object %s lib \n"), ob->id.name + 2); } + reports->count.missing_obproxies++; } else { /* this triggers object_update to always use a copy */ @@ -887,15 +894,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) BLO_read_id_address(reader, ob->id.lib, &ob->data); if (ob->data == NULL && poin != NULL) { - if (ob->id.lib) { - printf("Can't find obdata of %s lib %s\n", ob->id.name + 2, ob->id.lib->filepath); - } - else { - printf("Object %s lost data.\n", ob->id.name + 2); - } - ob->type = OB_EMPTY; - warn = true; if (ob->pose) { /* we can't call #BKE_pose_free() here because of library linking @@ -911,6 +910,18 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) ob->pose = NULL; ob->mode &= ~OB_MODE_POSE; } + + if (ob->id.lib) { + BLO_reportf_wrap(reports, + RPT_INFO, + TIP_("Can't find obdata of %s lib %s\n"), + ob->id.name + 2, + ob->id.lib->filepath); + } + else { + BLO_reportf_wrap(reports, RPT_INFO, TIP_("Object %s lost data\n"), ob->id.name + 2); + } + reports->count.missing_obdata++; } for (int a = 0; a < ob->totcol; a++) { BLO_read_id_address(reader, ob->id.lib, &ob->mat[a]); @@ -992,10 +1003,6 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob1); BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob2); } - - if (warn) { - BLO_reportf_wrap(BLO_read_lib_reports(reader), RPT_WARNING, "Warning in console"); - } } /* XXX deprecated - old animation system */ diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 04e13fbd1d6..c3a33115613 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -108,6 +108,9 @@ typedef struct BlendFileReadReport { * during this file read. */ int missing_libraries; int missing_linked_id; + /* Some sub-categories of the above `missing_linked_id` counter. */ + int missing_obdata; + int missing_obproxies; /* Number of root override IDs that were resynced. */ int resynced_lib_overrides; } count; diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 06aaf95f232..92f3eb67783 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -797,6 +797,7 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports) bf_reports->count.resynced_lib_overrides, duration_lib_override_recursive_resync_minutes, duration_lib_override_recursive_resync_seconds); + if (bf_reports->resynced_lib_overrides_libraries_count != 0) { for (LinkNode *node_lib = bf_reports->resynced_lib_overrides_libraries; node_lib != NULL; node_lib = node_lib->next) { @@ -805,14 +806,22 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports) bf_reports->reports, RPT_INFO, "Library %s needs overrides resync.", library->filepath); } } + if (bf_reports->count.missing_libraries != 0 || bf_reports->count.missing_linked_id != 0) { BKE_reportf(bf_reports->reports, RPT_WARNING, - "%d libraries and %d linked data-blocks are missing, please check the " - "Info and Outliner editors for details", + "%d libraries and %d linked data-blocks are missing (including %d ObjectData and " + "%d Proxies), please check the Info and Outliner editors for details", bf_reports->count.missing_libraries, - bf_reports->count.missing_linked_id); + bf_reports->count.missing_linked_id, + bf_reports->count.missing_obdata, + bf_reports->count.missing_obproxies); } + else { + BLI_assert(bf_reports->count.missing_obdata == 0); + BLI_assert(bf_reports->count.missing_obproxies == 0); + } + if (bf_reports->resynced_lib_overrides_libraries_count != 0) { BKE_reportf(bf_reports->reports, RPT_WARNING, -- cgit v1.2.3 From b35a96e195b100dbad68721065f431b168b7945b Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 3 Aug 2021 12:36:59 +0200 Subject: Fix T90346: particle force field self effect amount off by one When calculating the particle step in `get_effector_tot`, we have to round up (otherwise we might get an extra round in the for-loop in `BKE_effectors_apply` for certain cases). Example from the report: - 10.000 particles, Effector Amount 3 - was rounding the step down to 3333 - going into the for-loop for 0, 3333, 6666 and 9999 (4 times) - now rounding the step up to 3334 - going into the for-loop for 0, 3334 and 6668 (3 times as desired) Maniphest Tasks: T90346 Differential Revision: https://developer.blender.org/D12113 --- source/blender/blenkernel/intern/effect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index fc1721eaf3a..334118ddf3f 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -861,7 +861,7 @@ static void get_effector_tot( int totpart = eff->psys->totpart; int amount = eff->psys->part->effector_amount; - *step = (totpart > amount) ? totpart / amount : 1; + *step = (totpart > amount) ? (int)ceil((float)totpart / (float)amount) : 1; } } else { -- cgit v1.2.3 From dbd34a5acb3d0be2bdbad54a427153de49e47f8e Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 2 Aug 2021 14:12:44 +0200 Subject: Fix T90364: buttons (partially) behind animchannel search block search When channels are scrolled to be (partially) behind the search bar, their widget buttons would still be interactive, preventing the seach buttons to be usable. We have to make sure the events are consumed from the search and dont reach other UI blocks. We can do so by flagging the block `UI_BLOCK_CLIP_EVENTS` -- but also have to make sure the bounds are calculated correctly (otherwise the check relating `UI_BLOCK_CLIP_EVENTS` in `ui_but_find_mouse_over_ex` wont trigger properly. Maniphest Tasks: T90364 Differential Revision: https://developer.blender.org/D12103 --- source/blender/editors/animation/time_scrub_ui.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 6af033f3cf2..182e61e53b6 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -244,6 +244,10 @@ void ED_time_scrub_channel_search_draw(const bContext *C, ARegion *region, bDope UI_block_align_end(block); UI_block_layout_resolve(block, NULL, NULL); + /* Make sure the events are consumed from the search and dont reach other UI blocks since this is + * drawn on top of animchannels. */ + UI_block_flag_enable(block, UI_BLOCK_CLIP_EVENTS); + UI_block_bounds_set_normal(block, 0); UI_block_end(C, block); UI_block_draw(C, block); -- cgit v1.2.3 From 0342fb5d20cd13b495a7511ccfdfc3044d83cb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 3 Aug 2021 13:43:24 +0200 Subject: Fix T90387: division by zero when trying to invert scale Fix division by zero when `BKE_bone_parent_transform_invert()` inverts a scale vector with zero components. Zero values in the to-be-inverted vector are now simply skipped, i.e. remain zero after inversion. This at least ensures that `invert_v3_safe(invert_v3_safe(vector))` results in the same vector. This commit does NOT fix the conceptual problem that an inversion of a potentially non-invertible vector is relied upon. It just avoids the division by zero. --- source/blender/blenkernel/intern/armature.c | 2 +- source/blender/blenlib/BLI_math_vector.h | 1 + source/blender/blenlib/intern/math_vector_inline.c | 13 +++++++++++++ source/blender/blenlib/tests/BLI_math_vector_test.cc | 18 ++++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 434d696f9bd..1f02b084534 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -1882,7 +1882,7 @@ void BKE_bone_parent_transform_invert(struct BoneParentTransform *bpt) { invert_m4(bpt->rotscale_mat); invert_m4(bpt->loc_mat); - invert_v3(bpt->post_scale); + invert_v3_safe(bpt->post_scale); } void BKE_bone_parent_transform_combine(const struct BoneParentTransform *in1, diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index 2f4cf1721af..860ba14a3ed 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -185,6 +185,7 @@ MINLINE void negate_v3_db(double r[3]); MINLINE void invert_v2(float r[2]); MINLINE void invert_v3(float r[3]); +MINLINE void invert_v3_safe(float r[3]); /* Invert the vector, but leaves zero values as zero. */ MINLINE void abs_v2(float r[2]); MINLINE void abs_v2_v2(float r[2], const float a[2]); diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index 55f7a152b83..dddefd60b1b 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -847,6 +847,19 @@ MINLINE void invert_v3(float r[3]) r[2] = 1.0f / r[2]; } +MINLINE void invert_v3_safe(float r[3]) +{ + if (r[0] != 0.0f) { + r[0] = 1.0f / r[0]; + } + if (r[1] != 0.0f) { + r[1] = 1.0f / r[1]; + } + if (r[2] != 0.0f) { + r[2] = 1.0f / r[2]; + } +} + MINLINE void abs_v2(float r[2]) { r[0] = fabsf(r[0]); diff --git a/source/blender/blenlib/tests/BLI_math_vector_test.cc b/source/blender/blenlib/tests/BLI_math_vector_test.cc index 7e75a521d4c..955f02b8065 100644 --- a/source/blender/blenlib/tests/BLI_math_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_math_vector_test.cc @@ -45,3 +45,21 @@ TEST(math_vector, ClampVecWithVecs) EXPECT_FLOAT_EQ(1.0f, c[0]); EXPECT_FLOAT_EQ(3.0f, c[1]); } + +TEST(math_vector, test_invert_v3_safe) +{ + float v3_with_zeroes[3] = {0.0f, 2.0f, 3.0f}; + invert_v3_safe(v3_with_zeroes); + EXPECT_FLOAT_EQ(0.0f, v3_with_zeroes[0]); + EXPECT_FLOAT_EQ(0.5f, v3_with_zeroes[1]); + EXPECT_FLOAT_EQ(0.33333333333f, v3_with_zeroes[2]); + + float v3_without_zeroes[3] = {1.0f, 2.0f, 3.0f}; + float inverted_unsafe[3] = {1.0f, 2.0f, 3.0f}; + invert_v3_safe(v3_without_zeroes); + invert_v3(inverted_unsafe); + + EXPECT_FLOAT_EQ(inverted_unsafe[0], v3_without_zeroes[0]); + EXPECT_FLOAT_EQ(inverted_unsafe[1], v3_without_zeroes[1]); + EXPECT_FLOAT_EQ(inverted_unsafe[2], v3_without_zeroes[2]); +} -- cgit v1.2.3 From cec103d5a905b8236a2196464f4e7d7728eb65c5 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 3 Aug 2021 13:55:57 +0200 Subject: Silenced clang-tidy warnings. --- .../blender/editors/include/ED_keyframes_keylist.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h index 92b57b89cf8..e7d1b5d4363 100644 --- a/source/blender/editors/include/ED_keyframes_keylist.h +++ b/source/blender/editors/include/ED_keyframes_keylist.h @@ -153,44 +153,44 @@ bool ED_keylist_frame_range(const struct AnimKeylist *keylist, struct Range2f *r /* F-Curve */ void fcurve_to_keylist(struct AnimData *adt, struct FCurve *fcu, - struct AnimKeylist *keys, + struct AnimKeylist *keylist, int saction_flag); /* Action Group */ void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, - struct AnimKeylist *keys, + struct AnimKeylist *keylist, int saction_flag); /* Action */ void action_to_keylist(struct AnimData *adt, struct bAction *act, - struct AnimKeylist *keys, + struct AnimKeylist *keylist, int saction_flag); /* Object */ void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, - struct AnimKeylist *keys, + struct AnimKeylist *keylist, int saction_flag); /* Cache File */ void cachefile_to_keylist(struct bDopeSheet *ads, struct CacheFile *cache_file, - struct AnimKeylist *keys, + struct AnimKeylist *keylist, int saction_flag); /* Scene */ void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, - struct AnimKeylist *keys, + struct AnimKeylist *keylist, int saction_flag); /* DopeSheet Summary */ -void summary_to_keylist(struct bAnimContext *ac, struct AnimKeylist *keys, int saction_flag); +void summary_to_keylist(struct bAnimContext *ac, struct AnimKeylist *keylist, int saction_flag); /* Grease Pencil datablock summary */ void gpencil_to_keylist(struct bDopeSheet *ads, struct bGPdata *gpd, - struct AnimKeylist *keys, + struct AnimKeylist *keylist, const bool active); /* Grease Pencil Layer */ -void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct AnimKeylist *keys); +void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct AnimKeylist *keylist); /* Mask */ -void mask_to_keylist(struct bDopeSheet *ads, struct MaskLayer *masklay, struct AnimKeylist *keys); +void mask_to_keylist(struct bDopeSheet *ads, struct MaskLayer *masklay, struct AnimKeylist *keylist); /* ActKeyColumn API ---------------- */ /* Comparator callback used for ActKeyColumns and cframe float-value pointer */ -- cgit v1.2.3 From d3dd735fead26fdd9c645237597635b7cb6b135f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 3 Aug 2021 22:45:37 +1000 Subject: UI: building without Python again Also quiet some warnings. --- source/blender/editors/interface/interface.c | 2 ++ source/blender/editors/interface/interface_icons.c | 1 + source/blender/editors/util/numinput.c | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index a2b25aed582..d3a3df98d99 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -4017,9 +4017,11 @@ uiBut *ui_but_change_type(uiBut *but, eButType new_type) UNUSED_VARS_NDEBUG(found_layout); ui_button_group_replace_but_ptr(uiLayoutGetBlock(but->layout), old_but_ptr, but); } +#ifdef WITH_PYTHON if (UI_editsource_enable_check()) { UI_editsource_but_replace(old_but_ptr, but); } +#endif } return but; diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 0ffc5659191..6755eded05c 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -2425,6 +2425,7 @@ void UI_icon_draw_ex(float x, ImBuf *UI_icon_alert_imbuf_get(eAlertIcon icon) { #ifdef WITH_HEADLESS + UNUSED_VARS(icon); return NULL; #else const int ALERT_IMG_SIZE = 256; diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index 15d672dea56..823837e2a42 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -309,7 +309,7 @@ bool user_string_to_number(bContext *C, return success; #else - UNUSED_VARS(C, unit, type); + UNUSED_VARS(C, unit, type, use_single_line_error, r_error); *r_value = atof(str); return true; #endif -- cgit v1.2.3 From 7724251af81f7222fa552815525ed256fbbb2e95 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 3 Aug 2021 23:24:18 +1000 Subject: WM: don't store selection properties typically set in the key-map While this was already the case for the most part some selection operators stored common settings for reuse such as "toggle", "extend" & "deselect". Disabling storing these settings for later execution as it means failure to set these options in the key-map re-uses the value of the shortcut that was last called. Skip saving these settings since this is a case where reusing them isn't helpful. Resolves T90275. --- source/blender/editors/mesh/editmesh_select.c | 28 +++++++---- source/blender/editors/space_node/node_select.cc | 3 +- .../editors/space_outliner/outliner_select.c | 3 +- .../editors/space_sequencer/sequencer_select.c | 8 +++- .../blender/editors/space_view3d/view3d_select.c | 18 ++++--- source/blender/editors/uvedit/uvedit_select.c | 55 +++++++++++++--------- 6 files changed, 74 insertions(+), 41 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 830c9abb41e..8e38d41f971 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -1860,10 +1860,16 @@ void MESH_OT_loop_select(wmOperatorType *ot) ot->flag = OPTYPE_UNDO; /* properties */ - RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "Extend the selection"); - RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection"); - RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection"); - RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "Select ring"); + PropertyRNA *prop; + + prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "Extend the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "Select ring"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } void MESH_OT_edgering_select(wmOperatorType *ot) @@ -1880,10 +1886,16 @@ void MESH_OT_edgering_select(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); - RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection"); - RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection"); - RNA_def_boolean(ot->srna, "ring", 1, "Select Ring", "Select ring"); + /* Properties. */ + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "ring", 1, "Select Ring", "Select ring"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 6c07c41f451..a1068f29624 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -664,7 +664,8 @@ void NODE_OT_select(wmOperatorType *ot) /* properties */ WM_operator_properties_generic_select(ot); - RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", ""); prop = RNA_def_boolean(ot->srna, "deselect_all", diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index aaa52f6b649..898e66e7a39 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -1682,7 +1682,8 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot) ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO; PropertyRNA *prop; - RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation"); + prop = RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean( ot->srna, "extend_range", false, "Extend Range", "Select a range from active element"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 5980bfe37cd..333edd0ed5f 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -999,7 +999,9 @@ void SEQUENCER_OT_select_linked_pick(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* Properties. */ - RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ @@ -1227,7 +1229,9 @@ void SEQUENCER_OT_select_side_of_frame(wmOperatorType *ot) ot->flag = OPTYPE_UNDO; /* Properties. */ - RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); ot->prop = RNA_def_enum(ot->srna, "side", sequencer_select_left_right_types, 0, "Side", ""); } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index ecf43c734e2..5ec3e9cae5a 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1589,9 +1589,12 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE); ot->prop = prop; - RNA_def_boolean(ot->srna, "extend", 0, "Extend", ""); - RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); - RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); + prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } static Base *object_mouse_select_menu(bContext *C, @@ -1764,9 +1767,12 @@ void VIEW3D_OT_bone_select_menu(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE); ot->prop = prop; - RNA_def_boolean(ot->srna, "extend", 0, "Extend", ""); - RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); - RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); + prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } static bool bone_mouse_select_menu(bContext *C, const uint *buffer, diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 20aadb84b7b..4c597d80534 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -2140,11 +2140,12 @@ void UV_OT_select(wmOperatorType *ot) /* properties */ PropertyRNA *prop; - RNA_def_boolean(ot->srna, - "extend", - 0, - "Extend", - "Extend selection rather than clearing the existing selection"); + prop = RNA_def_boolean(ot->srna, + "extend", + 0, + "Extend", + "Extend selection rather than clearing the existing selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "deselect_all", false, @@ -2152,7 +2153,7 @@ void UV_OT_select(wmOperatorType *ot) "Deselect all when nothing under the cursor"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - RNA_def_float_vector( + prop = RNA_def_float_vector( ot->srna, "location", 2, @@ -2163,6 +2164,7 @@ void UV_OT_select(wmOperatorType *ot) "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ @@ -2296,12 +2298,14 @@ void UV_OT_select_loop(wmOperatorType *ot) ot->poll = ED_operator_uvedit; /* requires space image */ /* properties */ - RNA_def_boolean(ot->srna, - "extend", - 0, - "Extend", - "Extend selection rather than clearing the existing selection"); - RNA_def_float_vector( + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, + "extend", + 0, + "Extend", + "Extend selection rather than clearing the existing selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_float_vector( ot->srna, "location", 2, @@ -2312,6 +2316,7 @@ void UV_OT_select_loop(wmOperatorType *ot) "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ @@ -2494,17 +2499,20 @@ void UV_OT_select_linked_pick(wmOperatorType *ot) ot->poll = ED_operator_uvedit; /* requires space image */ /* properties */ - RNA_def_boolean(ot->srna, - "extend", - 0, - "Extend", - "Extend selection rather than clearing the existing selection"); - RNA_def_boolean(ot->srna, - "deselect", - 0, - "Deselect", - "Deselect linked UV vertices rather than selecting them"); - RNA_def_float_vector( + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, + "extend", + 0, + "Extend", + "Extend selection rather than clearing the existing selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, + "deselect", + 0, + "Deselect", + "Deselect linked UV vertices rather than selecting them"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_float_vector( ot->srna, "location", 2, @@ -2515,6 +2523,7 @@ void UV_OT_select_linked_pick(wmOperatorType *ot) "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ -- cgit v1.2.3 From 391af6bea256e11cbc599ac8b15278e2f956ea01 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 3 Aug 2021 15:48:54 +0200 Subject: Cleanup: Replace int with bool for pointcache function Was using an int for boolean return value. --- source/blender/blenkernel/BKE_pointcache.h | 2 +- source/blender/blenkernel/intern/pointcache.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index 3f99a0dc793..c83fca767a1 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -302,7 +302,7 @@ void BKE_ptcache_remove(void); /************ ID specific functions ************************/ void BKE_ptcache_id_clear(PTCacheID *id, int mode, unsigned int cfra); -int BKE_ptcache_id_exist(PTCacheID *id, int cfra); +bool BKE_ptcache_id_exist(PTCacheID *id, int cfra); int BKE_ptcache_id_reset(struct Scene *scene, PTCacheID *id, int mode); void BKE_ptcache_id_time(PTCacheID *pid, struct Scene *scene, diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 9ed5b0230e6..a6adff35a7f 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -2753,18 +2753,18 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra) pid->cache->flag |= PTCACHE_FLAG_INFO_DIRTY; } -int BKE_ptcache_id_exist(PTCacheID *pid, int cfra) +bool BKE_ptcache_id_exist(PTCacheID *pid, int cfra) { if (!pid->cache) { - return 0; + return false; } if (cfra < pid->cache->startframe || cfra > pid->cache->endframe) { - return 0; + return false; } if (pid->cache->cached_frames && pid->cache->cached_frames[cfra - pid->cache->startframe] == 0) { - return 0; + return false; } if (pid->cache->flag & PTCACHE_DISK_CACHE) { @@ -2779,10 +2779,10 @@ int BKE_ptcache_id_exist(PTCacheID *pid, int cfra) for (; pm; pm = pm->next) { if (pm->frame == cfra) { - return 1; + return true; } } - return 0; + return false; } void BKE_ptcache_id_time( PTCacheID *pid, Scene *scene, float cfra, int *startframe, int *endframe, float *timescale) -- cgit v1.2.3 From 4e1a1821e04aa54d536f49018156e0f9cf95cf7e Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 3 Aug 2021 11:29:28 -0300 Subject: Fix T90313: Align to Transform Orientation Axis Property Doesn't Work `Orientation Axis` is a property incompatible with `Align` mode and should not be visible. --- source/blender/editors/transform/transform_ops.c | 35 +++++++++++++++--------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 2c424d8ace3..45c077b8a07 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -371,25 +371,24 @@ static void transformops_exit(bContext *C, wmOperator *op) G.moving = 0; } +static int transformops_mode(wmOperator *op) +{ + for (TransformModeItem *tmode = transform_modes; tmode->idname; tmode++) { + if (op->type->idname == tmode->idname) { + return tmode->mode; + } + } + + return RNA_enum_get(op->ptr, "mode"); +} + static int transformops_data(bContext *C, wmOperator *op, const wmEvent *event) { int retval = 1; if (op->customdata == NULL) { TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data2"); - TransformModeItem *tmode; - int mode = -1; - - for (tmode = transform_modes; tmode->idname; tmode++) { - if (op->type->idname == tmode->idname) { - mode = tmode->mode; - break; - } - } - - if (mode == -1) { - mode = RNA_enum_get(op->ptr, "mode"); - } + int mode = transformops_mode(op); retval = initTransform(C, t, op, event, mode); /* store data */ @@ -556,6 +555,16 @@ static bool transform_poll_property(const bContext *UNUSED(C), } } + /* Orientation Axis. */ + { + if (STREQ(prop_id, "orient_axis")) { + eTfmMode mode = (eTfmMode)transformops_mode(op); + if (mode == TFM_ALIGN) { + return false; + } + } + } + /* Proportional Editing. */ { PropertyRNA *prop_pet = RNA_struct_find_property(op->ptr, "use_proportional_edit"); -- cgit v1.2.3 From a25a1f39aa1de148605b85ee5f18e52e8038c303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 3 Aug 2021 17:12:03 +0200 Subject: Cleanup: interface, reduce indentation of copy_to_selected_button() Reduce cognitive complexity of `copy_to_selected_button()` by flipping conditions, returning early, and using `continue`. No functional changes. --- source/blender/editors/interface/interface_ops.c | 102 +++++++++++++---------- 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index a75cc839ae4..dd10d942fc9 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -998,55 +998,69 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll) UI_context_active_but_prop_get(C, &ptr, &prop, &index); /* if there is a valid property that is editable... */ - if (ptr.data && prop) { - char *path = NULL; - bool use_path_from_id; - ListBase lb = {NULL}; - - if (UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path) && - !BLI_listbase_is_empty(&lb)) { - LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) { - if (link->ptr.data != ptr.data) { - if (use_path_from_id) { - /* Path relative to ID. */ - lprop = NULL; - RNA_id_pointer_create(link->ptr.owner_id, &idptr); - RNA_path_resolve_property(&idptr, path, &lptr, &lprop); - } - else if (path) { - /* Path relative to elements from list. */ - lprop = NULL; - RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop); - } - else { - lptr = link->ptr; - lprop = prop; - } + if (ptr.data == NULL || prop == NULL) { + return false; + } - if (lptr.data == ptr.data) { - /* lptr might not be the same as link->ptr! */ - continue; - } + char *path = NULL; + bool use_path_from_id; + ListBase lb = {NULL}; - if (lprop == prop) { - if (RNA_property_editable(&lptr, lprop)) { - if (poll) { - success = true; - break; - } - if (RNA_property_copy(bmain, &lptr, &ptr, prop, (all) ? -1 : index)) { - RNA_property_update(C, &lptr, prop); - success = true; - } - } - } - } - } - } + if (!UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path)) { + return false; + } + if (BLI_listbase_is_empty(&lb)) { MEM_SAFE_FREE(path); - BLI_freelistN(&lb); + return false; } + LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) { + if (link->ptr.data == ptr.data) { + continue; + } + + if (use_path_from_id) { + /* Path relative to ID. */ + lprop = NULL; + RNA_id_pointer_create(link->ptr.owner_id, &idptr); + RNA_path_resolve_property(&idptr, path, &lptr, &lprop); + } + else if (path) { + /* Path relative to elements from list. */ + lprop = NULL; + RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop); + } + else { + lptr = link->ptr; + lprop = prop; + } + + if (lptr.data == ptr.data) { + /* lptr might not be the same as link->ptr! */ + continue; + } + + if (lprop != prop) { + continue; + } + + if (!RNA_property_editable(&lptr, lprop)) { + continue; + } + + if (poll) { + success = true; + break; + } + if (RNA_property_copy(bmain, &lptr, &ptr, prop, (all) ? -1 : index)) { + RNA_property_update(C, &lptr, prop); + success = true; + } + } + + MEM_SAFE_FREE(path); + BLI_freelistN(&lb); + return success; } -- cgit v1.2.3 From 652fbc200500497a67bd11d18b786587ba34e3d9 Mon Sep 17 00:00:00 2001 From: Ankit Meel Date: Tue, 3 Aug 2021 20:49:40 +0530 Subject: macOS: Portable builds with dynamic libraries. For Blender.app: dropping libomp.dylib next to Blender executable is enough for it getting picked up since `@executable_path` is an rpath. For non-distributed binaries datatoc, makesdna, tests etc, code for copying libomp.dylib to build folder is removed and replaced by CMake's rpath option for *build* tree. For bpy.so, the post build rpath change has also been replaced by CMake rpath option for *install* tree. Since -id has been changed in D11748, remove the `install_name_tool -change ...` command. Any dylib can just be dropped at `MAC_BLENDER_TARGET_DYLIBS_DIR` hereafter. Appending dylib path to `CMAKE_BUILD_RPATH` will be needed for datatoc etc if linked against one (instead of copying the dylibs around). Reviewed By: #platform_macos, brecht Differential Revision: https://developer.blender.org/D11997 --- CMakeLists.txt | 4 +++ build_files/cmake/platform/platform_apple.cmake | 38 ++++++++++++------------- source/creator/CMakeLists.txt | 30 +++++++++---------- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c6408bee2c..b7dfb56ff02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,6 +110,10 @@ if(POLICY CMP0074) cmake_policy(SET CMP0074 NEW) endif() +# Install CODE|SCRIPT allow the use of generator expressions. +if(POLICY CMP0087) + cmake_policy(SET CMP0087 NEW) +endif() #----------------------------------------------------------------------------- # Load some macros. include(build_files/cmake/macros.cmake) diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index 70973eeda99..529c01db009 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -411,25 +411,9 @@ if(WITH_OPENMP) set(OPENMP_FOUND ON) set(OpenMP_C_FLAGS "-Xclang -fopenmp -I'${LIBDIR}/openmp/include'") set(OpenMP_CXX_FLAGS "-Xclang -fopenmp -I'${LIBDIR}/openmp/include'") - set(OpenMP_LINKER_FLAGS "-L'${LIBDIR}/openmp/lib' -lomp") - - # Copy libomp.dylib to allow executables like datatoc and tests to work. - # `@executable_path/../Resources/lib/` `LC_ID_DYLIB` is added by the deps builder. - # For single config generator datatoc, tests etc. - execute_process( - COMMAND mkdir -p ${CMAKE_BINARY_DIR}/Resources/lib - COMMAND cp -p ${LIBDIR}/openmp/lib/libomp.dylib ${CMAKE_BINARY_DIR}/Resources/lib/libomp.dylib - ) - # For multi-config generator datatoc, etc. - execute_process( - COMMAND mkdir -p ${CMAKE_BINARY_DIR}/bin/Resources/lib - COMMAND cp -p ${LIBDIR}/openmp/lib/libomp.dylib ${CMAKE_BINARY_DIR}/bin/Resources/lib/libomp.dylib - ) - # For multi-config generator tests. - execute_process( - COMMAND mkdir -p ${CMAKE_BINARY_DIR}/bin/tests/Resources/lib - COMMAND cp -p ${LIBDIR}/openmp/lib/libomp.dylib ${CMAKE_BINARY_DIR}/bin/tests/Resources/lib/libomp.dylib - ) + set(OpenMP_LIBRARY_DIR "${LIBDIR}/openmp/lib/") + set(OpenMP_LINKER_FLAGS "-L'${OpenMP_LIBRARY_DIR}' -lomp") + set(OpenMP_LIBRARY "${OpenMP_LIBRARY_DIR}/libomp.dylib") endif() endif() @@ -511,3 +495,19 @@ if(WITH_COMPILER_CCACHE) endif() endif() endif() + +# For binaries that are built but not installed (also not distributed) (datatoc, +# 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, but +# needs no changes since it already looks for dylibs next to the executable by +# default (@executable_path). +# +# For the installed Python module, CMAKE_INSTALL_RPATH is modified to find the +# dylib in an adjacent folder. +set(CMAKE_SKIP_BUILD_RPATH FALSE) +list(APPEND CMAKE_BUILD_RPATH "${OpenMP_LIBRARY_DIR}") +if(WITH_PYTHON_MODULE) + list(APPEND CMAKE_INSTALL_RPATH "@loader_path/../Resources/${BLENDER_VERSION}/lib") +endif() diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index c3aeffe8fda..f7179dfb7e9 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -345,8 +345,13 @@ 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 a default + # rpath, so dropping libraries next to Blender is enough. + set(MAC_BLENDER_TARGET_DYLIBS_DIR "$") endif() # Skip relinking on cpack / install set_target_properties(blender PROPERTIES BUILD_WITH_INSTALL_RPATH true) @@ -1041,23 +1046,16 @@ elseif(APPLE) if(WITH_OPENMP AND OPENMP_CUSTOM) install( - FILES ${LIBDIR}/openmp/lib/libomp.dylib - DESTINATION Blender.app/Contents/Resources/lib + FILES "${OpenMP_LIBRARY}" + DESTINATION "${MAC_BLENDER_TARGET_DYLIBS_DIR}" + ) + endif() + + if(WITH_COMPILER_ASAN) + install( + FILES "${COMPILER_ASAN_LIBRARY}" + DESTINATION "${MAC_BLENDER_TARGET_DYLIBS_DIR}" ) - if(WITH_PYTHON_MODULE) - # Move the dylib in a Blender version folder to keep the corresponding OpenMP version. - install( - DIRECTORY ${CMAKE_BINARY_DIR}/Resources/lib - DESTINATION ${TARGETDIR_VER} - ) - add_custom_command(TARGET blender POST_BUILD - # The old `LC_LOAD_DYLIB` is the `LC_ID_DYLIB` of the LIBDIR OpenMP dylib. - # Change it to support multiple rpaths. - COMMAND xcrun install_name_tool -change "@executable_path/../Resources/lib/libomp.dylib" "@rpath/libomp.dylib" "$" - # For installation into site-packages. - COMMAND xcrun install_name_tool -add_rpath "@loader_path/../Resources/${BLENDER_VERSION}/lib" "$" - ) - endif() endif() # python -- cgit v1.2.3 From 57281b73c45197ccf78476b73ed85a3ea30699e9 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 3 Aug 2021 17:20:25 +0200 Subject: Install_deps: Always re-create shortcuts to installed lib paths. For some reasons looks like those shortcuts could get out of sync, which created weird hard to understand building errors. So for sake of simplicity and security, just re-create them all the time, just like we update ld paths. --- build_files/build_environment/install_deps.sh | 149 +++++++++++++++----------- 1 file changed, 86 insertions(+), 63 deletions(-) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 26281887e1f..ff4aad79bb6 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -1447,9 +1447,7 @@ compile_Python() { make -j$THREADS && make install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "Python--$PYTHON_VERSION failed to compile, exiting" exit 1 fi @@ -1465,6 +1463,9 @@ compile_Python() { INFO "If you want to force rebuild of this lib, use the --force-python option." fi + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "python-$PYTHON_VERSION_SHORT" # Extra step: install required modules with pip. @@ -1558,9 +1559,7 @@ compile_Boost() { --prefix=$_inst --disable-icu boost.locale.icu=off install ./b2 --clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "Boost-$BOOST_VERSION failed to compile, exiting" exit 1 fi @@ -1574,7 +1573,9 @@ compile_Boost() { INFO "If you want to force rebuild of this lib, use the --force-boost option." fi - # Just always run it, much simpler this way! + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "boost" } @@ -1687,9 +1688,7 @@ compile_TBB() { make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "TBB-$TBB_VERSION$TBB_VERSION_UPDATE failed to compile, exiting" exit 1 fi @@ -1703,6 +1702,9 @@ compile_TBB() { INFO "If you want to force rebuild of this lib, use the --force-tbb option." fi + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "tbb" } @@ -1822,9 +1824,7 @@ compile_OCIO() { make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "OpenColorIO-$OCIO_VERSION failed to compile, exiting" exit 1 fi @@ -1838,6 +1838,9 @@ compile_OCIO() { INFO "If you want to force rebuild of this lib, use the --force-ocio option." fi + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "ocio" } @@ -1953,9 +1956,7 @@ compile_OPENEXR() { make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "OpenEXR-$OPENEXR_VERSION failed to compile, exiting" exit 1 fi @@ -1971,7 +1972,9 @@ compile_OPENEXR() { _with_built_openexr=true - # Just always run it, much simpler this way! + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "openexr" } @@ -2112,9 +2115,7 @@ compile_OIIO() { make -j$THREADS && make install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "OpenImageIO-$OIIO_VERSION failed to compile, exiting" exit 1 fi @@ -2128,7 +2129,9 @@ compile_OIIO() { INFO "If you want to force rebuild of this lib, use the --force-oiio option." fi - # Just always run it, much simpler this way! + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "oiio" } @@ -2237,9 +2240,7 @@ compile_LLVM() { make -j$THREADS && make install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "LLVM-$LLVM_VERSION failed to compile, exiting" exit 1 fi @@ -2252,6 +2253,10 @@ compile_LLVM() { INFO "Own LLVM-$LLVM_VERSION (CLANG included) is up to date, nothing to do!" INFO "If you want to force rebuild of this lib, use the --force-llvm option." fi + + if [ -d $_inst ]; then + _create_inst_shortcut + fi } # ---------------------------------------------------------------------------- @@ -2390,9 +2395,7 @@ compile_OSL() { make -j$THREADS && make install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "OpenShadingLanguage-$OSL_VERSION failed to compile, exiting" exit 1 fi @@ -2406,6 +2409,9 @@ compile_OSL() { INFO "If you want to force rebuild of this lib, use the --force-osl option." fi + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "osl" } @@ -2506,9 +2512,7 @@ compile_OSD() { make -j$THREADS && make install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "OpenSubdiv-$OSD_VERSION failed to compile, exiting" exit 1 fi @@ -2522,6 +2526,9 @@ compile_OSD() { INFO "If you want to force rebuild of this lib, use the --force-osd option." fi + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "osd" } @@ -2611,9 +2618,7 @@ compile_BLOSC() { make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "Blosc-$OPENVDB_BLOSC_VERSION failed to compile, exiting" exit 1 fi @@ -2626,6 +2631,9 @@ compile_BLOSC() { magic_compile_set blosc-$OPENVDB_BLOSC_VERSION $blosc_magic + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "blosc" } @@ -2716,9 +2724,7 @@ install_NanoVDB() { #~ mkdir -p $_inst #~ cp -r $_src/include $_inst/include - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "NanoVDB-v$OPENVDB_VERSION failed to install, exiting" exit 1 fi @@ -2730,6 +2736,10 @@ install_NanoVDB() { else INFO "Own NanoVDB-v$OPENVDB_VERSION is up to date, nothing to do!" fi + + if [ -d $_inst ]; then + _create_inst_shortcut + fi } @@ -2849,9 +2859,7 @@ compile_OPENVDB() { make -j$THREADS install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "OpenVDB-$OPENVDB_VERSION failed to compile, exiting" exit 1 fi @@ -2865,6 +2873,9 @@ compile_OPENVDB() { INFO "If you want to force rebuild of this lib, use the --force-openvdb option." fi + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "openvdb" if [ "$WITH_NANOVDB" = true ]; then @@ -2962,9 +2973,7 @@ compile_ALEMBIC() { make -j$THREADS install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "Alembic-$ALEMBIC_VERSION failed to compile, exiting" exit 1 fi @@ -2978,6 +2987,9 @@ compile_ALEMBIC() { INFO "If you want to force rebuild of this lib, use the --force-alembic option." fi + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "alembic" } @@ -3062,9 +3074,7 @@ compile_USD() { make -j$THREADS install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "USD-$USD_VERSION failed to compile, exiting" exit 1 fi @@ -3078,6 +3088,9 @@ compile_USD() { INFO "If you want to force rebuild of this lib, use the --force-usd option." fi + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "usd" } @@ -3171,9 +3184,7 @@ compile_OpenCOLLADA() { make -j$THREADS && make install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "OpenCOLLADA-$OPENCOLLADA_VERSION failed to compile, exiting" exit 1 fi @@ -3186,6 +3197,10 @@ compile_OpenCOLLADA() { INFO "Own OpenCOLLADA-$OPENCOLLADA_VERSION is up to date, nothing to do!" INFO "If you want to force rebuild of this lib, use the --force-opencollada option." fi + + if [ -d $_inst ]; then + _create_inst_shortcut + fi } # ---------------------------------------------------------------------------- @@ -3286,9 +3301,7 @@ compile_Embree() { make -j$THREADS && make install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "Embree-$EMBREE_VERSION failed to compile, exiting" exit 1 fi @@ -3301,6 +3314,10 @@ compile_Embree() { INFO "Own Embree-$EMBREE_VERSION is up to date, nothing to do!" INFO "If you want to force rebuild of this lib, use the --force-embree option." fi + + if [ -d $_inst ]; then + _create_inst_shortcut + fi } # ---------------------------------------------------------------------------- @@ -3363,9 +3380,7 @@ install_ISPC() { mkdir -p $_inst cp -r $_src/bin $_inst/bin - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "ISPC-v$ISPC_VERSION failed to install, exiting" exit 1 fi @@ -3378,6 +3393,10 @@ install_ISPC() { INFO "Own ISPC-v$ISPC_VERSION is up to date, nothing to do!" fi + if [ -d $_inst ]; then + _create_inst_shortcut + fi + _ispc_path_bin=$_inst/bin run_ldconfig "ispc" } @@ -3477,9 +3496,7 @@ compile_OIDN() { make -j$THREADS && make install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "OpenImageDenoise-$OIDN_VERSION failed to compile, exiting" exit 1 fi @@ -3493,6 +3510,9 @@ compile_OIDN() { INFO "If you want to force rebuild of this lib, use the --force-oidn option." fi + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "oidn" } @@ -3609,9 +3629,7 @@ compile_FFmpeg() { make -j$THREADS && make install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "FFmpeg-$FFMPEG_VERSION failed to compile, exiting" exit 1 fi @@ -3624,6 +3642,10 @@ compile_FFmpeg() { INFO "Own ffmpeg-$FFMPEG_VERSION is up to date, nothing to do!" INFO "If you want to force rebuild of this lib, use the --force-ffmpeg option." fi + + if [ -d $_inst ]; then + _create_inst_shortcut + fi } # ---------------------------------------------------------------------------- @@ -3722,9 +3744,7 @@ compile_XR_OpenXR_SDK() { make -j$THREADS && make install make clean - if [ -d $_inst ]; then - _create_inst_shortcut - else + if [ ! -d $_inst ]; then ERROR "XR-OpenXR-SDK-$XR_OPENXR_VERSION failed to compile, exiting" exit 1 fi @@ -3738,6 +3758,9 @@ compile_XR_OpenXR_SDK() { INFO "If you want to force rebuild of this lib, use the --force-xr-openxr option." fi + if [ -d $_inst ]; then + _create_inst_shortcut + fi run_ldconfig "xr-openxr-sdk" } -- cgit v1.2.3 From 39e914cee7573fea073b19e039cfa04779b35c72 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 3 Aug 2021 13:32:18 -0300 Subject: Fix select engine buffer having wrong vertex size The theme used was wrong and the vertex size is twice as set in the theme. --- source/blender/draw/engines/select/select_engine.c | 2 +- source/blender/draw/intern/draw_manager.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c index 86b4a0ac727..96ab8a28e09 100644 --- a/source/blender/draw/engines/select/select_engine.c +++ b/source/blender/draw/engines/select/select_engine.c @@ -193,7 +193,7 @@ static void select_cache_init(void *vedata) if (e_data.context.select_mode & SCE_SELECT_VERTEX) { DRW_PASS_CREATE(psl->select_id_vert_pass, state); pd->shgrp_vert = DRW_shgroup_create(sh->select_id_flat, psl->select_id_vert_pass); - DRW_shgroup_uniform_float_copy(pd->shgrp_vert, "sizeVertex", G_draw.block.sizeVertex); + DRW_shgroup_uniform_float_copy(pd->shgrp_vert, "sizeVertex", 2 * G_draw.block.sizeVertex); } } diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 35072518b66..a8cbe7b18b5 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2682,6 +2682,7 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons drw_viewport_var_init(); /* Update UBO's */ + UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW); DRW_globals_update(); /* Init Select Engine */ -- cgit v1.2.3 From 41357d556f4e3b286ab4ecfaeb990cc40bf08333 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 3 Aug 2021 19:37:34 +0200 Subject: Fix broken logic in Windows directory query function Mistake in a5bbdd6998ab --- source/blender/blenlib/intern/storage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c index 19b925535e2..47bb2f0e8dd 100644 --- a/source/blender/blenlib/intern/storage.c +++ b/source/blender/blenlib/intern/storage.c @@ -114,7 +114,7 @@ double BLI_dir_free_space(const char *dir) tmp[0] = '\\'; tmp[1] = 0; /* Just a fail-safe. */ - if (ELEM(dir[0] == '/', '\\')) { + if (ELEM(dir[0], '/', '\\')) { tmp[0] = '\\'; tmp[1] = 0; } -- cgit v1.2.3 From 18d900caca8317d33216bbc778c07b9f6ce3da84 Mon Sep 17 00:00:00 2001 From: Ankit Meel Date: Wed, 4 Aug 2021 01:22:27 +0530 Subject: macOS: Fix OpenMP dynamic loader error. --- build_files/cmake/platform/platform_apple.cmake | 9 ++++++--- source/creator/CMakeLists.txt | 3 +-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index 529c01db009..a130d265dff 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -500,14 +500,17 @@ 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, but -# needs no changes since it already looks for dylibs next to the executable by -# default (@executable_path). +# 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. 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() diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index f7179dfb7e9..e928be571a2 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -349,8 +349,7 @@ elseif(APPLE) 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 a default - # rpath, so dropping libraries next to Blender is enough. + # Dylibs folder for Blender executable. @executable_path is an rpath. set(MAC_BLENDER_TARGET_DYLIBS_DIR "$") endif() # Skip relinking on cpack / install -- cgit v1.2.3 From fb87d236edb7e98c5fc526b9829e6bc6b8916828 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 3 Aug 2021 17:34:14 -0300 Subject: Fix calculation of 'projmat_dimensions' `r_left`, `r_right`, `r_bottom` and `r_top` were ignoring `clip_near` value when in perspective view. Also rename `projmat` to `winmat` in these cases. --- source/blender/blenlib/BLI_math_geom.h | 4 +- source/blender/blenlib/intern/math_geom.c | 64 +++++++++++++++---------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 9ac14a6edfe..bcda25ca533 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -682,14 +682,14 @@ void planes_from_projmat(const float mat[4][4], float near[4], float far[4]); -void projmat_dimensions(const float projmat[4][4], +void projmat_dimensions(const float winmat[4][4], float *r_left, float *r_right, float *r_bottom, float *r_top, float *r_near, float *r_far); -void projmat_dimensions_db(const float projmat[4][4], +void projmat_dimensions_db(const float winmat[4][4], double *r_left, double *r_right, double *r_bottom, diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 803291e4a3b..823e72a91e5 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -4962,7 +4962,7 @@ void planes_from_projmat(const float mat[4][4], } } -void projmat_dimensions(const float projmat[4][4], +void projmat_dimensions(const float winmat[4][4], float *r_left, float *r_right, float *r_bottom, @@ -4970,27 +4970,27 @@ void projmat_dimensions(const float projmat[4][4], float *r_near, float *r_far) { - bool is_persp = projmat[3][3] == 0.0f; - + const bool is_persp = winmat[3][3] == 0.0f; if (is_persp) { - *r_left = (projmat[2][0] - 1.0f) / projmat[0][0]; - *r_right = (projmat[2][0] + 1.0f) / projmat[0][0]; - *r_bottom = (projmat[2][1] - 1.0f) / projmat[1][1]; - *r_top = (projmat[2][1] + 1.0f) / projmat[1][1]; - *r_near = projmat[3][2] / (projmat[2][2] - 1.0f); - *r_far = projmat[3][2] / (projmat[2][2] + 1.0f); + const float near = winmat[3][2] / (winmat[2][2] - 1.0f); + *r_left = near * ((winmat[2][0] - 1.0f) / winmat[0][0]); + *r_right = near * ((winmat[2][0] + 1.0f) / winmat[0][0]); + *r_bottom = near * ((winmat[2][1] - 1.0f) / winmat[1][1]); + *r_top = near * ((winmat[2][1] + 1.0f) / winmat[1][1]); + *r_near = near; + *r_far = winmat[3][2] / (winmat[2][2] + 1.0f); } else { - *r_left = (-projmat[3][0] - 1.0f) / projmat[0][0]; - *r_right = (-projmat[3][0] + 1.0f) / projmat[0][0]; - *r_bottom = (-projmat[3][1] - 1.0f) / projmat[1][1]; - *r_top = (-projmat[3][1] + 1.0f) / projmat[1][1]; - *r_near = (projmat[3][2] + 1.0f) / projmat[2][2]; - *r_far = (projmat[3][2] - 1.0f) / projmat[2][2]; + *r_left = (-winmat[3][0] - 1.0f) / winmat[0][0]; + *r_right = (-winmat[3][0] + 1.0f) / winmat[0][0]; + *r_bottom = (-winmat[3][1] - 1.0f) / winmat[1][1]; + *r_top = (-winmat[3][1] + 1.0f) / winmat[1][1]; + *r_near = (winmat[3][2] + 1.0f) / winmat[2][2]; + *r_far = (winmat[3][2] - 1.0f) / winmat[2][2]; } } -void projmat_dimensions_db(const float projmat_fl[4][4], +void projmat_dimensions_db(const float winmat_fl[4][4], double *r_left, double *r_right, double *r_bottom, @@ -4998,26 +4998,26 @@ void projmat_dimensions_db(const float projmat_fl[4][4], double *r_near, double *r_far) { - double projmat[4][4]; - copy_m4d_m4(projmat, projmat_fl); - - bool is_persp = projmat[3][3] == 0.0f; + double winmat[4][4]; + copy_m4d_m4(winmat, winmat_fl); + const bool is_persp = winmat[3][3] == 0.0f; if (is_persp) { - *r_left = (projmat[2][0] - 1.0) / projmat[0][0]; - *r_right = (projmat[2][0] + 1.0) / projmat[0][0]; - *r_bottom = (projmat[2][1] - 1.0) / projmat[1][1]; - *r_top = (projmat[2][1] + 1.0) / projmat[1][1]; - *r_near = projmat[3][2] / (projmat[2][2] - 1.0); - *r_far = projmat[3][2] / (projmat[2][2] + 1.0); + const double near = winmat[3][2] / (winmat[2][2] - 1.0); + *r_left = near * ((winmat[2][0] - 1.0) / winmat[0][0]); + *r_right = near * ((winmat[2][0] + 1.0) / winmat[0][0]); + *r_bottom = near * ((winmat[2][1] - 1.0) / winmat[1][1]); + *r_top = near * ((winmat[2][1] + 1.0) / winmat[1][1]); + *r_near = near; + *r_far = winmat[3][2] / (winmat[2][2] + 1.0); } else { - *r_left = (-projmat[3][0] - 1.0) / projmat[0][0]; - *r_right = (-projmat[3][0] + 1.0) / projmat[0][0]; - *r_bottom = (-projmat[3][1] - 1.0) / projmat[1][1]; - *r_top = (-projmat[3][1] + 1.0) / projmat[1][1]; - *r_near = (projmat[3][2] + 1.0) / projmat[2][2]; - *r_far = (projmat[3][2] - 1.0) / projmat[2][2]; + *r_left = (-winmat[3][0] - 1.0) / winmat[0][0]; + *r_right = (-winmat[3][0] + 1.0) / winmat[0][0]; + *r_bottom = (-winmat[3][1] - 1.0) / winmat[1][1]; + *r_top = (-winmat[3][1] + 1.0) / winmat[1][1]; + *r_near = (winmat[3][2] + 1.0) / winmat[2][2]; + *r_far = (winmat[3][2] - 1.0) / winmat[2][2]; } } -- cgit v1.2.3 From 0d2589d08c615d1bc07e5156b41b9ffda6c3ba27 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 4 Aug 2021 10:03:07 +1000 Subject: Cleanup: spelling --- source/blender/blenlib/BLI_array.h | 2 +- .../blender/blenlib/intern/polyfill_2d_beautify.c | 2 +- .../blender/bmesh/tools/bmesh_decimate_collapse.c | 4 ++-- .../compositor/operations/COM_GlareBaseOperation.h | 4 ++-- source/blender/editors/animation/time_scrub_ui.c | 4 ++-- source/blender/editors/mask/mask_select.c | 4 ++-- source/blender/editors/uvedit/uvedit_smart_stitch.c | 2 +- source/blender/io/usd/intern/usd_capi_import.cc | 5 ++--- source/blender/io/usd/intern/usd_reader_material.h | 21 +++++++++++---------- source/blender/io/usd/intern/usd_reader_mesh.cc | 18 +++++++++--------- 10 files changed, 33 insertions(+), 33 deletions(-) diff --git a/source/blender/blenlib/BLI_array.h b/source/blender/blenlib/BLI_array.h index 6bf29a6168f..084f573e8c7 100644 --- a/source/blender/blenlib/BLI_array.h +++ b/source/blender/blenlib/BLI_array.h @@ -156,7 +156,7 @@ void _bli_array_grow_func(void **arr_p, * \{ */ /** - * not part of the 'API' but handy funcs, + * Not part of the 'API' but handy functions, * same purpose as #BLI_array_staticdeclare() * but use when the max size is known ahead of time */ #define BLI_array_fixedstack_declare(arr, maxstatic, realsize, allocstr) \ diff --git a/source/blender/blenlib/intern/polyfill_2d_beautify.c b/source/blender/blenlib/intern/polyfill_2d_beautify.c index 7781e3a0f6f..ed07b002e32 100644 --- a/source/blender/blenlib/intern/polyfill_2d_beautify.c +++ b/source/blender/blenlib/intern/polyfill_2d_beautify.c @@ -25,7 +25,7 @@ * on a simple polygon representation where we _know_: * * - The polygon is primitive with no holes with a continuous boundary. - * - Tris have consistent winding. + * - Triangles have consistent winding. * - 2d (saves some hassles projecting face pairs on an axis for every edge-rotation) * also saves us having to store all previous edge-states (see #EdRotState in bmesh_beautify.c) * diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c index 20b6903b239..97fccbe01fd 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c @@ -935,9 +935,9 @@ static bool bm_edge_collapse_is_degenerate_topology(BMEdge *e_first) } /** - * special, highly limited edge collapse function + * Special, highly limited edge collapse function * intended for speed over flexibility. - * can only collapse edges connected to (1, 2) tris. + * can only collapse edges connected to (1, 2) triangles. * * Important - don't add vert/edge/face data on collapsing! * diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h index 50db4e02940..6dac6f5ecc7 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.h +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h @@ -23,8 +23,8 @@ namespace blender::compositor { -/* utility functions used by glare, tonemap and lens distortion */ -/* soms macros for color handling */ +/* Utility functions used by glare, tone-map and lens distortion. */ +/* Some macros for color handling. */ typedef float fRGB[4]; /* TODO: replace with BLI_math_vector. */ diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 182e61e53b6..8aeb6a57124 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -244,8 +244,8 @@ void ED_time_scrub_channel_search_draw(const bContext *C, ARegion *region, bDope UI_block_align_end(block); UI_block_layout_resolve(block, NULL, NULL); - /* Make sure the events are consumed from the search and dont reach other UI blocks since this is - * drawn on top of animchannels. */ + /* Make sure the events are consumed from the search and don't reach other UI blocks since this + * is drawn on top of animation-channels. */ UI_block_flag_enable(block, UI_BLOCK_CLIP_EVENTS); UI_block_bounds_set_normal(block, 0); UI_block_end(C, block); diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index 3bb05a27c54..8ddc3758e4e 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -164,8 +164,8 @@ void ED_mask_select_flush_all(Mask *mask) LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) { spline->flag &= ~SELECT; - /* intentionally _dont_ do this in the mask layer loop - * so we clear flags on all splines */ + /* Intentionally *don't* do this in the mask layer loop + * so we clear flags on all splines. */ if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) { continue; } diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 535a0e00347..1bcd1fad9d6 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1769,7 +1769,7 @@ static void stitch_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void GPU_blend(GPU_BLEND_ALPHA); - /* Static Tris */ + /* Static Triangles. */ if (stitch_preview->static_tris) { UI_GetThemeColor4fv(TH_STITCH_PREVIEW_ACTIVE, col); vbo = GPU_vertbuf_create_with_format(&format); diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index 8255fca284c..789ff20ba82 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -365,8 +365,7 @@ bool USD_import(struct bContext *C, { blender::io::usd::ensure_usd_plugin_path_registered(); - /* Using new here since MEM_* funcs do not call ctor to properly initialize - * data. */ + /* Using new here since `MEM_*` functions do not call constructor to properly initialize data. */ ImportJobData *job = new ImportJobData(); job->bmain = CTX_data_main(C); job->scene = CTX_data_scene(C); @@ -496,7 +495,7 @@ CacheReader *CacheReader_open_usd_object(CacheArchiveHandle *handle, USDPrimReader *usd_reader = archive->create_reader(prim); if (usd_reader == nullptr) { - /* This object is not supported */ + /* This object is not supported. */ return nullptr; } usd_reader->object(object); diff --git a/source/blender/io/usd/intern/usd_reader_material.h b/source/blender/io/usd/intern/usd_reader_material.h index 3e8fc675931..a17504bd590 100644 --- a/source/blender/io/usd/intern/usd_reader_material.h +++ b/source/blender/io/usd/intern/usd_reader_material.h @@ -55,28 +55,29 @@ struct NodePlacementContext { /* Converts USD materials to Blender representation. */ -/* By default, the USDMaterialReader creates a Blender material with +/** + By default, the #USDMaterialReader creates a Blender material with * the same name as the USD material. If the USD material has a - * UsdPreviewSurface source, the Blender material's viewport display + * #UsdPreviewSurface source, the Blender material's viewport display * color, roughness and metallic properties are set to the corresponding - * UsdPreoviewSurface inputs. + * #UsdPreoviewSurface inputs. * * If the Import USD Preview option is enabled, the current implementation - * converts UsdPreviewSurface to Blender nodes as follows: + * converts #UsdPreviewSurface to Blender nodes as follows: * - * UsdPreviewSurface -> Pricipled BSDF - * UsdUVTexture -> Texture Image + Normal Map - * UsdPrimvarReader_float2 -> UV Map + * - #UsdPreviewSurface -> Principled BSDF + * - #UsdUVTexture -> Texture Image + Normal Map + * - UsdPrimvarReader_float2 -> UV Map * * Limitations: arbitrary primvar readers or UsdTransform2d not yet - * supported. For UsdUVTexture, only the file, st and sourceColorSpace + * supported. For #UsdUVTexture, only the file, st and #sourceColorSpace * inputs are handled. * * TODO(makowalski): Investigate adding support for converting additional * shaders and inputs. Supporting certain types of inputs, such as texture * scale and bias, will probably require creating Blender Group nodes with - * the corresponding inputs. */ - + * the corresponding inputs. + */ class USDMaterialReader { protected: USDImportParams params_; diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index f13da4680e2..9c75bc8afae 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -61,7 +61,7 @@ static const pxr::TfToken normalsPrimvar("normals", pxr::TfToken::Immortal); } // namespace usdtokens namespace utils { -/* Very similar to abc mesh utils. */ +/* Very similar to #blender::io::alembic::utils. */ static void build_mat_map(const Main *bmain, std::map *r_mat_map) { if (r_mat_map == nullptr) { @@ -71,7 +71,7 @@ static void build_mat_map(const Main *bmain, std::map * Material *material = static_cast(bmain->materials.first); for (; material; material = static_cast(material->id.next)) { - /* We have to do this because the stored material name is coming directly from usd. */ + /* We have to do this because the stored material name is coming directly from USD. */ (*r_mat_map)[pxr::TfMakeValidIdentifier(material->id.name + 2)] = material; } } @@ -212,7 +212,7 @@ void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime) is_initial_load_ = false; if (read_mesh != mesh) { - /* XXX fixme after 2.80; mesh->flag isn't copied by BKE_mesh_nomain_to_mesh() */ + /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */ /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_, &CD_MASK_MESH, true); @@ -334,7 +334,7 @@ void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bo pxr::TfToken uv_token; - /* If first time seeing uv token, store in map of uid, TfToken> */ + /* If first time seeing uv token, store in map of `uid, TfToken>`. */ if (uv_token_map_.find(layer_name) == uv_token_map_.end()) { uv_token = pxr::TfToken(layer_name); uv_token_map_.insert(std::make_pair(layer_name, uv_token)); @@ -347,7 +347,7 @@ void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bo if (uv_token.IsEmpty()) { continue; } - /* Early out if not first load and uvs arent animated. */ + /* Early out if not first load and UVs aren't animated. */ if (!load_uvs && primvar_varying_map_.find(uv_token) != primvar_varying_map_.end() && !primvar_varying_map_.at(uv_token)) { continue; @@ -630,7 +630,7 @@ void USDMeshReader::read_mesh_sample(ImportSettings *settings, const bool new_mesh) { /* Note that for new meshes we always want to read verts and polys, - * regradless of the value of the read_flag, to avoid a crash downstream + * regardless of the value of the read_flag, to avoid a crash downstream * in code that expect this data to be there. */ if (new_mesh || (settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) { @@ -684,7 +684,7 @@ void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime, } /* Find the geom subsets that have bound materials. - * We don't call pxr::UsdShadeMaterialBindingAPI::GetMaterialBindSubsets() + * We don't call #pxr::UsdShadeMaterialBindingAPI::GetMaterialBindSubsets() * because this function returns only those subsets that are in the 'materialBind' * family, but, in practice, applications (like Houdini) might export subsets * in different families that are bound to materials. @@ -780,7 +780,7 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, bool is_uv = false; - /* Assume all uvs are stored in one of these primvar types */ + /* Assume all UVs are stored in one of these primvar types */ if (type == pxr::SdfValueTypeNames->TexCoord2hArray || type == pxr::SdfValueTypeNames->TexCoord2fArray || type == pxr::SdfValueTypeNames->TexCoord2dArray) { @@ -817,7 +817,7 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, Mesh *active_mesh = existing_mesh; bool new_mesh = false; - /* TODO(makowalski): inmplement the optimization of only updating the mesh points when + /* TODO(makowalski): implement the optimization of only updating the mesh points when * the topology is consistent, as in the Alembic importer. */ ImportSettings settings; -- cgit v1.2.3 From 8a1c1279b3d9c5478d7e94c4875c141db750fbcb Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 4 Aug 2021 11:03:23 +1000 Subject: Icons: update alert icon script Missed from c549d736cff0d5013f05fb5240ef07671c5aa5ce. --- release/datafiles/alert_icons_update.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/release/datafiles/alert_icons_update.py b/release/datafiles/alert_icons_update.py index dba96c2126a..95e4ee7afbb 100644 --- a/release/datafiles/alert_icons_update.py +++ b/release/datafiles/alert_icons_update.py @@ -10,7 +10,7 @@ BASEDIR = os.path.abspath(os.path.dirname(__file__)) inkscape_path = 'inkscape' if sys.platform == 'darwin': - inkscape_app_path = '/Applications/Inkscape.app/Contents/Resources/script' + inkscape_app_path = '/Applications/Inkscape.app/Contents/MacOS/inkscape' if os.path.exists(inkscape_app_path): inkscape_path = inkscape_app_path @@ -19,7 +19,7 @@ cmd = ( os.path.join(BASEDIR, "alert_icons.svg"), "--export-width=1280", "--export-height=256", - "--without-gui", - "--export-png=" + os.path.join(BASEDIR, "alert_icons.png"), + "--export-type=png", + "--export-filename=" + os.path.join(BASEDIR, "alert_icons.png"), ) subprocess.check_call(cmd) -- cgit v1.2.3 From 7389fd9a35a3c8911f2d502ea2c745d574a2de21 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 4 Aug 2021 11:31:28 +1000 Subject: Icons: resolve various issues for generating icons - INKSCAPE_BIN environment variable was ignored by alert_icons_update & prvicons_update. - `make icons` wasn't regenerating alert icons. - Updating SVG icons failed using blender built with ASAN. --- GNUmakefile | 4 +++- release/datafiles/alert_icons_update.py | 6 +++--- release/datafiles/blender_icons_geom_update.py | 12 ++++++++---- release/datafiles/blender_icons_update.py | 16 ++++++++++------ release/datafiles/prvicons_update.py | 6 +++--- 5 files changed, 27 insertions(+), 17 deletions(-) mode change 100644 => 100755 release/datafiles/alert_icons_update.py diff --git a/GNUmakefile b/GNUmakefile index 7df561ed34f..635cc321d03 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -528,8 +528,10 @@ INKSCAPE_BIN?="inkscape" icons: .FORCE BLENDER_BIN=$(BLENDER_BIN) INKSCAPE_BIN=$(INKSCAPE_BIN) \ "$(BLENDER_DIR)/release/datafiles/blender_icons_update.py" - BLENDER_BIN=$(BLENDER_BIN) INKSCAPE_BIN=$(INKSCAPE_BIN) \ + INKSCAPE_BIN=$(INKSCAPE_BIN) \ "$(BLENDER_DIR)/release/datafiles/prvicons_update.py" + INKSCAPE_BIN=$(INKSCAPE_BIN) \ + "$(BLENDER_DIR)/release/datafiles/alert_icons_update.py" icons_geom: .FORCE BLENDER_BIN=$(BLENDER_BIN) \ diff --git a/release/datafiles/alert_icons_update.py b/release/datafiles/alert_icons_update.py old mode 100644 new mode 100755 index 95e4ee7afbb..a3951c114ae --- a/release/datafiles/alert_icons_update.py +++ b/release/datafiles/alert_icons_update.py @@ -7,15 +7,15 @@ import sys BASEDIR = os.path.abspath(os.path.dirname(__file__)) -inkscape_path = 'inkscape' +inkscape_bin = os.environ.get("INKSCAPE_BIN", "inkscape") if sys.platform == 'darwin': inkscape_app_path = '/Applications/Inkscape.app/Contents/MacOS/inkscape' if os.path.exists(inkscape_app_path): - inkscape_path = inkscape_app_path + inkscape_bin = inkscape_app_path cmd = ( - inkscape_path, + inkscape_bin, os.path.join(BASEDIR, "alert_icons.svg"), "--export-width=1280", "--export-height=256", diff --git a/release/datafiles/blender_icons_geom_update.py b/release/datafiles/blender_icons_geom_update.py index 5b95961ae6b..d5373d6b2e9 100755 --- a/release/datafiles/blender_icons_geom_update.py +++ b/release/datafiles/blender_icons_geom_update.py @@ -6,10 +6,9 @@ import subprocess import sys -def run(cmd): +def run(cmd, *, env=None): print(" ", " ".join(cmd)) - # Don't use check_call because asan causes nonzero exitcode :S - subprocess.call(cmd) + subprocess.check_call(cmd, env=env) def edit_text_file(filename, marker_begin, marker_end, content): @@ -73,7 +72,12 @@ for blend in icons_blend: "--group", "Export", "--output-dir", output_dir, ) - run(cmd) + + env = {} + # Developers may have ASAN enabled, avoid non-zero exit codes. + env["ASAN_OPTIONS"] = "exitcode=0:" + os.environ.get("ASAN_OPTIONS", "") + + run(cmd, env=env) files_new = set(names_and_time_from_path(output_dir)) icon_files.extend([ diff --git a/release/datafiles/blender_icons_update.py b/release/datafiles/blender_icons_update.py index 8167b8b25e6..ead74aac759 100755 --- a/release/datafiles/blender_icons_update.py +++ b/release/datafiles/blender_icons_update.py @@ -6,13 +6,17 @@ import subprocess import sys -def run(cmd): +def run(cmd, *, env=None): print(" ", " ".join(cmd)) - subprocess.check_call(cmd) + subprocess.check_call(cmd, env=env) BASEDIR = os.path.abspath(os.path.dirname(__file__)) +env = {} +# Developers may have ASAN enabled, avoid non-zero exit codes. +env["ASAN_OPTIONS"] = "exitcode=0:" + os.environ.get("ASAN_OPTIONS", "") + inkscape_bin = os.environ.get("INKSCAPE_BIN", "inkscape") blender_bin = os.environ.get("BLENDER_BIN", "blender") @@ -32,7 +36,7 @@ cmd = ( "--export-type=png", "--export-filename=" + os.path.join(BASEDIR, "blender_icons16.png"), ) -run(cmd) +run(cmd, env=env) cmd = ( inkscape_bin, @@ -42,7 +46,7 @@ cmd = ( "--export-type=png", "--export-filename=" + os.path.join(BASEDIR, "blender_icons32.png"), ) -run(cmd) +run(cmd, env=env) # For testing it can be good to clear all old @@ -64,7 +68,7 @@ cmd = ( "--minx_icon", "2", "--maxx_icon", "2", "--miny_icon", "2", "--maxy_icon", "2", "--spacex_icon", "1", "--spacey_icon", "1", ) -run(cmd) +run(cmd, env=env) cmd = ( blender_bin, "--background", "--factory-startup", "-noaudio", @@ -78,7 +82,7 @@ cmd = ( "--minx_icon", "4", "--maxx_icon", "4", "--miny_icon", "4", "--maxy_icon", "4", "--spacex_icon", "2", "--spacey_icon", "2", ) -run(cmd) +run(cmd, env=env) os.remove(os.path.join(BASEDIR, "blender_icons16.png")) os.remove(os.path.join(BASEDIR, "blender_icons32.png")) diff --git a/release/datafiles/prvicons_update.py b/release/datafiles/prvicons_update.py index fa526f88e96..c9bd8b44301 100755 --- a/release/datafiles/prvicons_update.py +++ b/release/datafiles/prvicons_update.py @@ -7,15 +7,15 @@ import sys BASEDIR = os.path.abspath(os.path.dirname(__file__)) -inkscape_path = 'inkscape' +inkscape_bin = os.environ.get("INKSCAPE_BIN", "inkscape") if sys.platform == 'darwin': inkscape_app_path = '/Applications/Inkscape.app/Contents/MacOS/inkscape' if os.path.exists(inkscape_app_path): - inkscape_path = inkscape_app_path + inkscape_bin = inkscape_app_path cmd = ( - inkscape_path, + inkscape_bin, os.path.join(BASEDIR, "prvicons.svg"), "--export-width=1792", "--export-height=256", -- cgit v1.2.3 From 71dc134f8908cda02a3e5e5800bb9da966c10ad2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 4 Aug 2021 11:43:56 +1000 Subject: Icons: add license headers to utilities --- release/datafiles/alert_icons_update.py | 20 ++++++++++++++++++ release/datafiles/blender_icons_geom_update.py | 20 ++++++++++++++++++ release/datafiles/blender_icons_update.py | 20 ++++++++++++++++++ release/datafiles/ctodata.py | 28 ++++++++++++-------------- release/datafiles/prvicons_update.py | 20 ++++++++++++++++++ 5 files changed, 93 insertions(+), 15 deletions(-) diff --git a/release/datafiles/alert_icons_update.py b/release/datafiles/alert_icons_update.py index a3951c114ae..34a2798a18c 100755 --- a/release/datafiles/alert_icons_update.py +++ b/release/datafiles/alert_icons_update.py @@ -1,5 +1,25 @@ #!/usr/bin/env python3 +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + # This script updates icons from the SVG file import os import subprocess diff --git a/release/datafiles/blender_icons_geom_update.py b/release/datafiles/blender_icons_geom_update.py index d5373d6b2e9..df4683809db 100755 --- a/release/datafiles/blender_icons_geom_update.py +++ b/release/datafiles/blender_icons_geom_update.py @@ -1,5 +1,25 @@ #!/usr/bin/env python3 +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + # This script updates icons from the BLEND file import os import subprocess diff --git a/release/datafiles/blender_icons_update.py b/release/datafiles/blender_icons_update.py index ead74aac759..dab3cd76a87 100755 --- a/release/datafiles/blender_icons_update.py +++ b/release/datafiles/blender_icons_update.py @@ -1,5 +1,25 @@ #!/usr/bin/env python3 +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + # This script updates icons from the SVG file import os import subprocess diff --git a/release/datafiles/ctodata.py b/release/datafiles/ctodata.py index 0471faad565..20f119c4264 100755 --- a/release/datafiles/ctodata.py +++ b/release/datafiles/ctodata.py @@ -1,27 +1,25 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# ***** BEGIN GPL LICENSE BLOCK ***** +# ##### BEGIN GPL LICENSE BLOCK ##### # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. +# This program is 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. +# 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. +# 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. # All rights reserved. # -# ***** END GPL LICENCE BLOCK ***** - +# ##### END GPL LICENSE BLOCK ##### # diff --git a/release/datafiles/prvicons_update.py b/release/datafiles/prvicons_update.py index c9bd8b44301..ad42ede9772 100755 --- a/release/datafiles/prvicons_update.py +++ b/release/datafiles/prvicons_update.py @@ -1,5 +1,25 @@ #!/usr/bin/env python3 +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + # This script updates icons from the SVG file import os import subprocess -- cgit v1.2.3 From 5950b3ab739f0d57cdac44c57d4448c0a5dbebb2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 4 Aug 2021 12:43:07 +1000 Subject: Cleanup: de-duplicate ID renaming utility for versioning --- source/blender/blenloader/intern/versioning_280.c | 53 +++++--------------- .../blender/blenloader/intern/versioning_common.cc | 37 ++++++++++++++ .../blender/blenloader/intern/versioning_common.h | 6 +++ .../blenloader/intern/versioning_defaults.c | 57 ++++++---------------- 4 files changed, 71 insertions(+), 82 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index af05c4b902f..93959237f29 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -111,40 +111,13 @@ #include "BLO_readfile.h" #include "readfile.h" +#include "versioning_common.h" + #include "MEM_guardedalloc.h" /* Make preferences read-only, use versioning_userdef.c. */ #define U (*((const UserDef *)&U)) -/** - * Rename if the ID doesn't exist. - */ -static ID *rename_id_for_versioning(Main *bmain, - const short id_type, - const char *name_src, - const char *name_dst) -{ - /* We can ignore libraries */ - ListBase *lb = which_libbase(bmain, id_type); - ID *id = NULL; - LISTBASE_FOREACH (ID *, idtest, lb) { - if (idtest->lib == NULL) { - if (STREQ(idtest->name + 2, name_src)) { - id = idtest; - } - if (STREQ(idtest->name + 2, name_dst)) { - return NULL; - } - } - } - if (id != NULL) { - BLI_strncpy(id->name + 2, name_dst, sizeof(id->name) - 2); - /* We know it's unique, this just sorts. */ - BLI_libblock_ensure_unique_name(bmain, id->name); - } - return id; -} - static bScreen *screen_parent_find(const bScreen *screen) { /* Can avoid lookup if screen state isn't maximized/full @@ -1679,32 +1652,32 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) Brush *brush; Material *ma; /* Pen Soft brush. */ - brush = (Brush *)rename_id_for_versioning(bmain, ID_BR, "Draw Soft", "Pencil Soft"); + brush = (Brush *)do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft"); if (brush) { brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; } - rename_id_for_versioning(bmain, ID_BR, "Draw Pencil", "Pencil"); - rename_id_for_versioning(bmain, ID_BR, "Draw Pen", "Pen"); - rename_id_for_versioning(bmain, ID_BR, "Draw Ink", "Ink Pen"); - rename_id_for_versioning(bmain, ID_BR, "Draw Noise", "Ink Pen Rough"); - rename_id_for_versioning(bmain, ID_BR, "Draw Marker", "Marker Bold"); - rename_id_for_versioning(bmain, ID_BR, "Draw Block", "Marker Chisel"); + do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil"); + do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen"); + do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen"); + do_versions_rename_id(bmain, ID_BR, "Draw Noise", "Ink Pen Rough"); + do_versions_rename_id(bmain, ID_BR, "Draw Marker", "Marker Bold"); + do_versions_rename_id(bmain, ID_BR, "Draw Block", "Marker Chisel"); ma = BLI_findstring(&bmain->materials, "Black", offsetof(ID, name) + 2); if (ma && ma->gp_style) { - rename_id_for_versioning(bmain, ID_MA, "Black", "Solid Stroke"); + do_versions_rename_id(bmain, ID_MA, "Black", "Solid Stroke"); } ma = BLI_findstring(&bmain->materials, "Red", offsetof(ID, name) + 2); if (ma && ma->gp_style) { - rename_id_for_versioning(bmain, ID_MA, "Red", "Squares Stroke"); + do_versions_rename_id(bmain, ID_MA, "Red", "Squares Stroke"); } ma = BLI_findstring(&bmain->materials, "Grey", offsetof(ID, name) + 2); if (ma && ma->gp_style) { - rename_id_for_versioning(bmain, ID_MA, "Grey", "Solid Fill"); + do_versions_rename_id(bmain, ID_MA, "Grey", "Solid Fill"); } ma = BLI_findstring(&bmain->materials, "Black Dots", offsetof(ID, name) + 2); if (ma && ma->gp_style) { - rename_id_for_versioning(bmain, ID_MA, "Black Dots", "Dots Stroke"); + do_versions_rename_id(bmain, ID_MA, "Black Dots", "Dots Stroke"); } brush = BLI_findstring(&bmain->brushes, "Pencil", offsetof(ID, name) + 2); diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index f5083b8e259..208c02b60d1 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -20,9 +20,15 @@ /* allow readfile to use deprecated functionality */ #define DNA_DEPRECATED_ALLOW +#include + #include "DNA_screen_types.h" #include "BLI_listbase.h" +#include "BLI_string.h" + +#include "BKE_lib_id.h" +#include "BKE_main.h" #include "MEM_guardedalloc.h" @@ -48,3 +54,34 @@ ARegion *do_versions_add_region_if_not_found(ListBase *regionbase, BLI_insertlinkafter(regionbase, link_after_region, new_region); return new_region; } + +/** + * Rename if the ID doesn't exist. + * + * \return the ID (if found). + */ +ID *do_versions_rename_id(Main *bmain, + const short id_type, + const char *name_src, + const char *name_dst) +{ + /* We can ignore libraries */ + ListBase *lb = which_libbase(bmain, id_type); + ID *id = nullptr; + LISTBASE_FOREACH (ID *, idtest, lb) { + if (idtest->lib == nullptr) { + if (STREQ(idtest->name + 2, name_src)) { + id = idtest; + } + if (STREQ(idtest->name + 2, name_dst)) { + return nullptr; + } + } + } + if (id != nullptr) { + BLI_strncpy(id->name + 2, name_dst, sizeof(id->name) - 2); + /* We know it's unique, this just sorts. */ + BLI_libblock_ensure_unique_name(bmain, id->name); + } + return id; +} diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index a1769d4639e..47e0b74a3e4 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -22,6 +22,7 @@ struct ARegion; struct ListBase; +struct Main; #ifdef __cplusplus extern "C" { @@ -32,6 +33,11 @@ struct ARegion *do_versions_add_region_if_not_found(struct ListBase *regionbase, const char *name, int link_after_region_type); +ID *do_versions_rename_id(Main *bmain, + const short id_type, + const char *name_src, + const char *name_dst); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 8362e001ea6..7ff624b44da 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -65,38 +65,11 @@ #include "BLO_readfile.h" +#include "versioning_common.h" + /* Make preferences read-only, use versioning_userdef.c. */ #define U (*((const UserDef *)&U)) -/** - * Rename if the ID doesn't exist. - */ -static ID *rename_id_for_versioning(Main *bmain, - const short id_type, - const char *name_src, - const char *name_dst) -{ - /* We can ignore libraries */ - ListBase *lb = which_libbase(bmain, id_type); - ID *id = NULL; - LISTBASE_FOREACH (ID *, idtest, lb) { - if (idtest->lib == NULL) { - if (STREQ(idtest->name + 2, name_src)) { - id = idtest; - } - if (STREQ(idtest->name + 2, name_dst)) { - return NULL; - } - } - } - if (id != NULL) { - BLI_strncpy(id->name + 2, name_dst, sizeof(id->name) - 2); - /* We know it's unique, this just sorts. */ - BLI_libblock_ensure_unique_name(bmain, id->name); - } - return id; -} - static bool blo_is_builtin_template(const char *app_template) { /* For all builtin templates shipped with Blender. */ @@ -406,28 +379,28 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) Brush *brush; /* Pencil brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Pencil", "Pencil"); + do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil"); /* Pen brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Pen", "Pen"); + do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen"); /* Pen Soft brush. */ - brush = (Brush *)rename_id_for_versioning(bmain, ID_BR, "Draw Soft", "Pencil Soft"); + brush = (Brush *)do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft"); if (brush) { brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; } /* Ink Pen brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Ink", "Ink Pen"); + do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen"); /* Ink Pen Rough brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Noise", "Ink Pen Rough"); + do_versions_rename_id(bmain, ID_BR, "Draw Noise", "Ink Pen Rough"); /* Marker Bold brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Marker", "Marker Bold"); + do_versions_rename_id(bmain, ID_BR, "Draw Marker", "Marker Bold"); /* Marker Chisel brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Block", "Marker Chisel"); + do_versions_rename_id(bmain, ID_BR, "Draw Block", "Marker Chisel"); /* Remove useless Fill Area.001 brush. */ brush = BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2); @@ -438,10 +411,10 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) /* Rename and fix materials and enable default object lights on. */ if (app_template && STREQ(app_template, "2D_Animation")) { Material *ma = NULL; - rename_id_for_versioning(bmain, ID_MA, "Black", "Solid Stroke"); - rename_id_for_versioning(bmain, ID_MA, "Red", "Squares Stroke"); - rename_id_for_versioning(bmain, ID_MA, "Grey", "Solid Fill"); - rename_id_for_versioning(bmain, ID_MA, "Black Dots", "Dots Stroke"); + do_versions_rename_id(bmain, ID_MA, "Black", "Solid Stroke"); + do_versions_rename_id(bmain, ID_MA, "Red", "Squares Stroke"); + do_versions_rename_id(bmain, ID_MA, "Grey", "Solid Fill"); + do_versions_rename_id(bmain, ID_MA, "Black Dots", "Dots Stroke"); /* Dots Stroke. */ ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2); @@ -553,8 +526,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } /* Objects */ - rename_id_for_versioning(bmain, ID_OB, "Lamp", "Light"); - rename_id_for_versioning(bmain, ID_LA, "Lamp", "Light"); + do_versions_rename_id(bmain, ID_OB, "Lamp", "Light"); + do_versions_rename_id(bmain, ID_LA, "Lamp", "Light"); if (app_template && STREQ(app_template, "2D_Animation")) { for (Object *object = bmain->objects.first; object; object = object->id.next) { -- cgit v1.2.3 From 26f1a5e2c8d705443ab51a608b5721b8f0178de3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 4 Aug 2021 12:57:20 +1000 Subject: Fix T90417: font loading creates duplicate ID names Also repair any errors in existing files. Error from e0dd3fe5872ba37ff188e292b80b46fcf8df413c. --- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/intern/font.c | 8 ++------ source/blender/blenloader/intern/versioning_300.c | 5 +++++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index e76e3ed8fe0..0d5835a5eed 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 15 +#define BLENDER_FILE_SUBVERSION 16 /* 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/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index d0b9aeefa55..37fc14911fe 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -337,13 +337,9 @@ VFont *BKE_vfont_load(Main *bmain, const char *filepath) vfd = BLI_vfontdata_from_freetypefont(pf); if (vfd) { - vfont = BKE_libblock_alloc(bmain, ID_VF, filename, 0); + /* If there's a font name, use it for the ID name. */ + vfont = BKE_libblock_alloc(bmain, ID_VF, vfd->name[0] ? vfd->name : filename, 0); vfont->data = vfd; - - /* if there's a font name, use it for the ID name */ - if (vfd->name[0] != '\0') { - BLI_strncpy(vfont->id.name + 2, vfd->name, sizeof(vfont->id.name) - 2); - } BLI_strncpy(vfont->filepath, filepath, sizeof(vfont->filepath)); /* if autopack is on store the packedfile in de font structure */ diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 9aec18ea279..7b5f95408ea 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -691,6 +691,11 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 16)) { + ListBase *lb = which_libbase(bmain, ID_VF); + BKE_main_id_repair_duplicate_names_listbase(lb); + } + /** * Versioning code until next subversion bump goes here. * -- cgit v1.2.3 From 0f455765907528ea9d6c15e9d224d81d507e51ad Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Tue, 3 Aug 2021 23:14:03 -0400 Subject: Geometry Nodes: Curve Set Spline Type This node sets the selected (or all) splines in curve to a chosen target spline type. Poly, Bezier, and NURB splines can be converted to any of the other types. This is meant to be a building block node, useful in many procedural situations. In the future the node could be optimized with multi-threading, or by avoiding copying in many cases, either by retrieving the curve for write access or by passing the raw vectors to the new splines where possible. With edits from Hans Goudey (@HooglyBoogly) Differential Revision: https://developer.blender.org/D12013 --- 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 | 11 + source/blender/makesrna/intern/rna_nodetree.c | 17 ++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../geometry/nodes/node_geo_curve_spline_type.cc | 307 +++++++++++++++++++++ 9 files changed, 341 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 09820291222..03859b04c50 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -510,6 +510,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveTrim"), NodeItem("GeometryNodeCurveLength"), NodeItem("GeometryNodeCurveReverse"), + NodeItem("GeometryNodeCurveSplineType"), NodeItem("GeometryNodeCurveSetHandles"), ]), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a1bc6fa505b..084ec20c172 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1475,6 +1475,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070 #define GEO_NODE_CURVE_TRIM 1071 #define GEO_NODE_CURVE_SET_HANDLES 1072 +#define GEO_NODE_CURVE_SPLINE_TYPE 1073 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 9888e23a7bd..54bcdf4c92a 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5149,6 +5149,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_resample(); register_node_type_geo_curve_reverse(); register_node_type_geo_curve_set_handles(); + register_node_type_geo_curve_spline_type(); register_node_type_geo_curve_subdivide(); register_node_type_geo_curve_to_mesh(); register_node_type_geo_curve_to_points(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 5152098f57a..4b08aeb2008 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1355,6 +1355,11 @@ typedef struct NodeSwitch { uint8_t input_type; } NodeSwitch; +typedef struct NodeGeometryCurveSplineType { + /* GeometryNodeSplineType. */ + uint8_t spline_type; +} NodeGeometryCurveSplineType; + typedef struct NodeGeometryCurveSetHandles { /* GeometryNodeCurveHandleType. */ uint8_t handle_type; @@ -1828,6 +1833,12 @@ typedef enum GeometryNodeBooleanOperation { GEO_NODE_BOOLEAN_DIFFERENCE = 2, } GeometryNodeBooleanOperation; +typedef enum GeometryNodeSplineType { + GEO_NODE_SPLINE_TYPE_BEZIER = 0, + GEO_NODE_SPLINE_TYPE_NURBS = 1, + GEO_NODE_SPLINE_TYPE_POLY = 2, +} GeometryNodeSplineType; + typedef enum GeometryNodeCurvePrimitiveCircleMode { GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS = 0, GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS = 1 diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 3d4256db335..cd7cbbf76d3 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9441,6 +9441,23 @@ static void def_geo_attribute_vector_rotate(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_spline_type(StructRNA *srna) +{ + static const EnumPropertyItem type_items[] = { + {GEO_NODE_SPLINE_TYPE_BEZIER, "BEZIER", ICON_NONE, "Bezier", "Set the splines to Bezier"}, + {GEO_NODE_SPLINE_TYPE_NURBS, "NURBS", ICON_NONE, "NURBS", "Set the splines to NURBS"}, + {GEO_NODE_SPLINE_TYPE_POLY, "POLY", ICON_NONE, "Poly", "Set the splines to Poly"}, + {0, NULL, 0, NULL, NULL}}; + + PropertyRNA *prop; + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSplineType", "storage"); + + prop = RNA_def_property(srna, "spline_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "spline_type"); + RNA_def_property_enum_items(prop, type_items); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_set_handles(StructRNA *srna) { static const EnumPropertyItem type_items[] = { diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 36e5be6a292..7defb36bb83 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -176,6 +176,7 @@ set(SRC geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_curve_reverse.cc geometry/nodes/node_geo_curve_set_handles.cc + geometry/nodes/node_geo_curve_spline_type.cc geometry/nodes/node_geo_curve_subdivide.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_to_points.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 868fcbb33af..c2297796b97 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -63,6 +63,7 @@ void register_node_type_geo_curve_primitive_star(void); void register_node_type_geo_curve_resample(void); void register_node_type_geo_curve_reverse(void); void register_node_type_geo_curve_set_handles(void); +void register_node_type_geo_curve_spline_type(void); void register_node_type_geo_curve_subdivide(void); void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_to_points(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index a091f28f3a0..b368ac74894 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_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", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc new file mode 100644 index 00000000000..fe3f42625ae --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -0,0 +1,307 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_spline_type_in[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_spline_type_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +static void geo_node_curve_spline_type_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE); +} + +namespace blender::nodes { + +static void geo_node_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN( + sizeof(NodeGeometryCurveSplineType), __func__); + + data->spline_type = GEO_NODE_SPLINE_TYPE_POLY; + node->storage = data; +} + +template +static void scale_input_assign(const Span input, + const int scale, + const int offset, + const MutableSpan r_output) +{ + for (const int i : IndexRange(r_output.size())) { + r_output[i] = input[i * scale + offset]; + } +} + +template +static void scale_output_assign(const Span input, + const int scale, + const int offset, + const MutableSpan &r_output) +{ + for (const int i : IndexRange(input.size())) { + r_output[i * scale + offset] = input[i]; + } +} + +template +static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn) +{ + input_spline.attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + std::optional src = input_spline.attributes.get_for_read(name); + BLI_assert(src); + if (!output_spline.attributes.create(name, meta_data.data_type)) { + BLI_assert_unreachable(); + return false; + } + std::optional dst = output_spline.attributes.get_for_write(name); + if (!dst) { + BLI_assert_unreachable(); + return false; + } + + copy_fn(*src, *dst); + + return true; + }, + ATTR_DOMAIN_POINT); +} + +static SplinePtr convert_to_poly_spline(const Spline &input) +{ + std::unique_ptr output = std::make_unique(); + output->resize(input.positions().size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + Spline::copy_base_settings(input, *output); + output->attributes = input.attributes; + return output; +} + +static SplinePtr poly_to_nurbs(const Spline &input) +{ + std::unique_ptr output = std::make_unique(); + output->resize(input.positions().size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + output->weights().fill(1.0f); + output->set_resolution(12); + output->set_order(4); + Spline::copy_base_settings(input, *output); + output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->attributes = input.attributes; + return output; +} + +static SplinePtr bezier_to_nurbs(const Spline &input) +{ + const BezierSpline &bezier_spline = static_cast(input); + std::unique_ptr output = std::make_unique(); + output->resize(input.size() * 3); + + scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions()); + scale_output_assign(input.radii(), 3, 0, output->radii()); + scale_output_assign(input.tilts(), 3, 0, output->tilts()); + + scale_output_assign(bezier_spline.positions(), 3, 1, output->positions()); + scale_output_assign(input.radii(), 3, 1, output->radii()); + scale_output_assign(input.tilts(), 3, 1, output->tilts()); + + scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions()); + scale_output_assign(input.radii(), 3, 2, output->radii()); + scale_output_assign(input.tilts(), 3, 2, output->tilts()); + + Spline::copy_base_settings(input, *output); + output->weights().fill(1.0f); + output->set_resolution(12); + output->set_order(4); + output->set_cyclic(input.is_cyclic()); + output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->attributes.reallocate(output->size()); + copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + scale_output_assign(src.typed(), 3, 0, dst.typed()); + scale_output_assign(src.typed(), 3, 1, dst.typed()); + scale_output_assign(src.typed(), 3, 2, dst.typed()); + }); + }); + return output; +} + +static SplinePtr poly_to_bezier(const Spline &input) +{ + std::unique_ptr output = std::make_unique(); + output->resize(input.size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + output->handle_types_left().fill(BezierSpline::HandleType::Vector); + output->handle_types_right().fill(BezierSpline::HandleType::Vector); + output->set_resolution(12); + Spline::copy_base_settings(input, *output); + output->attributes = input.attributes; + return output; +} + +static SplinePtr nurbs_to_bezier(const Spline &input) +{ + const NURBSpline &nurbs_spline = static_cast(input); + std::unique_ptr output = std::make_unique(); + output->resize(input.size() / 3); + scale_input_assign(input.positions(), 3, 1, output->positions()); + scale_input_assign(input.positions(), 3, 0, output->handle_positions_left()); + scale_input_assign(input.positions(), 3, 2, output->handle_positions_right()); + scale_input_assign(input.radii(), 3, 2, output->radii()); + scale_input_assign(input.tilts(), 3, 2, output->tilts()); + output->handle_types_left().fill(BezierSpline::HandleType::Align); + output->handle_types_right().fill(BezierSpline::HandleType::Align); + output->set_resolution(nurbs_spline.resolution()); + Spline::copy_base_settings(input, *output); + output->attributes.reallocate(output->size()); + copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + scale_input_assign(src.typed(), 3, 1, dst.typed()); + }); + }); + return output; +} + +static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params) +{ + switch (input.type()) { + case Spline::Type::Bezier: + return input.copy(); + case Spline::Type::Poly: + return poly_to_bezier(input); + case Spline::Type::NURBS: + if (input.size() < 6) { + params.error_message_add( + NodeWarningType::Info, + TIP_("NURBS must have minimum of 6 points for Bezier Conversion")); + return input.copy(); + } + else { + if (input.size() % 3 != 0) { + params.error_message_add(NodeWarningType::Info, + TIP_("NURBS must have multiples of 3 points for full Bezier " + "conversion, curve truncated")); + } + return nurbs_to_bezier(input); + } + } + BLI_assert_unreachable(); + return {}; +} + +static SplinePtr convert_to_nurbs(const Spline &input) +{ + switch (input.type()) { + case Spline::Type::NURBS: + return input.copy(); + case Spline::Type::Bezier: + return bezier_to_nurbs(input); + case Spline::Type::Poly: + return poly_to_nurbs(input); + } + BLI_assert_unreachable(); + return {}; +} + +static void geo_node_curve_spline_type_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSplineType *storage = + (const NodeGeometryCurveSplineType *)params.node().storage; + const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type; + + GeometrySet geometry_set = params.extract_input("Curve"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + if (!geometry_set.has_curve()) { + params.set_output("Curve", geometry_set); + return; + } + + const CurveComponent *curve_component = geometry_set.get_component_for_read(); + const CurveEval &curve = *curve_component->get_for_read(); + + const std::string selection_name = params.extract_input("Selection"); + GVArray_Typed selection = curve_component->attribute_get_for_read( + selection_name, ATTR_DOMAIN_CURVE, true); + + std::unique_ptr new_curve = std::make_unique(); + for (const int i : curve.splines().index_range()) { + if (selection[i]) { + switch (output_type) { + case GEO_NODE_SPLINE_TYPE_POLY: + new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i])); + break; + case GEO_NODE_SPLINE_TYPE_BEZIER: + new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params)); + break; + case GEO_NODE_SPLINE_TYPE_NURBS: + new_curve->add_spline(convert_to_nurbs(*curve.splines()[i])); + break; + } + } + else { + new_curve->add_spline(curve.splines()[i]->copy()); + } + } + + new_curve->attributes = curve.attributes; + params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_spline_type() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_curve_spline_type_in, geo_node_curve_spline_type_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec; + node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init); + node_type_storage(&ntype, + "NodeGeometryCurveSplineType", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = geo_node_curve_spline_type_layout; + + nodeRegisterType(&ntype); +} -- cgit v1.2.3 From 10464843dd3b175c0841f8d9a3f26751c12c8579 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 4 Aug 2021 13:13:02 +1000 Subject: Cleanup: add comment to fix for T90417 --- source/blender/blenloader/intern/versioning_300.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 7b5f95408ea..f1f7f2edd35 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -691,6 +691,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + /* Font names were copied directly into ID names, see: T90417. */ if (!MAIN_VERSION_ATLEAST(bmain, 300, 16)) { ListBase *lb = which_libbase(bmain, ID_VF); BKE_main_id_repair_duplicate_names_listbase(lb); -- cgit v1.2.3 From cd92b2350fc20f6c91128881b5fd20dd173d2308 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 4 Aug 2021 13:26:47 +1000 Subject: Cleanup: use C comments for descriptive text --- intern/ghost/intern/GHOST_ContextGLX.cpp | 4 +- intern/ghost/intern/GHOST_SystemX11.cpp | 3 +- intern/ghost/intern/GHOST_WindowX11.cpp | 18 ++++----- source/blender/blenkernel/intern/armature_test.cc | 6 +-- source/blender/blenkernel/intern/softbody.c | 4 +- source/blender/blenkernel/intern/text.c | 2 +- source/blender/blenlib/BLI_winstuff.h | 4 +- source/blender/blenlib/intern/path_util.c | 9 ++--- source/blender/blenloader/intern/versioning_290.c | 2 +- source/blender/compositor/nodes/COM_MaskNode.cc | 2 +- .../blender/compositor/nodes/COM_MovieClipNode.cc | 2 +- .../operations/COM_DilateErodeOperation.cc | 46 +++++++++++----------- .../operations/COM_OutputFileOperation.cc | 2 +- .../compositor/operations/COM_WrapOperation.cc | 12 +++--- source/blender/depsgraph/DEG_depsgraph.h | 2 +- .../gpu_shader_3D_point_uniform_size_aa_vert.glsl | 6 +-- source/blender/imbuf/intern/dds/ColorBlock.h | 2 +- .../blender/imbuf/intern/openexr/openexr_api.cpp | 2 +- source/blender/windowmanager/intern/wm_window.c | 2 +- 19 files changed, 63 insertions(+), 67 deletions(-) diff --git a/intern/ghost/intern/GHOST_ContextGLX.cpp b/intern/ghost/intern/GHOST_ContextGLX.cpp index eb49dc4f98b..78c7201ff5f 100644 --- a/intern/ghost/intern/GHOST_ContextGLX.cpp +++ b/intern/ghost/intern/GHOST_ContextGLX.cpp @@ -295,8 +295,8 @@ GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext() glXMakeCurrent(m_display, m_window, m_context); - // Seems that this has to be called after MakeCurrent, - // which means we cannot use glX extensions until after we create a context + /* Seems that this has to be called after #glXMakeCurrent, + * which means we cannot use `glX` extensions until after we create a context. */ initContextGLXEW(); if (m_window) { diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 9fcad8aabf7..9422d15692d 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -802,8 +802,7 @@ static bool checkTabletProximity(Display *display, XDevice *device) if (state) { XInputClass *cls = state->data; - // printf("%d class%s :\n", state->num_classes, - // (state->num_classes > 1) ? "es" : ""); + // printf("%d class%s :\n", state->num_classes, (state->num_classes > 1) ? "es" : ""); for (int loop = 0; loop < state->num_classes; loop++) { switch (cls->c_class) { case ValuatorClass: diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index 185d12717e7..de389951613 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -1275,15 +1275,15 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type { if (type == GHOST_kDrawingContextTypeOpenGL) { - // During development: - // try 4.x compatibility profile - // try 3.3 compatibility profile - // fall back to 3.0 if needed - // - // Final Blender 2.8: - // try 4.x core profile - // try 3.3 core profile - // no fallbacks + /* During development: + * - Try 4.x compatibility profile. + * - Try 3.3 compatibility profile. + * - Fall back to 3.0 if needed. + * + * Final Blender 2.8: + * - Try 4.x core profile + * - Try 3.3 core profile + * - No fall-backs. */ #if defined(WITH_GL_PROFILE_CORE) { diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc index 47853deec3e..99eb064d061 100644 --- a/source/blender/blenkernel/intern/armature_test.cc +++ b/source/blender/blenkernel/intern/armature_test.cc @@ -180,7 +180,7 @@ class BKE_armature_find_selected_bones_test : public testing::Test { BLI_addtail(&arm.bonebase, &bone2); // bone2 is root bone BLI_addtail(&bone2.childbase, &bone3); // bone3 has bone2 as parent - // Make sure the armature & its bones are visible, to make them selectable. + /* Make sure the armature & its bones are visible, to make them selectable. */ arm.layer = bone1.layer = bone2.layer = bone3.layer = 1; } }; @@ -200,8 +200,8 @@ TEST_F(BKE_armature_find_selected_bones_test, some_bones_selected) EXPECT_EQ(seen_bones[0], &bone1); EXPECT_EQ(seen_bones[1], &bone3); - EXPECT_FALSE(result.all_bones_selected); // Bone 2 was not selected. - EXPECT_FALSE(result.no_bones_selected); // Bones 1 and 3 were selected. + EXPECT_FALSE(result.all_bones_selected); /* Bone 2 was not selected. */ + EXPECT_FALSE(result.no_bones_selected); /* Bones 1 and 3 were selected. */ } TEST_F(BKE_armature_find_selected_bones_test, no_bones_selected) diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 1a408aceeb2..f41251f6ea5 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -3447,10 +3447,10 @@ static void softbody_step( float newtime = forcetime * 1.1f; /* hope for 1.1 times better conditions in next step */ if (sb->scratch->flag & SBF_DOFUZZY) { - ///* stay with this stepsize unless err really small */ + // /* stay with this stepsize unless err really small */ // if (err > SoftHeunTol/(2.0f*sb->fuzzyness)) { newtime = forcetime; - //} + // } } else { if (err > SoftHeunTol / 2.0f) { /* stay with this stepsize unless err really small */ diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 80ff8ce9162..6048e823abb 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -2331,7 +2331,7 @@ int txt_setcurr_tab_spaces(Text *text, int space) } while (text->curl->line[i] == indent) { - // we only count those tabs/spaces that are before any text or before the curs; + /* We only count those tabs/spaces that are before any text or before the curs; */ if (i == text->curc) { return i; } diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h index 0953e3f1946..bf09b56c779 100644 --- a/source/blender/blenlib/BLI_winstuff.h +++ b/source/blender/blenlib/BLI_winstuff.h @@ -45,7 +45,7 @@ #undef small -// These definitions are also in BLI_math for simplicity +/* These definitions are also in BLI_math for simplicity. */ #ifdef __cplusplus extern "C" { @@ -72,7 +72,7 @@ extern "C" { #if defined(_MSC_VER) # define R_OK 4 # define W_OK 2 -// not accepted by access() on windows +/* Not accepted by `access()` on windows. */ //# define X_OK 1 # define F_OK 0 #endif diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 99fae1f1616..4d0678035ba 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1102,12 +1102,9 @@ bool BLI_path_abs(char *path, const char *basepath) } #ifdef WIN32 - /* skip first two chars, which in case of - * absolute path will be drive:/blabla and - * in case of relpath //blabla/. So relpath - * // will be retained, rest will be nice and - * shiny win32 backward slashes :) -jesterKing - */ + /* NOTE(@jesterking): Skip first two chars, which in case of absolute path will + * be `drive:/blabla` and in case of `relpath` `//blabla/`. + * So `relpath` `//` will be retained, rest will be nice and shiny WIN32 backward slashes. */ BLI_str_replace_char(path + 2, '/', '\\'); #endif diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 09d43676b8f..7f7a2d97cbb 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -170,7 +170,7 @@ static void seq_convert_transform_crop(const Scene *scene, int image_size_x = scene->r.xsch; int image_size_y = scene->r.ysch; - /* Hardcoded legacy bit-flags which has been removed. */ + /* Hard-coded legacy bit-flags which has been removed. */ const uint32_t use_transform_flag = (1 << 16); const uint32_t use_crop_flag = (1 << 17); diff --git a/source/blender/compositor/nodes/COM_MaskNode.cc b/source/blender/compositor/nodes/COM_MaskNode.cc index ef171c01653..b5b23798160 100644 --- a/source/blender/compositor/nodes/COM_MaskNode.cc +++ b/source/blender/compositor/nodes/COM_MaskNode.cc @@ -41,7 +41,7 @@ void MaskNode::convertToOperations(NodeConverter &converter, NodeMask *data = (NodeMask *)editorNode->storage; Mask *mask = (Mask *)editorNode->id; - // always connect the output image + /* Always connect the output image. */ MaskOperation *operation = new MaskOperation(); if (editorNode->custom1 & CMP_NODEFLAG_MASK_FIXED) { diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cc b/source/blender/compositor/nodes/COM_MovieClipNode.cc index 50bd9b4d71b..b80071d27c7 100644 --- a/source/blender/compositor/nodes/COM_MovieClipNode.cc +++ b/source/blender/compositor/nodes/COM_MovieClipNode.cc @@ -62,7 +62,7 @@ void MovieClipNode::convertToOperations(NodeConverter &converter, } } - // always connect the output image + /* Always connect the output image. */ MovieClipOperation *operation = new MovieClipOperation(); operation->setMovieClip(movieClip); operation->setMovieClipUser(movieClipUser); diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index 2454f507664..e9305e0e192 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -24,7 +24,7 @@ namespace blender::compositor { -// DilateErode Distance Threshold +/* DilateErode Distance Threshold */ DilateErodeThresholdOperation::DilateErodeThresholdOperation() { this->addInputSocket(DataType::Value); @@ -258,7 +258,7 @@ void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device, device->COM_clEnqueueRange(dilateKernel, outputMemoryBuffer, 7, this); } -// Erode Distance +/* Erode Distance */ ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() { /* pass */ @@ -318,7 +318,7 @@ void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device, device->COM_clEnqueueRange(erodeKernel, outputMemoryBuffer, 7, this); } -// Dilate step +/* Dilate step */ DilateStepOperation::DilateStepOperation() { this->addInputSocket(DataType::Value); @@ -331,7 +331,7 @@ void DilateStepOperation::initExecution() this->m_inputProgram = this->getInputSocketReader(0); } -// small helper to pass data from initializeTileData to executePixel +/* Small helper to pass data from initializeTileData to executePixel. */ struct tile_info { rcti rect; int width; @@ -370,21 +370,21 @@ void *DilateStepOperation::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. - // We have to calculate the additional rows in the first pass, - // to have valid data available for the second pass. + /* NOTE: Cache buffer has original tilesize 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); float *rectf = result->buffer; - // temp holds maxima for every step in the algorithm, buf holds a - // single row or column of input values, padded with FLT_MAX's to - // simplify the logic. + /* temp holds maxima for every step in the algorithm, buf holds a + * single row or column of input values, padded with FLT_MAX's to + * simplify the logic. */ float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp"); float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window), "dilate erode buf"); - // The following is based on the van Herk/Gil-Werman algorithm for morphology operations. - // first pass, horizontal dilate/erode + /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations. + * first pass, horizontal dilate/erode. */ for (y = ymin; y < ymax; y++) { for (x = 0; x < bwidth + 5 * half_window; x++) { buf[x] = -FLT_MAX; @@ -409,7 +409,7 @@ void *DilateStepOperation::initializeTileData(rcti *rect) } } - // second pass, vertical dilate/erode + /* Second pass, vertical dilate/erode. */ for (x = 0; x < bwidth; x++) { for (y = 0; y < bheight + 5 * half_window; y++) { buf[y] = -FLT_MAX; @@ -475,7 +475,7 @@ bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } -// Erode step +/* Erode step */ ErodeStepOperation::ErodeStepOperation() : DilateStepOperation() { /* pass */ @@ -500,21 +500,21 @@ 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. - // We have to calculate the additional rows in the first pass, - // to have valid data available for the second pass. + /* NOTE: Cache buffer has original tilesize 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); float *rectf = result->buffer; - // temp holds maxima for every step in the algorithm, buf holds a - // single row or column of input values, padded with FLT_MAX's to - // simplify the logic. + /* temp holds maxima for every step in the algorithm, buf holds a + * single row or column of input values, padded with FLT_MAX's to + * simplify the logic. */ float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp"); float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window), "dilate erode buf"); - // The following is based on the van Herk/Gil-Werman algorithm for morphology operations. - // first pass, horizontal dilate/erode + /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations. + * first pass, horizontal dilate/erode */ for (y = ymin; y < ymax; y++) { for (x = 0; x < bwidth + 5 * half_window; x++) { buf[x] = FLT_MAX; @@ -539,7 +539,7 @@ void *ErodeStepOperation::initializeTileData(rcti *rect) } } - // second pass, vertical dilate/erode + /* Second pass, vertical dilate/erode. */ for (x = 0; x < bwidth; x++) { for (y = 0; y < bheight + 5 * half_window; y++) { buf[y] = FLT_MAX; diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 7e896046f01..6c5984e3414 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -160,7 +160,7 @@ int get_datatype_size(DataType datatype) static float *init_buffer(unsigned int width, unsigned int height, DataType datatype) { - // When initializing the tree during initial load the width and height can be zero. + /* When initializing the tree during initial load the width and height can be zero. */ if (width != 0 && height != 0) { int size = get_datatype_size(datatype); return (float *)MEM_callocN(width * height * size * sizeof(float), "OutputFile buffer"); diff --git a/source/blender/compositor/operations/COM_WrapOperation.cc b/source/blender/compositor/operations/COM_WrapOperation.cc index d0d2fcac3ac..888602114cc 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.cc +++ b/source/blender/compositor/operations/COM_WrapOperation.cc @@ -57,20 +57,20 @@ void WrapOperation::executePixelSampled(float output[4], float x, float y, Pixel MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, extend_y = MemoryBufferExtend::Clip; switch (m_wrappingType) { case CMP_NODE_WRAP_NONE: - // Intentionally empty, originalXPos and originalYPos have been set before + /* Intentionally empty, originalXPos and originalYPos have been set before. */ break; case CMP_NODE_WRAP_X: - // wrap only on the x-axis + /* Wrap only on the x-axis. */ nx = this->getWrappedOriginalXPos(x); extend_x = MemoryBufferExtend::Repeat; break; case CMP_NODE_WRAP_Y: - // wrap only on the y-axis + /* Wrap only on the y-axis. */ ny = this->getWrappedOriginalYPos(y); extend_y = MemoryBufferExtend::Repeat; break; case CMP_NODE_WRAP_XY: - // wrap on both + /* Wrap on both. */ nx = this->getWrappedOriginalXPos(x); ny = this->getWrappedOriginalYPos(y); extend_x = MemoryBufferExtend::Repeat; @@ -92,7 +92,7 @@ bool WrapOperation::determineDependingAreaOfInterest(rcti *input, newInput.ymax = input->ymax; if (ELEM(m_wrappingType, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY)) { - // wrap only on the x-axis if tile is wrapping + /* Wrap only on the x-axis if tile is wrapping. */ newInput.xmin = getWrappedOriginalXPos(input->xmin); newInput.xmax = roundf(getWrappedOriginalXPos(input->xmax)); if (newInput.xmin >= newInput.xmax) { @@ -101,7 +101,7 @@ bool WrapOperation::determineDependingAreaOfInterest(rcti *input, } } if (ELEM(m_wrappingType, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY)) { - // wrap only on the y-axis if tile is wrapping + /* Wrap only on the y-axis if tile is wrapping. */ newInput.ymin = getWrappedOriginalYPos(input->ymin); newInput.ymax = roundf(getWrappedOriginalYPos(input->ymax)); if (newInput.ymin >= newInput.ymax) { diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h index 749b1bba871..47dad17b482 100644 --- a/source/blender/depsgraph/DEG_depsgraph.h +++ b/source/blender/depsgraph/DEG_depsgraph.h @@ -85,7 +85,7 @@ extern "C" { // Get main depsgraph instance from context! /* Create new Depsgraph instance */ -// TODO: what args are needed here? What's the building-graph entry point? +/* TODO: what args are needed here? What's the building-graph entry point? */ Depsgraph *DEG_graph_new(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, diff --git a/source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl b/source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl index f5b6d2ea3ed..5d67658c639 100644 --- a/source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl @@ -14,14 +14,14 @@ void main() gl_Position = ModelViewProjectionMatrix * pos_4d; gl_PointSize = size; - // calculate concentric radii in pixels + /* Calculate concentric radii in pixels. */ float radius = 0.5 * size; - // start at the outside and progress toward the center + /* Start at the outside and progress toward the center. */ radii[0] = radius; radii[1] = radius - 1.0; - // convert to PointCoord units + /* Convert to PointCoord units. */ radii /= size; #ifdef USE_WORLD_CLIP_PLANES diff --git a/source/blender/imbuf/intern/dds/ColorBlock.h b/source/blender/imbuf/intern/dds/ColorBlock.h index 158695cfbf3..934837bb129 100644 --- a/source/blender/imbuf/intern/dds/ColorBlock.h +++ b/source/blender/imbuf/intern/dds/ColorBlock.h @@ -25,7 +25,7 @@ * Original license from NVIDIA follows. */ -// This code is in the public domain -- +/* This code is in the public domain -- */ #pragma once diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index d1fa26e1a3e..a465c6b92bc 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -69,7 +69,7 @@ extern "C" { -// The following prevents a linking error in debug mode for MSVC using the libs in CVS +/* The following prevents a linking error in debug mode for MSVC using the libs in SVN. */ #if defined(WITH_OPENEXR) && defined(_WIN32) && defined(DEBUG) && _MSC_VER < 1900 _CRTIMP void __cdecl _invalid_parameter_noinfo(void) { diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 8f37e7f34a9..93417213a65 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1058,7 +1058,7 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win) BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); if (win != wm->windrawable && win->ghostwin) { - // win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */ + // win->lmbut = 0; /* Keeps hanging when mouse-pressed while other window opened. */ wm_window_clear_drawable(wm); if (G.debug & G_DEBUG_EVENTS) { -- cgit v1.2.3 From effc0487106bc74590acf35a61541597b1149daa Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 4 Aug 2021 08:58:19 +0200 Subject: T90371: Asset: Drop Material Tooltip. This patch changes the drop named material tooltip to give feedback to the user what is going to happen when they invoke the change. There are 3 states: * "": Operator will be canceled as not all data is present (dropping on background.) * "Drop on (slot , replacing ). * "Drop on (slot mval); + + 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); + + char *result; + if (prev_mat) { + 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)."); + result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot); + } + return result; +} + static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Main *bmain = CTX_data_main(C); diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index 36b2966dc43..11ad3abcd54 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -868,7 +868,8 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) static char *datastack_drop_tooltip(bContext *UNUSED(C), wmDrag *drag, - const wmEvent *UNUSED(event)) + const wmEvent *UNUSED(event), + struct wmDropBox *UNUSED(drop)) { StackDropData *drop_data = drag->poin; switch (drop_data->drop_action) { @@ -1191,7 +1192,10 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event return false; } -static char *collection_drop_tooltip(bContext *C, wmDrag *drag, const wmEvent *event) +static char *collection_drop_tooltip(bContext *C, + wmDrag *drag, + const wmEvent *event, + wmDropBox *UNUSED(drop)) { CollectionDrop data; if (!event->shift && collection_drop_init(C, drag, event, &data)) { diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a2564469c16..0803b4c4776 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -53,6 +53,7 @@ #include "BKE_screen.h" #include "BKE_workspace.h" +#include "ED_object.h" #include "ED_render.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -528,6 +529,16 @@ static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event return view3d_drop_id_in_main_region_poll(C, drag, event, ID_MA); } +static char *view3d_mat_drop_tooltip(bContext *C, + wmDrag *drag, + const wmEvent *event, + struct wmDropBox *drop) +{ + wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, ID_MA); + RNA_string_set(drop->ptr, "name", asset_drag->name); + return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, event); +} + static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ID_Type id_type = view3d_drop_id_in_main_region_poll_get_id_type(C, drag, event); @@ -539,7 +550,8 @@ static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEven static char *view3d_object_data_drop_tooltip(bContext *UNUSED(C), wmDrag *UNUSED(drag), - const wmEvent *UNUSED(event)) + const wmEvent *UNUSED(event), + wmDropBox *UNUSED(drop)) { return BLI_strdup(TIP_("Create object instance from object-data")); } @@ -689,7 +701,7 @@ static void view3d_dropboxes(void) view3d_mat_drop_poll, view3d_id_drop_copy, WM_drag_free_imported_drag_ID, - NULL); + view3d_mat_drop_tooltip); WM_dropbox_add(lb, "VIEW3D_OT_background_image_add", view3d_ima_bg_drop_poll, diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 4d6cb941347..485d8e5a162 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -115,6 +115,7 @@ struct wmEvent; struct wmOperator; struct wmWindowManager; struct wmDrag; +struct wmDropBox; #include "BLI_compiler_attrs.h" #include "DNA_listBase.h" @@ -937,7 +938,8 @@ typedef struct wmDragAsset { typedef char *(*WMDropboxTooltipFunc)(struct bContext *, struct wmDrag *, - const struct wmEvent *event); + const struct wmEvent *event, + struct wmDropBox *drop); typedef struct wmDrag { struct wmDrag *next, *prev; diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index c58d3c53e03..dcbb502117e 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -220,14 +220,11 @@ void WM_drag_free_list(struct ListBase *lb) } } -static char *dropbox_tooltip(bContext *C, - wmDrag *drag, - const wmEvent *event, - const wmDropBox *drop) +static char *dropbox_tooltip(bContext *C, wmDrag *drag, const wmEvent *event, wmDropBox *drop) { char *tooltip = NULL; if (drop->tooltip) { - tooltip = drop->tooltip(C, drag, event); + tooltip = drop->tooltip(C, drag, event, drop); } if (!tooltip) { tooltip = BLI_strdup(WM_operatortype_name(drop->ot, drop->ptr)); -- cgit v1.2.3 From 3b0fab6dfaa00a6fd476d28c160fcd9219f9a973 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 4 Aug 2021 09:18:21 +0200 Subject: Override: API update_operations. The update_operations function will update the override structure of the local object. When working with overrides the override structure is only updated when the work-file is stored. When using scripts you might want to enforce the update of override properties and operations. This function removes a hack on the test cases. Reviewed By: mont29 Maniphest Tasks: T86656 Differential Revision: https://developer.blender.org/D10848 --- source/blender/makesrna/intern/rna_ID.c | 21 +++++++++++++++++++++ tests/python/bl_blendfile_library_overrides.py | 10 ++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 6df03d19538..e177f211c7f 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -741,6 +741,19 @@ static void rna_ID_override_template_create(ID *id, ReportList *reports) BKE_lib_override_library_template_create(id); } +static void rna_ID_override_library_update_operations(ID *id, + IDOverrideLibrary *UNUSED(override_library), + Main *bmain, + ReportList *reports) +{ + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + BKE_report(reports, RPT_ERROR, "ID isn't an override"); + return; + } + + BKE_lib_override_library_operations_create(bmain, id); +} + static IDOverrideLibraryProperty *rna_ID_override_library_properties_add( IDOverrideLibrary *override_library, ReportList *reports, const char rna_path[]) { @@ -1695,6 +1708,7 @@ static void rna_def_ID_override_library(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; + FunctionRNA *func; srna = RNA_def_struct(brna, "IDOverrideLibrary", NULL); RNA_def_struct_ui_text( @@ -1710,6 +1724,13 @@ static void rna_def_ID_override_library(BlenderRNA *brna) "List of overridden properties"); rna_def_ID_override_library_properties(brna, prop); + /* Update function. */ + func = RNA_def_function(srna, "update_operations", "rna_ID_override_library_update_operations"); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_SELF_ID | FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, + "Update the library override operations based on the " + "differences between this override ID and its reference"); + rna_def_ID_override_library_property(brna); } diff --git a/tests/python/bl_blendfile_library_overrides.py b/tests/python/bl_blendfile_library_overrides.py index c9c89c01cee..138c5fd13b5 100644 --- a/tests/python/bl_blendfile_library_overrides.py +++ b/tests/python/bl_blendfile_library_overrides.py @@ -45,10 +45,6 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase): bpy.ops.wm.save_as_mainfile(filepath=str(self.output_path), check_existing=False, compress=False) - def __ensure_override_library_updated(self): - # During save the override_library is updated. - bpy.ops.wm.save_as_mainfile(filepath=str(self.test_output_path), check_existing=False, compress=False) - def test_link_and_override_property(self): bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True) bpy.data.orphans_purge() @@ -64,8 +60,7 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase): assert(len(local_id.override_library.properties) == 0) local_id.location.y = 1.0 - - self.__ensure_override_library_updated() + local_id.override_library.update_operations() assert(len(local_id.override_library.properties) == 1) override_prop = local_id.override_library.properties[0] @@ -101,7 +96,6 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase): override_operation = override_prop.operations[0] assert(override_operation.operation == 'NOOP') assert(override_operation.subitem_local_index == -1) - local_id.location.y = 1.0 local_id.scale.x = 0.5 # `scale.x` will apply, but will be reverted when the library overrides @@ -110,7 +104,7 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase): assert(local_id.scale.x == 0.5) assert(local_id.location.y == 1.0) - self.__ensure_override_library_updated() + local_id.override_library.update_operations() assert(local_id.scale.x == 1.0) assert(local_id.location.y == 1.0) -- cgit v1.2.3 From 218df9941097bf973485ac343c070bdc6641a539 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 4 Aug 2021 09:18:21 +0200 Subject: Modifier: warn if the ocean simulation fails to allocate memory While most modifies don't handle out of memory cases, ocean simulation could attempt huge allocations: 2048 gb at the maximum resolution. Resolves T83952. --- source/blender/blenkernel/BKE_ocean.h | 6 +- source/blender/blenkernel/intern/ocean.c | 104 ++++++++++++++++++---------- source/blender/modifiers/intern/MOD_ocean.c | 14 ++-- 3 files changed, 82 insertions(+), 42 deletions(-) diff --git a/source/blender/blenkernel/BKE_ocean.h b/source/blender/blenkernel/BKE_ocean.h index 2c0c6989acf..380f9045520 100644 --- a/source/blender/blenkernel/BKE_ocean.h +++ b/source/blender/blenkernel/BKE_ocean.h @@ -74,11 +74,13 @@ struct Ocean *BKE_ocean_add(void); void BKE_ocean_free_data(struct Ocean *oc); void BKE_ocean_free(struct Ocean *oc); bool BKE_ocean_ensure(struct OceanModifierData *omd, const int resolution); -void BKE_ocean_init_from_modifier(struct Ocean *ocean, +bool BKE_ocean_init_from_modifier(struct Ocean *ocean, struct OceanModifierData const *omd, const int resolution); -void BKE_ocean_init(struct Ocean *o, +bool BKE_ocean_is_valid(const struct Ocean *o); + +bool BKE_ocean_init(struct Ocean *o, int M, int N, float Lx, diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 3aee5cd639d..30e9213cdae 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -650,6 +650,14 @@ static void ocean_compute_normal_z(TaskPool *__restrict pool, void *UNUSED(taskd fftw_execute(o->_N_z_plan); } +/** + * Return true if the ocean is valid and can be used. + */ +bool BKE_ocean_is_valid(const struct Ocean *o) +{ + return o->_k != NULL; +} + void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount) { TaskPool *pool; @@ -769,7 +777,10 @@ bool BKE_ocean_ensure(struct OceanModifierData *omd, const int resolution) return true; } -void BKE_ocean_init_from_modifier(struct Ocean *ocean, +/** + * Return true if the ocean data is valid and can be used. + */ +bool BKE_ocean_init_from_modifier(struct Ocean *ocean, struct OceanModifierData const *omd, const int resolution) { @@ -783,31 +794,34 @@ void BKE_ocean_init_from_modifier(struct Ocean *ocean, BKE_ocean_free_data(ocean); - BKE_ocean_init(ocean, - resolution * resolution, - resolution * resolution, - omd->spatial_size, - omd->spatial_size, - omd->wind_velocity, - omd->smallest_wave, - 1.0, - omd->wave_direction, - omd->damp, - omd->wave_alignment, - omd->depth, - omd->time, - omd->spectrum, - omd->fetch_jonswap, - omd->sharpen_peak_jonswap, - do_heightfield, - do_chop, - do_spray, - do_normals, - do_jacobian, - omd->seed); -} - -void BKE_ocean_init(struct Ocean *o, + return BKE_ocean_init(ocean, + resolution * resolution, + resolution * resolution, + omd->spatial_size, + omd->spatial_size, + omd->wind_velocity, + omd->smallest_wave, + 1.0, + omd->wave_direction, + omd->damp, + omd->wave_alignment, + omd->depth, + omd->time, + omd->spectrum, + omd->fetch_jonswap, + omd->sharpen_peak_jonswap, + do_heightfield, + do_chop, + do_spray, + do_normals, + do_jacobian, + omd->seed); +} + +/** + * Return true if the ocean data is valid and can be used. + */ +bool BKE_ocean_init(struct Ocean *o, int M, int N, float Lx, @@ -830,7 +844,6 @@ void BKE_ocean_init(struct Ocean *o, short do_jacobian, int seed) { - RNG *rng; int i, j, ii; BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE); @@ -858,18 +871,34 @@ void BKE_ocean_init(struct Ocean *o, o->_fetch_jonswap = fetch_jonswap; o->_sharpen_peak_jonswap = sharpen_peak_jonswap * 10.0f; + /* NOTE: most modifiers don't account for failure to allocate. + * In this case however a large resolution can easily perform large allocations that fail, + * support early exiting in this case. */ + if ((o->_k = (float *)MEM_mallocN(sizeof(float) * (size_t)M * (1 + N / 2), "ocean_k")) && + (o->_h0 = (fftw_complex *)MEM_mallocN(sizeof(fftw_complex) * (size_t)M * N, "ocean_h0")) && + (o->_h0_minus = (fftw_complex *)MEM_mallocN(sizeof(fftw_complex) * (size_t)M * N, + "ocean_h0_minus")) && + (o->_kx = (float *)MEM_mallocN(sizeof(float) * o->_M, "ocean_kx")) && + (o->_kz = (float *)MEM_mallocN(sizeof(float) * o->_N, "ocean_kz"))) { + /* Success. */ + } + else { + MEM_SAFE_FREE(o->_k); + MEM_SAFE_FREE(o->_h0); + MEM_SAFE_FREE(o->_h0_minus); + MEM_SAFE_FREE(o->_kx); + MEM_SAFE_FREE(o->_kz); + + BLI_rw_mutex_unlock(&o->oceanmutex); + return false; + } + o->_do_disp_y = do_height_field; o->_do_normals = do_normals; o->_do_spray = do_spray; o->_do_chop = do_chop; o->_do_jacobian = do_jacobian; - o->_k = (float *)MEM_mallocN(M * (1 + N / 2) * sizeof(float), "ocean_k"); - o->_h0 = (fftw_complex *)MEM_mallocN(M * N * sizeof(fftw_complex), "ocean_h0"); - o->_h0_minus = (fftw_complex *)MEM_mallocN(M * N * sizeof(fftw_complex), "ocean_h0_minus"); - o->_kx = (float *)MEM_mallocN(o->_M * sizeof(float), "ocean_kx"); - o->_kz = (float *)MEM_mallocN(o->_N * sizeof(float), "ocean_kz"); - /* make this robust in the face of erroneous usage */ if (o->_Lx == 0.0f) { o->_Lx = 0.001f; @@ -902,11 +931,11 @@ void BKE_ocean_init(struct Ocean *o, /* pre-calculate the k matrix */ for (i = 0; i < o->_M; i++) { for (j = 0; j <= o->_N / 2; j++) { - o->_k[i * (1 + o->_N / 2) + j] = sqrt(o->_kx[i] * o->_kx[i] + o->_kz[j] * o->_kz[j]); + o->_k[(size_t)i * (1 + o->_N / 2) + j] = sqrt(o->_kx[i] * o->_kx[i] + o->_kz[j] * o->_kz[j]); } } - rng = BLI_rng_new(seed); + RNG *rng = BLI_rng_new(seed); for (i = 0; i < o->_M; i++) { for (j = 0; j < o->_N; j++) { @@ -1029,6 +1058,8 @@ void BKE_ocean_init(struct Ocean *o, set_height_normalize_factor(o); BLI_rng_free(rng); + + return true; } void BKE_ocean_free_data(struct Ocean *oc) @@ -1700,10 +1731,11 @@ void BKE_ocean_bake(struct Ocean *UNUSED(o), (void)update_cb; } -void BKE_ocean_init_from_modifier(struct Ocean *UNUSED(ocean), +bool BKE_ocean_init_from_modifier(struct Ocean *UNUSED(ocean), struct OceanModifierData const *UNUSED(omd), int UNUSED(resolution)) { + return true; } #endif /* WITH_OCEANSIM */ diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 8f3206da5be..1c502b94bdb 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -95,8 +95,9 @@ static void initData(ModifierData *md) BKE_modifier_path_init(omd->cachepath, sizeof(omd->cachepath), "cache_ocean"); omd->ocean = BKE_ocean_add(); - BKE_ocean_init_from_modifier(omd->ocean, omd, omd->viewport_resolution); - simulate_ocean_modifier(omd); + if (BKE_ocean_init_from_modifier(omd->ocean, omd, omd->viewport_resolution)) { + simulate_ocean_modifier(omd); + } #else /* WITH_OCEANSIM */ UNUSED_VARS(md); #endif /* WITH_OCEANSIM */ @@ -132,8 +133,9 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla tomd->oceancache = NULL; tomd->ocean = BKE_ocean_add(); - BKE_ocean_init_from_modifier(tomd->ocean, tomd, tomd->viewport_resolution); - simulate_ocean_modifier(tomd); + if (BKE_ocean_init_from_modifier(tomd->ocean, tomd, tomd->viewport_resolution)) { + simulate_ocean_modifier(tomd); + } #else /* WITH_OCEANSIM */ /* unused */ (void)md; @@ -323,6 +325,10 @@ static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig, co static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { OceanModifierData *omd = (OceanModifierData *)md; + if (omd->ocean && !BKE_ocean_is_valid(omd->ocean)) { + BKE_modifier_set_error(ctx->object, md, "Failed to allocate memory"); + return mesh; + } int cfra_scene = (int)DEG_get_ctime(ctx->depsgraph); Object *ob = ctx->object; bool allocated_ocean = false; -- cgit v1.2.3 From cb67bfdba2e22d0ee967e50afe0912ac80d73e9b Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 4 Aug 2021 10:52:51 +0200 Subject: Viewport normal drawing with constant length Patch for: T37878 {F10169694} Reviewed By: fclem Differential Revision: https://developer.blender.org/D11487 --- release/datafiles/alert_icons.png | Bin 24026 -> 25576 bytes release/datafiles/blender_icons.svg | 5 +++++ .../datafiles/blender_icons16/icon16_fixed_size.dat | Bin 0 -> 1048 bytes .../datafiles/blender_icons16/icon16_force_boid.dat | Bin 1048 -> 1048 bytes .../datafiles/blender_icons32/icon32_fixed_size.dat | Bin 0 -> 4120 bytes .../datafiles/blender_icons32/icon32_force_boid.dat | Bin 4120 -> 4120 bytes release/scripts/startup/bl_ui/space_view3d.py | 8 +++++++- source/blender/blenloader/intern/versioning_300.c | 14 ++++++++++++++ .../blender/blenloader/intern/versioning_defaults.c | 1 + .../draw/engines/overlay/overlay_edit_mesh.c | 5 +++++ .../overlay/shaders/edit_mesh_normal_vert.glsl | 20 ++++++++++++++++++-- source/blender/editors/datafiles/CMakeLists.txt | 1 + source/blender/editors/include/UI_icons.h | 2 +- source/blender/makesdna/DNA_view3d_defaults.h | 1 + source/blender/makesdna/DNA_view3d_types.h | 4 ++++ source/blender/makesdna/DNA_view3d_types.h.rej | 9 +++++++++ source/blender/makesrna/intern/rna_space.c | 15 +++++++++++++++ 17 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 release/datafiles/blender_icons16/icon16_fixed_size.dat create mode 100644 release/datafiles/blender_icons32/icon32_fixed_size.dat create mode 100644 source/blender/makesdna/DNA_view3d_types.h.rej diff --git a/release/datafiles/alert_icons.png b/release/datafiles/alert_icons.png index e1cb671713f..0add7a3ccf8 100644 Binary files a/release/datafiles/alert_icons.png and b/release/datafiles/alert_icons.png differ diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index f894ca3f298..54357550847 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -17301,6 +17301,11 @@ d="m 1800,348 h 3 v 14 h -3 z m -4,7 h 16 v 3 h -16 z" inkscape:connector-curvature="0" /> + filesdna, "View3DOverlay", "float", "normals_constant_screen_size")) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + v3d->overlay.normals_constant_screen_size = 7.0f; + } + } + } + } + } } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 7ff624b44da..82c577d11a0 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -190,6 +190,7 @@ static void blo_update_defaults_screen(bScreen *screen, } /* Disable Curve Normals. */ v3d->overlay.edit_flag &= ~V3D_OVERLAY_EDIT_CU_NORMALS; + v3d->overlay.normals_constant_screen_size = 7.0f; } else if (area->spacetype == SPACE_CLIP) { SpaceClip *sclip = area->spacedata.first; diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c index a7ed6c777e8..3a2871249a2 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c +++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c @@ -132,6 +132,11 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata) DRW_shgroup_uniform_float_copy(grp, "normalSize", v3d->overlay.normals_length); DRW_shgroup_uniform_float_copy(grp, "alpha", backwire_opacity); DRW_shgroup_uniform_texture_ref(grp, "depthTex", depth_tex); + DRW_shgroup_uniform_bool_copy(grp, + "isConstantScreenSizeNormals", + (flag & V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS) != 0); + DRW_shgroup_uniform_float_copy( + grp, "normalScreenSize", v3d->overlay.normals_constant_screen_size); } { /* Mesh Analysis Pass */ diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl index 007495f84e0..f649a5cb3ed 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl @@ -1,5 +1,7 @@ uniform float normalSize; +uniform float normalScreenSize; +uniform bool isConstantScreenSizeNormals; uniform sampler2D depthTex; uniform float alpha = 1.0; @@ -49,11 +51,25 @@ void main() } vec3 n = normalize(normal_object_to_world(nor)); - vec3 world_pos = point_object_to_world(pos); if (gl_VertexID == 0) { - world_pos += n * normalSize; + if (isConstantScreenSizeNormals) { + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + if (is_persp) + { + float dist_fac = length(cameraPos - world_pos); + float cos_fac = dot(cameraForward, cameraVec(world_pos)); + world_pos += n * normalScreenSize * dist_fac * cos_fac * pixelFac * sizePixel; + } + else { + float frustrum_fac = mul_project_m4_v3_zfac(n) * sizePixel; + world_pos += n * normalScreenSize * frustrum_fac; + } + } + else { + world_pos += n * normalSize; + } } gl_Position = point_world_to_ndc(world_pos); diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index beb22d43930..4af6e1bdcb8 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -583,6 +583,7 @@ set(ICON_NAMES uv_facesel uv_islandsel uv_sync_select + fixed_size transform_origins gizmo orientation_cursor diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 7ccdc49d291..eb893fbe029 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -715,7 +715,7 @@ DEF_ICON(UV_ISLANDSEL) DEF_ICON(UV_SYNC_SELECT) DEF_ICON_BLANK(240) DEF_ICON_BLANK(241) -DEF_ICON_BLANK(242) +DEF_ICON(FIXED_SIZE) DEF_ICON(TRANSFORM_ORIGINS) DEF_ICON(GIZMO) DEF_ICON(ORIENTATION_CURSOR) diff --git a/source/blender/makesdna/DNA_view3d_defaults.h b/source/blender/makesdna/DNA_view3d_defaults.h index 9dfc37e57b1..c4d0c83b346 100644 --- a/source/blender/makesdna/DNA_view3d_defaults.h +++ b/source/blender/makesdna/DNA_view3d_defaults.h @@ -71,6 +71,7 @@ .gpencil_paper_opacity = 0.5f, \ .gpencil_grid_opacity = 0.9f, \ .gpencil_vertex_paint_opacity = 1.0f, \ + .normals_constant_screen_size = 7.0f, \ } #define _DNA_DEFAULT_View3DCursor \ diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 08b29c82707..4d88f6f0c15 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -204,6 +204,7 @@ typedef struct View3DOverlay { /** Edit mode settings. */ int edit_flag; float normals_length; + float normals_constant_screen_size; float backwire_opacity; /** Paint mode settings. */ @@ -238,6 +239,8 @@ typedef struct View3DOverlay { float gpencil_vertex_paint_opacity; /** Handles display type for curves. */ int handle_display; + + char _pad[4]; } View3DOverlay; /* View3DOverlay->handle_display */ @@ -551,6 +554,7 @@ enum { // V3D_OVERLAY_EDIT_CU_HANDLES = (1 << 20), V3D_OVERLAY_EDIT_CU_NORMALS = (1 << 21), + V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS = (1 << 22), }; /** #View3DOverlay.paint_flag */ diff --git a/source/blender/makesdna/DNA_view3d_types.h.rej b/source/blender/makesdna/DNA_view3d_types.h.rej new file mode 100644 index 00000000000..a62f1c4b7d3 --- /dev/null +++ b/source/blender/makesdna/DNA_view3d_types.h.rej @@ -0,0 +1,9 @@ +diff a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h (rejected hunks) +@@ -550,6 +553,7 @@ + /* V3D_OVERLAY_EDIT_CU_HANDLES = (1 << 20), */ + + V3D_OVERLAY_EDIT_CU_NORMALS = (1 << 21), ++ V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS = (1 << 22), + }; + + /** #View3DOverlay.paint_flag */ diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 796e62c8b2a..c7a5c43255a 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -4363,6 +4363,21 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) RNA_def_property_float_default(prop, 0.02); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "normals_constant_screen_size", PROP_FLOAT, PROP_PIXEL); + RNA_def_property_float_sdna(prop, NULL, "overlay.normals_constant_screen_size"); + RNA_def_property_ui_text(prop, "Normal Screen Size", "Screen size for normals in the 3D view"); + RNA_def_property_range(prop, 0.0, 100000.0); + RNA_def_property_ui_range(prop, 1.0, 100.0, 50, 0); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "use_normals_constant_screen_size", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "overlay.edit_flag", V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS); + RNA_def_property_ui_text(prop, + "Constant Screen Size Normals", + "Keep size of normals constant in relation to 3D view"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "backwire_opacity", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "overlay.backwire_opacity"); RNA_def_property_ui_text(prop, "Backwire Opacity", "Opacity when rendering transparent wires"); -- cgit v1.2.3 From b867df903fc89df06b835e2c9172612887efc761 Mon Sep 17 00:00:00 2001 From: Anthony Edlin Date: Wed, 4 Aug 2021 11:04:04 +0200 Subject: Make loopcut drawing consistent between gizmo and operator. Loopcut drawing from gizmo had thicker lines because it was using line smoothing without alpha blend, compared to thin jagged lines from operator. Make the drawing anti aliased and consistent by using 3D_POLYLINE/3D_POINT shaders, and making sure alpha blending is on. Reviewed By: #eevee_viewport, fclem Differential Revision: https://developer.blender.org/D11333 --- .../editors/mesh/editmesh_preselect_edgering.c | 28 +++++++++++++++++----- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_preselect_edgering.c b/source/blender/editors/mesh/editmesh_preselect_edgering.c index 43e36957dc9..c58f29917b1 100644 --- a/source/blender/editors/mesh/editmesh_preselect_edgering.c +++ b/source/blender/editors/mesh/editmesh_preselect_edgering.c @@ -20,6 +20,8 @@ #include "MEM_guardedalloc.h" +#include "DNA_userdef_types.h" + #include "BLI_math.h" #include "BLI_stack.h" @@ -160,16 +162,21 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl } GPU_depth_test(GPU_DEPTH_NONE); + GPU_blend(GPU_BLEND_ALPHA); GPU_matrix_push(); GPU_matrix_mul(matrix); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - immUniformThemeColor3(TH_GIZMO_PRIMARY); - if (psel->edges_len > 0) { + float viewport[4]; + GPU_viewport_size_get_f(viewport); + + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); + immUniform2fv("viewportSize", &viewport[2]); + immUniformThemeColor3(TH_GIZMO_PRIMARY); + immUniform1f("lineWidth", U.pixelsize); immBegin(GPU_PRIM_LINES, psel->edges_len * 2); for (int i = 0; i < psel->edges_len; i++) { @@ -178,10 +185,18 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl } immEnd(); + immUnbindProgram(); } if (psel->verts_len > 0) { - GPU_point_size(3.0f); + GPU_program_point_size(true); + immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + immUniformThemeColor3(TH_GIZMO_PRIMARY); + + /* Same size as an edit mode vertex */ + immUniform1f("size", + 2.0 * U.pixelsize * + (max_ff(1.0f, UI_GetThemeValuef(TH_VERTEX_SIZE) * (float)M_SQRT2 / 2.0f))); immBegin(GPU_PRIM_POINTS, psel->verts_len); @@ -190,14 +205,15 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl } immEnd(); + immUnbindProgram(); + GPU_program_point_size(false); } - immUnbindProgram(); - GPU_matrix_pop(); /* Reset default */ GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + GPU_blend(GPU_BLEND_NONE); } static void view3d_preselect_mesh_edgering_update_verts_from_edge( -- cgit v1.2.3 From c8004ab4078c98c54a70113c12bbb186403e90cf Mon Sep 17 00:00:00 2001 From: Gottfried Hofmann Date: Wed, 4 Aug 2021 11:20:10 +0200 Subject: Expose Color Management as argument for gpu.types.GPUOffScreen.draw_view3d() Fix for https://developer.blender.org/T84227 The problem was that https://developer.blender.org/rBe0ffb911a22bb03755687f45fc1a996870e059a8 turned color management for offscreen rendering off by default, which makes it non-color-managed in some cases. So the idea here is that script authors get the choice wether they want color managed non-color-managed output. Thus this patch introduces a new argument do_color_management as a bool to gpu.types.GPUOffScreen.draw_view3d(). Reviewed By: jbakker Differential Revision: https://developer.blender.org/D11645 --- source/blender/python/gpu/gpu_py_offscreen.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index 457f00b1267..02f72f20ac4 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -279,7 +279,7 @@ static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void * PyDoc_STRVAR( pygpu_offscreen_draw_view3d_doc, - ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n" + ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix, do_color_management=False)\n" "\n" " Draw the 3d viewport in the offscreen object.\n" "\n" @@ -294,7 +294,9 @@ PyDoc_STRVAR( " :arg view_matrix: View Matrix (e.g. ``camera.matrix_world.inverted()``).\n" " :type view_matrix: :class:`mathutils.Matrix`\n" " :arg projection_matrix: Projection Matrix (e.g. ``camera.calc_matrix_camera(...)``).\n" - " :type projection_matrix: :class:`mathutils.Matrix`\n"); + " :type projection_matrix: :class:`mathutils.Matrix`\n" + " :arg do_color_management: Color manage the output.\n" + " :type do_color_management: bool\n"); static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds) { MatrixObject *py_mat_view, *py_mat_projection; @@ -306,12 +308,14 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar View3D *v3d; ARegion *region; + bool do_color_management = false; + BPY_GPU_OFFSCREEN_CHECK_OBJ(self); static const char *_keywords[] = { - "scene", "view_layer", "view3d", "region", "view_matrix", "projection_matrix", NULL}; + "scene", "view_layer", "view3d", "region", "view_matrix", "projection_matrix", "do_color_management", NULL}; - static _PyArg_Parser _parser = {"OOOOO&O&:draw_view3d", _keywords, 0}; + static _PyArg_Parser _parser = {"OOOOO&O&|$O&:draw_view3d", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, @@ -322,7 +326,9 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar Matrix_Parse4x4, &py_mat_view, Matrix_Parse4x4, - &py_mat_projection) || + &py_mat_projection, + PyC_ParseBool, + &do_color_management) || (!(scene = PyC_RNA_AsPointer(py_scene, "Scene")) || !(view_layer = PyC_RNA_AsPointer(py_view_layer, "ViewLayer")) || !(v3d = PyC_RNA_AsPointer(py_view3d, "SpaceView3D")) || @@ -354,7 +360,7 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar true, true, "", - false, + do_color_management, true, self->ofs, NULL); -- cgit v1.2.3 From 8c21076addf4de01c2f5640c5aaa563d99655783 Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Wed, 4 Aug 2021 11:20:43 +0200 Subject: Fix T87635: Rename shader node "Specular" to "Specular BSDF" Node name edited in Specular node definition Reviewed By: fclem Maniphest Tasks: T87635 Differential Revision: https://developer.blender.org/D11022 --- source/blender/nodes/NOD_static_types.h | 2 +- source/blender/nodes/shader/nodes/node_shader_eevee_specular.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index b368ac74894..6c3f91df434 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -61,7 +61,7 @@ DefNode(ShaderNode, SH_NODE_COMBRGB, 0, "COMBRG DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "" ) -DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular", "" ) +DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular BSDF", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_LIGHT, def_sh_output, "OUTPUT_LIGHT", OutputLight, "Light Output", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_WORLD, def_sh_output, "OUTPUT_WORLD", OutputWorld, "World Output", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_LINESTYLE, def_sh_output_linestyle,"OUTPUT_LINESTYLE", OutputLineStyle, "Line Style Output", "" ) diff --git a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c index 56169b9be5e..015af19abb2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c +++ b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c @@ -91,7 +91,7 @@ void register_node_type_sh_eevee_specular(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_EEVEE_SPECULAR, "Specular", NODE_CLASS_SHADER, 0); + sh_node_type_base(&ntype, SH_NODE_EEVEE_SPECULAR, "Specular BSDF", NODE_CLASS_SHADER, 0); node_type_socket_templates(&ntype, sh_node_eevee_specular_in, sh_node_eevee_specular_out); node_type_init(&ntype, NULL); node_type_storage(&ntype, "", NULL, NULL); -- cgit v1.2.3 From d9a530c55e869376aa2d0e9175a659d965d38d2a Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 4 Aug 2021 11:29:43 +0200 Subject: Fix compile error without `WITH_OCEANSIM` enabled Was changed in 218df9941097. --- source/blender/blenkernel/intern/ocean.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 30e9213cdae..4d003ddc900 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -1639,7 +1639,7 @@ struct Ocean *BKE_ocean_add(void) return oc; } -void BKE_ocean_init(struct Ocean *UNUSED(o), +bool BKE_ocean_init(struct Ocean *UNUSED(o), int UNUSED(M), int UNUSED(N), float UNUSED(Lx), @@ -1662,6 +1662,7 @@ void BKE_ocean_init(struct Ocean *UNUSED(o), short UNUSED(do_jacobian), int UNUSED(seed)) { + return false; } void BKE_ocean_free_data(struct Ocean *UNUSED(oc)) -- cgit v1.2.3 From 051141acdecfd11f82cbe37bac368c2882dd4f92 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 4 Aug 2021 11:40:20 +0200 Subject: Outliner/LibOverrides: Fix logic of checks for drag'n'drop of Collections. Previous check was too blunt, preventing e.g. re-organization of collection overrides inside a local parent collection, which is perfectly valid operation. Reported by @hjalti from the studio, thanks! --- source/blender/editors/space_outliner/outliner_dragdrop.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index 11ad3abcd54..a82f516b125 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -1104,10 +1104,6 @@ static bool collection_drop_init(bContext *C, if (ID_IS_LINKED(to_collection)) { return false; } - /* Currently this should not be allowed (might be supported in the future though...). */ - if (ID_IS_OVERRIDE_LIBRARY(to_collection)) { - return false; - } /* Get drag datablocks. */ if (drag->type != WM_DRAG_ID) { @@ -1131,6 +1127,11 @@ static bool collection_drop_init(bContext *C, from_collection = NULL; } + /* Currently this should not be allowed, cannot edit items in an override of a Collection. */ + if (from_collection != NULL && ID_IS_OVERRIDE_LIBRARY(from_collection)) { + return false; + } + /* Get collections. */ if (GS(id->name) == ID_GR) { if (id == &to_collection->id) { @@ -1141,6 +1142,12 @@ static bool collection_drop_init(bContext *C, insert_type = TE_INSERT_INTO; } + /* Currently this should not be allowed, cannot edit items in an override of a Collection. */ + if (ID_IS_OVERRIDE_LIBRARY(to_collection) && + !ELEM(insert_type, TE_INSERT_AFTER, TE_INSERT_BEFORE)) { + return false; + } + data->from = from_collection; data->to = to_collection; data->te = te; -- cgit v1.2.3 From 557e7f135ec22b19cf809109ea5f6bd0be607dbd Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Aug 2021 12:31:48 +0200 Subject: Cleanup: initialize variable to quiet warning --- source/blender/imbuf/intern/metadata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/imbuf/intern/metadata.c b/source/blender/imbuf/intern/metadata.c index c59997b34f5..5a01c42cf00 100644 --- a/source/blender/imbuf/intern/metadata.c +++ b/source/blender/imbuf/intern/metadata.c @@ -44,7 +44,7 @@ void IMB_metadata_ensure(struct IDProperty **metadata) return; } - IDPropertyTemplate val; + IDPropertyTemplate val = {0}; *metadata = IDP_New(IDP_GROUP, &val, "metadata"); } -- cgit v1.2.3 From ac6b263906c92d6adaafd014db28e0863cfd13e9 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Aug 2021 12:32:25 +0200 Subject: Cleanup: inconsistent parameter name --- source/blender/python/generic/py_capi_utils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h index 1591413530c..bfe0e66e393 100644 --- a/source/blender/python/generic/py_capi_utils.h +++ b/source/blender/python/generic/py_capi_utils.h @@ -42,13 +42,13 @@ void PyC_Err_PrintWithFunc(PyObject *py_func); void PyC_FileAndNum(const char **r_filename, int *r_lineno); void PyC_FileAndNum_Safe(const char **r_filename, int *r_lineno); /* checks python is running */ int PyC_AsArray_FAST(void *array, - const size_t array_elem_size, + const size_t array_item_size, PyObject *value_fast, const Py_ssize_t length, const PyTypeObject *type, const char *error_prefix); int PyC_AsArray(void *array, - const size_t array_elem_size, + const size_t array_item_size, PyObject *value, const Py_ssize_t length, const PyTypeObject *type, -- cgit v1.2.3 From add719e31aa7d5668fd33cb1a1e02417a334be97 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 4 Aug 2021 12:36:06 +0200 Subject: Tweak to recent liboverride API addition: naming. Rename new API function introduced in recent rB3b0fab6dfaa0 to match our convention to put the action (verb) at the end of names: `operations_update`. Sorry for not catching that during review. --- source/blender/makesrna/intern/rna_ID.c | 4 ++-- tests/python/bl_blendfile_library_overrides.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index e177f211c7f..15704fc2523 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -741,7 +741,7 @@ static void rna_ID_override_template_create(ID *id, ReportList *reports) BKE_lib_override_library_template_create(id); } -static void rna_ID_override_library_update_operations(ID *id, +static void rna_ID_override_library_operations_update(ID *id, IDOverrideLibrary *UNUSED(override_library), Main *bmain, ReportList *reports) @@ -1725,7 +1725,7 @@ static void rna_def_ID_override_library(BlenderRNA *brna) rna_def_ID_override_library_properties(brna, prop); /* Update function. */ - func = RNA_def_function(srna, "update_operations", "rna_ID_override_library_update_operations"); + func = RNA_def_function(srna, "operations_update", "rna_ID_override_library_operations_update"); RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_SELF_ID | FUNC_USE_REPORTS); RNA_def_function_ui_description(func, "Update the library override operations based on the " diff --git a/tests/python/bl_blendfile_library_overrides.py b/tests/python/bl_blendfile_library_overrides.py index 138c5fd13b5..19f06dc698e 100644 --- a/tests/python/bl_blendfile_library_overrides.py +++ b/tests/python/bl_blendfile_library_overrides.py @@ -60,7 +60,7 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase): assert(len(local_id.override_library.properties) == 0) local_id.location.y = 1.0 - local_id.override_library.update_operations() + local_id.override_library.operations_update() assert(len(local_id.override_library.properties) == 1) override_prop = local_id.override_library.properties[0] @@ -104,7 +104,7 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase): assert(local_id.scale.x == 0.5) assert(local_id.location.y == 1.0) - local_id.override_library.update_operations() + local_id.override_library.operations_update() assert(local_id.scale.x == 1.0) assert(local_id.location.y == 1.0) -- cgit v1.2.3 From d2130c53276a985ab82b310c4a0c87b824116729 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 4 Aug 2021 21:09:21 +1000 Subject: Cleanup: remove *.rej from cb67bfdba2e22d0ee967e50afe0912ac80d73e9b --- source/blender/makesdna/DNA_view3d_types.h.rej | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 source/blender/makesdna/DNA_view3d_types.h.rej diff --git a/source/blender/makesdna/DNA_view3d_types.h.rej b/source/blender/makesdna/DNA_view3d_types.h.rej deleted file mode 100644 index a62f1c4b7d3..00000000000 --- a/source/blender/makesdna/DNA_view3d_types.h.rej +++ /dev/null @@ -1,9 +0,0 @@ -diff a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h (rejected hunks) -@@ -550,6 +553,7 @@ - /* V3D_OVERLAY_EDIT_CU_HANDLES = (1 << 20), */ - - V3D_OVERLAY_EDIT_CU_NORMALS = (1 << 21), -+ V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS = (1 << 22), - }; - - /** #View3DOverlay.paint_flag */ -- cgit v1.2.3 From 76dcf70dacedb1af4f5e04ed5016e0b8ea4eb305 Mon Sep 17 00:00:00 2001 From: Gavin Li Date: Wed, 4 Aug 2021 13:18:23 +0200 Subject: Fix transparent faces on certain AMD cards This patch fixes an issue with missing faces when assigning a material slot other than the first to faces on AMD TAHITI cards. Refer to T78390 and T74024 for a description of this issue. This patch also incorporates fix from T78390 for KAVERI. {F9029258} Reviewed By: fclem Differential Revision: https://developer.blender.org/D9305 --- source/blender/gpu/opengl/gl_backend.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 42b85da1f93..772fc19d919 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -283,7 +283,8 @@ static void detect_workarounds() } /* We have issues with this specific renderer. (see T74024) */ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE) && - strstr(renderer, "AMD VERDE")) { + (strstr(renderer, "AMD VERDE") || strstr(renderer, "AMD KAVERI") || + strstr(renderer, "AMD TAHITI"))) { GLContext::unused_fb_slot_workaround = true; GCaps.shader_image_load_store_support = false; GCaps.broken_amd_driver = true; -- cgit v1.2.3 From 26c2c617b468ee2900f584b0ec0c82dfd632849a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 4 Aug 2021 22:34:55 +1000 Subject: Fix T90421: edit-mode auto-smooth crash when angle set to 180degrees Error in 39b2a7bb7e815e051348bf5c5ec777d091324164 which failed to set edge flags with single threaded calculation, used for low poly models. --- source/blender/bmesh/intern/bmesh_mesh_normals.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index 746f094aed6..6504a2de116 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -1156,10 +1156,6 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm, edge_vectors = BLI_stack_new(sizeof(float[3]), __func__); } - if (split_angle_cos != -1.0f) { - bm_mesh_edges_sharp_tag(bm, fnos, has_clnors ? (float)M_PI : split_angle, false); - } - /* Clear all loops' tags (means none are to be skipped for now). */ int index_face, index_loop = 0; BM_ITER_MESH_INDEX (f_curr, &fiter, bm, BM_FACES_OF_MESH, index_face) { @@ -1171,10 +1167,16 @@ 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. */ + BM_elem_flag_disable(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); + } + /* We now know edges that can be smoothed (they are tagged), * and edges that will be hard (they aren't). * Now, time to generate the normals. -- cgit v1.2.3 From e5d4a0c5596daaae14251872755df1e71e54e108 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 4 Aug 2021 10:38:03 -0300 Subject: BLI: add double version of 'scaleform' No functional changes. New utility. --- source/blender/blenlib/BLI_math_base.h | 3 +++ source/blender/blenlib/intern/math_base_inline.c | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index 88dc20a64f2..e877503e835 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -120,6 +120,9 @@ MINLINE double interpd(double a, double b, double t); MINLINE float ratiof(float min, float max, float pos); MINLINE double ratiod(double min, double max, double pos); +MINLINE float scalenorm(float a, float b, float x); +MINLINE double scalenormd(double a, double b, double x); + /* NOTE: Compilers will upcast all types smaller than int to int when performing arithmetic * operation. */ MINLINE int square_s(short a); diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index 6e3846e59c6..a80c495ecf3 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -199,6 +199,13 @@ MINLINE float scalenorm(float a, float b, float x) return (x * (b - a)) + a; } +/* Map a normalized value, i.e. from interval [0, 1] to interval [a, b]. */ +MINLINE double scalenormd(double a, double b, double x) +{ + BLI_assert(x <= 1 && x >= 0); + return (x * (b - a)) + a; +} + /* Used for zoom values. */ MINLINE float power_of_2(float val) { -- cgit v1.2.3 From 145270d8d7c6812de9fd1ba50cc51ffc53953881 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 4 Aug 2021 10:46:42 -0300 Subject: Fix T90427: Center View to Mouse broken rBfb87d236edb7 made the values returned by `projmat_dimensions` more standardized following the documentations. But the functions in Blender that called `projmat_dimensions` followed a proposal that these values corresponded to a distance of 1m of clip. Adjust these functions to follow the new algorithm. --- source/blender/draw/intern/draw_manager_data.c | 7 ------- source/blender/gpu/intern/gpu_matrix.cc | 12 ++++++------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 2126385a352..af331c86a8b 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -1533,13 +1533,6 @@ static void draw_frustum_boundbox_calc(const float (*viewinv)[4], projmat_dimensions(projmat, &left, &right, &bottom, &top, &near, &far); - if (is_persp) { - left *= near; - right *= near; - bottom *= near; - top *= near; - } - r_bbox->vec[0][2] = r_bbox->vec[3][2] = r_bbox->vec[7][2] = r_bbox->vec[4][2] = -near; r_bbox->vec[0][0] = r_bbox->vec[3][0] = left; r_bbox->vec[4][0] = r_bbox->vec[7][0] = right; diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index 6eb9cb823d5..efa04568401 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -532,17 +532,17 @@ 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] = precalc->dims.xmin + co[0] * (precalc->dims.xmax - precalc->dims.xmin); - co[1] = precalc->dims.ymin + co[1] * (precalc->dims.ymax - precalc->dims.ymin); + 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 / + co[2] = (precalc->dims.zmax * precalc->dims.zmin) / (precalc->dims.zmax + co[2] * (precalc->dims.zmin - precalc->dims.zmax)); - co[0] *= co[2]; - co[1] *= co[2]; + co[0] *= co[2] / precalc->dims.zmin; + co[1] *= co[2] / precalc->dims.zmin; } else { - co[2] = precalc->dims.zmin + co[2] * (precalc->dims.zmax - precalc->dims.zmin); + co[2] = (float)scalenormd(precalc->dims.zmin, precalc->dims.zmax, co[2]); } co[2] *= -1; } -- cgit v1.2.3 From c1730ed16568784552678908becfad2389ac391f Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Wed, 4 Aug 2021 15:54:54 +0200 Subject: GPencil: New Brush option to define Caps type This is used to set the default caps type for the stroke. Before always was rounded and only could be changed later in Edit mode Two new buttons has been added to topbar. NOTE: New icons are been designed (T90414) The buttons are expanded to list in Properties panel. Reviewed By: mendio, HooglyBoogly Differential Revision: https://developer.blender.org/D11999 --- release/scripts/startup/bl_ui/properties_paint_common.py | 6 ++++++ source/blender/draw/intern/draw_cache_impl_gpencil.c | 3 +++ source/blender/editors/gpencil/gpencil_paint.c | 3 +++ source/blender/editors/gpencil/gpencil_primitive.c | 3 +++ source/blender/makesdna/DNA_brush_types.h | 4 +++- source/blender/makesrna/intern/rna_brush.c | 12 ++++++++++++ 6 files changed, 30 insertions(+), 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 4bfd2fd32b0..97a0c5abf24 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -1252,6 +1252,12 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) if brush.gpencil_tool == 'TINT': row = layout.row(align=True) row.prop(gp_settings, "vertex_mode", text="Mode") + else: + row = layout.row(align=True) + if context.region.type == 'TOOL_HEADER': + row.prop(gp_settings, "caps_type", text="", expand=True) + else: + row.prop(gp_settings, "caps_type", text="Caps Type") # FIXME: tools must use their own UI drawing! if tool.idname in { diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index 336ccd40d5c..2a476ab41bb 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -552,6 +552,9 @@ bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob) gps->caps[0] = gps->caps[1] = GP_STROKE_CAP_ROUND; 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; + gpd->runtime.sbuffer_gps = gps; } return gpd->runtime.sbuffer_gps; diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index d6f6dbb2b10..aaea1c0ddaf 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -996,6 +996,9 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) gps->inittime = p->inittime; gps->uv_scale = 1.0f; + /* Set stroke caps. */ + gps->caps[0] = gps->caps[1] = brush->gpencil_settings->caps_type; + /* allocate enough memory for a continuous array for storage points */ const int subdivide = brush->gpencil_settings->draw_subdivide; diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index cf49aefe2ea..27374f21b66 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -335,6 +335,9 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) gps->uv_scale = 1.0f; gps->inittime = 0.0f; + /* Set stroke caps. */ + gps->caps[0] = gps->caps[1] = brush->gpencil_settings->caps_type; + /* Apply the vertex color to fill. */ ED_gpencil_fill_vertex_color_set(ts, brush, gps); diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 986c009ac26..634ebdff253 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -76,7 +76,9 @@ typedef struct BrushGpencilSettings { float fill_threshold; /** Number of pixel to consider the leak is too small (x 2). */ short fill_leak; - char _pad2[2]; + /* Type of caps: eGPDstroke_Caps. */ + int8_t caps_type; + char _pad; int flag2; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 2b09ea51a84..eb968f9ecde 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1281,6 +1281,12 @@ static void rna_def_gpencil_options(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static EnumPropertyItem rna_enum_gpencil_brush_caps_types_items[] = { + {GP_STROKE_CAP_ROUND, "ROUND", ICON_HANDLETYPE_AUTO_CLAMP_VEC, "Round", ""}, + {GP_STROKE_CAP_FLAT, "FLAT", ICON_HANDLETYPE_VECTOR_VEC, "Flat", ""}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "BrushGpencilSettings", NULL); RNA_def_struct_sdna(srna, "BrushGpencilSettings"); RNA_def_struct_path_func(srna, "rna_BrushGpencilSettings_path"); @@ -1750,6 +1756,12 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_update( prop, NC_GPENCIL | ND_DATA, "rna_BrushGpencilSettings_eraser_mode_update"); + prop = RNA_def_property(srna, "caps_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "caps_type"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_caps_types_items); + RNA_def_property_ui_text(prop, "Caps Type", "The shape of the start and end of the stroke"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + prop = RNA_def_property(srna, "fill_draw_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "fill_draw_mode"); RNA_def_property_enum_items(prop, rna_enum_gpencil_fill_draw_modes_items); -- cgit v1.2.3 From 2af789d1f3b8a36d0578997bea5f907e31a613ed Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 4 Aug 2021 16:05:49 +0200 Subject: Added some TODO remarks. --- source/blender/editors/animation/anim_draw.c | 1 + source/blender/editors/animation/anim_motion_paths.c | 1 + 2 files changed, 2 insertions(+) diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 6469c47ab11..735f3b86924 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -522,6 +522,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ mask_to_keylist(&ads, masklay, keylist); } + /* TODO(jbakker): Keylists are ordered, no need to do any searching at all. */ /* find matching keyframe in the right direction */ do { aknext = ED_keylist_find_next(keylist, cfranext); diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index 2a3ae35aab0..d976f5f72ad 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -234,6 +234,7 @@ static void motionpath_get_global_framerange(ListBase *targets, int *r_sfra, int } } +/* TODO(jbakker): Remove complexity, keylists are ordered. */ static int motionpath_get_prev_keyframe(MPathTarget *mpt, struct AnimKeylist *keylist, int current_frame) -- cgit v1.2.3 From a8185d2d74d42389d881890897958c1223eb5cf1 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 4 Aug 2021 15:13:20 +0200 Subject: LibOverride: Add RNA API to reset/delete overrides. Ref. T86656. --- source/blender/makesrna/intern/rna_ID.c | 63 +++++++++++++++++++++++++- tests/python/bl_blendfile_library_overrides.py | 10 ++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 15704fc2523..e4c9b70a1ec 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -747,13 +747,52 @@ static void rna_ID_override_library_operations_update(ID *id, ReportList *reports) { if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { - BKE_report(reports, RPT_ERROR, "ID isn't an override"); + BKE_reportf(reports, RPT_ERROR, "ID '%s' isn't an override", id->name); return; } BKE_lib_override_library_operations_create(bmain, id); } +static void rna_ID_override_library_reset(ID *id, + IDOverrideLibrary *UNUSED(override_library), + Main *bmain, + ReportList *reports, + bool do_hierarchy) +{ + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + BKE_reportf(reports, RPT_ERROR, "ID '%s' isn't an override", id->name); + return; + } + + if (do_hierarchy) { + BKE_lib_override_library_id_hierarchy_reset(bmain, id); + } + else { + BKE_lib_override_library_id_reset(bmain, id); + } +} + +static void rna_ID_override_library_destroy(ID *id, + IDOverrideLibrary *UNUSED(override_library), + Main *bmain, + ReportList *reports, + bool do_hierarchy) +{ + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + BKE_reportf(reports, RPT_ERROR, "ID '%s' isn't an override", id->name); + return; + } + + if (do_hierarchy) { + BKE_lib_override_library_delete(bmain, id); + } + else { + BKE_libblock_remap(bmain, id, id->override_library->reference, ID_REMAP_SKIP_INDIRECT_USAGE); + BKE_id_delete(bmain, id); + } +} + static IDOverrideLibraryProperty *rna_ID_override_library_properties_add( IDOverrideLibrary *override_library, ReportList *reports, const char rna_path[]) { @@ -1731,6 +1770,28 @@ static void rna_def_ID_override_library(BlenderRNA *brna) "Update the library override operations based on the " "differences between this override ID and its reference"); + func = RNA_def_function(srna, "reset", "rna_ID_override_library_reset"); + RNA_def_function_ui_description(func, + "Reset this override to match again its linked reference ID"); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_SELF_ID | FUNC_USE_REPORTS); + RNA_def_boolean( + func, + "do_hierarchy", + true, + "", + "Also reset all the dependencies of this override to match their reference linked IDs"); + + func = RNA_def_function(srna, "destroy", "rna_ID_override_library_destroy"); + RNA_def_function_ui_description( + func, "Delete this override ID and remap its usages to its linked reference ID instead"); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_SELF_ID | FUNC_USE_REPORTS); + RNA_def_boolean(func, + "do_hierarchy", + true, + "", + "Also delete all the dependencies of this override and remap their usages to " + "their reference linked IDs"); + rna_def_ID_override_library_property(brna); } diff --git a/tests/python/bl_blendfile_library_overrides.py b/tests/python/bl_blendfile_library_overrides.py index 19f06dc698e..358f77d49ea 100644 --- a/tests/python/bl_blendfile_library_overrides.py +++ b/tests/python/bl_blendfile_library_overrides.py @@ -71,6 +71,16 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase): # Setting location.y overridded all elements in the location array. -1 is a wildcard. assert(override_operation.subitem_local_index == -1) + local_id.override_library.reset() + + assert(len(local_id.override_library.properties) == 0) + assert(local_id.location == local_id.override_library.reference.location) + + local_id_name = local_id.name + assert(bpy.data.objects.get((local_id_name, None), None) == local_id) + local_id.override_library.destroy() + assert(bpy.data.objects.get((local_id_name, None), None) == None) + def test_link_permissive(self): """ Linked assets with a permissive template. -- cgit v1.2.3 From 58ba75f9e3bfc4ff08dbd1f2ce269c65388dfcfd Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 4 Aug 2021 16:56:06 +0200 Subject: LibOverride RNA API: add removal of properties and operations. This should complete the basics of RNA API for library overrides. Ref. T86656. --- source/blender/makesrna/intern/rna_ID.c | 45 ++++++++++++++++++++++++++ tests/python/bl_blendfile_library_overrides.py | 24 ++++++++++++-- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index e4c9b70a1ec..8f8ad077935 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -807,6 +807,18 @@ static IDOverrideLibraryProperty *rna_ID_override_library_properties_add( return result; } +static void rna_ID_override_library_properties_remove(IDOverrideLibrary *override_library, + ReportList *reports, + IDOverrideLibraryProperty *override_property) +{ + if (BLI_findindex(&override_library->properties, override_property) == -1) { + BKE_report(reports, RPT_ERROR, "Override property cannot be removed"); + return; + } + + BKE_lib_override_library_property_delete(override_library, override_property); +} + static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_operations_add( IDOverrideLibraryProperty *override_property, ReportList *reports, @@ -834,6 +846,19 @@ static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_oper return result; } +static void rna_ID_override_library_property_operations_remove( + IDOverrideLibraryProperty *override_property, + ReportList *reports, + IDOverrideLibraryPropertyOperation *override_operation) +{ + if (BLI_findindex(&override_property->operations, override_operation) == -1) { + BKE_report(reports, RPT_ERROR, "Override operation cannot be removed"); + return; + } + + BKE_lib_override_library_property_operation_delete(override_property, override_operation); +} + static void rna_ID_update_tag(ID *id, Main *bmain, ReportList *reports, int flag) { /* XXX, new function for this! */ @@ -1685,6 +1710,16 @@ static void rna_def_ID_override_library_property_operations(BlenderRNA *brna, Pr "New Operation", "Created operation"); RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_ID_override_library_property_operations_remove"); + RNA_def_function_ui_description(func, "Remove and delete an operation"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, + "operation", + "IDOverrideLibraryPropertyOperation", + "Operation", + "Override operation to be deleted"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } static void rna_def_ID_override_library_property(BlenderRNA *brna) @@ -1741,6 +1776,16 @@ static void rna_def_ID_override_library_properties(BlenderRNA *brna, PropertyRNA parm = RNA_def_string( func, "rna_path", NULL, 256, "RNA Path", "RNA-Path of the property to add"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + + func = RNA_def_function(srna, "remove", "rna_ID_override_library_properties_remove"); + RNA_def_function_ui_description(func, "Remove and delete a property"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, + "property", + "IDOverrideLibraryProperty", + "Property", + "Override property to be deleted"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } static void rna_def_ID_override_library(BlenderRNA *brna) diff --git a/tests/python/bl_blendfile_library_overrides.py b/tests/python/bl_blendfile_library_overrides.py index 358f77d49ea..b44e4d48564 100644 --- a/tests/python/bl_blendfile_library_overrides.py +++ b/tests/python/bl_blendfile_library_overrides.py @@ -59,9 +59,9 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase): self.assertIsNone(local_id.data.override_library) assert(len(local_id.override_library.properties) == 0) + ##### Generate an override property & operation automaticaly by editing the local override data. local_id.location.y = 1.0 local_id.override_library.operations_update() - assert(len(local_id.override_library.properties) == 1) override_prop = local_id.override_library.properties[0] assert(override_prop.rna_path == "location") @@ -71,11 +71,31 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase): # Setting location.y overridded all elements in the location array. -1 is a wildcard. assert(override_operation.subitem_local_index == -1) + ##### Reset the override to its linked reference data. local_id.override_library.reset() - assert(len(local_id.override_library.properties) == 0) assert(local_id.location == local_id.override_library.reference.location) + ##### Generate an override property & operation manually using the API. + override_property = local_id.override_library.properties.add(rna_path="location") + override_property.operations.add(operation='REPLACE') + + assert(len(local_id.override_library.properties) == 1) + override_prop = local_id.override_library.properties[0] + assert(override_prop.rna_path == "location") + assert(len(override_prop.operations) == 1) + override_operation = override_prop.operations[0] + assert(override_operation.operation == 'REPLACE') + # Setting location.y overridded all elements in the location array. -1 is a wildcard. + assert(override_operation.subitem_local_index == -1) + + override_property = local_id.override_library.properties[0] + override_property.operations.remove(override_property.operations[0]) + local_id.override_library.properties.remove(override_property) + + assert(len(local_id.override_library.properties) == 0) + + ##### Delete the override. local_id_name = local_id.name assert(bpy.data.objects.get((local_id_name, None), None) == local_id) local_id.override_library.destroy() -- cgit v1.2.3 From c18d91918fbb18d8c0d2e95de210ba818937cd87 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 3 Aug 2021 12:20:28 +0200 Subject: Cycles: More flexible GI Approximation AO distance control The goal: allow to easily use AO approximation in scenes which combines both small and large scale objects. The idea: use per-object AO distance which will allow to override world settings. Instancer object will "propagate" its AO distance to all its instances unless the instance defines own distance (this allows to modify AO distance in the shot files, without requiring to modify props used in the shots. Available from the new Fats GI Approximation panel in object properties. Differential Revision: https://developer.blender.org/D12112 --- intern/cycles/blender/addon/properties.py | 8 ++++++++ intern/cycles/blender/addon/ui.py | 21 +++++++++++++++++++++ intern/cycles/blender/blender_object.cpp | 7 +++++++ intern/cycles/kernel/bvh/bvh_util.h | 10 ++++++++++ intern/cycles/kernel/kernel_bake.h | 6 ++++-- intern/cycles/kernel/kernel_path.h | 16 ++++++++++++---- intern/cycles/kernel/kernel_path_branched.h | 11 +++++++---- intern/cycles/kernel/kernel_types.h | 5 ++++- intern/cycles/kernel/split/kernel_scene_intersect.h | 5 ++++- intern/cycles/render/object.cpp | 3 +++ intern/cycles/render/object.h | 2 ++ 11 files changed, 82 insertions(+), 12 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 4cd75edee5f..70efb1054a2 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1268,6 +1268,14 @@ class CyclesObjectSettings(bpy.types.PropertyGroup): default=0.1, ) + ao_distance: FloatProperty( + name="AO Distance", + description="AO distance used for approximate global illumination (0 means use world setting)", + min=0.0, + default=0.0, + subtype='DISTANCE', + ) + is_shadow_catcher: BoolProperty( name="Shadow Catcher", description="Only render shadows on this object, for compositing renders into real footage", diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index e9a4ce77080..8846c621529 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1228,6 +1228,26 @@ class CYCLES_OBJECT_PT_shading_shadow_terminator(CyclesButtonsPanel, Panel): flow.prop(cob, "shadow_terminator_offset", text="Shading Offset") +class CYCLES_OBJECT_PT_gi_approximation(CyclesButtonsPanel, Panel): + bl_label = "Fast GI Approximation" + bl_context = "object" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + scene = context.scene + ob = context.object + + cob = ob.cycles + cscene = scene.cycles + + col = layout.column() + col.active = cscene.use_fast_gi + col.prop(cob, "ao_distance") + + class CYCLES_OBJECT_PT_visibility(CyclesButtonsPanel, Panel): bl_label = "Visibility" bl_context = "object" @@ -2305,6 +2325,7 @@ classes = ( CYCLES_OBJECT_PT_motion_blur, CYCLES_OBJECT_PT_shading, CYCLES_OBJECT_PT_shading_shadow_terminator, + CYCLES_OBJECT_PT_gi_approximation, CYCLES_OBJECT_PT_visibility, CYCLES_OBJECT_PT_visibility_ray_visibility, CYCLES_OBJECT_PT_visibility_culling, diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index f5e8db2aee1..65b5ac2c58f 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -297,6 +297,13 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, "shadow_terminator_geometry_offset"); object->set_shadow_terminator_geometry_offset(shadow_terminator_geometry_offset); + float ao_distance = get_float(cobject, "ao_distance"); + if (ao_distance == 0.0f && b_parent.ptr.data != b_ob.ptr.data) { + PointerRNA cparent = RNA_pointer_get(&b_parent.ptr, "cycles"); + ao_distance = get_float(cparent, "ao_distance"); + } + object->set_ao_distance(ao_distance); + /* sync the asset name for Cryptomatte */ BL::Object parent = b_ob.parent(); ustring parent_name; diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h index 867ea3af8d1..b1faebce957 100644 --- a/intern/cycles/kernel/bvh/bvh_util.h +++ b/intern/cycles/kernel/bvh/bvh_util.h @@ -239,4 +239,14 @@ ccl_device_forceinline int intersection_get_shader(KernelGlobals *ccl_restrict k return shader & SHADER_MASK; } +ccl_device_forceinline int intersection_get_object(KernelGlobals *ccl_restrict kg, + const Intersection *ccl_restrict isect) +{ + if (isect->object != OBJECT_NONE) { + return isect->object; + } + + return kernel_tex_fetch(__prim_object, isect->prim); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index d1f33a4d0f0..7da890b908d 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -81,7 +81,8 @@ ccl_device_noinline void compute_light_pass( kg, sd, emission_sd, L, &state, &ray, &throughput, &ss_indirect)) { while (ss_indirect.num_rays) { kernel_path_subsurface_setup_indirect(kg, &ss_indirect, &state, &ray, L, &throughput); - kernel_path_indirect(kg, &indirect_sd, emission_sd, &ray, throughput, &state, L); + kernel_path_indirect( + kg, &indirect_sd, emission_sd, &ray, throughput, &state, L, sd->object); } is_sss_sample = true; } @@ -97,7 +98,8 @@ ccl_device_noinline void compute_light_pass( state.ray_t = 0.0f; # endif /* compute indirect light */ - kernel_path_indirect(kg, &indirect_sd, emission_sd, &ray, throughput, &state, L); + kernel_path_indirect( + kg, &indirect_sd, emission_sd, &ray, throughput, &state, L, sd->object); /* sum and reset indirect light pass variables for the next samples */ path_radiance_sum_indirect(L); diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index 3430543bc8e..ec577fa20b0 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -58,7 +58,8 @@ ccl_device_forceinline bool kernel_path_scene_intersect(KernelGlobals *kg, ccl_addr_space PathState *state, Ray *ray, Intersection *isect, - PathRadiance *L) + PathRadiance *L, + const int last_object) { PROFILING_INIT(kg, PROFILING_SCENE_INTERSECT); @@ -66,6 +67,12 @@ ccl_device_forceinline bool kernel_path_scene_intersect(KernelGlobals *kg, if (path_state_ao_bounce(kg, state)) { ray->t = kernel_data.background.ao_distance; + if (last_object != OBJECT_NONE) { + const float object_ao_distance = kernel_tex_fetch(__objects, last_object).ao_distance; + if (object_ao_distance != 0.0f) { + ray->t = object_ao_distance; + } + } } bool hit = scene_intersect(kg, ray, visibility, isect); @@ -369,7 +376,8 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, Ray *ray, float3 throughput, PathState *state, - PathRadiance *L) + PathRadiance *L, + const int last_object) { # ifdef __SUBSURFACE__ SubsurfaceIndirectRays ss_indirect; @@ -382,7 +390,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, for (;;) { /* Find intersection with objects in scene. */ Intersection isect; - bool hit = kernel_path_scene_intersect(kg, state, ray, &isect, L); + bool hit = kernel_path_scene_intersect(kg, state, ray, &isect, L, last_object); /* Find intersection with lamps and compute emission for MIS. */ kernel_path_lamp_emission(kg, state, ray, throughput, &isect, sd, L); @@ -526,7 +534,7 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg, for (;;) { /* Find intersection with objects in scene. */ Intersection isect; - bool hit = kernel_path_scene_intersect(kg, state, ray, &isect, L); + bool hit = kernel_path_scene_intersect(kg, state, ray, &isect, L, sd.object); /* Find intersection with lamps and compute emission for MIS. */ kernel_path_lamp_emission(kg, state, ray, throughput, &isect, &sd, L); diff --git a/intern/cycles/kernel/kernel_path_branched.h b/intern/cycles/kernel/kernel_path_branched.h index 5ea7687ec3b..a1ee1bc107e 100644 --- a/intern/cycles/kernel/kernel_path_branched.h +++ b/intern/cycles/kernel/kernel_path_branched.h @@ -92,6 +92,7 @@ ccl_device_forceinline void kernel_branched_path_volume(KernelGlobals *kg, volume_ray.t = (hit) ? isect->t : FLT_MAX; float step_size = volume_stack_step_size(kg, state->volume_stack); + const int object = sd->object; # ifdef __VOLUME_DECOUPLED__ /* decoupled ray marching only supported on CPU */ @@ -134,7 +135,8 @@ ccl_device_forceinline void kernel_branched_path_volume(KernelGlobals *kg, if (result == VOLUME_PATH_SCATTERED && kernel_path_volume_bounce(kg, sd, &tp, &ps, &L->state, &pray)) { - kernel_path_indirect(kg, indirect_sd, emission_sd, &pray, tp * num_samples_inv, &ps, L); + kernel_path_indirect( + kg, indirect_sd, emission_sd, &pray, tp * num_samples_inv, &ps, L, object); /* for render passes, sum and reset indirect light pass variables * for the next samples */ @@ -180,7 +182,7 @@ ccl_device_forceinline void kernel_branched_path_volume(KernelGlobals *kg, kernel_path_volume_connect_light(kg, sd, emission_sd, tp, state, L); if (kernel_path_volume_bounce(kg, sd, &tp, &ps, &L->state, &pray)) { - kernel_path_indirect(kg, indirect_sd, emission_sd, &pray, tp, &ps, L); + kernel_path_indirect(kg, indirect_sd, emission_sd, &pray, tp, &ps, L, object); /* for render passes, sum and reset indirect light pass variables * for the next samples */ @@ -266,7 +268,8 @@ ccl_device_noinline_cpu void kernel_branched_path_surface_indirect_light(KernelG ps.rng_hash = state->rng_hash; - kernel_path_indirect(kg, indirect_sd, emission_sd, &bsdf_ray, tp * num_samples_inv, &ps, L); + kernel_path_indirect( + kg, indirect_sd, emission_sd, &bsdf_ray, tp * num_samples_inv, &ps, L, sd->object); /* for render passes, sum and reset indirect light pass variables * for the next samples */ @@ -395,7 +398,7 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg, for (;;) { /* Find intersection with objects in scene. */ Intersection isect; - bool hit = kernel_path_scene_intersect(kg, &state, &ray, &isect, L); + bool hit = kernel_path_scene_intersect(kg, &state, &ray, &isect, L, sd.object); # ifdef __VOLUME__ /* Volume integration. */ diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index f9ea3a2d0a8..7cbe18acf28 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1447,7 +1447,10 @@ typedef struct KernelObject { float shadow_terminator_shading_offset; float shadow_terminator_geometry_offset; - float pad1, pad2, pad3; + + float ao_distance; + + float pad1, pad2; } KernelObject; static_assert_align(KernelObject, 16); diff --git a/intern/cycles/kernel/split/kernel_scene_intersect.h b/intern/cycles/kernel/split/kernel_scene_intersect.h index 5fef3e045f8..9ac95aafd2f 100644 --- a/intern/cycles/kernel/split/kernel_scene_intersect.h +++ b/intern/cycles/kernel/split/kernel_scene_intersect.h @@ -65,7 +65,10 @@ ccl_device void kernel_scene_intersect(KernelGlobals *kg) PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; Intersection isect; - bool hit = kernel_path_scene_intersect(kg, state, &ray, &isect, L); + const int last_object = state->bounce > 0 ? + intersection_get_object(kg, &kernel_split_state.isect[ray_index]) : + OBJECT_NONE; + bool hit = kernel_path_scene_intersect(kg, state, &ray, &isect, L, last_object); kernel_split_state.isect[ray_index] = isect; if (!hit) { diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index 5fe4e9ed57f..c88d94fe4c2 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -102,6 +102,8 @@ NODE_DEFINE(Object) SOCKET_NODE(particle_system, "Particle System", ParticleSystem::get_node_type()); SOCKET_INT(particle_index, "Particle Index", 0); + SOCKET_FLOAT(ao_distance, "AO Distance", 0.0f); + return type; } @@ -428,6 +430,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s kobject.random_number = random_number; kobject.particle_index = particle_index; kobject.motion_offset = 0; + kobject.ao_distance = ob->ao_distance; if (geom->get_use_motion_blur()) { state->have_motion = true; diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index ebb7733c2aa..c52ddce48da 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -73,6 +73,8 @@ class Object : public Node { NODE_SOCKET_API(ParticleSystem *, particle_system); NODE_SOCKET_API(int, particle_index); + NODE_SOCKET_API(float, ao_distance) + Object(); ~Object(); -- cgit v1.2.3 From b5bfb5f34c1228df0ba673faf9d2a38a866115ac Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Wed, 4 Aug 2021 08:33:19 -0700 Subject: UI: VFont Display Names When displaying the names of fonts for 3D Text objects, use the same format as shown in File Browser: Family name + Style name. They are currently shown with Postscript Name, which doesn't match well. see D12069 for more details. Differential Revision: https://developer.blender.org/D12069 Reviewed by Campbell Barton --- source/blender/blenlib/intern/freetypefont.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c index 98da85e3a8c..15c4910310f 100644 --- a/source/blender/blenlib/intern/freetypefont.c +++ b/source/blender/blenlib/intern/freetypefont.c @@ -40,6 +40,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string.h" +#include "BLI_string_utf8.h" #include "BLI_utildefines.h" #include "BLI_vfontdata.h" @@ -286,7 +287,6 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf) const FT_ULong charcode_reserve = 256; FT_ULong charcode = 0, lcode; FT_UInt glyph_index; - const char *fontname; VFontData *vfd; /* load the freetype font */ @@ -299,9 +299,11 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf) /* allocate blender font */ vfd = MEM_callocN(sizeof(*vfd), "FTVFontData"); - /* get the name */ - fontname = FT_Get_Postscript_Name(face); - BLI_strncpy(vfd->name, (fontname == NULL) ? "" : fontname, sizeof(vfd->name)); + /* Get the name. */ + if (face->family_name) { + BLI_snprintf(vfd->name, sizeof(vfd->name), "%s %s", face->family_name, face->style_name); + BLI_utf8_invalid_strip(vfd->name, strlen(vfd->name)); + } /* Extract the first 256 character from TTF */ lcode = charcode = FT_Get_First_Char(face, &glyph_index); -- cgit v1.2.3 From ae920d789ed3986e7c50049c074739fed7d33f32 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Wed, 4 Aug 2021 09:31:01 -0700 Subject: VSE: Allow Wingdings and Symbol Fonts This patch makes us less restrictive on the allowed types of FreeType font character maps we allow, rather than primarily unicode-only. This allows us to use some legacy, symbol, specialty, and proprietary fonts like Wingdings. Note we were a little less restrictive with vfonts, used for 3D Text Objects, so this patch primarily helps VSE. See D12124 for details and examples. Differential Revision: https://developer.blender.org/D12124 Reviewed by Brecht Van Lommel --- source/blender/blenfont/intern/blf_font.c | 10 ++++++-- source/blender/blenlib/intern/freetypefont.c | 37 +++++++++++----------------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 53c4135254a..6e2be4a8353 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -1359,9 +1359,15 @@ FontBLF *blf_font_new(const char *name, const char *filename) return NULL; } - err = FT_Select_Charmap(font->face, ft_encoding_unicode); + err = FT_Select_Charmap(font->face, FT_ENCODING_UNICODE); if (err) { - printf("Can't set the unicode character map!\n"); + err = FT_Select_Charmap(font->face, FT_ENCODING_APPLE_ROMAN); + } + if (err && font->face->num_charmaps > 0) { + err = FT_Select_Charmap(font->face, font->face->charmaps[0]->encoding); + } + if (err) { + printf("Can't set a character map!\n"); FT_Done_Face(font->face); MEM_freeN(font); return NULL; diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c index 15c4910310f..a8b50b66f5f 100644 --- a/source/blender/blenlib/intern/freetypefont.c +++ b/source/blender/blenlib/intern/freetypefont.c @@ -305,32 +305,23 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf) BLI_utf8_invalid_strip(vfd->name, strlen(vfd->name)); } + /* Select a character map. */ + err = FT_Select_Charmap(face, FT_ENCODING_UNICODE); + if (err) { + err = FT_Select_Charmap(face, FT_ENCODING_APPLE_ROMAN); + } + if (err && face->num_charmaps > 0) { + err = FT_Select_Charmap(face, face->charmaps[0]->encoding); + } + if (err) { + FT_Done_Face(face); + MEM_freeN(vfd); + return NULL; + } + /* Extract the first 256 character from TTF */ lcode = charcode = FT_Get_First_Char(face, &glyph_index); - /* No `charmap` found from the TTF so we need to figure it out. */ - if (glyph_index == 0) { - FT_CharMap found = NULL; - FT_CharMap charmap; - int n; - - for (n = 0; n < face->num_charmaps; n++) { - charmap = face->charmaps[n]; - if (charmap->encoding == FT_ENCODING_APPLE_ROMAN) { - found = charmap; - break; - } - } - - err = FT_Set_Charmap(face, found); - - if (err) { - return NULL; - } - - lcode = charcode = FT_Get_First_Char(face, &glyph_index); - } - /* Blender default BFont is not "complete". */ const bool complete_font = (face->ascender != 0) && (face->descender != 0) && (face->ascender != face->descender); -- cgit v1.2.3 From 10b9621079823048e73d4677413bc5e59f833dcf Mon Sep 17 00:00:00 2001 From: Romain Toumi Date: Mon, 2 Aug 2021 19:54:04 +0200 Subject: Fix Cycles material slots list being too short Bring it in line with Eevee. Differential Revision: https://developer.blender.org/D11982 --- intern/cycles/blender/addon/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 8846c621529..336fb02ba36 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1102,7 +1102,7 @@ class CYCLES_PT_context_material(CyclesButtonsPanel, Panel): if ob: is_sortable = len(ob.material_slots) > 1 - rows = 1 + rows = 3 if (is_sortable): rows = 4 -- cgit v1.2.3 From 8abf6efcf60fa7f609f38fda0e085d84a72b6dba Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 4 Aug 2021 17:46:55 +0200 Subject: Cleanup: rename restrict to hide/visibility in Object, Collection, MaskLayer This makes the internal naming consistent with the public API. And also gives us a visibility_flag rather than restrictflag that can be extended with more flags. --- source/blender/blenkernel/BKE_collection.h | 7 ++- source/blender/blenkernel/intern/collection.c | 6 +- source/blender/blenkernel/intern/layer.c | 28 ++++----- source/blender/blenkernel/intern/lib_override.c | 7 +-- source/blender/blenkernel/intern/mask.c | 4 +- source/blender/blenkernel/intern/mask_rasterize.c | 2 +- source/blender/blenkernel/intern/object_dupli.cc | 6 +- source/blender/blenkernel/intern/scene.c | 2 +- source/blender/blenloader/intern/versioning_280.c | 14 ++--- source/blender/bmesh/intern/bmesh_walkers_impl.c | 8 +-- .../depsgraph/intern/builder/deg_builder_nodes.cc | 6 +- .../intern/builder/deg_builder_nodes_view_layer.cc | 6 +- .../builder/deg_builder_relations_view_layer.cc | 6 +- source/blender/editors/animation/anim_filter.c | 4 +- source/blender/editors/animation/anim_markers.c | 2 +- source/blender/editors/mask/mask_add.c | 2 +- source/blender/editors/mask/mask_draw.c | 8 +-- source/blender/editors/mask/mask_ops.c | 24 ++++---- source/blender/editors/mask/mask_query.c | 8 +-- source/blender/editors/mask/mask_relationships.c | 4 +- source/blender/editors/mask/mask_select.c | 20 +++---- source/blender/editors/mask/mask_shapekey.c | 4 +- source/blender/editors/object/object_add.c | 2 +- source/blender/editors/object/object_bake_api.c | 10 ++-- source/blender/editors/object/object_edit.c | 6 +- source/blender/editors/render/render_preview.c | 8 +-- source/blender/editors/screen/screen_ops.c | 2 +- source/blender/editors/space_clip/clip_utils.c | 2 +- .../editors/space_outliner/outliner_collections.c | 10 ++-- .../editors/transform/transform_convert_mask.c | 4 +- .../gpencil_modifiers/intern/lineart/lineart_cpu.c | 4 +- source/blender/makesdna/DNA_collection_types.h | 6 +- source/blender/makesdna/DNA_layer_types.h | 2 +- source/blender/makesdna/DNA_mask_types.h | 10 ++-- source/blender/makesdna/DNA_object_types.h | 10 ++-- source/blender/makesdna/intern/dna_rename_defs.h | 2 + source/blender/makesrna/intern/rna_collection.c | 12 ++-- source/blender/makesrna/intern/rna_layer.c | 2 +- source/blender/makesrna/intern/rna_mask.c | 6 +- source/blender/makesrna/intern/rna_object.c | 70 ++++++++++++---------- 40 files changed, 177 insertions(+), 169 deletions(-) diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index 0326386e5c1..8b8d0f7b107 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -197,13 +197,14 @@ typedef void (*BKE_scene_collections_Cb)(struct Collection *ob, void *data); #define FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN(_collection, _object, _mode) \ { \ int _base_flag = (_mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT : BASE_ENABLED_RENDER; \ - int _object_restrict_flag = (_mode == DAG_EVAL_VIEWPORT) ? OB_RESTRICT_VIEWPORT : \ - OB_RESTRICT_RENDER; \ + int _object_visibility_flag = (_mode == DAG_EVAL_VIEWPORT) ? OB_HIDE_VIEWPORT : \ + OB_HIDE_RENDER; \ int _base_id = 0; \ for (Base *_base = (Base *)BKE_collection_object_cache_get(_collection).first; _base; \ _base = _base->next, _base_id++) { \ Object *_object = _base->object; \ - if ((_base->flag & _base_flag) && (_object->restrictflag & _object_restrict_flag) == 0) { + if ((_base->flag & _base_flag) && \ + (_object->visibility_flag & _object_visibility_flag) == 0) { #define FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END \ } \ diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index b90214f1814..dbcd80fe065 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -806,10 +806,10 @@ static void collection_object_cache_fill(ListBase *lb, /* Only collection flags are checked here currently, object restrict flag is checked * in FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN since it can be animated * without updating the cache. */ - if (((child_restrict & COLLECTION_RESTRICT_VIEWPORT) == 0)) { + if (((child_restrict & COLLECTION_HIDE_VIEWPORT) == 0)) { base->flag |= BASE_ENABLED_VIEWPORT; } - if (((child_restrict & COLLECTION_RESTRICT_RENDER) == 0)) { + if (((child_restrict & COLLECTION_HIDE_RENDER) == 0)) { base->flag |= BASE_ENABLED_RENDER; } } @@ -1755,7 +1755,7 @@ static bool collection_objects_select(ViewLayer *view_layer, Collection *collect { bool changed = false; - if (collection->flag & COLLECTION_RESTRICT_SELECT) { + if (collection->flag & COLLECTION_HIDE_SELECT) { return false; } diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index ae1863f0a47..5940569ba76 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -602,7 +602,7 @@ static bool layer_collection_hidden(ViewLayer *view_layer, LayerCollection *lc) } /* Check visiblilty restriction flags */ - if (lc->flag & LAYER_COLLECTION_HIDE || lc->collection->flag & COLLECTION_RESTRICT_VIEWPORT) { + if (lc->flag & LAYER_COLLECTION_HIDE || lc->collection->flag & COLLECTION_HIDE_VIEWPORT) { return true; } @@ -1005,17 +1005,17 @@ static void layer_collection_objects_sync(ViewLayer *view_layer, BLI_addtail(r_lb_new_object_bases, base); } - if ((collection_restrict & COLLECTION_RESTRICT_VIEWPORT) == 0) { + if ((collection_restrict & COLLECTION_HIDE_VIEWPORT) == 0) { base->flag_from_collection |= (BASE_ENABLED_VIEWPORT | BASE_VISIBLE_DEPSGRAPH); if ((layer_restrict & LAYER_COLLECTION_HIDE) == 0) { base->flag_from_collection |= BASE_VISIBLE_VIEWLAYER; } - if (((collection_restrict & COLLECTION_RESTRICT_SELECT) == 0)) { + if (((collection_restrict & COLLECTION_HIDE_SELECT) == 0)) { base->flag_from_collection |= BASE_SELECTABLE; } } - if ((collection_restrict & COLLECTION_RESTRICT_RENDER) == 0) { + if ((collection_restrict & COLLECTION_HIDE_RENDER) == 0) { base->flag_from_collection |= BASE_ENABLED_RENDER; } @@ -1150,11 +1150,11 @@ static void layer_collection_sync(ViewLayer *view_layer, /* We separate restrict viewport and visible view layer because a layer collection can be * hidden in the view layer yet (locally) visible in a viewport (if it is not restricted). */ - if (child_collection_restrict & COLLECTION_RESTRICT_VIEWPORT) { - child_layer->runtime_flag |= LAYER_COLLECTION_RESTRICT_VIEWPORT; + if (child_collection_restrict & COLLECTION_HIDE_VIEWPORT) { + child_layer->runtime_flag |= LAYER_COLLECTION_HIDE_VIEWPORT; } - if (((child_layer->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) == 0) && + if (((child_layer->runtime_flag & LAYER_COLLECTION_HIDE_VIEWPORT) == 0) && ((child_layer_restrict & LAYER_COLLECTION_HIDE) == 0)) { child_layer->runtime_flag |= LAYER_COLLECTION_VISIBLE_VIEW_LAYER; } @@ -1333,7 +1333,7 @@ void BKE_main_collection_sync_remap(const Main *bmain) */ bool BKE_layer_collection_objects_select(ViewLayer *view_layer, LayerCollection *lc, bool deselect) { - if (lc->collection->flag & COLLECTION_RESTRICT_SELECT) { + if (lc->collection->flag & COLLECTION_HIDE_SELECT) { return false; } @@ -1369,7 +1369,7 @@ bool BKE_layer_collection_objects_select(ViewLayer *view_layer, LayerCollection bool BKE_layer_collection_has_selected_objects(ViewLayer *view_layer, LayerCollection *lc) { - if (lc->collection->flag & COLLECTION_RESTRICT_SELECT) { + if (lc->collection->flag & COLLECTION_HIDE_SELECT) { return false; } @@ -1457,7 +1457,7 @@ bool BKE_object_is_visible_in_viewport(const View3D *v3d, const struct Object *o { BLI_assert(v3d != NULL); - if (ob->restrictflag & OB_RESTRICT_VIEWPORT) { + if (ob->visibility_flag & OB_HIDE_VIEWPORT) { return false; } @@ -2146,14 +2146,14 @@ void BKE_base_eval_flags(Base *base) base->flag |= (base->flag_from_collection & g_base_collection_flags); /* Apply object restrictions. */ - const int object_restrict = base->object->restrictflag; - if (object_restrict & OB_RESTRICT_VIEWPORT) { + const int object_restrict = base->object->visibility_flag; + if (object_restrict & OB_HIDE_VIEWPORT) { base->flag &= ~BASE_ENABLED_VIEWPORT; } - if (object_restrict & OB_RESTRICT_RENDER) { + if (object_restrict & OB_HIDE_RENDER) { base->flag &= ~BASE_ENABLED_RENDER; } - if (object_restrict & OB_RESTRICT_SELECT) { + if (object_restrict & OB_HIDE_SELECT) { base->flag &= ~BASE_SELECTABLE; } diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 8e67547b719..38687fa47b2 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -855,8 +855,8 @@ static void lib_override_library_create_post_process(Main *bmain, default_instantiating_collection = BKE_collection_add( bmain, (Collection *)id_root, "OVERRIDE_HIDDEN"); /* Hide the collection from viewport and render. */ - default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_RENDER; + default_instantiating_collection->flag |= COLLECTION_HIDE_VIEWPORT | + COLLECTION_HIDE_RENDER; break; } case ID_OB: { @@ -1731,8 +1731,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, override_resync_residual_storage = BKE_collection_add( bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME); /* Hide the collection from viewport and render. */ - override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_RENDER; + override_resync_residual_storage->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER; } /* Necessary to improve performances, and prevent layers matching override sub-collections to be diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 34dd38164c2..f40d1db60ff 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -429,7 +429,7 @@ MaskLayer *BKE_mask_layer_copy(const MaskLayer *masklay) masklay_new->blend_flag = masklay->blend_flag; masklay_new->flag = masklay->flag; masklay_new->falloff = masklay->falloff; - masklay_new->restrictflag = masklay->restrictflag; + masklay_new->visibility_flag = masklay->visibility_flag; for (spline = masklay->splines.first; spline; spline = spline->next) { MaskSpline *spline_new = BKE_mask_spline_copy(spline); @@ -2092,7 +2092,7 @@ void BKE_mask_clipboard_copy_from_layer(MaskLayer *mask_layer) MaskSpline *spline; /* Nothing to do if selection if disabled for the given layer. */ - if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->visibility_flag & MASK_HIDE_SELECT) { return; } diff --git a/source/blender/blenkernel/intern/mask_rasterize.c b/source/blender/blenkernel/intern/mask_rasterize.c index 00ed7d86975..8acc929a089 100644 --- a/source/blender/blenkernel/intern/mask_rasterize.c +++ b/source/blender/blenkernel/intern/mask_rasterize.c @@ -619,7 +619,7 @@ void BKE_maskrasterize_handle_init(MaskRasterHandle *mr_handle, unsigned int tot_boundary_found = 0; #endif - if (masklay->restrictflag & MASK_RESTRICT_RENDER) { + if (masklay->visibility_flag & MASK_HIDE_RENDER) { /* skip the layer */ mr_handle->layers_tot--; masklay_index--; diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 77969328365..141a9a25eca 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -1554,15 +1554,15 @@ static const DupliGenerator gen_dupli_particles = { static const DupliGenerator *get_dupli_generator(const DupliContext *ctx) { int transflag = ctx->object->transflag; - int restrictflag = ctx->object->restrictflag; + int visibility_flag = ctx->object->visibility_flag; if ((transflag & OB_DUPLI) == 0 && ctx->object->runtime.geometry_set_eval == nullptr) { return nullptr; } /* Should the dupli's be generated for this object? - Respect restrict flags. */ - if (DEG_get_mode(ctx->depsgraph) == DAG_EVAL_RENDER ? (restrictflag & OB_RESTRICT_RENDER) : - (restrictflag & OB_RESTRICT_VIEWPORT)) { + if (DEG_get_mode(ctx->depsgraph) == DAG_EVAL_RENDER ? (visibility_flag & OB_HIDE_RENDER) : + (visibility_flag & OB_HIDE_VIEWPORT)) { return nullptr; } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 3f75d0963c6..6d60f5e91b4 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -2305,7 +2305,7 @@ Object *BKE_scene_camera_switch_find(Scene *scene) Object *first_camera = NULL; LISTBASE_FOREACH (TimeMarker *, m, &scene->markers) { - if (m->camera && (m->camera->restrictflag & OB_RESTRICT_RENDER) == 0) { + if (m->camera && (m->camera->visibility_flag & OB_HIDE_RENDER) == 0) { if ((m->frame <= ctime) && (m->frame > frame)) { camera = m->camera; frame = m->frame; diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 93959237f29..9d65488e8d4 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -321,7 +321,7 @@ static void do_version_layer_collection_post(ViewLayer *view_layer, lc->flag |= LAYER_COLLECTION_EXCLUDE; } if (enabled && !selectable) { - lc->collection->flag |= COLLECTION_RESTRICT_SELECT; + lc->collection->flag |= COLLECTION_HIDE_SELECT; } } @@ -450,7 +450,7 @@ static void do_version_layers_to_collections(Main *bmain, Scene *scene) collections[layer] = collection; if (!(scene->lay & (1 << layer))) { - collection->flag |= COLLECTION_RESTRICT_VIEWPORT | COLLECTION_RESTRICT_RENDER; + collection->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER; } } @@ -1198,7 +1198,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) /* Add fake user for all existing groups. */ id_fake_user_set(&collection->id); - if (collection->flag & (COLLECTION_RESTRICT_VIEWPORT | COLLECTION_RESTRICT_RENDER)) { + if (collection->flag & (COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER)) { continue; } @@ -1229,8 +1229,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) char name[MAX_ID_NAME]; BLI_snprintf(name, sizeof(name), DATA_("Hidden %d"), coll_idx + 1); *collection_hidden = BKE_collection_add(bmain, collection, name); - (*collection_hidden)->flag |= COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_RENDER; + (*collection_hidden)->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER; } BKE_collection_object_add(bmain, *collection_hidden, ob); @@ -4083,9 +4082,8 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 280, 75)) { for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { if (scene->master_collection != NULL) { - scene->master_collection->flag &= ~(COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_SELECT | - COLLECTION_RESTRICT_RENDER); + scene->master_collection->flag &= ~(COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_SELECT | + COLLECTION_HIDE_RENDER); } UnitSettings *unit = &scene->unit; diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c index e66afcd88d9..7931e953295 100644 --- a/source/blender/bmesh/intern/bmesh_walkers_impl.c +++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c @@ -203,9 +203,9 @@ static void *bmw_VertShellWalker_step(BMWalker *walker) curedge = shellWalk.curedge; do { if (!BLI_gset_haskey(walker->visit_set, curedge)) { - if (!walker->restrictflag || - (walker->restrictflag && - BMO_edge_flag_test(walker->bm, curedge, walker->restrictflag))) { + if (!walker->visibility_flag || + (walker->visibility_flag && + BMO_edge_flag_test(walker->bm, curedge, walker->visibility_flag))) { BMwShellWalker *newstate; v_old = BM_edge_other_vert(curedge, shellWalk.base); @@ -714,7 +714,7 @@ static void *bmw_IslandboundWalker_step(BMWalker *walker) iwalk->base = owalk.base; #if 0 - if (!BMO_face_flag_test(walker->bm, l->f, walker->restrictflag)) { + if (!BMO_face_flag_test(walker->bm, l->f, walker->visibility_flag)) { iwalk->curloop = l->radial_next; } else { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 8d1074d912f..22bce10937d 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -648,9 +648,9 @@ void DepsgraphNodeBuilder::build_idproperties(IDProperty *id_property) void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collection, Collection *collection) { - const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_RESTRICT_VIEWPORT : - COLLECTION_RESTRICT_RENDER; - const bool is_collection_restricted = (collection->flag & restrict_flag); + const int visibility_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_HIDE_VIEWPORT : + COLLECTION_HIDE_RENDER; + const bool is_collection_restricted = (collection->flag & visibility_flag); const bool is_collection_visible = !is_collection_restricted && is_parent_collection_visible_; IDNode *id_node; if (built_map_.checkIsBuiltAndTag(collection)) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc index 29aa05b83db..b6e3f4fa935 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc @@ -60,11 +60,11 @@ namespace blender::deg { void DepsgraphNodeBuilder::build_layer_collections(ListBase *lb) { - const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_RESTRICT_VIEWPORT : - COLLECTION_RESTRICT_RENDER; + const int visibility_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_HIDE_VIEWPORT : + COLLECTION_HIDE_RENDER; for (LayerCollection *lc = (LayerCollection *)lb->first; lc; lc = lc->next) { - if (lc->collection->flag & restrict_flag) { + if (lc->collection->flag & visibility_flag) { continue; } if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc index 24876049942..6527f165552 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc @@ -61,11 +61,11 @@ namespace blender::deg { void DepsgraphRelationBuilder::build_layer_collections(ListBase *lb) { - const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_RESTRICT_VIEWPORT : - COLLECTION_RESTRICT_RENDER; + const int visibility_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_HIDE_VIEWPORT : + COLLECTION_HIDE_RENDER; for (LayerCollection *lc = (LayerCollection *)lb->first; lc; lc = lc->next) { - if ((lc->collection->flag & restrict_flag)) { + if ((lc->collection->flag & visibility_flag)) { continue; } if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) { diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index b2d387ea898..020518b5813 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1892,7 +1892,7 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, } /* outliner restrict-flag */ - if (ob->restrictflag & OB_RESTRICT_VIEWPORT) { + if (ob->visibility_flag & OB_HIDE_VIEWPORT) { continue; } } @@ -3098,7 +3098,7 @@ static bool animdata_filter_base_is_ok(bDopeSheet *ads, Base *base, int filter_m } /* outliner restrict-flag */ - if (ob->restrictflag & OB_RESTRICT_VIEWPORT) { + if (ob->visibility_flag & OB_HIDE_VIEWPORT) { return false; } } diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index cfcea950955..0de3f429bc7 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -445,7 +445,7 @@ static void draw_marker_name(const uchar *text_color, if (marker->camera) { Object *camera = marker->camera; name = camera->id.name + 2; - if (camera->restrictflag & OB_RESTRICT_RENDER) { + if (camera->visibility_flag & OB_HIDE_RENDER) { final_text_color[3] = 100; } } diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index ad71f4d9da7..110f4541e8f 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -521,7 +521,7 @@ static int add_vertex_exec(bContext *C, wmOperator *op) MaskLayer *mask_layer = BKE_mask_layer_active(mask); - if (mask_layer && mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer && mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { mask_layer = NULL; } diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index de8ea8e21eb..22232e9c87e 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -191,7 +191,7 @@ static void draw_spline_points(const bContext *C, const char draw_type) { const bool is_spline_sel = (spline->flag & SELECT) && - (mask_layer->restrictflag & MASK_RESTRICT_SELECT) == 0; + (mask_layer->visibility_flag & MASK_HIDE_SELECT) == 0; const bool is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH) != 0; uchar rgb_spline[4]; @@ -529,7 +529,7 @@ static void draw_spline_curve(const bContext *C, uchar rgb_tmp[4]; const bool is_spline_sel = (spline->flag & SELECT) && - (mask_layer->restrictflag & MASK_RESTRICT_SELECT) == 0; + (mask_layer->visibility_flag & MASK_HIDE_SELECT) == 0; const bool is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH) != 0; const bool is_fill = (spline->flag & MASK_SPLINE_NOFILL) == 0; @@ -604,7 +604,7 @@ static void draw_mask_layers(const bContext *C, mask_layer = mask_layer->next, i++) { const bool is_active = (i == mask->masklay_act); - if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) { + if (mask_layer->visibility_flag & MASK_HIDE_VIEW) { continue; } @@ -613,7 +613,7 @@ static void draw_mask_layers(const bContext *C, /* draw curve itself first... */ draw_spline_curve(C, mask_layer, spline, draw_flag, draw_type, is_active, width, height); - if (!(mask_layer->restrictflag & MASK_RESTRICT_SELECT)) { + if (!(mask_layer->visibility_flag & MASK_HIDE_SELECT)) { /* ...and then handles over the curve so they're nicely visible */ draw_spline_points(C, mask_layer, spline, draw_flag, draw_type); } diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c index 6fa7457ce14..fd5925bbd0c 100644 --- a/source/blender/editors/mask/mask_ops.c +++ b/source/blender/editors/mask/mask_ops.c @@ -287,7 +287,7 @@ static bool spline_under_mouse_get(const bContext *C, } for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer != NULL; mask_layer = mask_layer->next) { - if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->visibility_flag & MASK_HIDE_SELECT) { continue; } @@ -1322,7 +1322,7 @@ static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op)) Mask *mask = CTX_data_edit_mask(C); LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -1403,7 +1403,7 @@ static int delete_exec(bContext *C, wmOperator *UNUSED(op)) MaskSpline *spline; int mask_layer_shape_ofs = 0; - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -1523,7 +1523,7 @@ static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { bool changed_layer = false; - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -1581,7 +1581,7 @@ static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op) LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { bool changed_layer = false; - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -1642,7 +1642,7 @@ static int set_handle_type_exec(bContext *C, wmOperator *op) bool changed = false; LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -1724,9 +1724,9 @@ static int mask_hide_view_clear_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & OB_RESTRICT_VIEWPORT) { + if (mask_layer->visibility_flag & OB_HIDE_VIEWPORT) { ED_mask_layer_select_set(mask_layer, select); - mask_layer->restrictflag &= ~OB_RESTRICT_VIEWPORT; + mask_layer->visibility_flag &= ~OB_HIDE_VIEWPORT; changed = true; } } @@ -1766,7 +1766,7 @@ static int mask_hide_view_set_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->visibility_flag & MASK_HIDE_SELECT) { continue; } @@ -1774,7 +1774,7 @@ static int mask_hide_view_set_exec(bContext *C, wmOperator *op) if (ED_mask_layer_select_check(mask_layer)) { ED_mask_layer_select_set(mask_layer, false); - mask_layer->restrictflag |= OB_RESTRICT_VIEWPORT; + mask_layer->visibility_flag |= OB_HIDE_VIEWPORT; changed = true; if (mask_layer == BKE_mask_layer_active(mask)) { BKE_mask_layer_active_set(mask, NULL); @@ -1783,7 +1783,7 @@ static int mask_hide_view_set_exec(bContext *C, wmOperator *op) } else { if (!ED_mask_layer_select_check(mask_layer)) { - mask_layer->restrictflag |= OB_RESTRICT_VIEWPORT; + mask_layer->visibility_flag |= OB_HIDE_VIEWPORT; changed = true; if (mask_layer == BKE_mask_layer_active(mask)) { BKE_mask_layer_active_set(mask, NULL); @@ -1825,7 +1825,7 @@ static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op)) bool changed = false; LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) { + if (mask_layer->visibility_flag & (MASK_HIDE_SELECT | MASK_HIDE_VIEW)) { continue; } diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c index cd51026d20c..e66c4e45e27 100644 --- a/source/blender/editors/mask/mask_query.c +++ b/source/blender/editors/mask/mask_query.c @@ -85,7 +85,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, *mask_layer_eval = mask_eval->masklayers.first; mask_layer_orig != NULL; mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) { - if (mask_layer_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -245,7 +245,7 @@ MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, mask_layer_orig != NULL; mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) { - if (mask_layer_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -409,7 +409,7 @@ bool ED_mask_feather_find_nearest(const bContext *C, int i, tot_feather_point; float(*feather_points)[2], (*fp)[2]; - if (mask_layer_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -638,7 +638,7 @@ bool ED_mask_selected_minmax(const bContext *C, INIT_MINMAX2(min, max); for (MaskLayer *mask_layer = mask_eval->masklayers.first; mask_layer != NULL; mask_layer = mask_layer->next) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } for (MaskSpline *spline = mask_layer->splines.first; spline != NULL; spline = spline->next) { diff --git a/source/blender/editors/mask/mask_relationships.c b/source/blender/editors/mask/mask_relationships.c index 971e1c948c9..9c4740b3087 100644 --- a/source/blender/editors/mask/mask_relationships.c +++ b/source/blender/editors/mask/mask_relationships.c @@ -46,7 +46,7 @@ static int mask_parent_clear_exec(bContext *C, wmOperator *UNUSED(op)) Mask *mask = CTX_data_edit_mask(C); LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -138,7 +138,7 @@ static int mask_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) } LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index 8ddc3758e4e..6a1be8dcef3 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -68,7 +68,7 @@ bool ED_mask_spline_select_check(const MaskSpline *spline) bool ED_mask_layer_select_check(const MaskLayer *mask_layer) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { return false; } @@ -110,7 +110,7 @@ void ED_mask_spline_select_set(MaskSpline *spline, const bool do_select) void ED_mask_layer_select_set(MaskLayer *mask_layer, const bool do_select) { - if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->visibility_flag & MASK_HIDE_SELECT) { if (do_select == true) { return; } @@ -134,7 +134,7 @@ void ED_mask_select_toggle_all(Mask *mask, int action) LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) { + if (mask_layer->visibility_flag & MASK_HIDE_VIEW) { continue; } @@ -142,7 +142,7 @@ void ED_mask_select_toggle_all(Mask *mask, int action) /* we don't have generic functions for this, its restricted to this operator * if one day we need to re-use such functionality, they can be split out */ - if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->visibility_flag & MASK_HIDE_SELECT) { continue; } LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) { @@ -166,7 +166,7 @@ void ED_mask_select_flush_all(Mask *mask) /* Intentionally *don't* do this in the mask layer loop * so we clear flags on all splines. */ - if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) { + if (mask_layer->visibility_flag & MASK_HIDE_VIEW) { continue; } @@ -465,7 +465,7 @@ static int box_select_exec(bContext *C, wmOperator *op) /* do actual selection */ LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -550,7 +550,7 @@ static bool do_lasso_select_mask(bContext *C, /* do actual selection */ LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -691,7 +691,7 @@ static int circle_select_exec(bContext *C, wmOperator *op) /* do actual selection */ LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -820,7 +820,7 @@ static int mask_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) /* do actual selection */ LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -870,7 +870,7 @@ static int mask_select_more_less(bContext *C, bool more) Mask *mask = CTX_data_edit_mask(C); LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c index a5a3489c143..6620096c39a 100644 --- a/source/blender/editors/mask/mask_shapekey.c +++ b/source/blender/editors/mask/mask_shapekey.c @@ -144,7 +144,7 @@ static int mask_shape_key_feather_reset_exec(bContext *C, wmOperator *UNUSED(op) LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -238,7 +238,7 @@ static int mask_shape_key_rekey_exec(bContext *C, wmOperator *op) const bool do_location = RNA_boolean_get(op->ptr, "location"); LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 8ae74fbfafa..f98f3242163 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -3530,7 +3530,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - basen->object->restrictflag &= ~OB_RESTRICT_VIEWPORT; + basen->object->visibility_flag &= ~OB_HIDE_VIEWPORT; int mval[2]; if (object_add_drop_xy_get(C, op, &mval)) { diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 43358f51396..0a2df655395 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -1311,7 +1311,7 @@ static int bake(const BakeAPIRender *bkr, } else { ob_cage_eval = DEG_get_evaluated_object(depsgraph, ob_cage); - ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; + ob_cage_eval->visibility_flag |= OB_HIDE_RENDER; ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); } } @@ -1411,7 +1411,7 @@ static int bake(const BakeAPIRender *bkr, /* initialize highpoly_data */ highpoly[i].ob = ob_iter; highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter); - highpoly[i].ob_eval->restrictflag &= ~OB_RESTRICT_RENDER; + highpoly[i].ob_eval->visibility_flag &= ~OB_HIDE_RENDER; highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false, false); @@ -1427,10 +1427,10 @@ static int bake(const BakeAPIRender *bkr, BLI_assert(i == tot_highpoly); if (ob_cage != NULL) { - ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; + ob_cage_eval->visibility_flag |= OB_HIDE_RENDER; ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); } - ob_low_eval->restrictflag |= OB_RESTRICT_RENDER; + ob_low_eval->visibility_flag |= OB_HIDE_RENDER; ob_low_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); /* populate the pixel arrays with the corresponding face data for each high poly object */ @@ -1473,7 +1473,7 @@ static int bake(const BakeAPIRender *bkr, } else { /* If low poly is not renderable it should have failed long ago. */ - BLI_assert((ob_low_eval->restrictflag & OB_RESTRICT_RENDER) == 0); + BLI_assert((ob_low_eval->visibility_flag & OB_HIDE_RENDER) == 0); if (RE_bake_has_engine(re)) { ok = RE_bake_engine(re, diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 6108691b2f1..5697c2c973d 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -384,7 +384,7 @@ static int object_hide_collection_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS); if (v3d->flag & V3D_LOCAL_COLLECTIONS) { - if (lc->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) { + if (lc->runtime_flag & LAYER_COLLECTION_HIDE_VIEWPORT) { return OPERATOR_CANCELLED; } if (toggle) { @@ -421,7 +421,7 @@ void ED_collection_hide_menu_draw(const bContext *C, uiLayout *layout) continue; } - if (lc->collection->flag & COLLECTION_RESTRICT_VIEWPORT) { + if (lc->collection->flag & COLLECTION_HIDE_VIEWPORT) { continue; } @@ -926,7 +926,7 @@ static bool editmode_toggle_poll(bContext *C) } /* if hidden but in edit mode, we still display */ - if ((ob->restrictflag & OB_RESTRICT_VIEWPORT) && !(ob->mode & OB_MODE_EDIT)) { + if ((ob->visibility_flag & OB_HIDE_VIEWPORT) && !(ob->mode & OB_MODE_EDIT)) { return false; } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index fe1e850dcba..bd4c83c107e 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -273,10 +273,10 @@ static void switch_preview_collection_visibilty(ViewLayer *view_layer, const ePr for (lc = lc->layer_collections.first; lc; lc = lc->next) { if (STREQ(lc->collection->id.name + 2, collection_name)) { - lc->collection->flag &= ~COLLECTION_RESTRICT_RENDER; + lc->collection->flag &= ~COLLECTION_HIDE_RENDER; } else { - lc->collection->flag |= COLLECTION_RESTRICT_RENDER; + lc->collection->flag |= COLLECTION_HIDE_RENDER; } } } @@ -288,10 +288,10 @@ static void switch_preview_floor_visibility(ViewLayer *view_layer, LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (STREQ(base->object->id.name + 2, "Floor")) { if (pr_method == PR_ICON_RENDER) { - base->object->restrictflag |= OB_RESTRICT_RENDER; + base->object->visibility_flag |= OB_HIDE_RENDER; } else { - base->object->restrictflag &= ~OB_RESTRICT_RENDER; + base->object->visibility_flag &= ~OB_HIDE_RENDER; } } } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 107466a8a0b..0bc2a224a4b 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -381,7 +381,7 @@ bool ED_operator_console_active(bContext *C) static bool ed_object_hidden(const Object *ob) { /* if hidden but in edit mode, we still display, can happen with animation */ - return ((ob->restrictflag & OB_RESTRICT_VIEWPORT) && !(ob->mode & OB_MODE_EDIT)); + return ((ob->visibility_flag & OB_HIDE_VIEWPORT) && !(ob->mode & OB_MODE_EDIT)); } bool ED_operator_object_active(bContext *C) diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c index 7194e78e940..23dd290e13f 100644 --- a/source/blender/editors/space_clip/clip_utils.c +++ b/source/blender/editors/space_clip/clip_utils.c @@ -492,7 +492,7 @@ static bool mask_has_selection(const bContext *C) } LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) { diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index 6538f5709b7..1ec1afe86fc 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -1263,22 +1263,22 @@ static bool collection_flag_poll(bContext *C, bool clear, int flag) static bool collection_enable_poll(bContext *C) { - return collection_flag_poll(C, true, COLLECTION_RESTRICT_VIEWPORT); + return collection_flag_poll(C, true, COLLECTION_HIDE_VIEWPORT); } static bool collection_disable_poll(bContext *C) { - return collection_flag_poll(C, false, COLLECTION_RESTRICT_VIEWPORT); + return collection_flag_poll(C, false, COLLECTION_HIDE_VIEWPORT); } static bool collection_enable_render_poll(bContext *C) { - return collection_flag_poll(C, true, COLLECTION_RESTRICT_RENDER); + return collection_flag_poll(C, true, COLLECTION_HIDE_RENDER); } static bool collection_disable_render_poll(bContext *C) { - return collection_flag_poll(C, false, COLLECTION_RESTRICT_RENDER); + return collection_flag_poll(C, false, COLLECTION_HIDE_RENDER); } static int collection_flag_exec(bContext *C, wmOperator *op) @@ -1288,7 +1288,7 @@ static int collection_flag_exec(bContext *C, wmOperator *op) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); const bool is_render = strstr(op->idname, "render"); const bool clear = strstr(op->idname, "show") || strstr(op->idname, "enable"); - int flag = is_render ? COLLECTION_RESTRICT_RENDER : COLLECTION_RESTRICT_VIEWPORT; + int flag = is_render ? COLLECTION_HIDE_RENDER : COLLECTION_HIDE_VIEWPORT; struct CollectionEditData data = { .scene = scene, .space_outliner = space_outliner, diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c index 54df8270702..1a25cfd1efb 100644 --- a/source/blender/editors/transform/transform_convert_mask.c +++ b/source/blender/editors/transform/transform_convert_mask.c @@ -293,7 +293,7 @@ void createTransMaskingData(bContext *C, TransInfo *t) for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { MaskSpline *spline; - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (masklay->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -351,7 +351,7 @@ void createTransMaskingData(bContext *C, TransInfo *t) for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { MaskSpline *spline; - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (masklay->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 8762ca1f384..f02b73e8430 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1968,8 +1968,8 @@ static int lineart_usage_check(Collection *c, Object *ob, bool is_render) if (c->gobject.first) { if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) { - if ((is_render && (c->flag & COLLECTION_RESTRICT_RENDER)) || - ((!is_render) && (c->flag & COLLECTION_RESTRICT_VIEWPORT))) { + if ((is_render && (c->flag & COLLECTION_HIDE_RENDER)) || + ((!is_render) && (c->flag & COLLECTION_HIDE_VIEWPORT))) { return OBJECT_LRT_EXCLUDE; } if (ob->lineart.usage == OBJECT_LRT_INHERIT) { diff --git a/source/blender/makesdna/DNA_collection_types.h b/source/blender/makesdna/DNA_collection_types.h index 1defa8b782b..3eba02de2a3 100644 --- a/source/blender/makesdna/DNA_collection_types.h +++ b/source/blender/makesdna/DNA_collection_types.h @@ -102,10 +102,10 @@ typedef struct Collection { /* Collection->flag */ enum { - COLLECTION_RESTRICT_VIEWPORT = (1 << 0), /* Disable in viewports. */ - COLLECTION_RESTRICT_SELECT = (1 << 1), /* Not selectable in viewport. */ + COLLECTION_HIDE_VIEWPORT = (1 << 0), /* Disable in viewports. */ + COLLECTION_HIDE_SELECT = (1 << 1), /* Not selectable in viewport. */ /* COLLECTION_DISABLED_DEPRECATED = (1 << 2), */ /* Not used anymore */ - COLLECTION_RESTRICT_RENDER = (1 << 3), /* Disable in renders. */ + COLLECTION_HIDE_RENDER = (1 << 3), /* Disable in renders. */ COLLECTION_HAS_OBJECT_CACHE = (1 << 4), /* Runtime: object_cache is populated. */ COLLECTION_IS_MASTER = (1 << 5), /* Is master collection embedded in the scene. */ COLLECTION_HAS_OBJECT_CACHE_INSTANCED = (1 << 6), /* for object_cache_instanced. */ diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index 828c6ff2a51..63e4597150c 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -222,7 +222,7 @@ enum { enum { LAYER_COLLECTION_HAS_OBJECTS = (1 << 0), /* LAYER_COLLECTION_VISIBLE_DEPSGRAPH = (1 << 1), */ /* UNUSED */ - LAYER_COLLECTION_RESTRICT_VIEWPORT = (1 << 2), + LAYER_COLLECTION_HIDE_VIEWPORT = (1 << 2), LAYER_COLLECTION_VISIBLE_VIEW_LAYER = (1 << 4), }; diff --git a/source/blender/makesdna/DNA_mask_types.h b/source/blender/makesdna/DNA_mask_types.h index e6a7c004078..419118a38f4 100644 --- a/source/blender/makesdna/DNA_mask_types.h +++ b/source/blender/makesdna/DNA_mask_types.h @@ -174,7 +174,7 @@ typedef struct MaskLayer { /** For animation. */ char flag; /** Matching 'Object' flag of the same name - eventually use in the outliner. */ - char restrictflag; + char visibility_flag; } MaskLayer; /* MaskParent->flag */ @@ -206,10 +206,10 @@ enum { MASK_SPLINE_OFFSET_SMOOTH = 1, }; -/* ob->restrictflag */ -#define MASK_RESTRICT_VIEW (1 << 0) -#define MASK_RESTRICT_SELECT (1 << 1) -#define MASK_RESTRICT_RENDER (1 << 2) +/* MaskLayer->visibility_flag */ +#define MASK_HIDE_VIEW (1 << 0) +#define MASK_HIDE_SELECT (1 << 1) +#define MASK_HIDE_RENDER (1 << 2) /* SpaceClip->mask_draw_flag */ #define MASK_DRAWFLAG_SMOOTH (1 << 0) diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 60a34fef899..a37536f7022 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -385,7 +385,7 @@ typedef struct Object { short softflag; /** For restricting view, select, render etc. accessible in outliner. */ - char restrictflag; + char visibility_flag; /** Flag for pinning. */ char shapeflag; @@ -670,11 +670,11 @@ enum { # define OB_FLAG_UNUSED_12 (1 << 12) /* cleared */ #endif -/* ob->restrictflag */ +/* ob->visibility_flag */ enum { - OB_RESTRICT_VIEWPORT = 1 << 0, - OB_RESTRICT_SELECT = 1 << 1, - OB_RESTRICT_RENDER = 1 << 2, + OB_HIDE_VIEWPORT = 1 << 0, + OB_HIDE_SELECT = 1 << 1, + OB_HIDE_RENDER = 1 << 2, }; /* ob->shapeflag */ diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index d363e40e4f0..2feebbfd4f4 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -82,11 +82,13 @@ DNA_STRUCT_RENAME_ELEM(FluidDomainSettings, guiding_vel_factor, guide_vel_factor DNA_STRUCT_RENAME_ELEM(FluidEffectorSettings, guiding_mode, guide_mode) DNA_STRUCT_RENAME_ELEM(Image, name, filepath) DNA_STRUCT_RENAME_ELEM(Library, name, filepath) +DNA_STRUCT_RENAME_ELEM(MaskLayer, restrictflag, visibility_flag) DNA_STRUCT_RENAME_ELEM(MovieClip, name, filepath) DNA_STRUCT_RENAME_ELEM(Object, col, color) DNA_STRUCT_RENAME_ELEM(Object, dup_group, instance_collection) DNA_STRUCT_RENAME_ELEM(Object, dupfacesca, instance_faces_scale) DNA_STRUCT_RENAME_ELEM(Object, size, scale) +DNA_STRUCT_RENAME_ELEM(Object, restrictflag, visibility_flag) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_group, instance_collection) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_ob, instance_object) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dupliweights, instance_weights) diff --git a/source/blender/makesrna/intern/rna_collection.c b/source/blender/makesrna/intern/rna_collection.c index 608a8e51b09..77276f47689 100644 --- a/source/blender/makesrna/intern/rna_collection.c +++ b/source/blender/makesrna/intern/rna_collection.c @@ -322,17 +322,17 @@ static void rna_Collection_flag_set(PointerRNA *ptr, const bool value, const int static void rna_Collection_hide_select_set(PointerRNA *ptr, bool value) { - rna_Collection_flag_set(ptr, value, COLLECTION_RESTRICT_SELECT); + rna_Collection_flag_set(ptr, value, COLLECTION_HIDE_SELECT); } static void rna_Collection_hide_viewport_set(PointerRNA *ptr, bool value) { - rna_Collection_flag_set(ptr, value, COLLECTION_RESTRICT_VIEWPORT); + rna_Collection_flag_set(ptr, value, COLLECTION_HIDE_VIEWPORT); } static void rna_Collection_hide_render_set(PointerRNA *ptr, bool value) { - rna_Collection_flag_set(ptr, value, COLLECTION_RESTRICT_RENDER); + rna_Collection_flag_set(ptr, value, COLLECTION_HIDE_RENDER); } static void rna_Collection_flag_update(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -496,7 +496,7 @@ void RNA_def_collections(BlenderRNA *brna) /* Flags */ prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_RESTRICT_SELECT); + RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_HIDE_SELECT); RNA_def_property_boolean_funcs(prop, NULL, "rna_Collection_hide_select_set"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1); @@ -504,7 +504,7 @@ void RNA_def_collections(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_Collection_flag_update"); prop = RNA_def_property(srna, "hide_viewport", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_RESTRICT_VIEWPORT); + RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_HIDE_VIEWPORT); RNA_def_property_boolean_funcs(prop, NULL, "rna_Collection_hide_viewport_set"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1); @@ -512,7 +512,7 @@ void RNA_def_collections(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_Collection_flag_update"); prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_RESTRICT_RENDER); + RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_HIDE_RENDER); RNA_def_property_boolean_funcs(prop, NULL, "rna_Collection_hide_render_set"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1); diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c index 0414afe1514..ab4cbc429ce 100644 --- a/source/blender/makesrna/intern/rna_layer.c +++ b/source/blender/makesrna/intern/rna_layer.c @@ -134,7 +134,7 @@ static bool rna_LayerCollection_visible_get(LayerCollection *layer_collection, b } if (v3d->local_collections_uuid & layer_collection->local_collections_bits) { - return (layer_collection->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) == 0; + return (layer_collection->runtime_flag & LAYER_COLLECTION_HIDE_VIEWPORT) == 0; } return false; diff --git a/source/blender/makesrna/intern/rna_mask.c b/source/blender/makesrna/intern/rna_mask.c index 8c7d9698a67..9c90c209389 100644 --- a/source/blender/makesrna/intern/rna_mask.c +++ b/source/blender/makesrna/intern/rna_mask.c @@ -991,19 +991,19 @@ static void rna_def_mask_layer(BlenderRNA *brna) /* restrict */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", MASK_RESTRICT_VIEW); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", MASK_HIDE_VIEW); RNA_def_property_ui_text(prop, "Restrict View", "Restrict visibility in the viewport"); RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1); RNA_def_property_update(prop, NC_MASK | ND_DRAW, NULL); prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", MASK_RESTRICT_SELECT); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", MASK_HIDE_SELECT); RNA_def_property_ui_text(prop, "Restrict Select", "Restrict selection in the viewport"); RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1); RNA_def_property_update(prop, NC_MASK | ND_DRAW, NULL); prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", MASK_RESTRICT_RENDER); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", MASK_HIDE_RENDER); RNA_def_property_ui_text(prop, "Restrict Render", "Restrict renderability"); RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1); RNA_def_property_update(prop, NC_MASK | NA_EDITED, NULL); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index ed681291e29..f0b83f3eb46 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -2927,6 +2927,44 @@ static void rna_def_object_lineart(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_object_lineart_update"); } +static void rna_def_object_visibility(StructRNA *srna) +{ + PropertyRNA *prop; + + /* Hide options. */ + prop = RNA_def_property(srna, "hide_viewport", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_HIDE_VIEWPORT); + RNA_def_property_ui_text(prop, "Disable in Viewports", "Globally disable in viewports"); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); + + prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_HIDE_SELECT); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Disable Selection", "Disable selection in viewport"); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); + + prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_HIDE_RENDER); + RNA_def_property_ui_text(prop, "Disable in Renders", "Globally disable in renders"); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); + + /* Instancer options. */ + prop = RNA_def_property(srna, "show_instancer_for_render", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "duplicator_visibility_flag", OB_DUPLI_FLAG_RENDER); + RNA_def_property_ui_text(prop, "Render Instancer", "Make instancer visible when rendering"); + RNA_def_property_update( + prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update"); + + prop = RNA_def_property(srna, "show_instancer_for_viewport", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "duplicator_visibility_flag", OB_DUPLI_FLAG_VIEWPORT); + RNA_def_property_ui_text(prop, "Display Instancer", "Make instancer visible in the viewport"); + RNA_def_property_update( + prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update"); +} + static void rna_def_object(BlenderRNA *brna) { StructRNA *srna; @@ -3506,37 +3544,7 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_struct_type(prop, "RigidBodyConstraint"); RNA_def_property_ui_text(prop, "Rigid Body Constraint", "Constraint constraining rigid bodies"); - /* restrict */ - prop = RNA_def_property(srna, "hide_viewport", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", OB_RESTRICT_VIEWPORT); - RNA_def_property_ui_text(prop, "Disable in Viewports", "Globally disable in viewports"); - RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); - - prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", OB_RESTRICT_SELECT); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Disable Selection", "Disable selection in viewport"); - RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); - - prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", OB_RESTRICT_RENDER); - RNA_def_property_ui_text(prop, "Disable in Renders", "Globally disable in renders"); - RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); - - prop = RNA_def_property(srna, "show_instancer_for_render", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "duplicator_visibility_flag", OB_DUPLI_FLAG_RENDER); - RNA_def_property_ui_text(prop, "Render Instancer", "Make instancer visible when rendering"); - RNA_def_property_update( - prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update"); - - prop = RNA_def_property(srna, "show_instancer_for_viewport", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "duplicator_visibility_flag", OB_DUPLI_FLAG_VIEWPORT); - RNA_def_property_ui_text(prop, "Display Instancer", "Make instancer visible in the viewport"); - RNA_def_property_update( - prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update"); + rna_def_object_visibility(srna); /* instancing */ prop = RNA_def_property(srna, "instance_type", PROP_ENUM, PROP_NONE); -- cgit v1.2.3 From 49acc52e02ec9e0577d3fa1f298720139d9f751b Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 4 Aug 2021 20:49:50 +0200 Subject: Cycles: make object Fast GI Approximation panel a subpanel of Shading --- intern/cycles/blender/addon/ui.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 336fb02ba36..e804f697571 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1228,10 +1228,10 @@ class CYCLES_OBJECT_PT_shading_shadow_terminator(CyclesButtonsPanel, Panel): flow.prop(cob, "shadow_terminator_offset", text="Shading Offset") -class CYCLES_OBJECT_PT_gi_approximation(CyclesButtonsPanel, Panel): +class CYCLES_OBJECT_PT_shading_gi_approximation(CyclesButtonsPanel, Panel): bl_label = "Fast GI Approximation" + bl_parent_id = "CYCLES_OBJECT_PT_shading" bl_context = "object" - bl_options = {'DEFAULT_CLOSED'} def draw(self, context): layout = self.layout @@ -2325,7 +2325,7 @@ classes = ( CYCLES_OBJECT_PT_motion_blur, CYCLES_OBJECT_PT_shading, CYCLES_OBJECT_PT_shading_shadow_terminator, - CYCLES_OBJECT_PT_gi_approximation, + CYCLES_OBJECT_PT_shading_gi_approximation, CYCLES_OBJECT_PT_visibility, CYCLES_OBJECT_PT_visibility_ray_visibility, CYCLES_OBJECT_PT_visibility_culling, -- cgit v1.2.3 From 1d1020b79ff3ac9cc23921aa2e7ba1ef16c930ce Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Wed, 4 Aug 2021 13:19:16 -0700 Subject: Win32 IME: Remove ime_status_ This removes one member of GHOST_ImeWin32 that is not used and cannot be used in the future. It is holding the result of ImmIsIME, which is whether an input language supports IME. It does not indicate that one is in use, turned on, composing, in English mode, etc. see D12131 for more information. Differential Revision: https://developer.blender.org/D12131 Reviewed by Ray Molenkamp --- intern/ghost/intern/GHOST_ImeWin32.cpp | 10 ++-------- intern/ghost/intern/GHOST_ImeWin32.h | 16 +--------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/intern/ghost/intern/GHOST_ImeWin32.cpp b/intern/ghost/intern/GHOST_ImeWin32.cpp index 8daa07b5003..f6baa601db9 100644 --- a/intern/ghost/intern/GHOST_ImeWin32.cpp +++ b/intern/ghost/intern/GHOST_ImeWin32.cpp @@ -32,7 +32,6 @@ GHOST_ImeWin32::GHOST_ImeWin32() : is_composing_(false), - ime_status_(false), input_language_id_(LANG_USER_DEFAULT), conversion_modes_(IME_CMODE_ALPHANUMERIC), sentence_mode_(IME_SMODE_NONE), @@ -47,18 +46,13 @@ GHOST_ImeWin32::~GHOST_ImeWin32() { } -bool GHOST_ImeWin32::SetInputLanguage() +void GHOST_ImeWin32::SetInputLanguage() { /** - * Retrieve the current keyboard layout from Windows and determine whether - * or not the current input context has IMEs. - * Also save its input language for language-specific operations required - * while composing a text. + * Retrieve the current input language. */ HKL keyboard_layout = ::GetKeyboardLayout(0); input_language_id_ = LOWORD(keyboard_layout); - ime_status_ = ::ImmIsIME(keyboard_layout); - return ime_status_; } WORD GHOST_ImeWin32::GetInputLanguage() diff --git a/intern/ghost/intern/GHOST_ImeWin32.h b/intern/ghost/intern/GHOST_ImeWin32.h index bcc4b6eef7c..7ccb0cdfd06 100644 --- a/intern/ghost/intern/GHOST_ImeWin32.h +++ b/intern/ghost/intern/GHOST_ImeWin32.h @@ -148,13 +148,8 @@ class GHOST_ImeWin32 { /** * Retrieves the input language from Windows and update it. - * Return values - * * true - * The given input language has IMEs. - * * false - * The given input language does not have IMEs. */ - bool SetInputLanguage(); + void SetInputLanguage(); /* Returns the current input language id. */ WORD GetInputLanguage(); @@ -350,15 +345,6 @@ class GHOST_ImeWin32 { */ bool is_composing_; - /** - * This value represents whether or not the current input context has IMEs. - * The following table shows the list of IME status: - * Value Description - * false The current input language does not have IMEs. - * true The current input language has IMEs. - */ - bool ime_status_; - /** * The current input Language ID retrieved from Windows, which consists of: * * Primary Language ID (bit 0 to bit 9), which shows a natural language -- cgit v1.2.3 From 07b702f82846322df4b0b6065db90d7e3ebf3eb1 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Wed, 4 Aug 2021 14:30:16 -0700 Subject: Win32 IME: Rename SetInputLanguage() GHOST_ImeWin32::SetInputLanguage() has a confusing name because it does not set the input language. It actually retrieves the current input locale from the OS and caches the value of the current input language ID. Therefore this patch renames it to "UpdateInputLanguage" Differential Revision: https://developer.blender.org/D12134 Reviewed by Ray Molenkamp --- intern/ghost/intern/GHOST_ImeWin32.cpp | 8 ++++---- intern/ghost/intern/GHOST_ImeWin32.h | 4 ++-- intern/ghost/intern/GHOST_SystemWin32.cpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/intern/ghost/intern/GHOST_ImeWin32.cpp b/intern/ghost/intern/GHOST_ImeWin32.cpp index f6baa601db9..343f4d68078 100644 --- a/intern/ghost/intern/GHOST_ImeWin32.cpp +++ b/intern/ghost/intern/GHOST_ImeWin32.cpp @@ -46,13 +46,13 @@ GHOST_ImeWin32::~GHOST_ImeWin32() { } -void GHOST_ImeWin32::SetInputLanguage() +void GHOST_ImeWin32::UpdateInputLanguage() { /** - * Retrieve the current input language. + * Store the current input language. */ - HKL keyboard_layout = ::GetKeyboardLayout(0); - input_language_id_ = LOWORD(keyboard_layout); + HKL input_locale = ::GetKeyboardLayout(0); + input_language_id_ = LOWORD(input_locale); } WORD GHOST_ImeWin32::GetInputLanguage() diff --git a/intern/ghost/intern/GHOST_ImeWin32.h b/intern/ghost/intern/GHOST_ImeWin32.h index 7ccb0cdfd06..d430a7d745d 100644 --- a/intern/ghost/intern/GHOST_ImeWin32.h +++ b/intern/ghost/intern/GHOST_ImeWin32.h @@ -88,7 +88,7 @@ class GHOST_EventIME : public GHOST_Event { * An application CAN call ::DefWindowProc(). * 2.5. WM_INPUTLANGCHANGE (0x0051) * Call the functions listed below: - * - GHOST_ImeWin32::SetInputLanguage(). + * - GHOST_ImeWin32::UpdateInputLanguage(). * An application CAN call ::DefWindowProc(). */ @@ -149,7 +149,7 @@ class GHOST_ImeWin32 { /** * Retrieves the input language from Windows and update it. */ - void SetInputLanguage(); + void UpdateInputLanguage(); /* Returns the current input language id. */ WORD GetInputLanguage(); diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 60fd175dbf7..347067eae50 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1424,7 +1424,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, case WM_INPUTLANGCHANGE: { system->handleKeyboardChange(); #ifdef WITH_INPUT_IME - window->getImeInput()->SetInputLanguage(); + window->getImeInput()->UpdateInputLanguage(); window->getImeInput()->UpdateConversionStatus(hwnd); #endif break; @@ -1471,7 +1471,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } case WM_IME_SETCONTEXT: { GHOST_ImeWin32 *ime = window->getImeInput(); - ime->SetInputLanguage(); + ime->UpdateInputLanguage(); ime->CreateImeWindow(hwnd); ime->CleanupComposition(hwnd); ime->CheckFirst(hwnd); -- cgit v1.2.3 From d6ca7ab20ed6f4f1df29cfc55b88beaecdec4525 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 4 Aug 2021 19:24:19 -0400 Subject: Cleanup: make format --- .../engines/overlay/shaders/edit_mesh_normal_vert.glsl | 3 +-- source/blender/editors/include/ED_keyframes_keylist.h | 4 +++- source/blender/python/gpu/gpu_py_offscreen.c | 15 +++++++++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl index f649a5cb3ed..d370943db03 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl @@ -56,8 +56,7 @@ void main() if (gl_VertexID == 0) { if (isConstantScreenSizeNormals) { bool is_persp = (ProjectionMatrix[3][3] == 0.0); - if (is_persp) - { + if (is_persp) { float dist_fac = length(cameraPos - world_pos); float cos_fac = dot(cameraForward, cameraVec(world_pos)); world_pos += n * normalScreenSize * dist_fac * cos_fac * pixelFac * sizePixel; diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h index e7d1b5d4363..d3690fa3aa0 100644 --- a/source/blender/editors/include/ED_keyframes_keylist.h +++ b/source/blender/editors/include/ED_keyframes_keylist.h @@ -190,7 +190,9 @@ void gpencil_to_keylist(struct bDopeSheet *ads, /* Grease Pencil Layer */ void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct AnimKeylist *keylist); /* Mask */ -void mask_to_keylist(struct bDopeSheet *ads, struct MaskLayer *masklay, struct AnimKeylist *keylist); +void mask_to_keylist(struct bDopeSheet *ads, + struct MaskLayer *masklay, + struct AnimKeylist *keylist); /* ActKeyColumn API ---------------- */ /* Comparator callback used for ActKeyColumns and cframe float-value pointer */ diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index 02f72f20ac4..32053df5e97 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -279,7 +279,8 @@ static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void * PyDoc_STRVAR( pygpu_offscreen_draw_view3d_doc, - ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix, do_color_management=False)\n" + ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix, " + "do_color_management=False)\n" "\n" " Draw the 3d viewport in the offscreen object.\n" "\n" @@ -312,8 +313,14 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar BPY_GPU_OFFSCREEN_CHECK_OBJ(self); - static const char *_keywords[] = { - "scene", "view_layer", "view3d", "region", "view_matrix", "projection_matrix", "do_color_management", NULL}; + static const char *_keywords[] = {"scene", + "view_layer", + "view3d", + "region", + "view_matrix", + "projection_matrix", + "do_color_management", + NULL}; static _PyArg_Parser _parser = {"OOOOO&O&|$O&:draw_view3d", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, @@ -327,7 +334,7 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar &py_mat_view, Matrix_Parse4x4, &py_mat_projection, - PyC_ParseBool, + PyC_ParseBool, &do_color_management) || (!(scene = PyC_RNA_AsPointer(py_scene, "Scene")) || !(view_layer = PyC_RNA_AsPointer(py_view_layer, "ViewLayer")) || -- cgit v1.2.3 From 171433e841379e7efad069bbda9880fb271e2fc4 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 4 Aug 2021 19:26:01 -0400 Subject: PyDoc: Improve description of texture.evaluate Inspired by the old 2.49 docs: https://docs.blender.org/api/249PythonDoc/Texture.Texture-class.html#evaluate --- source/blender/makesrna/intern/rna_texture_api.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/source/blender/makesrna/intern/rna_texture_api.c b/source/blender/makesrna/intern/rna_texture_api.c index 42b3e4420c1..c46b9acf986 100644 --- a/source/blender/makesrna/intern/rna_texture_api.c +++ b/source/blender/makesrna/intern/rna_texture_api.c @@ -61,12 +61,30 @@ void RNA_api_texture(StructRNA *srna) func = RNA_def_function(srna, "evaluate", "texture_evaluate"); RNA_def_function_ui_description(func, "Evaluate the texture at the coordinates given"); - parm = RNA_def_float_vector(func, "value", 3, NULL, -FLT_MAX, FLT_MAX, "", "", -1e4, 1e4); + parm = RNA_def_float_vector(func, + "value", + 3, + NULL, + -FLT_MAX, + FLT_MAX, + "The object coordinates (x,y,z) used to generate/map the texture", + "", + -1e4, + 1e4); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - /* return location and normal */ parm = RNA_def_float_vector( - func, "result", 4, NULL, -FLT_MAX, FLT_MAX, "Result", NULL, -1e4, 1e4); + func, + "result", + 4, + NULL, + -FLT_MAX, + FLT_MAX, + "The result of the texture where (x,y,z,w) are (red, green, blue, intensity). For greyscale " + "textures, often intensity only will be used", + NULL, + -1e4, + 1e4); RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0); RNA_def_function_output(func, parm); } -- cgit v1.2.3 From 438d645a365045b4d89d59af59e17584f0593dc4 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 4 Aug 2021 20:25:50 -0400 Subject: PyDoc: Update GPU Example of `draw_view3d` This function was changed in rBc8004ab4078c98c54a70113c12bbb186403e90cf but didnt update the example. Part of T84227 --- doc/python_api/examples/gpu.9.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/python_api/examples/gpu.9.py b/doc/python_api/examples/gpu.9.py index e358cb517bd..b0400ce7809 100644 --- a/doc/python_api/examples/gpu.9.py +++ b/doc/python_api/examples/gpu.9.py @@ -31,7 +31,8 @@ def draw(): context.space_data, context.region, view_matrix, - projection_matrix) + projection_matrix, + True) gpu.state.depth_mask_set(False) draw_texture_2d(offscreen.texture_color, (10, 10), WIDTH, HEIGHT) -- cgit v1.2.3 From 1def985d78db113ea4426311692941e44f0467eb Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Wed, 4 Aug 2021 19:32:24 -0600 Subject: Windows: Add icons and icons_geom to make.bat This adds support for building the icons from make.bat unlike bash there is no passing environment variables on the command line. The scripts go out of their way to locate both blender and inkscape however if they are not found, the user is given a helpful error message telling them how to set the variables. Although some extra help can be given there, if your normal build is a 2019 full build running `make 2019 full icons` will help it find the blender executable as well. finally if you know the name of your build folder running `make builddir build_windows_Lite_x64_vc16_Release icons` will also work, if all fails you can point directly to the blender executable by running `set BLENDER_BIN=c:\where\blender\lives\blender.exe` before running `make icons` or `make icons_geom` The python scripts needed some small modifications since without the PATHEXT, SystemRoot and SystemDrive environment variables python will not initialize properly on windows. (Not blender related, even mainline python won't start without those) --- build_files/windows/find_blender.cmd | 28 +++++++++++++++++ build_files/windows/find_inkscape.cmd | 21 +++++++++++++ build_files/windows/icons.cmd | 42 ++++++++++++++++++++++++++ build_files/windows/icons_geom.cmd | 29 ++++++++++++++++++ build_files/windows/parse_arguments.cmd | 6 ++++ build_files/windows/reset_variables.cmd | 2 ++ make.bat | 10 ++++++ release/datafiles/blender_icons_geom_update.py | 5 +++ release/datafiles/blender_icons_update.py | 6 ++++ 9 files changed, 149 insertions(+) create mode 100644 build_files/windows/find_blender.cmd create mode 100644 build_files/windows/find_inkscape.cmd create mode 100644 build_files/windows/icons.cmd create mode 100644 build_files/windows/icons_geom.cmd diff --git a/build_files/windows/find_blender.cmd b/build_files/windows/find_blender.cmd new file mode 100644 index 00000000000..9adbfd35ae9 --- /dev/null +++ b/build_files/windows/find_blender.cmd @@ -0,0 +1,28 @@ +REM First see if there is an environment variable set +if EXIST "%BLENDER_BIN%" ( + goto detect_blender_done +) + +REM Check the build folder next, if ninja was used there will be no +REM debug/release folder +set BLENDER_BIN=%BUILD_DIR%\bin\blender.exe +if EXIST "%BLENDER_BIN%" ( + goto detect_blender_done +) + +REM Check the release folder next +set BLENDER_BIN=%BUILD_DIR%\bin\release\blender.exe +if EXIST "%BLENDER_BIN%" ( + goto detect_blender_done +) + +REM Check the debug folder next +set BLENDER_BIN=%BUILD_DIR%\bin\debug\blender.exe +if EXIST "%BLENDER_BIN%" ( + goto detect_blender_done +) + +REM at this point, we don't know where blender is, clear the variable +set BLENDER_BIN= + +:detect_blender_done diff --git a/build_files/windows/find_inkscape.cmd b/build_files/windows/find_inkscape.cmd new file mode 100644 index 00000000000..2fa27f70d47 --- /dev/null +++ b/build_files/windows/find_inkscape.cmd @@ -0,0 +1,21 @@ +REM First see if there is an environment variable set +if EXIST "%INKSCAPE_BIN%" ( + goto detect_inkscape_done +) + +REM Then see if inkscape is available in the path +for %%X in (inkscape.exe) do (set INKSCAPE_BIN=%%~$PATH:X) +if EXIST "%INKSCAPE_BIN%" ( + goto detect_inkscape_done +) + +REM Finally see if it is perhaps installed at the default location +set INKSCAPE_BIN=%ProgramFiles%\Inkscape\bin\inkscape.exe +if EXIST "%INKSCAPE_BIN%" ( + goto detect_inkscape_done +) + +REM If still not found clear the variable +set INKSCAPE_BIN= + +:detect_inkscape_done diff --git a/build_files/windows/icons.cmd b/build_files/windows/icons.cmd new file mode 100644 index 00000000000..473a40885a8 --- /dev/null +++ b/build_files/windows/icons.cmd @@ -0,0 +1,42 @@ +if EXIST %PYTHON% ( + goto detect_python_done +) + +set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe +if EXIST %PYTHON% ( + goto detect_python_done +) + +echo python not found at %PYTHON% +exit /b 1 + +:detect_python_done +echo found python (%PYTHON%) + +call "%~dp0\find_inkscape.cmd" + +if EXIST "%INKSCAPE_BIN%" ( + goto detect_inkscape_done +) + +echo unable to locate inkscape, run "set inkscape_BIN=full_path_to_inkscape.exe" +exit /b 1 + +:detect_inkscape_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 + +%PYTHON% -B %BLENDER_DIR%\release\datafiles\blender_icons_update.py +%PYTHON% -B %BLENDER_DIR%\release\datafiles\prvicons_update.py +%PYTHON% -B %BLENDER_DIR%\release\datafiles\alert_icons_update.py + +:EOF diff --git a/build_files/windows/icons_geom.cmd b/build_files/windows/icons_geom.cmd new file mode 100644 index 00000000000..18312daf35b --- /dev/null +++ b/build_files/windows/icons_geom.cmd @@ -0,0 +1,29 @@ +if EXIST %PYTHON% ( + goto detect_python_done +) + +set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe +if EXIST %PYTHON% ( + goto detect_python_done +) + +echo python not found at %PYTHON% +exit /b 1 + +:detect_python_done +echo found python (%PYTHON%) + +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 + +%PYTHON% -B %BLENDER_DIR%\release\datafiles\blender_icons_geom_update.py + +:EOF diff --git a/build_files/windows/parse_arguments.cmd b/build_files/windows/parse_arguments.cmd index 000b98c992e..c71093f2394 100644 --- a/build_files/windows/parse_arguments.cmd +++ b/build_files/windows/parse_arguments.cmd @@ -107,6 +107,12 @@ if NOT "%1" == "" ( set FORMAT=1 set FORMAT_ARGS=%2 %3 %4 %5 %6 %7 %8 %9 goto EOF + ) else if "%1" == "icons" ( + set ICONS=1 + goto EOF + ) else if "%1" == "icons_geom" ( + set ICONS_GEOM=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 262874713eb..590d4ca246a 100644 --- a/build_files/windows/reset_variables.cmd +++ b/build_files/windows/reset_variables.cmd @@ -31,3 +31,5 @@ set PYDEBUG_CMAKE_ARGS= set FORMAT= set TEST= set BUILD_WITH_SCCACHE= +set ICONS= +set ICONS_GEOM= \ No newline at end of file diff --git a/make.bat b/make.bat index ea80bd591f7..75d424202ae 100644 --- a/make.bat +++ b/make.bat @@ -58,6 +58,16 @@ if "%BUILD_UPDATE%" == "1" ( call "%BLENDER_DIR%\build_files\windows\set_build_dir.cmd" +if "%ICONS%" == "1" ( + call "%BLENDER_DIR%\build_files\windows\icons.cmd" + goto EOF +) + +if "%ICONS_GEOM%" == "1" ( + call "%BLENDER_DIR%\build_files\windows\icons_geom.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" diff --git a/release/datafiles/blender_icons_geom_update.py b/release/datafiles/blender_icons_geom_update.py index df4683809db..257b7a49285 100755 --- a/release/datafiles/blender_icons_geom_update.py +++ b/release/datafiles/blender_icons_geom_update.py @@ -96,6 +96,11 @@ for blend in icons_blend: env = {} # Developers may have ASAN enabled, avoid non-zero exit codes. env["ASAN_OPTIONS"] = "exitcode=0:" + os.environ.get("ASAN_OPTIONS", "") + # These NEED to be set on windows for python to initialize properly. + if sys.platform[:3] == "win": + env["PATHEXT"] = os.environ.get("PATHEXT", "") + env["SystemDrive"] = os.environ.get("SystemDrive", "") + env["SystemRoot"] = os.environ.get("SystemRoot", "") run(cmd, env=env) files_new = set(names_and_time_from_path(output_dir)) diff --git a/release/datafiles/blender_icons_update.py b/release/datafiles/blender_icons_update.py index dab3cd76a87..524cc512f29 100755 --- a/release/datafiles/blender_icons_update.py +++ b/release/datafiles/blender_icons_update.py @@ -37,6 +37,12 @@ env = {} # Developers may have ASAN enabled, avoid non-zero exit codes. env["ASAN_OPTIONS"] = "exitcode=0:" + os.environ.get("ASAN_OPTIONS", "") +# These NEED to be set on windows for python to initialize properly. +if sys.platform[:3] == "win": + env["PATHEXT"] = os.environ.get("PATHEXT", "") + env["SystemDrive"] = os.environ.get("SystemDrive", "") + env["SystemRoot"] = os.environ.get("SystemRoot", "") + inkscape_bin = os.environ.get("INKSCAPE_BIN", "inkscape") blender_bin = os.environ.get("BLENDER_BIN", "blender") -- cgit v1.2.3 From 604ae5f7b628e5ce78bd0313ec119634eafa1048 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 5 Aug 2021 12:02:49 +1000 Subject: Cleanup: tab indentation for CMake / GNUmakefile --- GNUmakefile | 32 ++++++++++++++----------- build_files/cmake/platform/platform_win32.cmake | 4 ++-- extern/CMakeLists.txt | 2 +- source/blender/compositor/CMakeLists.txt | 13 +++++----- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 635cc321d03..d620e5c4363 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -63,7 +63,7 @@ Package Targets * package_debian: Build a debian package. * package_pacman: Build an arch linux pacman package. - * package_archive: Build an archive package. + * package_archive: Build an archive package. Testing Targets Not associated with building Blender. @@ -167,7 +167,7 @@ endef # This makefile is not meant for Windows ifeq ($(OS),Windows_NT) - $(error On Windows, use "cmd //c make.bat" instead of "make") + $(error On Windows, use "cmd //c make.bat" instead of "make") endif # System Vars @@ -379,7 +379,7 @@ deps: .FORCE @cmake -H"$(DEPS_SOURCE_DIR)" \ -B"$(DEPS_BUILD_DIR)" \ - -DHARVEST_TARGET=$(DEPS_INSTALL_DIR) + -DHARVEST_TARGET=$(DEPS_INSTALL_DIR) @echo @echo Building dependencies ... @@ -456,7 +456,8 @@ project_eclipse: .FORCE check_cppcheck: .FORCE $(CMAKE_CONFIG) cd "$(BUILD_DIR)" ; \ - $(PYTHON) "$(BLENDER_DIR)/build_files/cmake/cmake_static_check_cppcheck.py" 2> \ + $(PYTHON) \ + "$(BLENDER_DIR)/build_files/cmake/cmake_static_check_cppcheck.py" 2> \ "$(BLENDER_DIR)/check_cppcheck.txt" @echo "written: check_cppcheck.txt" @@ -518,8 +519,9 @@ source_archive: .FORCE python3 ./build_files/utils/make_source_archive.py source_archive_complete: .FORCE - cmake -S "$(BLENDER_DIR)/build_files/build_environment" -B"$(BUILD_DIR)/source_archive" \ - -DCMAKE_BUILD_TYPE_INIT:STRING=$(BUILD_TYPE) -DPACKAGE_USE_UPSTREAM_SOURCES=OFF + cmake \ + -S "$(BLENDER_DIR)/build_files/build_environment" -B"$(BUILD_DIR)/source_archive" \ + -DCMAKE_BUILD_TYPE_INIT:STRING=$(BUILD_TYPE) -DPACKAGE_USE_UPSTREAM_SOURCES=OFF # This assumes CMake is still using a default `PACKAGE_DIR` variable: python3 ./build_files/utils/make_source_archive.py --include-packages "$(BUILD_DIR)/source_archive/packages" @@ -527,11 +529,11 @@ source_archive_complete: .FORCE INKSCAPE_BIN?="inkscape" icons: .FORCE BLENDER_BIN=$(BLENDER_BIN) INKSCAPE_BIN=$(INKSCAPE_BIN) \ - "$(BLENDER_DIR)/release/datafiles/blender_icons_update.py" + "$(BLENDER_DIR)/release/datafiles/blender_icons_update.py" INKSCAPE_BIN=$(INKSCAPE_BIN) \ - "$(BLENDER_DIR)/release/datafiles/prvicons_update.py" + "$(BLENDER_DIR)/release/datafiles/prvicons_update.py" INKSCAPE_BIN=$(INKSCAPE_BIN) \ - "$(BLENDER_DIR)/release/datafiles/alert_icons_update.py" + "$(BLENDER_DIR)/release/datafiles/alert_icons_update.py" icons_geom: .FORCE BLENDER_BIN=$(BLENDER_BIN) \ @@ -545,7 +547,7 @@ update_code: .FORCE format: .FORCE PATH="../lib/${OS_NCASE}_${CPU}/llvm/bin/:../lib/${OS_NCASE}_centos7_${CPU}/llvm/bin/:../lib/${OS_NCASE}/llvm/bin/:$(PATH)" \ - $(PYTHON) source/tools/utils_maintenance/clang_format_paths.py $(PATHS) + $(PYTHON) source/tools/utils_maintenance/clang_format_paths.py $(PATHS) # ----------------------------------------------------------------------------- @@ -555,8 +557,9 @@ format: .FORCE # Simple version of ./doc/python_api/sphinx_doc_gen.sh with no PDF generation. doc_py: .FORCE ASAN_OPTIONS=halt_on_error=0:${ASAN_OPTIONS} \ - $(BLENDER_BIN) --background -noaudio --factory-startup \ - --python doc/python_api/sphinx_doc_gen.py + $(BLENDER_BIN) \ + --background -noaudio --factory-startup \ + --python doc/python_api/sphinx_doc_gen.py sphinx-build -b html -j $(NPROCS) doc/python_api/sphinx-in doc/python_api/sphinx-out @echo "docs written into: '$(BLENDER_DIR)/doc/python_api/sphinx-out/index.html'" @@ -565,8 +568,9 @@ doc_doxy: .FORCE @echo "docs written into: '$(BLENDER_DIR)/doc/doxygen/html/index.html'" doc_dna: .FORCE - $(BLENDER_BIN) --background -noaudio --factory-startup \ - --python doc/blender_file_format/BlendFileDnaExporter_25.py + $(BLENDER_BIN) \ + --background -noaudio --factory-startup \ + --python doc/blender_file_format/BlendFileDnaExporter_25.py @echo "docs written into: '$(BLENDER_DIR)/doc/blender_file_format/dna.html'" doc_man: .FORCE diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index a0e91199c72..113c41c545b 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -217,8 +217,8 @@ else() endif() if(WITH_WINDOWS_PDB) - set(PDB_INFO_OVERRIDE_FLAGS "${SYMBOL_FORMAT_RELEASE}") - set(PDB_INFO_OVERRIDE_LINKER_FLAGS "/DEBUG /OPT:REF /OPT:ICF /INCREMENTAL:NO") + set(PDB_INFO_OVERRIDE_FLAGS "${SYMBOL_FORMAT_RELEASE}") + set(PDB_INFO_OVERRIDE_LINKER_FLAGS "/DEBUG /OPT:REF /OPT:ICF /INCREMENTAL:NO") endif() string(APPEND CMAKE_CXX_FLAGS_DEBUG " /MDd ${SYMBOL_FORMAT}") diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 824b2fb0e9c..7f7d91f0765 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -111,5 +111,5 @@ if(WITH_MOD_FLUID) endif() if (WITH_COMPOSITOR) - add_subdirectory(smaa_areatex) + add_subdirectory(smaa_areatex) endif() diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 000ba298c2d..ee287c65fe9 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -597,16 +597,16 @@ add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS) set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations) set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h) add_custom_command( - OUTPUT ${GENSRC} - COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR} - COMMAND "$" ${GENSRC} - DEPENDS smaa_areatex + OUTPUT ${GENSRC} + COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR} + COMMAND "$" ${GENSRC} + DEPENDS smaa_areatex ) add_custom_target(smaa_areatex_header - SOURCES ${GENSRC} + SOURCES ${GENSRC} ) list(APPEND SRC - ${GENSRC} + ${GENSRC} ) unset(GENSRC) unset(GENSRC_DIR) @@ -650,4 +650,3 @@ if(WITH_GTESTS) include(GTestTesting) blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") endif() - -- cgit v1.2.3 From d1c5e2e050a57216f9555098313d65511455e042 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 5 Aug 2021 12:02:50 +1000 Subject: Cleanup: license headers These were removed globally in 65ec7ec524e667ec95ce947a95f6273088dffee6. Some files re-introduced these conventions since. --- source/blender/blenlib/BLI_enumerable_thread_specific.hh | 2 +- source/blender/gpencil_modifiers/intern/MOD_gpencillength.c | 7 +------ source/blender/io/usd/intern/usd_reader_stage.cc | 2 +- source/blender/io/usd/intern/usd_reader_stage.h | 2 +- source/blender/makesdna/DNA_lineart_types.h | 8 -------- source/blender/modifiers/intern/MOD_nodes_evaluator.cc | 2 +- source/blender/modifiers/intern/MOD_nodes_evaluator.hh | 2 +- .../blender/nodes/composite/nodes/node_composite_antialiasing.c | 6 ------ 8 files changed, 6 insertions(+), 25 deletions(-) diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh index 3051d980d45..b5981028893 100644 --- a/source/blender/blenlib/BLI_enumerable_thread_specific.hh +++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh @@ -10,7 +10,7 @@ * 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, + * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c index fd94ac92bc3..857c683d95a 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c @@ -1,6 +1,4 @@ /* - * ***** BEGIN GPL LICENSE BLOCK ***** - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 @@ -12,14 +10,11 @@ * 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, + * 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) 2017, Blender Foundation * This is a new part of Blender - * - * ***** END GPL LICENSE BLOCK ***** - * */ /** \file diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc index d3693f783ec..233b3d9da4d 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.cc +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -10,7 +10,7 @@ * 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, + * 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) 2021 Tangent Animation and diff --git a/source/blender/io/usd/intern/usd_reader_stage.h b/source/blender/io/usd/intern/usd_reader_stage.h index 7cc557f7802..ba223962c0c 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.h +++ b/source/blender/io/usd/intern/usd_reader_stage.h @@ -10,7 +10,7 @@ * 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, + * 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) 2021 Tangent Animation and diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index e93cf050e18..cdb09c3af50 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -1,6 +1,4 @@ /* - * ***** BEGIN GPL LICENSE BLOCK ***** - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 @@ -17,12 +15,6 @@ * * The Original Code is Copyright (C) 2010 Blender Foundation. * All rights reserved. - * - * The Original Code is: all of this file. - * - * Contributor(s): none yet. - * - * ***** END GPL LICENSE BLOCK ***** */ #pragma once diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 1391587fa7d..47dfd9bc8f6 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -10,7 +10,7 @@ * 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, + * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh index f4ee6242dcb..d8c60d31986 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh @@ -10,7 +10,7 @@ * 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, + * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c index 81e2408fcf9..a5906c31093 100644 --- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c +++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c @@ -1,6 +1,4 @@ /* - * ***** BEGIN GPL LICENSE BLOCK ***** - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 @@ -19,10 +17,6 @@ * All rights reserved. * * The Original Code is: all of this file. - * - * Contributor(s): IRIE Shinsuke - * - * ***** END GPL LICENSE BLOCK ***** */ /** \file -- cgit v1.2.3 From d3d4be1db3a09084eb2ff329bb7a530a87481480 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Thu, 5 Aug 2021 13:14:26 +0900 Subject: XR: Action Binding Improvements Provides several important improvements to the runtime action bindings operation and internal API. Moves input-specific action data (input thresholds, input regions, pose offsets/spaces) from actions to more granular action bindings. This allows a single action to be mapped to a variety of inputs, without having to share a single input threshold, region, or space. Also removes the need for action space creation API, as spaces for pose actions will be automatically created with the bindings. The correct action data for the current inputs is set by calling xrGetCurrentInteractionProfile() to get the current profile and then retrieving the corresponding mapped data. Does not bring about any changes for users since only internal runtime functionality is currently affected. Reviewed By: Julian Eisel Differential Revision: http://developer.blender.org/D12077 --- intern/ghost/GHOST_C-api.h | 19 +- intern/ghost/GHOST_Types.h | 24 +- intern/ghost/intern/GHOST_C-api.cpp | 29 +- intern/ghost/intern/GHOST_XrAction.cpp | 188 ++++---- intern/ghost/intern/GHOST_XrAction.h | 47 +- intern/ghost/intern/GHOST_XrSession.cpp | 87 +--- intern/ghost/intern/GHOST_XrSession.h | 9 +- source/blender/makesdna/DNA_xr_types.h | 9 + source/blender/windowmanager/CMakeLists.txt | 2 +- source/blender/windowmanager/WM_api.h | 26 +- .../blender/windowmanager/xr/intern/wm_xr_action.c | 454 +++++++++++++++++++ .../windowmanager/xr/intern/wm_xr_actions.c | 480 --------------------- .../blender/windowmanager/xr/intern/wm_xr_intern.h | 5 +- 13 files changed, 654 insertions(+), 725 deletions(-) create mode 100644 source/blender/windowmanager/xr/intern/wm_xr_action.c delete mode 100644 source/blender/windowmanager/xr/intern/wm_xr_actions.c diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index 46e3888a367..fea5a545807 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -1066,22 +1066,6 @@ void GHOST_XrDestroyActions(GHOST_XrContextHandle xr_context, uint32_t count, const char *const *action_names); -/** - * Create spaces for pose-based OpenXR actions. - */ -int GHOST_XrCreateActionSpaces(GHOST_XrContextHandle xr_context, - const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos); - -/** - * Destroy previously created spaces for OpenXR actions. - */ -void GHOST_XrDestroyActionSpaces(GHOST_XrContextHandle xr_context, - const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos); - /** * Create input/output path bindings for OpenXR actions. */ @@ -1096,7 +1080,8 @@ int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_context, void GHOST_XrDestroyActionBindings(GHOST_XrContextHandle xr_context, const char *action_set_name, uint32_t count, - const GHOST_XrActionProfileInfo *infos); + const char *const *action_names, + const char *const *profile_paths); /** * Attach all created action sets to the current OpenXR session. diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 94a3fd86b73..fb19b9535ad 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -719,29 +719,27 @@ typedef struct GHOST_XrActionInfo { const char **subaction_paths; /** States for each subaction path. */ void *states; + /** Input thresholds/regions for each subaction path. */ + float *float_thresholds; + int16_t *axis_flags; GHOST_XrCustomdataFreeFn customdata_free_fn; void *customdata; /* wmXrAction */ } GHOST_XrActionInfo; -typedef struct GHOST_XrActionSpaceInfo { - const char *action_name; - uint32_t count_subaction_paths; - const char **subaction_paths; - /** Poses for each subaction path. */ - const GHOST_XrPose *poses; -} GHOST_XrActionSpaceInfo; - typedef struct GHOST_XrActionBindingInfo { - const char *action_name; - uint32_t count_interaction_paths; - /** Interaction path: User (sub-action) path + component path. */ - const char **interaction_paths; + const char *component_path; + float float_threshold; + int16_t axis_flag; + GHOST_XrPose pose; } GHOST_XrActionBindingInfo; typedef struct GHOST_XrActionProfileInfo { + const char *action_name; const char *profile_path; - uint32_t count_bindings; + uint32_t count_subaction_paths; + const char **subaction_paths; + /* Bindings for each subaction path. */ const GHOST_XrActionBindingInfo *bindings; } GHOST_XrActionProfileInfo; diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index cb409595e50..0bc9be26eb1 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -961,28 +961,6 @@ void GHOST_XrDestroyActions(GHOST_XrContextHandle xr_contexthandle, GHOST_XR_CAPI_CALL(xr_session->destroyActions(action_set_name, count, action_names), xr_context); } -int GHOST_XrCreateActionSpaces(GHOST_XrContextHandle xr_contexthandle, - const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos) -{ - GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; - GHOST_XrSession *xr_session = xr_context->getSession(); - GHOST_XR_CAPI_CALL_RET(xr_session->createActionSpaces(action_set_name, count, infos), - xr_context); - return 0; -} - -void GHOST_XrDestroyActionSpaces(GHOST_XrContextHandle xr_contexthandle, - const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos) -{ - GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; - GHOST_XrSession *xr_session = xr_context->getSession(); - GHOST_XR_CAPI_CALL(xr_session->destroyActionSpaces(action_set_name, count, infos), xr_context); -} - int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name, uint32_t count, @@ -998,11 +976,14 @@ int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_contexthandle, void GHOST_XrDestroyActionBindings(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name, uint32_t count, - const GHOST_XrActionProfileInfo *infos) + const char *const *action_names, + const char *const *profile_paths) { GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; GHOST_XrSession *xr_session = xr_context->getSession(); - GHOST_XR_CAPI_CALL(xr_session->destroyActionBindings(action_set_name, count, infos), xr_context); + GHOST_XR_CAPI_CALL( + xr_session->destroyActionBindings(action_set_name, count, action_names, profile_paths), + xr_context); } int GHOST_XrAttachActionSets(GHOST_XrContextHandle xr_contexthandle) diff --git a/intern/ghost/intern/GHOST_XrAction.cpp b/intern/ghost/intern/GHOST_XrAction.cpp index 9c4f7fbc7d8..676a3367ee1 100644 --- a/intern/ghost/intern/GHOST_XrAction.cpp +++ b/intern/ghost/intern/GHOST_XrAction.cpp @@ -33,24 +33,22 @@ * * \{ */ -GHOST_XrActionSpace::GHOST_XrActionSpace(XrInstance instance, - XrSession session, +GHOST_XrActionSpace::GHOST_XrActionSpace(XrSession session, XrAction action, - const GHOST_XrActionSpaceInfo &info, - uint32_t subaction_idx) + const char *action_name, + const char *profile_path, + XrPath subaction_path, + const char *subaction_path_str, + const GHOST_XrPose &pose) { - const char *subaction_path = info.subaction_paths[subaction_idx]; - CHECK_XR(xrStringToPath(instance, subaction_path, &m_subaction_path), - (std::string("Failed to get user path \"") + subaction_path + "\".").data()); - XrActionSpaceCreateInfo action_space_info{XR_TYPE_ACTION_SPACE_CREATE_INFO}; action_space_info.action = action; - action_space_info.subactionPath = m_subaction_path; - copy_ghost_pose_to_openxr_pose(info.poses[subaction_idx], action_space_info.poseInActionSpace); + action_space_info.subactionPath = subaction_path; + copy_ghost_pose_to_openxr_pose(pose, action_space_info.poseInActionSpace); CHECK_XR(xrCreateActionSpace(session, &action_space_info, &m_space), - (std::string("Failed to create space \"") + subaction_path + "\" for action \"" + - info.action_name + "\".") + (std::string("Failed to create space \"") + subaction_path_str + "\" for action \"" + + action_name + "\" and profile \"" + profile_path + "\".") .data()); } @@ -66,11 +64,6 @@ XrSpace GHOST_XrActionSpace::getSpace() const return m_space; } -const XrPath &GHOST_XrActionSpace::getSubactionPath() const -{ - return m_subaction_path; -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -79,13 +72,19 @@ const XrPath &GHOST_XrActionSpace::getSubactionPath() const * \{ */ GHOST_XrActionProfile::GHOST_XrActionProfile(XrInstance instance, + XrSession session, XrAction action, - const char *profile_path, - const GHOST_XrActionBindingInfo &info) + GHOST_XrActionType type, + const GHOST_XrActionProfileInfo &info) { - CHECK_XR( - xrStringToPath(instance, profile_path, &m_profile), - (std::string("Failed to get interaction profile path \"") + profile_path + "\".").data()); + CHECK_XR(xrStringToPath(instance, info.profile_path, &m_profile), + (std::string("Failed to get interaction profile path \"") + info.profile_path + "\".") + .data()); + + const bool is_float_action = (type == GHOST_kXrActionTypeFloatInput || + type == GHOST_kXrActionTypeVector2fInput); + const bool is_button_action = (is_float_action || type == GHOST_kXrActionTypeBooleanInput); + const bool is_pose_action = (type == GHOST_kXrActionTypePoseInput); /* Create bindings. */ XrInteractionProfileSuggestedBinding bindings_info{ @@ -93,31 +92,80 @@ GHOST_XrActionProfile::GHOST_XrActionProfile(XrInstance instance, bindings_info.interactionProfile = m_profile; bindings_info.countSuggestedBindings = 1; - for (uint32_t interaction_idx = 0; interaction_idx < info.count_interaction_paths; - ++interaction_idx) { - const char *interaction_path = info.interaction_paths[interaction_idx]; + for (uint32_t subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) { + const char *subaction_path_str = info.subaction_paths[subaction_idx]; + const GHOST_XrActionBindingInfo &binding_info = info.bindings[subaction_idx]; + + const std::string interaction_path = std::string(subaction_path_str) + + binding_info.component_path; if (m_bindings.find(interaction_path) != m_bindings.end()) { continue; } XrActionSuggestedBinding sbinding; sbinding.action = action; - CHECK_XR(xrStringToPath(instance, interaction_path, &sbinding.binding), + CHECK_XR(xrStringToPath(instance, interaction_path.data(), &sbinding.binding), (std::string("Failed to get interaction path \"") + interaction_path + "\".").data()); bindings_info.suggestedBindings = &sbinding; /* Although the bindings will be re-suggested in GHOST_XrSession::attachActionSets(), it * greatly improves error checking to suggest them here first. */ CHECK_XR(xrSuggestInteractionProfileBindings(instance, &bindings_info), - (std::string("Failed to create binding for profile \"") + profile_path + - "\" and action \"" + info.action_name + - "\". Are the profile and action paths correct?") + (std::string("Failed to create binding for action \"") + info.action_name + + "\" and profile \"" + info.profile_path + + "\". Are the action and profile paths correct?") .data()); m_bindings.insert({interaction_path, sbinding.binding}); + + if (m_subaction_data.find(subaction_path_str) == m_subaction_data.end()) { + std::map::iterator it = + m_subaction_data + .emplace( + std::piecewise_construct, std::make_tuple(subaction_path_str), std::make_tuple()) + .first; + GHOST_XrSubactionData &subaction = it->second; + + CHECK_XR(xrStringToPath(instance, subaction_path_str, &subaction.subaction_path), + (std::string("Failed to get user path \"") + subaction_path_str + "\".").data()); + + if (is_float_action || is_button_action) { + if (is_float_action) { + subaction.float_threshold = binding_info.float_threshold; + } + if (is_button_action) { + subaction.axis_flag = binding_info.axis_flag; + } + } + else if (is_pose_action) { + /* Create action space for pose bindings. */ + subaction.space = std::make_unique(session, + action, + info.action_name, + info.profile_path, + subaction.subaction_path, + subaction_path_str, + binding_info.pose); + } + } } } +XrPath GHOST_XrActionProfile::getProfile() const +{ + return m_profile; +} + +const GHOST_XrSubactionData *GHOST_XrActionProfile::getSubaction(XrPath subaction_path) const +{ + for (auto &[subaction_path_str, subaction] : m_subaction_data) { + if (subaction.subaction_path == subaction_path) { + return &subaction; + } + } + return nullptr; +} + void GHOST_XrActionProfile::getBindings( XrAction action, std::map> &r_bindings) const { @@ -152,6 +200,8 @@ GHOST_XrAction::GHOST_XrAction(XrInstance instance, const GHOST_XrActionInfo &info) : m_type(info.type), m_states(info.states), + m_float_thresholds(info.float_thresholds), + m_axis_flags(info.axis_flags), m_custom_data_( std::make_unique(info.customdata, info.customdata_free_fn)) { @@ -201,52 +251,25 @@ GHOST_XrAction::~GHOST_XrAction() } } -bool GHOST_XrAction::createSpace(XrInstance instance, - XrSession session, - const GHOST_XrActionSpaceInfo &info) -{ - uint32_t subaction_idx = 0; - for (; subaction_idx < info.count_subaction_paths; ++subaction_idx) { - if (m_spaces.find(info.subaction_paths[subaction_idx]) != m_spaces.end()) { - return false; - } - } - - for (subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) { - m_spaces.emplace(std::piecewise_construct, - std::make_tuple(info.subaction_paths[subaction_idx]), - std::make_tuple(instance, session, m_action, info, subaction_idx)); - } - - return true; -} - -void GHOST_XrAction::destroySpace(const char *subaction_path) -{ - if (m_spaces.find(subaction_path) != m_spaces.end()) { - m_spaces.erase(subaction_path); - } -} - bool GHOST_XrAction::createBinding(XrInstance instance, - const char *profile_path, - const GHOST_XrActionBindingInfo &info) + XrSession session, + const GHOST_XrActionProfileInfo &info) { - if (m_profiles.find(profile_path) != m_profiles.end()) { + if (m_profiles.find(info.profile_path) != m_profiles.end()) { return false; } m_profiles.emplace(std::piecewise_construct, - std::make_tuple(profile_path), - std::make_tuple(instance, m_action, profile_path, info)); + std::make_tuple(info.profile_path), + std::make_tuple(instance, session, m_action, m_type, info)); return true; } -void GHOST_XrAction::destroyBinding(const char *interaction_profile_path) +void GHOST_XrAction::destroyBinding(const char *profile_path) { - if (m_profiles.find(interaction_profile_path) != m_profiles.end()) { - m_profiles.erase(interaction_profile_path); + if (m_profiles.find(profile_path) != m_profiles.end()) { + m_profiles.erase(profile_path); } } @@ -255,6 +278,10 @@ void GHOST_XrAction::updateState(XrSession session, XrSpace reference_space, const XrTime &predicted_display_time) { + const bool is_float_action = (m_type == GHOST_kXrActionTypeFloatInput || + m_type == GHOST_kXrActionTypeVector2fInput); + const bool is_button_action = (is_float_action || m_type == GHOST_kXrActionTypeBooleanInput); + XrActionStateGetInfo state_info{XR_TYPE_ACTION_STATE_GET_INFO}; state_info.action = m_action; @@ -262,6 +289,28 @@ void GHOST_XrAction::updateState(XrSession session, for (size_t subaction_idx = 0; subaction_idx < count_subaction_paths; ++subaction_idx) { state_info.subactionPath = m_subaction_paths[subaction_idx]; + /* Set subaction data based on current interaction profile. */ + XrInteractionProfileState profile_state{XR_TYPE_INTERACTION_PROFILE_STATE}; + CHECK_XR(xrGetCurrentInteractionProfile(session, state_info.subactionPath, &profile_state), + "Failed to get current interaction profile."); + + const GHOST_XrSubactionData *subaction = nullptr; + for (auto &[profile_path, profile] : m_profiles) { + if (profile.getProfile() == profile_state.interactionProfile) { + subaction = profile.getSubaction(state_info.subactionPath); + break; + } + } + + if (subaction != nullptr) { + if (is_float_action) { + m_float_thresholds[subaction_idx] = subaction->float_threshold; + } + if (is_button_action) { + m_axis_flags[subaction_idx] = subaction->axis_flag; + } + } + switch (m_type) { case GHOST_kXrActionTypeBooleanInput: { XrActionStateBoolean state{XR_TYPE_ACTION_STATE_BOOLEAN}; @@ -299,14 +348,9 @@ void GHOST_XrAction::updateState(XrSession session, xrGetActionStatePose(session, &state_info, &state), (std::string("Failed to get state for pose action \"") + action_name + "\".").data()); if (state.isActive) { - XrSpace pose_space = XR_NULL_HANDLE; - for (auto &[path, space] : m_spaces) { - if (space.getSubactionPath() == state_info.subactionPath) { - pose_space = space.getSpace(); - break; - } - } - + XrSpace pose_space = ((subaction != nullptr) && (subaction->space != nullptr)) ? + subaction->space->getSpace() : + XR_NULL_HANDLE; if (pose_space != XR_NULL_HANDLE) { XrSpaceLocation space_location{XR_TYPE_SPACE_LOCATION}; CHECK_XR( diff --git a/intern/ghost/intern/GHOST_XrAction.h b/intern/ghost/intern/GHOST_XrAction.h index 32445c616bd..e2a89e87278 100644 --- a/intern/ghost/intern/GHOST_XrAction.h +++ b/intern/ghost/intern/GHOST_XrAction.h @@ -34,38 +34,53 @@ class GHOST_XrActionSpace { public: GHOST_XrActionSpace() = delete; /* Default constructor for map storage. */ - GHOST_XrActionSpace(XrInstance instance, - XrSession session, + GHOST_XrActionSpace(XrSession session, XrAction action, - const GHOST_XrActionSpaceInfo &info, - uint32_t subaction_idx); + const char *action_name, + const char *profile_path, + XrPath subaction_path, + const char *subaction_path_str, + const GHOST_XrPose &pose); ~GHOST_XrActionSpace(); XrSpace getSpace() const; - const XrPath &getSubactionPath() const; private: XrSpace m_space = XR_NULL_HANDLE; - XrPath m_subaction_path = XR_NULL_PATH; }; /* -------------------------------------------------------------------- */ +typedef struct GHOST_XrSubactionData { + XrPath subaction_path = XR_NULL_PATH; + float float_threshold; + int16_t axis_flag; + std::unique_ptr space = nullptr; +} GHOST_XrSubactionData; + +/* -------------------------------------------------------------------- */ + class GHOST_XrActionProfile { public: GHOST_XrActionProfile() = delete; /* Default constructor for map storage. */ GHOST_XrActionProfile(XrInstance instance, + XrSession session, XrAction action, - const char *profile_path, - const GHOST_XrActionBindingInfo &info); + GHOST_XrActionType type, + const GHOST_XrActionProfileInfo &info); ~GHOST_XrActionProfile() = default; + XrPath getProfile() const; + const GHOST_XrSubactionData *getSubaction(XrPath subaction_path) const; void getBindings(XrAction action, std::map> &r_bindings) const; private: XrPath m_profile = XR_NULL_PATH; - /* Bindings identified by interaction (user (subaction) + component) path. */ + + /** Subaction data identified by user (subaction) path. */ + std::map m_subaction_data; + /** Bindings identified by interaction (user (subaction) + component) path. */ std::map m_bindings; }; @@ -77,12 +92,9 @@ class GHOST_XrAction { GHOST_XrAction(XrInstance instance, XrActionSet action_set, const GHOST_XrActionInfo &info); ~GHOST_XrAction(); - bool createSpace(XrInstance instance, XrSession session, const GHOST_XrActionSpaceInfo &info); - void destroySpace(const char *subaction_path); - bool createBinding(XrInstance instance, - const char *profile_path, - const GHOST_XrActionBindingInfo &info); + XrSession session, + const GHOST_XrActionProfileInfo &info); void destroyBinding(const char *profile_path); void updateState(XrSession session, @@ -105,12 +117,13 @@ class GHOST_XrAction { std::vector m_subaction_paths; /** States for each subaction path. */ void *m_states; + /** Input thresholds/regions for each subaction path. */ + float *m_float_thresholds; + int16_t *m_axis_flags; std::unique_ptr m_custom_data_ = nullptr; /* wmXrAction */ - /* Spaces identified by user (subaction) path. */ - std::map m_spaces; - /* Profiles identified by interaction profile path. */ + /** Profiles identified by interaction profile path. */ std::map m_profiles; }; diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index 919d11d22a9..a29ec1cc560 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -610,9 +610,9 @@ void GHOST_XrSession::destroyActions(const char *action_set_name, } } -bool GHOST_XrSession::createActionSpaces(const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos) +bool GHOST_XrSession::createActionBindings(const char *action_set_name, + uint32_t count, + const GHOST_XrActionProfileInfo *infos) { GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); if (action_set == nullptr) { @@ -622,98 +622,37 @@ bool GHOST_XrSession::createActionSpaces(const char *action_set_name, XrInstance instance = m_context->getInstance(); XrSession session = m_oxr->session; - for (uint32_t action_idx = 0; action_idx < count; ++action_idx) { - const GHOST_XrActionSpaceInfo &info = infos[action_idx]; + for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) { + const GHOST_XrActionProfileInfo &info = infos[profile_idx]; GHOST_XrAction *action = action_set->findAction(info.action_name); if (action == nullptr) { continue; } - if (!action->createSpace(instance, session, info)) { - return false; - } + action->createBinding(instance, session, info); } return true; } -void GHOST_XrSession::destroyActionSpaces(const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos) +void GHOST_XrSession::destroyActionBindings(const char *action_set_name, + uint32_t count, + const char *const *action_names, + const char *const *profile_paths) { GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); if (action_set == nullptr) { return; } - for (uint32_t action_idx = 0; action_idx < count; ++action_idx) { - const GHOST_XrActionSpaceInfo &info = infos[action_idx]; - - GHOST_XrAction *action = action_set->findAction(info.action_name); + for (uint32_t i = 0; i < count; ++i) { + GHOST_XrAction *action = action_set->findAction(action_names[i]); if (action == nullptr) { continue; } - for (uint32_t subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) { - action->destroySpace(info.subaction_paths[subaction_idx]); - } - } -} - -bool GHOST_XrSession::createActionBindings(const char *action_set_name, - uint32_t count, - const GHOST_XrActionProfileInfo *infos) -{ - GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); - if (action_set == nullptr) { - return false; - } - - XrInstance instance = m_context->getInstance(); - - for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) { - const GHOST_XrActionProfileInfo &info = infos[profile_idx]; - const char *profile_path = info.profile_path; - - for (uint32_t binding_idx = 0; binding_idx < info.count_bindings; ++binding_idx) { - const GHOST_XrActionBindingInfo &binding = info.bindings[binding_idx]; - - GHOST_XrAction *action = action_set->findAction(binding.action_name); - if (action == nullptr) { - continue; - } - - action->createBinding(instance, profile_path, binding); - } - } - - return true; -} - -void GHOST_XrSession::destroyActionBindings(const char *action_set_name, - uint32_t count, - const GHOST_XrActionProfileInfo *infos) -{ - GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); - if (action_set == nullptr) { - return; - } - - for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) { - const GHOST_XrActionProfileInfo &info = infos[profile_idx]; - const char *profile_path = info.profile_path; - - for (uint32_t binding_idx = 0; binding_idx < info.count_bindings; ++binding_idx) { - const GHOST_XrActionBindingInfo &binding = info.bindings[binding_idx]; - - GHOST_XrAction *action = action_set->findAction(binding.action_name); - if (action == nullptr) { - continue; - } - - action->destroyBinding(profile_path); - } + action->destroyBinding(profile_paths[i]); } } diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h index d448585d14c..cdeef153fb1 100644 --- a/intern/ghost/intern/GHOST_XrSession.h +++ b/intern/ghost/intern/GHOST_XrSession.h @@ -60,18 +60,13 @@ class GHOST_XrSession { void destroyActions(const char *action_set_name, uint32_t count, const char *const *action_names); - bool createActionSpaces(const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos); - void destroyActionSpaces(const char *action_set_name, - uint32_t count, - const GHOST_XrActionSpaceInfo *infos); bool createActionBindings(const char *action_set_name, uint32_t count, const GHOST_XrActionProfileInfo *infos); void destroyActionBindings(const char *action_set_name, uint32_t count, - const GHOST_XrActionProfileInfo *infos); + const char *const *action_names, + const char *const *profile_paths); bool attachActionSets(); /** diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index fc00d5eb839..c0928e1519f 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -74,6 +74,15 @@ typedef enum eXrOpFlag { XR_OP_MODAL = 2, } eXrOpFlag; +typedef enum eXrAxisFlag { + /** For axis-based inputs (thumbstick/trackpad/etc). Determines the region for action execution + (mutually exclusive per axis). */ + XR_AXIS0_POS = (1 << 0), + XR_AXIS0_NEG = (1 << 1), + XR_AXIS1_POS = (1 << 2), + XR_AXIS1_NEG = (1 << 3), +} eXrAxisFlag; + #ifdef __cplusplus } #endif diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index e513c49c11b..8b0ceb02b5f 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -203,7 +203,7 @@ if(WITH_XR_OPENXR) list(APPEND SRC xr/intern/wm_xr.c - xr/intern/wm_xr_actions.c + xr/intern/wm_xr_action.c xr/intern/wm_xr_draw.c xr/intern/wm_xr_session.c diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 136c639caea..fb973592a57 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -978,34 +978,24 @@ bool WM_xr_action_create(wmXrData *xr, eXrActionType type, unsigned int count_subaction_paths, const char **subaction_paths, - const float *float_threshold, struct wmOperatorType *ot, struct IDProperty *op_properties, eXrOpFlag op_flag); void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name); -bool WM_xr_action_space_create(wmXrData *xr, - const char *action_set_name, - const char *action_name, - unsigned int count_subaction_paths, - const char **subaction_paths, - const struct wmXrPose *poses); -void WM_xr_action_space_destroy(wmXrData *xr, - const char *action_set_name, - const char *action_name, - unsigned int count_subaction_paths, - const char **subaction_paths); bool WM_xr_action_binding_create(wmXrData *xr, const char *action_set_name, - const char *profile_path, const char *action_name, - unsigned int count_interaction_paths, - const char **interaction_paths); + const char *profile_path, + unsigned int count_subaction_paths, + const char **subaction_paths, + const char **component_paths, + const float *float_thresholds, + const eXrAxisFlag *axis_flags, + const struct wmXrPose *poses); void WM_xr_action_binding_destroy(wmXrData *xr, const char *action_set_name, - const char *profile_path, const char *action_name, - unsigned int count_interaction_paths, - const char **interaction_paths); + const char *profile_path); /* If action_set_name is NULL, then all action sets will be treated as active. */ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c new file mode 100644 index 00000000000..ee4cfcccaa7 --- /dev/null +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -0,0 +1,454 @@ +/* + * 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. + */ + +/** \file + * \ingroup wm + * + * \name Window-Manager XR Actions + * + * Uses the Ghost-XR API to manage OpenXR actions. + * All functions are designed to be usable by RNA / the Python API. + */ + +#include "BLI_math.h" + +#include "GHOST_C-api.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_xr_intern.h" + +/* -------------------------------------------------------------------- */ +/** \name XR-Action API + * + * API functions for managing OpenXR actions. + * + * \{ */ + +static wmXrActionSet *action_set_create(const char *action_set_name) +{ + wmXrActionSet *action_set = MEM_callocN(sizeof(*action_set), __func__); + action_set->name = MEM_mallocN(strlen(action_set_name) + 1, "XrActionSet_Name"); + strcpy(action_set->name, action_set_name); + + return action_set; +} + +static void action_set_destroy(void *val) +{ + wmXrActionSet *action_set = val; + + MEM_SAFE_FREE(action_set->name); + + MEM_freeN(action_set); +} + +static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name) +{ + return GHOST_XrGetActionSetCustomdata(xr->runtime->context, action_set_name); +} + +static wmXrAction *action_create(const char *action_name, + eXrActionType type, + unsigned int count_subaction_paths, + const char **subaction_paths, + wmOperatorType *ot, + IDProperty *op_properties, + eXrOpFlag op_flag) +{ + wmXrAction *action = MEM_callocN(sizeof(*action), __func__); + action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name"); + strcpy(action->name, action_name); + action->type = type; + + const unsigned int count = count_subaction_paths; + action->count_subaction_paths = count; + + action->subaction_paths = MEM_mallocN(sizeof(*action->subaction_paths) * count, + "XrAction_SubactionPaths"); + for (unsigned int i = 0; i < count; ++i) { + action->subaction_paths[i] = MEM_mallocN(strlen(subaction_paths[i]) + 1, + "XrAction_SubactionPath"); + strcpy(action->subaction_paths[i], subaction_paths[i]); + } + + size_t size; + switch (type) { + case XR_BOOLEAN_INPUT: + size = sizeof(bool); + break; + case XR_FLOAT_INPUT: + size = sizeof(float); + break; + case XR_VECTOR2F_INPUT: + size = sizeof(float) * 2; + break; + case XR_POSE_INPUT: + size = sizeof(GHOST_XrPose); + break; + case XR_VIBRATION_OUTPUT: + return action; + } + action->states = MEM_calloc_arrayN(count, size, "XrAction_States"); + action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev"); + + const bool is_float_action = (type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT); + const bool is_button_action = (is_float_action || type == XR_BOOLEAN_INPUT); + if (is_float_action) { + action->float_thresholds = MEM_calloc_arrayN( + count, sizeof(*action->float_thresholds), "XrAction_FloatThresholds"); + } + if (is_button_action) { + action->axis_flags = MEM_calloc_arrayN( + count, sizeof(*action->axis_flags), "XrAction_AxisFlags"); + } + + action->ot = ot; + action->op_properties = op_properties; + action->op_flag = op_flag; + + return action; +} + +static void action_destroy(void *val) +{ + wmXrAction *action = val; + + MEM_SAFE_FREE(action->name); + + const unsigned int count = action->count_subaction_paths; + char **subaction_paths = action->subaction_paths; + if (subaction_paths) { + for (unsigned int i = 0; i < count; ++i) { + MEM_SAFE_FREE(subaction_paths[i]); + } + MEM_freeN(subaction_paths); + } + + MEM_SAFE_FREE(action->states); + MEM_SAFE_FREE(action->states_prev); + + MEM_SAFE_FREE(action->float_thresholds); + MEM_SAFE_FREE(action->axis_flags); + + MEM_freeN(action); +} + +static wmXrAction *action_find(wmXrData *xr, const char *action_set_name, const char *action_name) +{ + return GHOST_XrGetActionCustomdata(xr->runtime->context, action_set_name, action_name); +} + +bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name) +{ + if (action_set_find(xr, action_set_name)) { + return false; + } + + wmXrActionSet *action_set = action_set_create(action_set_name); + + GHOST_XrActionSetInfo info = { + .name = action_set_name, + .customdata_free_fn = action_set_destroy, + .customdata = action_set, + }; + + if (!GHOST_XrCreateActionSet(xr->runtime->context, &info)) { + return false; + } + + return true; +} + +void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return; + } + + wmXrSessionState *session_state = &xr->runtime->session_state; + + if (action_set == session_state->active_action_set) { + if (action_set->controller_pose_action) { + wm_xr_session_controller_data_clear(session_state); + action_set->controller_pose_action = NULL; + } + if (action_set->active_modal_action) { + action_set->active_modal_action = NULL; + } + session_state->active_action_set = NULL; + } + + GHOST_XrDestroyActionSet(xr->runtime->context, action_set_name); +} + +bool WM_xr_action_create(wmXrData *xr, + const char *action_set_name, + const char *action_name, + eXrActionType type, + unsigned int count_subaction_paths, + const char **subaction_paths, + wmOperatorType *ot, + IDProperty *op_properties, + eXrOpFlag op_flag) +{ + if (action_find(xr, action_set_name, action_name)) { + return false; + } + + wmXrAction *action = action_create(action_name, + type, + count_subaction_paths, + subaction_paths, + ot, + op_properties, + op_flag); + + GHOST_XrActionInfo info = { + .name = action_name, + .count_subaction_paths = count_subaction_paths, + .subaction_paths = subaction_paths, + .states = action->states, + .float_thresholds = action->float_thresholds, + .axis_flags = (int16_t *)action->axis_flags, + .customdata_free_fn = action_destroy, + .customdata = action, + }; + + switch (type) { + case XR_BOOLEAN_INPUT: + info.type = GHOST_kXrActionTypeBooleanInput; + break; + case XR_FLOAT_INPUT: + info.type = GHOST_kXrActionTypeFloatInput; + break; + case XR_VECTOR2F_INPUT: + info.type = GHOST_kXrActionTypeVector2fInput; + break; + case XR_POSE_INPUT: + info.type = GHOST_kXrActionTypePoseInput; + break; + case XR_VIBRATION_OUTPUT: + info.type = GHOST_kXrActionTypeVibrationOutput; + break; + } + + if (!GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info)) { + return false; + } + + return true; +} + +void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return; + } + + wmXrAction *action = action_find(xr, action_set_name, action_name); + if (!action) { + return; + } + + if (action_set->controller_pose_action && + STREQ(action_set->controller_pose_action->name, action_name)) { + if (action_set == xr->runtime->session_state.active_action_set) { + wm_xr_session_controller_data_clear(&xr->runtime->session_state); + } + action_set->controller_pose_action = NULL; + } + if (action_set->active_modal_action && + STREQ(action_set->active_modal_action->name, action_name)) { + action_set->active_modal_action = NULL; + } + + GHOST_XrDestroyActions(xr->runtime->context, action_set_name, 1, &action_name); +} + + +bool WM_xr_action_binding_create(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char *profile_path, + unsigned int count_subaction_paths, + const char **subaction_paths, + const char **component_paths, + const float *float_thresholds, + const eXrAxisFlag *axis_flags, + const struct wmXrPose *poses) +{ + GHOST_XrActionBindingInfo *binding_infos = MEM_calloc_arrayN( + count_subaction_paths, sizeof(*binding_infos), __func__); + + for (unsigned int i = 0; i < count_subaction_paths; ++i) { + GHOST_XrActionBindingInfo *binding_info = &binding_infos[i]; + binding_info->component_path = component_paths[i]; + if (float_thresholds) { + binding_info->float_threshold = float_thresholds[i]; + } + if (axis_flags) { + binding_info->axis_flag = axis_flags[i]; + } + if (poses) { + copy_v3_v3(binding_info->pose.position, poses[i].position); + copy_qt_qt(binding_info->pose.orientation_quat, poses[i].orientation_quat); + } + } + + GHOST_XrActionProfileInfo profile_info = { + .action_name = action_name, + .profile_path = profile_path, + .count_subaction_paths = count_subaction_paths, + .subaction_paths = subaction_paths, + .bindings = binding_infos, + }; + + bool ret = GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); + + MEM_freeN(binding_infos); + return ret; +} + +void WM_xr_action_binding_destroy(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char *profile_path) +{ + GHOST_XrDestroyActionBindings( + xr->runtime->context, action_set_name, 1, &action_name, &profile_path); +} + +bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return false; + } + + { + /* Unset active modal action (if any). */ + wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set; + if (active_action_set) { + wmXrAction *active_modal_action = active_action_set->active_modal_action; + if (active_modal_action) { + if (active_modal_action->active_modal_path) { + active_modal_action->active_modal_path = NULL; + } + active_action_set->active_modal_action = NULL; + } + } + } + + xr->runtime->session_state.active_action_set = action_set; + + if (action_set->controller_pose_action) { + wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr); + } + + return true; +} + +bool WM_xr_controller_pose_action_set(wmXrData *xr, + const char *action_set_name, + const char *action_name) +{ + wmXrActionSet *action_set = action_set_find(xr, action_set_name); + if (!action_set) { + return false; + } + + wmXrAction *action = action_find(xr, action_set_name, action_name); + if (!action) { + return false; + } + + action_set->controller_pose_action = action; + + if (action_set == xr->runtime->session_state.active_action_set) { + wm_xr_session_controller_data_populate(action, xr); + } + + return true; +} + +bool WM_xr_action_state_get(const wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char *subaction_path, + wmXrActionState *r_state) +{ + const wmXrAction *action = action_find((wmXrData *)xr, action_set_name, action_name); + if (!action) { + return false; + } + + r_state->type = (int)action->type; + + /* Find the action state corresponding to the subaction path. */ + for (unsigned int i = 0; i < action->count_subaction_paths; ++i) { + if (STREQ(subaction_path, action->subaction_paths[i])) { + switch (action->type) { + case XR_BOOLEAN_INPUT: + r_state->state_boolean = ((bool *)action->states)[i]; + break; + case XR_FLOAT_INPUT: + r_state->state_float = ((float *)action->states)[i]; + break; + case XR_VECTOR2F_INPUT: + copy_v2_v2(r_state->state_vector2f, ((float(*)[2])action->states)[i]); + break; + case XR_POSE_INPUT: { + const GHOST_XrPose *pose = &((GHOST_XrPose *)action->states)[i]; + copy_v3_v3(r_state->state_pose.position, pose->position); + copy_qt_qt(r_state->state_pose.orientation_quat, pose->orientation_quat); + break; + } + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + return true; + } + } + + return false; +} + +bool WM_xr_haptic_action_apply(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const int64_t *duration, + const float *frequency, + const float *amplitude) +{ + return GHOST_XrApplyHapticAction( + xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ? + true : + false; +} + +void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name) +{ + GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name); +} + +/** \} */ /* XR-Action API */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actions.c b/source/blender/windowmanager/xr/intern/wm_xr_actions.c deleted file mode 100644 index 7eabd29baa0..00000000000 --- a/source/blender/windowmanager/xr/intern/wm_xr_actions.c +++ /dev/null @@ -1,480 +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. - */ - -/** \file - * \ingroup wm - * - * \name Window-Manager XR Actions - * - * Uses the Ghost-XR API to manage OpenXR actions. - * All functions are designed to be usable by RNA / the Python API. - */ - -#include "BLI_math.h" - -#include "GHOST_C-api.h" - -#include "MEM_guardedalloc.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "wm_xr_intern.h" - -/* -------------------------------------------------------------------- */ -/** \name XR-Action API - * - * API functions for managing OpenXR actions. - * - * \{ */ - -static wmXrActionSet *action_set_create(const char *action_set_name) -{ - wmXrActionSet *action_set = MEM_callocN(sizeof(*action_set), __func__); - action_set->name = MEM_mallocN(strlen(action_set_name) + 1, "XrActionSet_Name"); - strcpy(action_set->name, action_set_name); - - return action_set; -} - -static void action_set_destroy(void *val) -{ - wmXrActionSet *action_set = val; - - MEM_SAFE_FREE(action_set->name); - - MEM_freeN(action_set); -} - -static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name) -{ - return GHOST_XrGetActionSetCustomdata(xr->runtime->context, action_set_name); -} - -static wmXrAction *action_create(const char *action_name, - eXrActionType type, - unsigned int count_subaction_paths, - const char **subaction_paths, - const float *float_threshold, - wmOperatorType *ot, - IDProperty *op_properties, - eXrOpFlag op_flag) -{ - wmXrAction *action = MEM_callocN(sizeof(*action), __func__); - action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name"); - strcpy(action->name, action_name); - action->type = type; - - const unsigned int count = count_subaction_paths; - action->count_subaction_paths = count; - - action->subaction_paths = MEM_mallocN(sizeof(*action->subaction_paths) * count, - "XrAction_SubactionPaths"); - for (unsigned int i = 0; i < count; ++i) { - action->subaction_paths[i] = MEM_mallocN(strlen(subaction_paths[i]) + 1, - "XrAction_SubactionPath"); - strcpy(action->subaction_paths[i], subaction_paths[i]); - } - - size_t size; - switch (type) { - case XR_BOOLEAN_INPUT: - size = sizeof(bool); - break; - case XR_FLOAT_INPUT: - size = sizeof(float); - break; - case XR_VECTOR2F_INPUT: - size = sizeof(float) * 2; - break; - case XR_POSE_INPUT: - size = sizeof(GHOST_XrPose); - break; - case XR_VIBRATION_OUTPUT: - return action; - } - action->states = MEM_calloc_arrayN(count, size, "XrAction_States"); - action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev"); - - if (float_threshold) { - BLI_assert(type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT); - action->float_threshold = *float_threshold; - CLAMP(action->float_threshold, 0.0f, 1.0f); - } - - action->ot = ot; - action->op_properties = op_properties; - action->op_flag = op_flag; - - return action; -} - -static void action_destroy(void *val) -{ - wmXrAction *action = val; - - MEM_SAFE_FREE(action->name); - - const unsigned int count = action->count_subaction_paths; - char **subaction_paths = action->subaction_paths; - if (subaction_paths) { - for (unsigned int i = 0; i < count; ++i) { - MEM_SAFE_FREE(subaction_paths[i]); - } - MEM_freeN(subaction_paths); - } - - MEM_SAFE_FREE(action->states); - MEM_SAFE_FREE(action->states_prev); - - MEM_freeN(action); -} - -static wmXrAction *action_find(wmXrData *xr, const char *action_set_name, const char *action_name) -{ - return GHOST_XrGetActionCustomdata(xr->runtime->context, action_set_name, action_name); -} - -bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name) -{ - if (action_set_find(xr, action_set_name)) { - return false; - } - - wmXrActionSet *action_set = action_set_create(action_set_name); - - GHOST_XrActionSetInfo info = { - .name = action_set_name, - .customdata_free_fn = action_set_destroy, - .customdata = action_set, - }; - - if (!GHOST_XrCreateActionSet(xr->runtime->context, &info)) { - return false; - } - - return true; -} - -void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name) -{ - wmXrActionSet *action_set = action_set_find(xr, action_set_name); - if (!action_set) { - return; - } - - wmXrSessionState *session_state = &xr->runtime->session_state; - - if (action_set == session_state->active_action_set) { - if (action_set->controller_pose_action) { - wm_xr_session_controller_data_clear(session_state); - action_set->controller_pose_action = NULL; - } - if (action_set->active_modal_action) { - action_set->active_modal_action = NULL; - } - session_state->active_action_set = NULL; - } - - GHOST_XrDestroyActionSet(xr->runtime->context, action_set_name); -} - -bool WM_xr_action_create(wmXrData *xr, - const char *action_set_name, - const char *action_name, - eXrActionType type, - unsigned int count_subaction_paths, - const char **subaction_paths, - const float *float_threshold, - wmOperatorType *ot, - IDProperty *op_properties, - eXrOpFlag op_flag) -{ - if (action_find(xr, action_set_name, action_name)) { - return false; - } - - wmXrAction *action = action_create(action_name, - type, - count_subaction_paths, - subaction_paths, - float_threshold, - ot, - op_properties, - op_flag); - - GHOST_XrActionInfo info = { - .name = action_name, - .count_subaction_paths = count_subaction_paths, - .subaction_paths = subaction_paths, - .states = action->states, - .customdata_free_fn = action_destroy, - .customdata = action, - }; - - switch (type) { - case XR_BOOLEAN_INPUT: - info.type = GHOST_kXrActionTypeBooleanInput; - break; - case XR_FLOAT_INPUT: - info.type = GHOST_kXrActionTypeFloatInput; - break; - case XR_VECTOR2F_INPUT: - info.type = GHOST_kXrActionTypeVector2fInput; - break; - case XR_POSE_INPUT: - info.type = GHOST_kXrActionTypePoseInput; - break; - case XR_VIBRATION_OUTPUT: - info.type = GHOST_kXrActionTypeVibrationOutput; - break; - } - - if (!GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info)) { - return false; - } - - return true; -} - -void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name) -{ - wmXrActionSet *action_set = action_set_find(xr, action_set_name); - if (!action_set) { - return; - } - - if (action_set->controller_pose_action && - STREQ(action_set->controller_pose_action->name, action_name)) { - if (action_set == xr->runtime->session_state.active_action_set) { - wm_xr_session_controller_data_clear(&xr->runtime->session_state); - } - action_set->controller_pose_action = NULL; - } - if (action_set->active_modal_action && - STREQ(action_set->active_modal_action->name, action_name)) { - action_set->active_modal_action = NULL; - } - - wmXrAction *action = action_find(xr, action_set_name, action_name); - if (!action) { - return; - } -} - -bool WM_xr_action_space_create(wmXrData *xr, - const char *action_set_name, - const char *action_name, - unsigned int count_subaction_paths, - const char **subaction_paths, - const wmXrPose *poses) -{ - GHOST_XrActionSpaceInfo info = { - .action_name = action_name, - .count_subaction_paths = count_subaction_paths, - .subaction_paths = subaction_paths, - }; - - GHOST_XrPose *ghost_poses = MEM_malloc_arrayN( - count_subaction_paths, sizeof(*ghost_poses), __func__); - for (unsigned int i = 0; i < count_subaction_paths; ++i) { - const wmXrPose *pose = &poses[i]; - GHOST_XrPose *ghost_pose = &ghost_poses[i]; - copy_v3_v3(ghost_pose->position, pose->position); - copy_qt_qt(ghost_pose->orientation_quat, pose->orientation_quat); - } - info.poses = ghost_poses; - - bool ret = GHOST_XrCreateActionSpaces(xr->runtime->context, action_set_name, 1, &info) ? true : - false; - MEM_freeN(ghost_poses); - return ret; -} - -void WM_xr_action_space_destroy(wmXrData *xr, - const char *action_set_name, - const char *action_name, - unsigned int count_subaction_paths, - const char **subaction_paths) -{ - GHOST_XrActionSpaceInfo info = { - .action_name = action_name, - .count_subaction_paths = count_subaction_paths, - .subaction_paths = subaction_paths, - }; - - GHOST_XrDestroyActionSpaces(xr->runtime->context, action_set_name, 1, &info); -} - -bool WM_xr_action_binding_create(wmXrData *xr, - const char *action_set_name, - const char *profile_path, - const char *action_name, - unsigned int count_interaction_paths, - const char **interaction_paths) -{ - GHOST_XrActionBindingInfo binding_info = { - .action_name = action_name, - .count_interaction_paths = count_interaction_paths, - .interaction_paths = interaction_paths, - }; - - GHOST_XrActionProfileInfo profile_info = { - .profile_path = profile_path, - .count_bindings = 1, - .bindings = &binding_info, - }; - - return GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); -} - -void WM_xr_action_binding_destroy(wmXrData *xr, - const char *action_set_name, - const char *profile_path, - const char *action_name, - unsigned int count_interaction_paths, - const char **interaction_paths) -{ - GHOST_XrActionBindingInfo binding_info = { - .action_name = action_name, - .count_interaction_paths = count_interaction_paths, - .interaction_paths = interaction_paths, - }; - - GHOST_XrActionProfileInfo profile_info = { - .profile_path = profile_path, - .count_bindings = 1, - .bindings = &binding_info, - }; - - GHOST_XrDestroyActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); -} - -bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name) -{ - wmXrActionSet *action_set = action_set_find(xr, action_set_name); - if (!action_set) { - return false; - } - - { - /* Unset active modal action (if any). */ - wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set; - if (active_action_set) { - wmXrAction *active_modal_action = active_action_set->active_modal_action; - if (active_modal_action) { - if (active_modal_action->active_modal_path) { - active_modal_action->active_modal_path = NULL; - } - active_action_set->active_modal_action = NULL; - } - } - } - - xr->runtime->session_state.active_action_set = action_set; - - if (action_set->controller_pose_action) { - wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr); - } - - return true; -} - -bool WM_xr_controller_pose_action_set(wmXrData *xr, - const char *action_set_name, - const char *action_name) -{ - wmXrActionSet *action_set = action_set_find(xr, action_set_name); - if (!action_set) { - return false; - } - - wmXrAction *action = action_find(xr, action_set_name, action_name); - if (!action) { - return false; - } - - action_set->controller_pose_action = action; - - if (action_set == xr->runtime->session_state.active_action_set) { - wm_xr_session_controller_data_populate(action, xr); - } - - return true; -} - -bool WM_xr_action_state_get(const wmXrData *xr, - const char *action_set_name, - const char *action_name, - const char *subaction_path, - wmXrActionState *r_state) -{ - const wmXrAction *action = action_find((wmXrData *)xr, action_set_name, action_name); - if (!action) { - return false; - } - - BLI_assert(action->type == (eXrActionType)r_state->type); - - /* Find the action state corresponding to the subaction path. */ - for (unsigned int i = 0; i < action->count_subaction_paths; ++i) { - if (STREQ(subaction_path, action->subaction_paths[i])) { - switch ((eXrActionType)r_state->type) { - case XR_BOOLEAN_INPUT: - r_state->state_boolean = ((bool *)action->states)[i]; - break; - case XR_FLOAT_INPUT: - r_state->state_float = ((float *)action->states)[i]; - break; - case XR_VECTOR2F_INPUT: - copy_v2_v2(r_state->state_vector2f, ((float(*)[2])action->states)[i]); - break; - case XR_POSE_INPUT: { - const GHOST_XrPose *pose = &((GHOST_XrPose *)action->states)[i]; - copy_v3_v3(r_state->state_pose.position, pose->position); - copy_qt_qt(r_state->state_pose.orientation_quat, pose->orientation_quat); - break; - } - case XR_VIBRATION_OUTPUT: - BLI_assert_unreachable(); - break; - } - return true; - } - } - - return false; -} - -bool WM_xr_haptic_action_apply(wmXrData *xr, - const char *action_set_name, - const char *action_name, - const int64_t *duration, - const float *frequency, - const float *amplitude) -{ - return GHOST_XrApplyHapticAction( - xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ? - true : - false; -} - -void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name) -{ - GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name); -} - -/** \} */ /* XR-Action API */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 6415f96e322..4530aeaa428 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -122,8 +122,9 @@ typedef struct wmXrAction { /** Previous states, stored to determine XR events. */ void *states_prev; - /** Input threshold for float/vector2f actions. */ - float float_threshold; + /** Input thresholds/regions for each subaction path. */ + float *float_thresholds; + eXrAxisFlag *axis_flags; /** The currently active subaction path (if any) for modal actions. */ char **active_modal_path; -- cgit v1.2.3 From 450593ddf0959681b05686368b80963840f297d0 Mon Sep 17 00:00:00 2001 From: Ankit Meel Date: Thu, 5 Aug 2021 11:00:56 +0530 Subject: Cleanup: Fix Clang braced-scalar-init warning --- source/blender/blenkernel/intern/mesh_remesh_voxel.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index 63b0403dcea..9f5703a015d 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -292,7 +292,7 @@ Mesh *BKE_mesh_remesh_voxel(const Mesh *mesh, void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source) { - BVHTreeFromMesh bvhtree = {{nullptr}}; + BVHTreeFromMesh bvhtree = {nullptr}; BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2); MVert *target_verts = (MVert *)CustomData_get_layer(&target->vdata, CD_MVERT); @@ -330,7 +330,7 @@ void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source) void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source) { - BVHTreeFromMesh bvhtree = {{nullptr}}; + BVHTreeFromMesh bvhtree = {nullptr}; const MPoly *target_polys = (const MPoly *)CustomData_get_layer(&target->pdata, CD_MPOLY); const MVert *target_verts = (const MVert *)CustomData_get_layer(&target->vdata, CD_MVERT); @@ -377,7 +377,7 @@ void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source) void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source) { - BVHTreeFromMesh bvhtree = {{nullptr}}; + BVHTreeFromMesh bvhtree = {nullptr}; BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2); int tot_color_layer = CustomData_number_of_layers(&source->vdata, CD_PROP_COLOR); -- cgit v1.2.3 From 2b51124d6a59bedf00397d4ef2fd4442c4fddd22 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 5 Aug 2021 16:44:01 +1000 Subject: Fix T89450: Crash slicing BMEditSelSeq Slicing with indices greater than the length of the sequence would crash. --- source/blender/python/bmesh/bmesh_py_types_select.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/source/blender/python/bmesh/bmesh_py_types_select.c b/source/blender/python/bmesh/bmesh_py_types_select.c index 9bb9815f731..99f17bcec8f 100644 --- a/source/blender/python/bmesh/bmesh_py_types_select.c +++ b/source/blender/python/bmesh/bmesh_py_types_select.c @@ -205,7 +205,6 @@ static PyObject *bpy_bmeditselseq_subscript_slice(BPy_BMEditSelSeq *self, Py_ssize_t stop) { int count = 0; - bool ok; PyObject *list; BMEditSelection *ese; @@ -214,30 +213,22 @@ static PyObject *bpy_bmeditselseq_subscript_slice(BPy_BMEditSelSeq *self, list = PyList_New(0); - ese = self->bm->selected.first; - - ok = (ese != NULL); - - if (UNLIKELY(ok == false)) { - return list; - } - - /* first loop up-until the start */ - for (ok = true; ok; ok = ((ese = ese->next) != NULL)) { + /* First loop up-until the start. */ + for (ese = self->bm->selected.first; ese; ese = ese->next) { if (count == start) { break; } count++; } - /* add items until stop */ - do { + /* Add items until stop. */ + for (; ese; ese = ese->next) { PyList_APPEND(list, BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head)); count++; if (count == stop) { break; } - } while ((ese = ese->next)); + } return list; } -- cgit v1.2.3 From d8582d966fe4ae46c82fcbb69d3d0ca62238ea93 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 5 Aug 2021 16:44:03 +1000 Subject: Fix slicing with negative indices Negative indices that remained negative after adding the sequence length caused incorrect slicing. With the default scene for example: bpy.context.scene.objects[-4:2] Gave a different result to: tuple(bpy.context.scene.objects)[-4:2] Clamp indices above zero so loops that step forward works as intended. --- source/blender/python/bmesh/bmesh_py_types.c | 2 ++ source/blender/python/bmesh/bmesh_py_types_customdata.c | 2 ++ source/blender/python/bmesh/bmesh_py_types_select.c | 2 ++ source/blender/python/intern/bpy_rna.c | 4 ++++ 4 files changed, 10 insertions(+) diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 612446e6d19..e5e601e0eb6 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -3241,9 +3241,11 @@ static PyObject *bpy_bmelemseq_subscript(BPy_BMElemSeq *self, PyObject *key) const Py_ssize_t len = bpy_bmelemseq_length(self); if (start < 0) { start += len; + CLAMP_MIN(start, 0); } if (stop < 0) { stop += len; + CLAMP_MIN(stop, 0); } } diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c index ff06cf43026..0aa92158524 100644 --- a/source/blender/python/bmesh/bmesh_py_types_customdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c @@ -843,9 +843,11 @@ static PyObject *bpy_bmlayercollection_subscript(BPy_BMLayerCollection *self, Py const Py_ssize_t len = bpy_bmlayercollection_length(self); if (start < 0) { start += len; + CLAMP_MIN(start, 0); } if (stop < 0) { stop += len; + CLAMP_MIN(stop, 0); } } diff --git a/source/blender/python/bmesh/bmesh_py_types_select.c b/source/blender/python/bmesh/bmesh_py_types_select.c index 99f17bcec8f..b89822a080c 100644 --- a/source/blender/python/bmesh/bmesh_py_types_select.c +++ b/source/blender/python/bmesh/bmesh_py_types_select.c @@ -273,9 +273,11 @@ static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *ke const Py_ssize_t len = bpy_bmeditselseq_length(self); if (start < 0) { start += len; + CLAMP_MIN(start, 0); } if (stop < 0) { stop += len; + CLAMP_MIN(stop, 0); } } diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index dff96f74d62..ec2e70ad262 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -2825,9 +2825,11 @@ static PyObject *pyrna_prop_collection_subscript(BPy_PropertyRNA *self, PyObject const Py_ssize_t len = (Py_ssize_t)RNA_property_collection_length(&self->ptr, self->prop); if (start < 0) { start += len; + CLAMP_MIN(start, 0); } if (stop < 0) { stop += len; + CLAMP_MIN(stop, 0); } } @@ -2955,9 +2957,11 @@ static int pyrna_prop_collection_ass_subscript(BPy_PropertyRNA *self, Py_ssize_t len = (Py_ssize_t)RNA_property_collection_length(&self->ptr, self->prop); if (start < 0) { start += len; + CLAMP_MIN(start, 0); } if (stop < 0) { stop += len; + CLAMP_MIN(stop, 0); } } -- cgit v1.2.3 From f5acfd9c04697ee046297e1c7b36d0cef9e2440a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 5 Aug 2021 16:48:29 +1000 Subject: Cleanup: remove redundant parenthesis --- intern/cycles/kernel/kernel_path.h | 2 +- source/blender/blenkernel/intern/anim_sys.c | 4 ++-- source/blender/blenkernel/intern/autoexec.c | 2 +- source/blender/blenkernel/intern/fluid.c | 2 +- source/blender/blenkernel/intern/lib_override.c | 8 ++++---- source/blender/blenkernel/intern/pbvh.c | 2 +- source/blender/blenkernel/intern/pointcache.c | 2 +- source/blender/blenkernel/intern/scene.c | 8 ++++---- source/blender/blenkernel/intern/studiolight.c | 24 +++++++++++----------- source/blender/blenkernel/intern/tracking_auto.c | 4 ++-- source/blender/blenkernel/intern/writeffmpeg.c | 2 +- source/blender/blenlib/intern/math_geom.c | 8 ++++---- source/blender/blenloader/intern/versioning_260.c | 2 +- .../blender/blenloader/intern/versioning_legacy.c | 2 +- .../operations/COM_CryptomatteOperation.cc | 4 ++-- .../builder/deg_builder_relations_view_layer.cc | 2 +- .../draw/engines/workbench/workbench_private.h | 2 +- source/blender/draw/intern/draw_manager.c | 2 +- source/blender/draw/intern/draw_manager_exec.c | 4 ++-- source/blender/editors/armature/pose_transform.c | 4 ++-- source/blender/editors/asset/intern/asset_ops.cc | 2 +- source/blender/editors/curve/editcurve_add.c | 4 ++-- source/blender/editors/interface/interface.c | 12 +++++------ .../blender/editors/interface/interface_handlers.c | 2 +- source/blender/editors/interface/interface_icons.c | 2 +- source/blender/editors/screen/screen_ops.c | 2 +- source/blender/editors/sculpt_paint/sculpt_uv.c | 4 ++-- source/blender/editors/space_file/fsmenu.c | 2 +- .../editors/space_sequencer/sequencer_draw.c | 2 +- .../editors/space_sequencer/sequencer_proxy.c | 4 ++-- .../editors/space_sequencer/sequencer_view.c | 2 +- .../editors/space_view3d/view3d_navigate_walk.c | 16 +++++++-------- .../blender/editors/space_view3d/view3d_select.c | 2 +- source/blender/editors/transform/transform.c | 2 +- .../editors/transform/transform_convert_armature.c | 2 +- .../editors/transform/transform_snap_sequencer.c | 2 +- source/blender/editors/uvedit/uvedit_select.c | 2 +- .../freestyle/intern/python/BPy_Freestyle.cpp | 2 +- .../freestyle/intern/view_map/ViewMapBuilder.cpp | 10 ++++----- source/blender/io/avi/intern/avi_endian.c | 2 +- .../composite/nodes/node_composite_switchview.c | 2 +- .../blender/python/bmesh/bmesh_py_types_meshdata.c | 6 +++--- source/blender/python/intern/bpy_gizmo_wrap.c | 2 +- source/blender/python/intern/bpy_utils_units.c | 2 +- source/blender/render/intern/pipeline.c | 2 +- source/blender/render/intern/render_result.c | 2 +- .../blender/windowmanager/intern/wm_gesture_ops.c | 2 +- source/creator/creator_args.c | 4 ++-- 48 files changed, 95 insertions(+), 95 deletions(-) diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index ec577fa20b0..92a097de9e1 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -260,7 +260,7 @@ ccl_device_forceinline bool kernel_path_shader_apply(KernelGlobals *kg, PROFILING_INIT(kg, PROFILING_SHADER_APPLY); #ifdef __SHADOW_TRICKS__ - if ((sd->object_flag & SD_OBJECT_SHADOW_CATCHER)) { + if (sd->object_flag & SD_OBJECT_SHADOW_CATCHER) { if (state->flag & PATH_RAY_TRANSPARENT_BACKGROUND) { state->flag |= (PATH_RAY_SHADOW_CATCHER | PATH_RAY_STORE_SHADOW_INFO); diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index fae75762cde..10a865880f2 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -426,7 +426,7 @@ bool BKE_animsys_rna_path_resolve(PointerRNA *ptr, } /* less than 1.0 evaluates to false, use epsilon to avoid float error */ -#define ANIMSYS_FLOAT_AS_BOOL(value) ((value) > ((1.0f - FLT_EPSILON))) +#define ANIMSYS_FLOAT_AS_BOOL(value) ((value) > (1.0f - FLT_EPSILON)) bool BKE_animsys_read_from_rna_path(PathResolvedRNA *anim_rna, float *r_value) { @@ -2625,7 +2625,7 @@ static void animsys_create_action_track_strip(const AnimData *adt, bAction *action = adt->action; - if ((adt->flag & ADT_NLA_EDIT_ON)) { + if (adt->flag & ADT_NLA_EDIT_ON) { action = adt->tmpact; } diff --git a/source/blender/blenkernel/intern/autoexec.c b/source/blender/blenkernel/intern/autoexec.c index 07b096af941..3aec646b024 100644 --- a/source/blender/blenkernel/intern/autoexec.c +++ b/source/blender/blenkernel/intern/autoexec.c @@ -56,7 +56,7 @@ bool BKE_autoexec_match(const char *path) if (path_cmp->path[0] == '\0') { /* pass */ } - else if ((path_cmp->flag & USER_PATHCMP_GLOB)) { + else if (path_cmp->flag & USER_PATHCMP_GLOB) { if (fnmatch(path_cmp->path, path, fnmatch_flags) == 0) { return true; } diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 92fd220549a..d33697d551e 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -4153,7 +4153,7 @@ static void BKE_fluid_modifier_process( { const int scene_framenr = (int)DEG_get_ctime(depsgraph); - if ((fmd->type & MOD_FLUID_TYPE_FLOW)) { + if (fmd->type & MOD_FLUID_TYPE_FLOW) { BKE_fluid_modifier_processFlow(fmd, depsgraph, scene, ob, me, scene_framenr); } else if (fmd->type & MOD_FLUID_TYPE_EFFEC) { diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 38687fa47b2..072304566e5 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -571,7 +571,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat * would use one of those. * NOTE: missing IDs (aka placeholders) are never overridden. */ if (ELEM(GS(to_id->name), ID_OB, ID_GR)) { - if ((to_id->tag & LIB_TAG_MISSING)) { + if (to_id->tag & LIB_TAG_MISSING) { to_id->tag |= missing_tag; } else { @@ -604,7 +604,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) const bool is_resync = data->is_resync; BLI_assert(!data->is_override); - if ((id_root->tag & LIB_TAG_MISSING)) { + if (id_root->tag & LIB_TAG_MISSING) { id_root->tag |= data->missing_tag; } else { @@ -654,7 +654,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) if (instantiating_collection == NULL && instantiating_collection_override_candidate != NULL) { - if ((instantiating_collection_override_candidate->id.tag & LIB_TAG_MISSING)) { + if (instantiating_collection_override_candidate->id.tag & LIB_TAG_MISSING) { instantiating_collection_override_candidate->id.tag |= data->missing_tag; } else { @@ -730,7 +730,7 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data) BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); BLI_assert(data->is_override); - if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) { + if (id_root->override_library->reference->tag & LIB_TAG_MISSING) { id_root->tag |= data->missing_tag; } else { diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 461ffa7765e..ca1fada8c76 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1026,7 +1026,7 @@ static void pbvh_update_normals_accum_task_cb(void *__restrict userdata, PBVHNode *node = data->nodes[n]; float(*vnors)[3] = data->vnors; - if ((node->flag & PBVH_UpdateNormals)) { + if (node->flag & PBVH_UpdateNormals) { unsigned int mpoly_prev = UINT_MAX; float fn[3]; diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index a6adff35a7f..57225872c7e 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -3369,7 +3369,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker) } /* NOTE: breaking baking should leave calculated frames in cache, not clear it */ - if ((cancel || G.is_break)) { + if (cancel || G.is_break) { break; } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 6d60f5e91b4..9dab276af95 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -2178,7 +2178,7 @@ int BKE_scene_base_iter_next( /* exception: empty scene layer */ while ((*scene)->set) { (*scene) = (*scene)->set; - ViewLayer *view_layer_set = BKE_view_layer_default_render((*scene)); + ViewLayer *view_layer_set = BKE_view_layer_default_render(*scene); if (view_layer_set->object_bases.first) { *base = view_layer_set->object_bases.first; *ob = (*base)->object; @@ -2199,7 +2199,7 @@ int BKE_scene_base_iter_next( /* (*scene) is finished, now do the set */ while ((*scene)->set) { (*scene) = (*scene)->set; - ViewLayer *view_layer_set = BKE_view_layer_default_render((*scene)); + ViewLayer *view_layer_set = BKE_view_layer_default_render(*scene); if (view_layer_set->object_bases.first) { *base = view_layer_set->object_bases.first; *ob = (*base)->object; @@ -2898,7 +2898,7 @@ Base *_setlooper_base_step(Scene **sce_iter, ViewLayer *view_layer, Base *base) next_set: /* Reached the end, get the next base in the set. */ while ((*sce_iter = (*sce_iter)->set)) { - ViewLayer *view_layer_set = BKE_view_layer_default_render((*sce_iter)); + ViewLayer *view_layer_set = BKE_view_layer_default_render(*sce_iter); base = (Base *)view_layer_set->object_bases.first; if (base) { @@ -3118,7 +3118,7 @@ bool BKE_scene_multiview_is_render_view_active(const RenderData *rd, const Scene return false; } - if ((srv->viewflag & SCE_VIEW_DISABLE)) { + if (srv->viewflag & SCE_VIEW_DISABLE) { return false; } diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c index dc5162f201e..95436372a65 100644 --- a/source/blender/blenkernel/intern/studiolight.c +++ b/source/blender/blenkernel/intern/studiolight.c @@ -475,7 +475,7 @@ static void studiolight_load_equirect_image(StudioLight *sl) NULL, (failed || (specular_ibuf == NULL)) ? magenta : black, 1, 1, 4); } - if ((sl->flag & STUDIOLIGHT_TYPE_MATCAP)) { + if (sl->flag & STUDIOLIGHT_TYPE_MATCAP) { sl->matcap_diffuse.ibuf = diffuse_ibuf; sl->matcap_specular.ibuf = specular_ibuf; if (specular_ibuf != NULL) { @@ -1192,7 +1192,7 @@ static void studiolight_add_files_from_datafolder(const int folder_id, uint totfile = BLI_filelist_dir_contents(folder, &dir); int i; for (i = 0; i < totfile; i++) { - if ((dir[i].type & S_IFREG)) { + if (dir[i].type & S_IFREG) { studiolight_add_file(dir[i].path, flag); } } @@ -1473,7 +1473,7 @@ struct StudioLight *BKE_studiolight_find_default(int flag) } LISTBASE_FOREACH (StudioLight *, sl, &studiolights) { - if ((sl->flag & flag)) { + if (sl->flag & flag) { return sl; } } @@ -1484,7 +1484,7 @@ struct StudioLight *BKE_studiolight_find(const char *name, int flag) { LISTBASE_FOREACH (StudioLight *, sl, &studiolights) { if (STREQLEN(sl->name, name, FILE_MAXFILE)) { - if ((sl->flag & flag)) { + if (sl->flag & flag) { return sl; } @@ -1542,32 +1542,32 @@ void BKE_studiolight_ensure_flag(StudioLight *sl, int flag) return; } - if ((flag & STUDIOLIGHT_EXTERNAL_IMAGE_LOADED)) { + if (flag & STUDIOLIGHT_EXTERNAL_IMAGE_LOADED) { studiolight_load_equirect_image(sl); } - if ((flag & STUDIOLIGHT_RADIANCE_BUFFERS_CALCULATED)) { + if (flag & STUDIOLIGHT_RADIANCE_BUFFERS_CALCULATED) { studiolight_calculate_radiance_cubemap_buffers(sl); } - if ((flag & STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED)) { + if (flag & STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED) { if (!studiolight_load_spherical_harmonics_coefficients(sl)) { studiolight_calculate_diffuse_light(sl); } } - if ((flag & STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE)) { + if (flag & STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE) { studiolight_create_equirect_radiance_gputexture(sl); } - if ((flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_GPUTEXTURE)) { + if (flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_GPUTEXTURE) { studiolight_create_equirect_irradiance_gputexture(sl); } - if ((flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED)) { + if (flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED) { if (!studiolight_load_irradiance_equirect_image(sl)) { studiolight_calculate_irradiance_equirect_image(sl); } } - if ((flag & STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE)) { + if (flag & STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE) { studiolight_create_matcap_diffuse_gputexture(sl); } - if ((flag & STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE)) { + if (flag & STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE) { studiolight_create_matcap_specular_gputexture(sl); } } diff --git a/source/blender/blenkernel/intern/tracking_auto.c b/source/blender/blenkernel/intern/tracking_auto.c index f107fc4d6bc..92ff0911cf3 100644 --- a/source/blender/blenkernel/intern/tracking_auto.c +++ b/source/blender/blenkernel/intern/tracking_auto.c @@ -322,7 +322,7 @@ static bool tracking_check_marker_margin(const libmv_Marker *libmv_marker, static bool autotrack_is_marker_usable(const MovieTrackingMarker *marker) { - if ((marker->flag & MARKER_DISABLED)) { + if (marker->flag & MARKER_DISABLED) { return false; } return true; @@ -797,7 +797,7 @@ void BKE_autotrack_context_finish(AutoTrackContext *context) clip, context->start_scene_frame); LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, plane_tracks_base) { - if ((plane_track->flag & PLANE_TRACK_AUTOKEY)) { + if (plane_track->flag & PLANE_TRACK_AUTOKEY) { continue; } for (int track_index = 0; track_index < context->num_all_tracks; track_index++) { diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 9b3103a638b..32057709c38 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -741,7 +741,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } - if ((of->oformat->flags & AVFMT_GLOBALHEADER)) { + if (of->oformat->flags & AVFMT_GLOBALHEADER) { PRINT("Using global header\n"); c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 823e72a91e5..8afb6b5a2be 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -1787,7 +1787,7 @@ bool isect_ray_tri_v3(const float ray_origin[3], } *r_lambda = f * dot_v3v3(e2, q); - if ((*r_lambda < 0.0f)) { + if (*r_lambda < 0.0f) { return false; } @@ -1864,7 +1864,7 @@ bool isect_ray_tri_epsilon_v3(const float ray_origin[3], } *r_lambda = f * dot_v3v3(e2, q); - if ((*r_lambda < 0.0f)) { + if (*r_lambda < 0.0f) { return false; } @@ -2024,7 +2024,7 @@ bool isect_ray_tri_threshold_v3(const float ray_origin[3], cross_v3_v3v3(q, s, e1); *r_lambda = f * dot_v3v3(e2, q); - if ((*r_lambda < 0.0f)) { + if (*r_lambda < 0.0f) { return false; } @@ -3325,7 +3325,7 @@ bool isect_ray_aabb_v3_simple(const float orig[3], t[5] = (double)(bb_max[2] - orig[2]) * invdirz; hit_dist[0] = (float)fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5])); hit_dist[1] = (float)fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5])); - if ((hit_dist[1] < 0.0f || hit_dist[0] > hit_dist[1])) { + if ((hit_dist[1] < 0.0f) || (hit_dist[0] > hit_dist[1])) { return false; } diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 858f5d85a90..7c644fa3b55 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -2390,7 +2390,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) for (md = ob->modifiers.first; md; md = md->next) { if (md->type == eModifierType_Triangulate) { TriangulateModifierData *tmd = (TriangulateModifierData *)md; - if ((tmd->flag & MOD_TRIANGULATE_BEAUTY)) { + if (tmd->flag & MOD_TRIANGULATE_BEAUTY) { tmd->quad_method = MOD_TRIANGULATE_QUAD_BEAUTY; tmd->ngon_method = MOD_TRIANGULATE_NGON_BEAUTY; } diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 95cfc9975d7..81371e1c1ed 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -1335,7 +1335,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) ME_OPT_EDGES = (1 << 8), }; - if ((me->flag & ME_SUBSURF)) { + if (me->flag & ME_SUBSURF) { SubsurfModifierData *smd = (SubsurfModifierData *)BKE_modifier_new( eModifierType_Subsurf); diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc index 52ae1d6d5b5..1a86fadad76 100644 --- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc @@ -57,8 +57,8 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat ::memcpy(&m3hash, &input[0], sizeof(uint32_t)); /* Since the red channel is likely to be out of display range, * setting green and blue gives more meaningful images. */ - output[1] = ((float)((m3hash << 8)) / (float)UINT32_MAX); - output[2] = ((float)((m3hash << 16)) / (float)UINT32_MAX); + output[1] = ((float)(m3hash << 8) / (float)UINT32_MAX); + output[2] = ((float)(m3hash << 16) / (float)UINT32_MAX); } for (float hash : m_objectIndex) { if (input[0] == hash) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc index 6527f165552..c37fb1b83a4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc @@ -65,7 +65,7 @@ void DepsgraphRelationBuilder::build_layer_collections(ListBase *lb) COLLECTION_HIDE_RENDER; for (LayerCollection *lc = (LayerCollection *)lb->first; lc; lc = lc->next) { - if ((lc->collection->flag & visibility_flag)) { + if (lc->collection->flag & visibility_flag) { continue; } if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) { diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index 453ef9c7f8e..e17bd7d9956 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -399,7 +399,7 @@ typedef struct WORKBENCH_ViewLayerData { /* inline helper functions */ BLI_INLINE bool workbench_is_specular_highlight_enabled(WORKBENCH_PrivateData *wpd) { - if ((wpd->shading.flag & V3D_SHADING_SPECULAR_HIGHLIGHT)) { + if (wpd->shading.flag & V3D_SHADING_SPECULAR_HIGHLIGHT) { if (STUDIOLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) { return (wpd->studio_light->flag & STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS) != 0; } diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index a8cbe7b18b5..11e2856147f 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -512,7 +512,7 @@ static void drw_context_state_init(void) if (DST.draw_ctx.object_mode & OB_MODE_POSE) { DST.draw_ctx.object_pose = DST.draw_ctx.obact; } - else if ((DST.draw_ctx.object_mode & OB_MODE_ALL_WEIGHT_PAINT)) { + else if (DST.draw_ctx.object_mode & OB_MODE_ALL_WEIGHT_PAINT) { DST.draw_ctx.object_pose = BKE_object_pose_armature_get(DST.draw_ctx.obact); } else { diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 7f7696d485c..22356a3c57b 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -283,11 +283,11 @@ void DRW_state_reset_ex(DRWState state) static void drw_state_validate(void) { /* Cannot write to stencil buffer without stencil test. */ - if ((DST.state & DRW_STATE_WRITE_STENCIL_ENABLED)) { + if (DST.state & DRW_STATE_WRITE_STENCIL_ENABLED) { BLI_assert(DST.state & DRW_STATE_STENCIL_TEST_ENABLED); } /* Cannot write to depth buffer without depth test. */ - if ((DST.state & DRW_STATE_WRITE_DEPTH)) { + if (DST.state & DRW_STATE_WRITE_DEPTH) { BLI_assert(DST.state & DRW_STATE_DEPTH_TEST_ENABLED); } } diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 1118e84ef4f..3798ca308ed 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -908,7 +908,7 @@ static int pose_paste_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); /* Recalculate paths if any of the bones have paths... */ - if ((ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) { + if (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) { ED_pose_recalculate_paths(C, scene, ob, POSE_PATH_CALC_RANGE_FULL); } @@ -1219,7 +1219,7 @@ static int pose_clear_transform_generic_exec(bContext *C, ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); /* now recalculate paths */ - if ((ob_iter->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) { + if (ob_iter->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) { ED_pose_recalculate_paths(C, scene, ob_iter, POSE_PATH_CALC_RANGE_FULL); } diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index f18cf9712db..579803f7ff7 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -110,7 +110,7 @@ void AssetMarkHelper::reportResults(ReportList &reports) const { /* User feedback on failure. */ if (!wasSuccessful()) { - if ((stats.tot_already_asset > 0)) { + if (stats.tot_already_asset > 0) { BKE_report(&reports, RPT_ERROR, "Selected data-blocks are already assets (or do not support use as assets)"); diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index 2be55accd3a..d1fe162fc4a 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -431,7 +431,7 @@ Nurb *ED_curve_add_nurbs_primitive( if (newob && (U.flag & USER_ADD_VIEWALIGNED) == 0) { ed_editnurb_spin(umat, NULL, obedit, tmp_vec, tmp_cent); } - else if ((U.flag & USER_ADD_VIEWALIGNED)) { + else if (U.flag & USER_ADD_VIEWALIGNED) { ed_editnurb_spin(viewmat, NULL, obedit, zvec, mat[3]); } else { @@ -466,7 +466,7 @@ Nurb *ED_curve_add_nurbs_primitive( if (newob && (U.flag & USER_ADD_VIEWALIGNED) == 0) { ed_editnurb_spin(umat, NULL, obedit, tmp_vec, tmp_cent); } - else if ((U.flag & USER_ADD_VIEWALIGNED)) { + else if (U.flag & USER_ADD_VIEWALIGNED) { ed_editnurb_spin(viewmat, NULL, obedit, zvec, mat[3]); } else { diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index d3a3df98d99..fd75be5b847 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -6173,7 +6173,7 @@ int UI_but_return_value_get(uiBut *but) void UI_but_drag_set_id(uiBut *but, ID *id) { but->dragtype = WM_DRAG_ID; - if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } @@ -6200,7 +6200,7 @@ void UI_but_drag_set_asset(uiBut *but, but->dragtype = WM_DRAG_ASSET; ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ - if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); } but->dragpoin = asset_drag; @@ -6212,7 +6212,7 @@ void UI_but_drag_set_asset(uiBut *but, void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr) { but->dragtype = WM_DRAG_RNA; - if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } @@ -6222,7 +6222,7 @@ void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr) void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free) { but->dragtype = WM_DRAG_PATH; - if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } @@ -6235,7 +6235,7 @@ void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free) void UI_but_drag_set_name(uiBut *but, const char *name) { but->dragtype = WM_DRAG_NAME; - if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } @@ -6253,7 +6253,7 @@ void UI_but_drag_set_image( { but->dragtype = WM_DRAG_PATH; ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ - if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index bfc03a95949..d920ebbe11a 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -6779,7 +6779,7 @@ static bool ui_numedit_but_HSVCIRCLE(uiBut *but, ui_color_picker_hsv_to_rgb(hsv, rgb); - if ((cpicker->use_luminosity_lock)) { + if (cpicker->use_luminosity_lock) { if (!is_zero_v3(rgb)) { normalize_v3_length(rgb, cpicker->luminosity_lock_value); } diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 6755eded05c..2d59bfb92c8 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1014,7 +1014,7 @@ static void init_iconfile_list(struct ListBase *list) int index = 1; for (int i = 0; i < totfile; i++) { - if ((dir[i].type & S_IFREG)) { + if (dir[i].type & S_IFREG) { const char *filename = dir[i].relname; if (BLI_path_extension_check(filename, ".png")) { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 0bc2a224a4b..d8cef889a40 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -2906,7 +2906,7 @@ static void areas_do_frame_follow(bContext *C, bool middle) LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { /* do follow here if editor type supports it */ - if ((screen_ctx->redraws_flag & TIME_FOLLOW)) { + if (screen_ctx->redraws_flag & TIME_FOLLOW) { if ((region->regiontype == RGN_TYPE_WINDOW && ELEM(area->spacetype, SPACE_SEQ, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) || (area->spacetype == SPACE_CLIP && region->regiontype == RGN_TYPE_PREVIEW)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 771e0e1e47b..e5ca5e4defd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -190,7 +190,7 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em, /* This is supposed to happen only if "Pin Edges" is on, * since we have initialization on stroke start. * If ever uv brushes get their own mode we should check for toolsettings option too. */ - if ((sculptdata->uv[i].flag & MARK_BOUNDARY)) { + if (sculptdata->uv[i].flag & MARK_BOUNDARY) { continue; } @@ -268,7 +268,7 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em, /* This is supposed to happen only if "Pin Edges" is on, * since we have initialization on stroke start. * If ever uv brushes get their own mode we should check for toolsettings option too. */ - if ((sculptdata->uv[i].flag & MARK_BOUNDARY)) { + if (sculptdata->uv[i].flag & MARK_BOUNDARY) { continue; } diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 2d1151c8f4d..776bb0b3bb7 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -969,7 +969,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) BLI_join_dirfile(name, sizeof(name), xdg_runtime_dir, "gvfs/"); const uint dir_len = BLI_filelist_dir_contents(name, &dir); for (uint i = 0; i < dir_len; i++) { - if ((dir[i].type & S_IFDIR)) { + if (dir[i].type & S_IFDIR) { const char *dirname = dir[i].relname; if (dirname[0] != '.') { /* Dir names contain a lot of unwanted text. diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 3f8dea8b533..0472e1264ce 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1139,7 +1139,7 @@ static void draw_seq_strip(const bContext *C, } /* Draw strip offsets when flag is enabled or during "solo preview". */ - if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) { + if (sseq->flag & SEQ_SHOW_STRIP_OVERLAY) { if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) { if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) { draw_sequence_extensions_overlay(scene, seq, pos, pixely); diff --git a/source/blender/editors/space_sequencer/sequencer_proxy.c b/source/blender/editors/space_sequencer/sequencer_proxy.c index 2dcc2d389d9..16d14b5fa72 100644 --- a/source/blender/editors/space_sequencer/sequencer_proxy.c +++ b/source/blender/editors/space_sequencer/sequencer_proxy.c @@ -131,7 +131,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list"); LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { - if ((seq->flag & SELECT)) { + if (seq->flag & SELECT) { ListBase queue = {NULL, NULL}; LinkData *link; short stop = 0, do_update; @@ -197,7 +197,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op) } LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { - if ((seq->flag & SELECT)) { + if (seq->flag & SELECT) { if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) { SEQ_proxy_set(seq, turnon); if (seq->strip->proxy == NULL) { diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index c5385e24d2c..a0a9cdd96b1 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -292,7 +292,7 @@ static int sequencer_view_selected_exec(bContext *C, wmOperator *op) } for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if ((seq->flag & SELECT)) { + if (seq->flag & SELECT) { xmin = min_ii(xmin, seq->startdisp); xmax = max_ii(xmax, seq->enddisp); diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c index 09936b41a74..1ac241013ed 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_walk.c +++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c @@ -548,7 +548,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->teleport.duration = U.walk_navigation.teleport_time; walk->mouse_speed = U.walk_navigation.mouse_speed; - if ((U.walk_navigation.flag & USER_WALK_GRAVITY)) { + if (U.walk_navigation.flag & USER_WALK_GRAVITY) { walk_navigation_mode_set(walk, WALK_MODE_GRAVITY); } else { @@ -563,7 +563,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->gravity_state = WALK_GRAVITY_STATE_OFF; - if ((walk->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY)) { + if (walk->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { walk->gravity = fabsf(walk->scene->physics_settings.gravity[2]); } else { @@ -1199,11 +1199,11 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm) direction = 0; - if ((walk->active_directions & WALK_BIT_FORWARD)) { + if (walk->active_directions & WALK_BIT_FORWARD) { direction += 1; } - if ((walk->active_directions & WALK_BIT_BACKWARD)) { + if (walk->active_directions & WALK_BIT_BACKWARD) { direction -= 1; } @@ -1223,11 +1223,11 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm) direction = 0; - if ((walk->active_directions & WALK_BIT_LEFT)) { + if (walk->active_directions & WALK_BIT_LEFT) { direction += 1; } - if ((walk->active_directions & WALK_BIT_RIGHT)) { + if (walk->active_directions & WALK_BIT_RIGHT) { direction -= 1; } @@ -1245,11 +1245,11 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm) direction = 0; - if ((walk->active_directions & WALK_BIT_UP)) { + if (walk->active_directions & WALK_BIT_UP) { direction -= 1; } - if ((walk->active_directions & WALK_BIT_DOWN)) { + if (walk->active_directions & WALK_BIT_DOWN) { direction = 1; } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 5ec3e9cae5a..2ce5684e874 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -2156,7 +2156,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, for (a = 0; a < hits; a++) { if (has_bones) { /* skip non-bone objects */ - if ((buffer[4 * a + 3] & 0xFFFF0000)) { + if (buffer[4 * a + 3] & 0xFFFF0000) { if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & 0xFFFF)) { basact = base; } diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index efcf7d587e1..96b84bd2a35 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1828,7 +1828,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if ((((Mesh *)(tc->obedit->data))->flag & ME_AUTOSMOOTH)) { + if (((Mesh *)(tc->obedit->data))->flag & ME_AUTOSMOOTH) { BMEditMesh *em = NULL; /* BKE_editmesh_from_object(t->obedit); */ bool do_skip = false; diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 1f1b1f8db97..f56d60b7376 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -1515,7 +1515,7 @@ int transform_convert_pose_transflags_update(Object *ob, for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { bone = pchan->bone; if (PBONE_VISIBLE(arm, bone)) { - if ((bone->flag & BONE_SELECTED)) { + if (bone->flag & BONE_SELECTED) { bone->flag |= BONE_TRANSFORM; } else { diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index a1f396eb503..6e926f36fba 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -141,7 +141,7 @@ static SeqCollection *query_snap_targets(const TransInfo *t, SeqCollection *snap const short snap_flag = SEQ_tool_settings_snap_flag_get(t->scene); SeqCollection *snap_targets = SEQ_collection_create(__func__); LISTBASE_FOREACH (Sequence *, seq, seqbase) { - if ((seq->flag & SELECT)) { + if (seq->flag & SELECT) { continue; /* Selected are being transformed. */ } if ((seq->flag & SEQ_MUTE) && (snap_flag & SEQ_SNAP_IGNORE_MUTED)) { diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 4c597d80534..5a82cd31112 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -3912,7 +3912,7 @@ BMLoop **ED_uvedit_selected_verts(Scene *scene, BMesh *bm, int len_max, int *r_v BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); - if ((luv->flag & MLOOPUV_VERTSEL)) { + if (luv->flag & MLOOPUV_VERTSEL) { BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG); verts[verts_len++] = l_iter; diff --git a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp index db85292c6c0..2d0021a1fe8 100644 --- a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp +++ b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp @@ -266,7 +266,7 @@ static PyObject *Freestyle_evaluateCurveMappingF(PyObject * /*self*/, PyObject * cumap = (CurveMapping *)py_srna->ptr.data; BKE_curvemapping_init(cumap); /* disable extrapolation if enabled */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE)) { + if (cumap->flag & CUMA_EXTEND_EXTRAPOLATE) { cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE; BKE_curvemapping_changed(cumap, false); } diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp index afb23690a84..b1aea8bf6cf 100644 --- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp +++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp @@ -527,7 +527,7 @@ static void computeCumulativeVisibility(ViewMap *ioViewMap, fe = fe->nextEdge(); continue; } - if ((maxCard < qiMajority)) { + if (maxCard < qiMajority) { // ARB: change &wFace to wFace and use reference in called function tmpQI = computeVisibility( ioViewMap, fe, grid, epsilon, *ve, &wFace, &foundOccluders); @@ -725,7 +725,7 @@ static void computeDetailedVisibility(ViewMap *ioViewMap, fe = fe->nextEdge(); continue; } - if ((maxCard < qiMajority)) { + if (maxCard < qiMajority) { // ARB: change &wFace to wFace and use reference in called function tmpQI = computeVisibility( ioViewMap, fe, grid, epsilon, *ve, &wFace, &foundOccluders); @@ -891,7 +891,7 @@ static void computeFastVisibility(ViewMap *ioViewMap, G &grid, real epsilon) continue; } if (even_test) { - if ((maxCard < qiMajority)) { + if (maxCard < qiMajority) { // ARB: change &wFace to wFace and use reference in called function tmpQI = computeVisibility( ioViewMap, fe, grid, epsilon, *ve, &wFace, &foundOccluders); @@ -1607,7 +1607,7 @@ void ViewMapBuilder::ComputeRayCastingVisibility(ViewMap *ioViewMap, real epsilo memset(qiClasses, 0, 256 * sizeof(*qiClasses)); set occluders; do { - if ((maxCard < qiMajority)) { + if (maxCard < qiMajority) { tmpQI = ComputeRayCastingVisibility(fe, _Grid, epsilon, occluders, &aFace, timestamp++); #if LOGGING @@ -1763,7 +1763,7 @@ void ViewMapBuilder::ComputeFastRayCastingVisibility(ViewMap *ioViewMap, real ep fe = (*ve)->fedgeA(); do { if (even_test) { - if ((maxCard < qiMajority)) { + if (maxCard < qiMajority) { tmpQI = ComputeRayCastingVisibility(fe, _Grid, epsilon, occluders, &aFace, timestamp++); // ARB: This is an error condition, not an alert condition. diff --git a/source/blender/io/avi/intern/avi_endian.c b/source/blender/io/avi/intern/avi_endian.c index 146206cd917..36cee7bcadc 100644 --- a/source/blender/io/avi/intern/avi_endian.c +++ b/source/blender/io/avi/intern/avi_endian.c @@ -42,7 +42,7 @@ static void invert(int *val) { int tval = *val; - *val = ((tval >> 24)) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | ((tval << 24)); + *val = (tval >> 24) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | (tval << 24); } static void sinvert(short int *val) diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.c b/source/blender/nodes/composite/nodes/node_composite_switchview.c index 3ffad8216de..ec5c79cc087 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switchview.c +++ b/source/blender/nodes/composite/nodes/node_composite_switchview.c @@ -132,7 +132,7 @@ static void init_switch_view(const bContext *C, PointerRNA *ptr) for (nr = 0, srv = rd->views.first; srv; srv = srv->next, nr++) { sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name); - if ((srv->viewflag & SCE_VIEW_DISABLE)) { + if (srv->viewflag & SCE_VIEW_DISABLE) { sock->flag |= SOCK_HIDDEN; } } diff --git a/source/blender/python/bmesh/bmesh_py_types_meshdata.c b/source/blender/python/bmesh/bmesh_py_types_meshdata.c index 8f4e07c30d3..d0c745e6a1d 100644 --- a/source/blender/python/bmesh/bmesh_py_types_meshdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_meshdata.c @@ -443,7 +443,7 @@ static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key, } if (value) { - /* dvert[group_index] = 0.5 */ + /* Handle `dvert[group_index] = 0.5`. */ if (i < 0) { PyErr_SetString(PyExc_KeyError, "BMDeformVert[key] = x: " @@ -453,7 +453,7 @@ static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key, MDeformWeight *dw = BKE_defvert_ensure_index(self->data, i); const float f = PyFloat_AsDouble(value); - if (f == -1 && PyErr_Occurred()) { // parsed key not a number + if (f == -1 && PyErr_Occurred()) { /* Parsed key not a number. */ PyErr_SetString(PyExc_TypeError, "BMDeformVert[key] = x: " "assigned value not a number"); @@ -463,7 +463,7 @@ static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key, dw->weight = clamp_f(f, 0.0f, 1.0f); } else { - /* del dvert[group_index] */ + /* Handle `del dvert[group_index]`. */ MDeformWeight *dw = BKE_defvert_find_index(self->data, i); if (dw == NULL) { diff --git a/source/blender/python/intern/bpy_gizmo_wrap.c b/source/blender/python/intern/bpy_gizmo_wrap.c index 42e0c7d0003..a05ec6b7000 100644 --- a/source/blender/python/intern/bpy_gizmo_wrap.c +++ b/source/blender/python/intern/bpy_gizmo_wrap.c @@ -79,7 +79,7 @@ static bool bpy_gizmotype_target_property_def(wmGizmoType *gzt, PyObject *item) goto fail; } - if ((params.array_length < 1 || params.array_length > RNA_MAX_ARRAY_LENGTH)) { + if ((params.array_length < 1) || (params.array_length > RNA_MAX_ARRAY_LENGTH)) { PyErr_SetString(PyExc_ValueError, "'array_length' out of range"); goto fail; } diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c index aa8cf8f2a9f..62f5a17c4dd 100644 --- a/source/blender/python/intern/bpy_utils_units.c +++ b/source/blender/python/intern/bpy_utils_units.c @@ -114,7 +114,7 @@ static PyObject *py_structseq_from_strings(PyTypeObject *py_type, BLI_assert(py_struct_seq != NULL); for (str_iter = str_items; *str_iter; str_iter++) { - PyStructSequence_SET_ITEM(py_struct_seq, pos++, PyUnicode_FromString((*str_iter))); + PyStructSequence_SET_ITEM(py_struct_seq, pos++, PyUnicode_FromString(*str_iter)); } return py_struct_seq; diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 333ee9ecd33..479ad9209f0 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -2524,7 +2524,7 @@ void RE_RenderAnim(Render *re, if (G.is_break == true) { /* remove touched file */ if (is_movie == false && do_write_file) { - if ((rd.mode & R_TOUCH)) { + if (rd.mode & R_TOUCH) { if (!is_multiview_name) { if ((BLI_file_size(name) == 0)) { /* BLI_exists(name) is implicit */ diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index 693cddbebbe..091f5964291 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -784,7 +784,7 @@ void render_result_views_new(RenderResult *rr, const RenderData *rd) render_result_views_free(rr); /* check renderdata for amount of views */ - if ((rd->scemode & R_MULTIVIEW)) { + if (rd->scemode & R_MULTIVIEW) { for (srv = rd->views.first; srv; srv = srv->next) { if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) { continue; diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 92ca0b87527..1c736647084 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -585,7 +585,7 @@ void wm_tweakevent_test(bContext *C, const wmEvent *event, int action) } else { /* no tweaks if event was handled */ - if ((action & WM_HANDLER_BREAK)) { + if (action & WM_HANDLER_BREAK) { WM_gesture_end(win, win->tweak); } else { diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index c75174dfff0..0f450624691 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -121,7 +121,7 @@ static bool parse_int_relative(const char *str, *r_err_msg = msg; return false; } - if ((errno == ERANGE) || ((value < INT_MIN || value > INT_MAX))) { + if ((errno == ERANGE) || ((value < INT_MIN) || (value > INT_MAX))) { static const char *msg = "exceeds range"; *r_err_msg = msg; return false; @@ -225,7 +225,7 @@ static bool parse_int_strict_range(const char *str, *r_err_msg = msg; return false; } - if ((errno == ERANGE) || ((value < min || value > max))) { + if ((errno == ERANGE) || ((value < min) || (value > max))) { static const char *msg = "exceeds range"; *r_err_msg = msg; return false; -- cgit v1.2.3 From 647a8bff06bfabeafa01333d4b9d8d8b8ae1c569 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 5 Aug 2021 18:17:56 +1000 Subject: Fix T90256: faces are flat shaded in edit mode with auto smooth Regression in 39b2a7bb7e815e051348bf5c5ec777d091324164. --- source/blender/bmesh/intern/bmesh_mesh_normals.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index 6504a2de116..8eda38046a1 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -50,8 +50,6 @@ static void bm_edge_tag_from_smooth(const float (*fnos)[3], BMEdge *e, const float split_angle_cos); -static void bm_edge_tag_clear(BMEdge *e); - /* -------------------------------------------------------------------- */ /** \name Update Vertex & Face Normals * \{ */ @@ -866,13 +864,6 @@ static void bm_edge_tag_from_smooth(const float (*fnos)[3], BMEdge *e, const flo } } -static void bm_edge_tag_clear(BMEdge *e) -{ - /* No need for atomics here as this is a single byte. */ - char *hflag_p = &e->head.hflag; - *hflag_p = *hflag_p & ~BM_ELEM_TAG; -} - /** * A version of #bm_edge_tag_from_smooth that sets sharp edges * when they would be considered smooth but exceed the split angle . @@ -941,6 +932,7 @@ static void bm_mesh_loops_calc_normals_for_vert_with_clnors(BMesh *bm, const bool has_clnors = true; LinkNode *loops_of_vert = NULL; int loops_of_vert_count = 0; + /* When false the caller must have already tagged the edges. */ const bool do_edge_tag = (split_angle_cos != EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS); /* The loop with the lowest index. */ @@ -954,13 +946,9 @@ static void bm_mesh_loops_calc_normals_for_vert_with_clnors(BMesh *bm, continue; } - /* Always set as #bm_mesh_loops_calc_normals_for_loop checks the tag. */ if (do_edge_tag) { bm_edge_tag_from_smooth(fnos, e_curr_iter, split_angle_cos); } - else { - bm_edge_tag_clear(e_curr_iter); - } do { /* Radial loops. */ if (l_curr->v != v) { @@ -1053,6 +1041,7 @@ static void bm_mesh_loops_calc_normals_for_vert_without_clnors( { const bool has_clnors = false; const short(*clnors_data)[2] = NULL; + /* When false the caller must have already tagged the edges. */ const bool do_edge_tag = (split_angle_cos != EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS); const int cd_loop_clnors_offset = -1; @@ -1066,13 +1055,9 @@ static void bm_mesh_loops_calc_normals_for_vert_without_clnors( continue; } - /* Always set as #bm_mesh_loops_calc_normals_for_loop checks the tag. */ if (do_edge_tag) { bm_edge_tag_from_smooth(fnos, e_curr_iter, split_angle_cos); } - else { - bm_edge_tag_clear(e_curr_iter); - } do { /* Radial loops. */ if (l_curr->v != v) { @@ -1167,8 +1152,9 @@ 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. */ - BM_elem_flag_disable(l_curr->e, 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); -- cgit v1.2.3 From 03e2f11d48dc4e7bb8124c4c4be3403aea56e0c8 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 5 Aug 2021 11:29:46 +0200 Subject: Fix T89835: Crash after Instancing to Scene after making linked Collection local. Even though the ID itself remain the same after being made local, from depsgraph point of view this is a different ID. Hence we need to tag all of its users for COW update, as well as rebuild depsgraph relationships. Should be also backported to LTS 2.93 (and 2.83 if possible). --- source/blender/blenkernel/intern/lib_id.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 62d29188c5a..5e1027c62af 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -73,6 +73,7 @@ #include "BKE_rigidbody.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "RNA_access.h" @@ -141,7 +142,8 @@ static int lib_id_clear_library_data_users_update_cb(LibraryIDLinkCallbackData * { ID *id = cb_data->user_data; if (*cb_data->id_pointer == id) { - DEG_id_tag_update_ex(cb_data->bmain, cb_data->id_owner, ID_RECALC_TAG_FOR_UNDO); + DEG_id_tag_update_ex( + cb_data->bmain, cb_data->id_owner, ID_RECALC_TAG_FOR_UNDO | ID_RECALC_COPY_ON_WRITE); return IDWALK_RET_STOP_ITER; } return IDWALK_RET_NOP; @@ -193,6 +195,8 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id) if (key != NULL) { lib_id_clear_library_data_ex(bmain, &key->id); } + + DEG_relations_tag_update(bmain); } void BKE_lib_id_clear_library_data(Main *bmain, ID *id) -- cgit v1.2.3 From 317f09ebf990ab4a5d033bab61a0aa8816772615 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 28 Jul 2021 17:36:40 +0200 Subject: Fix T90170: `RNA_property_pointer_get` creating data in non-thread-safe way. Protect this accessor with a local static mutex when it needs to create/write data. Ideally accessors should never create or modify data, but there are some cases where this bad behavior is currently unavoidable. This is the case of the Pointer accessor when the actual IDProperty has not yet been created. NOTE: this fixes a memory leak in liboverride diffing process when several different overrides use a same linked reference ID. Differential Revision: https://developer.blender.org/D12060 --- source/blender/makesrna/intern/rna_access.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 46d60bf0da9..c991216da11 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -36,6 +36,7 @@ #include "BLI_dynstr.h" #include "BLI_ghash.h" #include "BLI_math.h" +#include "BLI_threads.h" #include "BLI_utildefines.h" #include "BLF_api.h" @@ -3676,6 +3677,8 @@ PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop) PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop; IDProperty *idprop; + static ThreadMutex lock = BLI_MUTEX_INITIALIZER; + BLI_assert(RNA_property_type(prop) == PROP_POINTER); if ((idprop = rna_idproperty_check(&prop, ptr))) { @@ -3695,9 +3698,14 @@ PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop) return pprop->get(ptr); } if (prop->flag & PROP_IDPROPERTY) { - /* XXX temporary hack to add it automatically, reading should - * never do any write ops, to ensure thread safety etc. */ + /* NOTE: While creating/writing data in an accessor is really bad design-wise, this is + * currently very difficult to avoid in that case. So a global mutex is used to keep ensuring + * thread safety. */ + BLI_mutex_lock(&lock); + /* NOTE: We do not need to check again for existence of the pointer after locking here, since + * this is also done in #RNA_property_pointer_add itself. */ RNA_property_pointer_add(ptr, prop); + BLI_mutex_unlock(&lock); return RNA_property_pointer_get(ptr, prop); } return PointerRNA_NULL; -- cgit v1.2.3 From 02e0c6f42e76e2cd5d086bde336789991434eba5 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 5 Aug 2021 12:10:49 +0200 Subject: Fix T90430: Crash when dragging material Was trying to get asset information even when there was none, i.e. when the material wasn't an asset or not dragged from the Asset Browser. --- source/blender/editors/space_view3d/space_view3d.c | 4 ++-- source/blender/windowmanager/WM_api.h | 2 ++ source/blender/windowmanager/intern/wm_dragdrop.c | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 0803b4c4776..72d0c11e192 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -534,8 +534,8 @@ static char *view3d_mat_drop_tooltip(bContext *C, const wmEvent *event, struct wmDropBox *drop) { - wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, ID_MA); - RNA_string_set(drop->ptr, "name", asset_drag->name); + const char *name = WM_drag_get_item_name(drag); + RNA_string_set(drop->ptr, "name", name); return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, event); } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index fb973592a57..8c1511fd152 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -728,6 +728,8 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, struct wmDrag *drag, struct wmDropBox *drop); +const char *WM_drag_get_item_name(struct wmDrag *drag); + /* Set OpenGL viewport and scissor */ void wmViewport(const struct rcti *winrct); void wmPartialViewport(rcti *drawrct, const rcti *winrct, const rcti *partialrct); diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index dcbb502117e..76bb93b681c 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -469,7 +469,7 @@ static void wm_drop_operator_draw(const char *name, int x, int y) UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg); } -static const char *wm_drag_name(wmDrag *drag) +const char *WM_drag_get_item_name(wmDrag *drag) { switch (drag->type) { case WM_DRAG_ID: { @@ -583,11 +583,11 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) } if (rect) { - int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag)); + int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag)); drag_rect_minmax(rect, x, y, x + w, y + iconsize); } else { - UI_fontstyle_draw_simple(fstyle, x, y, wm_drag_name(drag), text_col); + UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col); } /* operator name with roundbox */ @@ -614,7 +614,7 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) } if (rect) { - int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag)); + int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag)); drag_rect_minmax(rect, x, y, x + w, y + iconsize); } else { -- cgit v1.2.3 From ed9759349b3da090d22bd34bc066b72025679dc4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 5 Aug 2021 20:41:23 +1000 Subject: Fix T89214: Smooth Vertices (Laplacian) produces NaN coordinates Vertices with no connected faces would attempt to divide by the combined face area causing a divide by zero. Use the same weight for wire vertices as vertices connected to zero area faces. --- source/blender/bmesh/operators/bmo_smooth_laplacian.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/bmesh/operators/bmo_smooth_laplacian.c b/source/blender/bmesh/operators/bmo_smooth_laplacian.c index b2b93bfd003..31f66ad952f 100644 --- a/source/blender/bmesh/operators/bmo_smooth_laplacian.c +++ b/source/blender/bmesh/operators/bmo_smooth_laplacian.c @@ -533,7 +533,10 @@ void bmo_smooth_laplacian_vert_exec(BMesh *bm, BMOperator *op) EIG_linear_solver_right_hand_side_add(sys->context, 1, m_vertex_id, v->co[1]); EIG_linear_solver_right_hand_side_add(sys->context, 2, m_vertex_id, v->co[2]); i = m_vertex_id; - if (sys->zerola[i] == 0) { + if ((sys->zerola[i] == 0) && + /* Non zero check is to account for vertices that aren't connected to a selected face. + * Without this wire edges become `nan`, see T89214. */ + (sys->ring_areas[i] != 0.0f)) { w = sys->vweights[i] * sys->ring_areas[i]; sys->vweights[i] = (w == 0.0f) ? 0.0f : -lambda_factor / (4.0f * w); w = sys->vlengths[i]; -- cgit v1.2.3 From f45860fba9c47466a18d92c1621c153aaef7be05 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 5 Aug 2021 12:52:12 +0200 Subject: Cleanup: Remove unused members in FileSelectParams --- source/blender/makesdna/DNA_space_types.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 7290647dbc6..27d5d83c7cb 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -754,13 +754,7 @@ typedef struct FileSelectParams { /** Max number of levels in dirtree to show at once, 0 to disable recursion. */ short recursion_level; - /* XXX --- still unused -- */ - /** Show font preview. */ - short f_fp; - /** String to use for font preview. */ - char fp_str[8]; - - /* XXX --- end unused -- */ + char _pad4[2]; } FileSelectParams; /** -- cgit v1.2.3 From fb1822ddeb28037544e94862112aa7338a0c278b Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Thu, 5 Aug 2021 21:11:01 +0900 Subject: XR: Controller Data Improvements Provides two key improvements to runtime controller data. 1. Separates controller poses into two components, "grip" and "aim", which are both required to accurately represent the controllers without manual offsets. Following their OpenXR definitions, the grip pose represents the user's hand when holding the controller, and the aim pose represents the controller's aiming source. 2. Runtime controller data is now stored as a dynamic array instead of a fixed array. This makes the API/functionality more adaptable to different systems. Does not bring about any changes for users since only internal runtime functionality is currently affected. Reviewed By: Julian Eisel Differential Revision: http://developer.blender.org/D12073 --- source/blender/windowmanager/WM_api.h | 17 ++- .../blender/windowmanager/xr/intern/wm_xr_action.c | 72 ++++++---- .../blender/windowmanager/xr/intern/wm_xr_draw.c | 32 ++--- .../blender/windowmanager/xr/intern/wm_xr_intern.h | 47 +++--- .../windowmanager/xr/intern/wm_xr_session.c | 160 +++++++++++++++------ 5 files changed, 215 insertions(+), 113 deletions(-) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 8c1511fd152..7e26d921bd7 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -962,12 +962,18 @@ bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_ro bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr, float r_viewmat[4][4], float *r_focal_len); -bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr, +bool WM_xr_session_state_controller_grip_location_get(const wmXrData *xr, unsigned int subaction_idx, float r_location[3]); -bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr, +bool WM_xr_session_state_controller_grip_rotation_get(const wmXrData *xr, unsigned int subaction_idx, float r_rotation[4]); +bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr, + unsigned int subaction_idx, + float r_location[3]); +bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, + unsigned int subaction_idx, + float r_rotation[4]); /* wm_xr_actions.c */ /* XR action functions to be called pre-XR session start. @@ -1002,9 +1008,10 @@ void WM_xr_action_binding_destroy(wmXrData *xr, /* If action_set_name is NULL, then all action sets will be treated as active. */ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name); -bool WM_xr_controller_pose_action_set(wmXrData *xr, - const char *action_set_name, - const char *action_name); +bool WM_xr_controller_pose_actions_set(wmXrData *xr, + const char *action_set_name, + const char *grip_action_name, + const char *aim_action_name); /* XR action functions to be called post-XR session start. */ bool WM_xr_action_state_get(const wmXrData *xr, diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c index ee4cfcccaa7..8f2de4bbbad 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_action.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -112,11 +112,11 @@ static wmXrAction *action_create(const char *action_name, const bool is_button_action = (is_float_action || type == XR_BOOLEAN_INPUT); if (is_float_action) { action->float_thresholds = MEM_calloc_arrayN( - count, sizeof(*action->float_thresholds), "XrAction_FloatThresholds"); + count, sizeof(*action->float_thresholds), "XrAction_FloatThresholds"); } if (is_button_action) { action->axis_flags = MEM_calloc_arrayN( - count, sizeof(*action->axis_flags), "XrAction_AxisFlags"); + count, sizeof(*action->axis_flags), "XrAction_AxisFlags"); } action->ot = ot; @@ -186,9 +186,9 @@ void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name) wmXrSessionState *session_state = &xr->runtime->session_state; if (action_set == session_state->active_action_set) { - if (action_set->controller_pose_action) { + if (action_set->controller_grip_action || action_set->controller_aim_action) { wm_xr_session_controller_data_clear(session_state); - action_set->controller_pose_action = NULL; + action_set->controller_grip_action = action_set->controller_aim_action = NULL; } if (action_set->active_modal_action) { action_set->active_modal_action = NULL; @@ -213,13 +213,8 @@ bool WM_xr_action_create(wmXrData *xr, return false; } - wmXrAction *action = action_create(action_name, - type, - count_subaction_paths, - subaction_paths, - ot, - op_properties, - op_flag); + wmXrAction *action = action_create( + action_name, type, count_subaction_paths, subaction_paths, ot, op_properties, op_flag); GHOST_XrActionInfo info = { .name = action_name, @@ -269,13 +264,16 @@ void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char return; } - if (action_set->controller_pose_action && - STREQ(action_set->controller_pose_action->name, action_name)) { + if ((action_set->controller_grip_action && + STREQ(action_set->controller_grip_action->name, action_name)) || + (action_set->controller_aim_action && + STREQ(action_set->controller_aim_action->name, action_name))) { if (action_set == xr->runtime->session_state.active_action_set) { wm_xr_session_controller_data_clear(&xr->runtime->session_state); } - action_set->controller_pose_action = NULL; + action_set->controller_grip_action = action_set->controller_aim_action = NULL; } + if (action_set->active_modal_action && STREQ(action_set->active_modal_action->name, action_name)) { action_set->active_modal_action = NULL; @@ -284,7 +282,6 @@ void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char GHOST_XrDestroyActions(xr->runtime->context, action_set_name, 1, &action_name); } - bool WM_xr_action_binding_create(wmXrData *xr, const char *action_set_name, const char *action_name, @@ -297,7 +294,7 @@ bool WM_xr_action_binding_create(wmXrData *xr, const struct wmXrPose *poses) { GHOST_XrActionBindingInfo *binding_infos = MEM_calloc_arrayN( - count_subaction_paths, sizeof(*binding_infos), __func__); + count_subaction_paths, sizeof(*binding_infos), __func__); for (unsigned int i = 0; i < count_subaction_paths; ++i) { GHOST_XrActionBindingInfo *binding_info = &binding_infos[i]; @@ -334,7 +331,7 @@ void WM_xr_action_binding_destroy(wmXrData *xr, const char *profile_path) { GHOST_XrDestroyActionBindings( - xr->runtime->context, action_set_name, 1, &action_name, &profile_path); + xr->runtime->context, action_set_name, 1, &action_name, &profile_path); } bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name) @@ -360,31 +357,54 @@ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name) xr->runtime->session_state.active_action_set = action_set; - if (action_set->controller_pose_action) { - wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr); + if (action_set->controller_grip_action && action_set->controller_aim_action) { + wm_xr_session_controller_data_populate( + action_set->controller_grip_action, action_set->controller_aim_action, xr); + } + else { + wm_xr_session_controller_data_clear(&xr->runtime->session_state); } return true; } -bool WM_xr_controller_pose_action_set(wmXrData *xr, - const char *action_set_name, - const char *action_name) +bool WM_xr_controller_pose_actions_set(wmXrData *xr, + const char *action_set_name, + const char *grip_action_name, + const char *aim_action_name) { wmXrActionSet *action_set = action_set_find(xr, action_set_name); if (!action_set) { return false; } - wmXrAction *action = action_find(xr, action_set_name, action_name); - if (!action) { + wmXrAction *grip_action = action_find(xr, action_set_name, grip_action_name); + if (!grip_action) { + return false; + } + + wmXrAction *aim_action = action_find(xr, action_set_name, aim_action_name); + if (!aim_action) { return false; } - action_set->controller_pose_action = action; + /* Ensure consistent subaction paths. */ + const unsigned int count = grip_action->count_subaction_paths; + if (count != aim_action->count_subaction_paths) { + return false; + } + + for (unsigned int i = 0; i < count; ++i) { + if (!STREQ(grip_action->subaction_paths[i], aim_action->subaction_paths[i])) { + return false; + } + } + + action_set->controller_grip_action = grip_action; + action_set->controller_aim_action = aim_action; if (action_set == xr->runtime->session_state.active_action_set) { - wm_xr_session_controller_data_populate(action, xr); + wm_xr_session_controller_data_populate(grip_action, aim_action, xr); } return true; diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index 4ac05e339b9..bbb73fc2007 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -38,18 +38,18 @@ #include "wm_surface.h" #include "wm_xr_intern.h" -void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]) +void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]) { - float iquat[4]; - invert_qt_qt_normalized(iquat, pose->orientation_quat); - quat_to_mat4(r_viewmat, iquat); - translate_m4(r_viewmat, -pose->position[0], -pose->position[1], -pose->position[2]); + quat_to_mat4(r_mat, pose->orientation_quat); + copy_v3_v3(r_mat[3], pose->position); } -void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]) +void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]) { - quat_to_mat4(r_mat, pose->orientation_quat); - copy_v3_v3(r_mat[3], pose->position); + float iquat[4]; + invert_qt_qt_normalized(iquat, pose->orientation_quat); + quat_to_mat4(r_imat, iquat); + translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]); } static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, @@ -59,6 +59,7 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, float r_proj_mat[4][4]) { GHOST_XrPose eye_pose; + float eye_inv[4][4], base_inv[4][4]; copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat); copy_v3_v3(eye_pose.position, draw_view->eye_pose.position); @@ -69,6 +70,12 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, sub_v3_v3(eye_pose.position, draw_data->eye_position_ofs); } + wm_xr_pose_to_imat(&eye_pose, eye_inv); + /* Calculate the base pose matrix (in world space!). */ + wm_xr_pose_to_imat(&draw_data->base_pose, base_inv); + + mul_m4_m4m4(r_view_mat, eye_inv, base_inv); + perspective_m4_fov(r_proj_mat, draw_view->fov.angle_left, draw_view->fov.angle_right, @@ -76,15 +83,6 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, draw_view->fov.angle_down, session_settings->clip_start, session_settings->clip_end); - - float eye_mat[4][4]; - float base_mat[4][4]; - - wm_xr_pose_to_viewmat(&eye_pose, eye_mat); - /* Calculate the base pose matrix (in world space!). */ - wm_xr_pose_to_viewmat(&draw_data->base_pose, base_mat); - - mul_m4_m4m4(r_view_mat, eye_mat, base_mat); } static void wm_xr_draw_viewport_buffers_to_active_framebuffer( diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 4530aeaa428..b6aff1f71f9 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -26,19 +26,6 @@ struct wmXrActionSet; -typedef struct wmXrControllerData { - /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256). - This subaction path will later be combined with a component path, and that combined path should - also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path = - /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value). - */ - char subaction_path[64]; - /** Last known controller pose (in world space) stored for queries. */ - GHOST_XrPose pose; - /** The last known controller matrix, calculated from above's controller pose. */ - float mat[4][4]; -} wmXrControllerData; - typedef struct wmXrSessionState { bool is_started; @@ -65,7 +52,7 @@ typedef struct wmXrSessionState { bool is_view_data_set; /** Last known controller data. */ - wmXrControllerData controllers[2]; + ListBase controllers; /* wmXrController */ /** The currently active action set that will be updated on calls to * wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and @@ -135,14 +122,27 @@ typedef struct wmXrAction { eXrOpFlag op_flag; } wmXrAction; +typedef struct wmXrController { + struct wmXrController *next, *prev; + /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256). + This subaction path will later be combined with a component path, and that combined path should + also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path = + /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value). + */ + char subaction_path[64]; + /* Pose (in world space) that represents the user's hand when holding the controller.*/ + GHOST_XrPose grip_pose; + float grip_mat[4][4]; + /* Pose (in world space) that represents the controller's aiming source. */ + GHOST_XrPose aim_pose; + float aim_mat[4][4]; +} wmXrController; + typedef struct wmXrActionSet { char *name; - - /** The XR pose action that determines the controller - * transforms. This is usually identified by the OpenXR path "/grip/pose" or "/aim/pose", - * although it could differ depending on the specification and hardware. */ - wmXrAction *controller_pose_action; - + /** XR pose actions that determine the controller grip/aim transforms. */ + wmXrAction *controller_grip_action; + wmXrAction *controller_aim_action; /** The currently active modal action (if any). */ wmXrAction *active_modal_action; } wmXrActionSet; @@ -165,10 +165,11 @@ void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle context); void wm_xr_session_actions_init(wmXrData *xr); void wm_xr_session_actions_update(wmXrData *xr); -void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action, +void wm_xr_session_controller_data_populate(const wmXrAction *grip_action, + const wmXrAction *aim_action, wmXrData *xr); void wm_xr_session_controller_data_clear(wmXrSessionState *state); -void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]); -void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]); +void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]); +void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]); void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 252f358c798..dd9cac2bb16 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -63,6 +63,16 @@ static void wm_xr_session_create_cb(void) wm_xr_session_actions_init(xr_data); } +static void wm_xr_session_controller_data_free(wmXrSessionState *state) +{ + BLI_freelistN(&state->controllers); +} + +static void wm_xr_session_data_free(wmXrSessionState *state) +{ + wm_xr_session_controller_data_free(state); +} + static void wm_xr_session_exit_cb(void *customdata) { wmXrData *xr_data = customdata; @@ -74,6 +84,7 @@ static void wm_xr_session_exit_cb(void *customdata) } /* Free the entire runtime data (including session state and context), to play safe. */ + wm_xr_session_data_free(&xr_data->runtime->session_state); wm_xr_runtime_data_free(&xr_data->runtime); } @@ -338,7 +349,7 @@ void wm_xr_session_state_update(const XrSessionSettings *settings, copy_v3_v3(state->viewer_pose.position, viewer_pose.position); copy_qt_qt(state->viewer_pose.orientation_quat, viewer_pose.orientation_quat); - wm_xr_pose_to_viewmat(&viewer_pose, state->viewer_viewmat); + wm_xr_pose_to_imat(&viewer_pose, state->viewer_viewmat); /* No idea why, but multiplying by two seems to make it match the VR view more. */ state->focal_len = 2.0f * fov_to_focallength(draw_view->fov.angle_right - draw_view->fov.angle_left, @@ -398,32 +409,71 @@ bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr, return true; } -bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr, +bool WM_xr_session_state_controller_grip_location_get(const wmXrData *xr, unsigned int subaction_idx, float r_location[3]) { if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set || - subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) { + (subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) { zero_v3(r_location); return false; } - copy_v3_v3(r_location, xr->runtime->session_state.controllers[subaction_idx].pose.position); + const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers, + subaction_idx); + BLI_assert(controller); + copy_v3_v3(r_location, controller->grip_pose.position); return true; } -bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr, +bool WM_xr_session_state_controller_grip_rotation_get(const wmXrData *xr, unsigned int subaction_idx, float r_rotation[4]) { if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set || - subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) { + (subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) { unit_qt(r_rotation); return false; } - copy_v4_v4(r_rotation, - xr->runtime->session_state.controllers[subaction_idx].pose.orientation_quat); + const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers, + subaction_idx); + BLI_assert(controller); + copy_qt_qt(r_rotation, controller->grip_pose.orientation_quat); + return true; +} + +bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr, + unsigned int subaction_idx, + float r_location[3]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set || + (subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) { + zero_v3(r_location); + return false; + } + + const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers, + subaction_idx); + BLI_assert(controller); + copy_v3_v3(r_location, controller->aim_pose.position); + return true; +} + +bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, + unsigned int subaction_idx, + float r_rotation[4]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set || + (subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) { + unit_qt(r_rotation); + return false; + } + + const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers, + subaction_idx); + BLI_assert(controller); + copy_qt_qt(r_rotation, controller->aim_pose.orientation_quat); return true; } @@ -443,16 +493,34 @@ void wm_xr_session_actions_init(wmXrData *xr) GHOST_XrAttachActionSets(xr->runtime->context); } -static void wm_xr_session_controller_mats_update(const XrSessionSettings *settings, - const wmXrAction *controller_pose_action, +static void wm_xr_session_controller_pose_calc(const GHOST_XrPose *raw_pose, + const float view_ofs[3], + const float base_mat[4][4], + GHOST_XrPose *r_pose, + float r_mat[4][4]) +{ + float m[4][4]; + /* Calculate controller matrix in world space. */ + wm_xr_pose_to_mat(raw_pose, m); + + /* Apply eye position and base pose offsets. */ + sub_v3_v3(m[3], view_ofs); + mul_m4_m4m4(r_mat, base_mat, m); + + /* Save final pose. */ + mat4_to_loc_quat(r_pose->position, r_pose->orientation_quat, r_mat); +} + +static void wm_xr_session_controller_data_update(const XrSessionSettings *settings, + const wmXrAction *grip_action, + const wmXrAction *aim_action, wmXrSessionState *state) { - const unsigned int count = (unsigned int)min_ii( - (int)controller_pose_action->count_subaction_paths, (int)ARRAY_SIZE(state->controllers)); + BLI_assert(grip_action->count_subaction_paths == aim_action->count_subaction_paths); + BLI_assert(grip_action->count_subaction_paths == BLI_listbase_count(&state->controllers)); - float view_ofs[3]; - float base_inv[4][4]; - float tmp[4][4]; + unsigned int subaction_idx = 0; + float view_ofs[3], base_mat[4][4]; if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { copy_v3_v3(view_ofs, state->prev_local_pose.position); @@ -464,22 +532,19 @@ static void wm_xr_session_controller_mats_update(const XrSessionSettings *settin add_v3_v3(view_ofs, state->prev_eye_position_ofs); } - wm_xr_pose_to_viewmat(&state->prev_base_pose, base_inv); - invert_m4(base_inv); - - for (unsigned int i = 0; i < count; ++i) { - wmXrControllerData *controller = &state->controllers[i]; - - /* Calculate controller matrix in world space. */ - wm_xr_controller_pose_to_mat(&((GHOST_XrPose *)controller_pose_action->states)[i], tmp); - - /* Apply eye position and base pose offsets. */ - sub_v3_v3(tmp[3], view_ofs); - mul_m4_m4m4(controller->mat, base_inv, tmp); + wm_xr_pose_to_mat(&state->prev_base_pose, base_mat); - /* Save final pose. */ - mat4_to_loc_quat( - controller->pose.position, controller->pose.orientation_quat, controller->mat); + LISTBASE_FOREACH_INDEX (wmXrController *, controller, &state->controllers, subaction_idx) { + wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)grip_action->states)[subaction_idx], + view_ofs, + base_mat, + &controller->grip_pose, + controller->grip_mat); + wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)aim_action->states)[subaction_idx], + view_ofs, + base_mat, + &controller->aim_pose, + controller->aim_mat); } } @@ -498,33 +563,44 @@ void wm_xr_session_actions_update(wmXrData *xr) return; } - /* Only update controller mats for active action set. */ + /* Only update controller data for active action set. */ if (active_action_set) { - if (active_action_set->controller_pose_action) { - wm_xr_session_controller_mats_update( - &xr->session_settings, active_action_set->controller_pose_action, state); + if (active_action_set->controller_grip_action && active_action_set->controller_aim_action) { + wm_xr_session_controller_data_update(&xr->session_settings, + active_action_set->controller_grip_action, + active_action_set->controller_aim_action, + state); } } } -void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action, wmXrData *xr) +void wm_xr_session_controller_data_populate(const wmXrAction *grip_action, + const wmXrAction *aim_action, + wmXrData *xr) { + UNUSED_VARS(aim_action); /* Only used for asserts. */ + wmXrSessionState *state = &xr->runtime->session_state; + ListBase *controllers = &state->controllers; + + BLI_assert(grip_action->count_subaction_paths == aim_action->count_subaction_paths); + const unsigned int count = grip_action->count_subaction_paths; - const unsigned int count = (unsigned int)min_ii( - (int)ARRAY_SIZE(state->controllers), (int)controller_pose_action->count_subaction_paths); + wm_xr_session_controller_data_free(state); for (unsigned int i = 0; i < count; ++i) { - wmXrControllerData *c = &state->controllers[i]; - strcpy(c->subaction_path, controller_pose_action->subaction_paths[i]); - memset(&c->pose, 0, sizeof(c->pose)); - zero_m4(c->mat); + wmXrController *controller = MEM_callocN(sizeof(*controller), __func__); + + BLI_assert(STREQ(grip_action->subaction_paths[i], aim_action->subaction_paths[i])); + strcpy(controller->subaction_path, grip_action->subaction_paths[i]); + + BLI_addtail(controllers, controller); } } void wm_xr_session_controller_data_clear(wmXrSessionState *state) { - memset(state->controllers, 0, sizeof(state->controllers)); + wm_xr_session_controller_data_free(state); } /** \} */ /* XR-Session Actions */ -- cgit v1.2.3 From be6409a74850054d53b6c2f965e03e64e00a622f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 5 Aug 2021 22:32:18 +1000 Subject: Fix fix invalid index use for edit-mesh laplacian smooth Only vertex indices were ensured to be correct. --- .../blender/bmesh/operators/bmo_smooth_laplacian.c | 25 +++++++++------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/source/blender/bmesh/operators/bmo_smooth_laplacian.c b/source/blender/bmesh/operators/bmo_smooth_laplacian.c index 31f66ad952f..1d72bb893b2 100644 --- a/source/blender/bmesh/operators/bmo_smooth_laplacian.c +++ b/source/blender/bmesh/operators/bmo_smooth_laplacian.c @@ -180,7 +180,7 @@ static void init_laplacian_matrix(LaplacianSystem *sys) BMVert *vn; BMVert *vf[4]; - BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, j) { + BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) { if (!BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_boundary(e)) { v1 = e->v1->co; v2 = e->v2->co; @@ -190,7 +190,6 @@ static void init_laplacian_matrix(LaplacianSystem *sys) w1 = len_v3v3(v1, v2); if (w1 > sys->min_area) { w1 = 1.0f / w1; - i = BM_elem_index_get(e); sys->eweights[i] = w1; sys->vlengths[idv1] += w1; sys->vlengths[idv2] += w1; @@ -202,13 +201,13 @@ static void init_laplacian_matrix(LaplacianSystem *sys) } } - BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) { + BM_ITER_MESH_INDEX (f, &fiter, sys->bm, BM_FACES_OF_MESH, i) { if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, i) { - vf[i] = vn; + BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, j) { + vf[j] = vn; } - has_4_vert = (i == 4) ? 1 : 0; + has_4_vert = (j == 4) ? 1 : 0; idv1 = BM_elem_index_get(vf[0]); idv2 = BM_elem_index_get(vf[1]); idv3 = BM_elem_index_get(vf[2]); @@ -268,8 +267,6 @@ static void init_laplacian_matrix(LaplacianSystem *sys) } } else { - i = BM_elem_index_get(f); - w1 = cotangent_tri_weight_v3(v1, v2, v3); w2 = cotangent_tri_weight_v3(v2, v3, v1); w3 = cotangent_tri_weight_v3(v3, v1, v2); @@ -302,12 +299,12 @@ static void fill_laplacian_matrix(LaplacianSystem *sys) BMVert *vn; BMVert *vf[4]; - BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) { + BM_ITER_MESH_INDEX (f, &fiter, sys->bm, BM_FACES_OF_MESH, i) { if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, i) { - vf[i] = vn; + BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, j) { + vf[j] = vn; } - has_4_vert = (i == 4) ? 1 : 0; + has_4_vert = (j == 4) ? 1 : 0; if (has_4_vert) { idv[0] = BM_elem_index_get(vf[0]); idv[1] = BM_elem_index_get(vf[1]); @@ -344,7 +341,6 @@ static void fill_laplacian_matrix(LaplacianSystem *sys) idv2 = BM_elem_index_get(vf[1]); idv3 = BM_elem_index_get(vf[2]); /* Is ring if number of faces == number of edges around vertice. */ - i = BM_elem_index_get(f); if (!vert_is_boundary(vf[0]) && sys->zerola[idv1] == 0) { EIG_linear_solver_matrix_add( sys->context, idv1, idv2, sys->fweights[i][2] * sys->vweights[idv1]); @@ -366,14 +362,13 @@ static void fill_laplacian_matrix(LaplacianSystem *sys) } } } - BM_ITER_MESH (e, &eiter, sys->bm, BM_EDGES_OF_MESH) { + BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) { if (!BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_boundary(e)) { v1 = e->v1->co; v2 = e->v2->co; idv1 = BM_elem_index_get(e->v1); idv2 = BM_elem_index_get(e->v2); if (sys->zerola[idv1] == 0 && sys->zerola[idv2] == 0) { - i = BM_elem_index_get(e); EIG_linear_solver_matrix_add( sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]); EIG_linear_solver_matrix_add( -- cgit v1.2.3 From 720ea8a67df06ba2242e7771b349cefd753f7ea5 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 5 Aug 2021 14:59:31 +0200 Subject: Fix T89963: crash when library override is applied to an object from a linked scene. LibOverride of scenes is not really supported currently, there are many issues with it. Will disable most user-accessible ways to create such overrides in a following commit. --- source/blender/blenkernel/intern/lib_override.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 072304566e5..67ed7d1b394 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1599,6 +1599,17 @@ static void lib_override_library_main_resync_on_library_indirect_level( (!ID_IS_LINKED(id) && library_indirect_level != 0)) { continue; } + + /* We cannot resync a scene that is currently active. */ + if (id == &scene->id) { + id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; + BKE_reportf(reports->reports, + RPT_WARNING, + "Scene '%s' was not resynced as it is the currently active one", + scene->id.name + 2); + continue; + } + Library *library = id->lib; int level = 0; -- cgit v1.2.3 From 0cff7c2a228589505bd2d66e966e5e84613b2786 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 5 Aug 2021 15:24:46 +0200 Subject: LibOverride: Make it reasonably impossible for users to create overrides of scenes. This is not supported currently,doing so through RNA API remains possible, but from regular UI operations it should not be doable anymore. Ref. T90459. --- source/blender/editors/space_outliner/outliner_tools.c | 12 ++++++++++++ source/blender/makesdna/DNA_ID.h | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 3edb12c5503..e3aec572bd3 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -845,8 +845,20 @@ 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 (!ID_IS_OVERRIDABLE_LIBRARY(te->store_elem->id)) { + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + BKE_reportf(reports, + RPT_WARNING, + "Could not create library override from data-block '%s', one of its parents " + "is not overridable ('%s')", + id_root->name, + te->store_elem->id->name); + return; + } te->store_elem->id->tag |= LIB_TAG_DOIT; } + success = BKE_lib_override_library_create( bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference, NULL); } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index c9d652ad03d..f7f4b0e6104 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -429,7 +429,8 @@ typedef struct PreviewImage { * BKE_library_override typically (especially due to the check on LIB_TAG_EXTERN). */ #define ID_IS_OVERRIDABLE_LIBRARY(_id) \ (ID_IS_LINKED(_id) && !ID_MISSING(_id) && (((const ID *)(_id))->tag & LIB_TAG_EXTERN) != 0 && \ - (BKE_idtype_get_info_from_id((const ID *)(_id))->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0) + (BKE_idtype_get_info_from_id((const ID *)(_id))->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0 && \ + !ELEM(GS(((ID *)(_id))->name), ID_SCE)) /* NOTE: The three checks below do not take into account whether given ID is linked or not (when * chaining overrides over several libraries). User must ensure the ID is not linked itself -- cgit v1.2.3 From e844e9e8f3bb6814e24749316003814156e2e2ce Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Thu, 5 Aug 2021 23:40:17 +0900 Subject: XR Controller Support Step 2: Action Maps Addresses the remaining portions of T77137 (Python API for Controller Interaction), which was partially completed by D10942. Adds an XR "action maps" system for loading XR action data from a Python script. Action maps are accessible via the Python API, and are used to pass default actions to the VR session during the xr_session_start_pre() callback. Since action maps are stored only as runtime data, they will be cleaned up with the rest of the VR runtime data on file read or exit. Reviewed By: Julian Eisel, Hans Goudey Differential Revision: https://developer.blender.org/D10943 --- intern/ghost/GHOST_C-api.h | 16 +- intern/ghost/intern/GHOST_C-api.cpp | 33 +- intern/ghost/intern/GHOST_XrAction.cpp | 64 +- intern/ghost/intern/GHOST_XrAction.h | 8 +- intern/ghost/intern/GHOST_XrSession.cpp | 31 +- intern/ghost/intern/GHOST_XrSession.h | 7 +- source/blender/makesdna/DNA_xr_types.h | 107 +- source/blender/makesrna/intern/rna_xr.c | 1602 +++++++++++++++++++- source/blender/windowmanager/CMakeLists.txt | 1 + source/blender/windowmanager/WM_api.h | 51 +- source/blender/windowmanager/xr/intern/wm_xr.c | 1 + .../blender/windowmanager/xr/intern/wm_xr_action.c | 99 +- .../windowmanager/xr/intern/wm_xr_actionmap.c | 565 +++++++ .../blender/windowmanager/xr/intern/wm_xr_intern.h | 59 +- 14 files changed, 2541 insertions(+), 103 deletions(-) create mode 100644 source/blender/windowmanager/xr/intern/wm_xr_actionmap.c diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index fea5a545807..83c67f83908 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -1102,6 +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 int64_t *duration, const float *frequency, const float *amplitude); @@ -1111,7 +1112,8 @@ 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 *action_name, + const char **subaction_path); /** * Get action set custom data (owned by Blender, not GHOST). @@ -1126,6 +1128,18 @@ void *GHOST_XrGetActionCustomdata(GHOST_XrContextHandle xr_context, const char *action_set_name, const char *action_name); +/** + * Get the number of actions in an action set. + */ +unsigned int GHOST_XrGetActionCount(GHOST_XrContextHandle xr_context, const char *action_set_name); + +/** + * Get custom data for all actions in an action set. + */ +void GHOST_XrGetActionCustomdataArray(GHOST_XrContextHandle xr_context, + const char *action_set_name, + void **r_customdata_array); + #endif /* WITH_XR_OPENXR */ #ifdef __cplusplus diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index 0bc9be26eb1..b1af5c131ab 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -1005,25 +1005,29 @@ 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 int64_t *duration, const float *frequency, const float *amplitude) { GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; GHOST_XrSession *xr_session = xr_context->getSession(); - GHOST_XR_CAPI_CALL_RET(xr_session->applyHapticAction( - action_set_name, action_name, *duration, *frequency, *amplitude), - xr_context); + GHOST_XR_CAPI_CALL_RET( + xr_session->applyHapticAction( + action_set_name, action_name, subaction_path, *duration, *frequency, *amplitude), + xr_context); return 0; } void GHOST_XrStopHapticAction(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name, - const char *action_name) + const char *action_name, + const char **subaction_path) { GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; GHOST_XrSession *xr_session = xr_context->getSession(); - GHOST_XR_CAPI_CALL(xr_session->stopHapticAction(action_set_name, action_name), xr_context); + GHOST_XR_CAPI_CALL(xr_session->stopHapticAction(action_set_name, action_name, subaction_path), + xr_context); } void *GHOST_XrGetActionSetCustomdata(GHOST_XrContextHandle xr_contexthandle, @@ -1046,4 +1050,23 @@ void *GHOST_XrGetActionCustomdata(GHOST_XrContextHandle xr_contexthandle, return 0; } +unsigned int GHOST_XrGetActionCount(GHOST_XrContextHandle xr_contexthandle, + const char *action_set_name) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XrSession *xr_session = xr_context->getSession(); + GHOST_XR_CAPI_CALL_RET(xr_session->getActionCount(action_set_name), xr_context); + return 0; +} + +void GHOST_XrGetActionCustomdataArray(GHOST_XrContextHandle xr_contexthandle, + const char *action_set_name, + void **r_customdata_array) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XrSession *xr_session = xr_context->getSession(); + GHOST_XR_CAPI_CALL(xr_session->getActionCustomdataArray(action_set_name, r_customdata_array), + xr_context); +} + #endif /* WITH_XR_OPENXR */ diff --git a/intern/ghost/intern/GHOST_XrAction.cpp b/intern/ghost/intern/GHOST_XrAction.cpp index 676a3367ee1..07eb42c14e6 100644 --- a/intern/ghost/intern/GHOST_XrAction.cpp +++ b/intern/ghost/intern/GHOST_XrAction.cpp @@ -208,8 +208,10 @@ GHOST_XrAction::GHOST_XrAction(XrInstance instance, m_subaction_paths.resize(info.count_subaction_paths); for (uint32_t i = 0; i < info.count_subaction_paths; ++i) { - CHECK_XR(xrStringToPath(instance, info.subaction_paths[i], &m_subaction_paths[i]), - (std::string("Failed to get user path \"") + info.subaction_paths[i] + "\".").data()); + const char *subaction_path_str = info.subaction_paths[i]; + CHECK_XR(xrStringToPath(instance, subaction_path_str, &m_subaction_paths[i]), + (std::string("Failed to get user path \"") + subaction_path_str + "\".").data()); + m_subaction_indices.insert({subaction_path_str, i}); } XrActionCreateInfo action_info{XR_TYPE_ACTION_CREATE_INFO}; @@ -373,6 +375,7 @@ void GHOST_XrAction::updateState(XrSession session, void GHOST_XrAction::applyHapticFeedback(XrSession session, const char *action_name, + const char **subaction_path_str, const int64_t &duration, const float &frequency, const float &litude) @@ -386,24 +389,46 @@ void GHOST_XrAction::applyHapticFeedback(XrSession session, XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO}; haptic_info.action = m_action; - for (std::vector::iterator it = m_subaction_paths.begin(); it != m_subaction_paths.end(); - ++it) { - haptic_info.subactionPath = *it; - CHECK_XR(xrApplyHapticFeedback(session, &haptic_info, (const XrHapticBaseHeader *)&vibration), - (std::string("Failed to apply haptic action \"") + action_name + "\".").data()); + if (subaction_path_str != nullptr) { + 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( + xrApplyHapticFeedback(session, &haptic_info, (const XrHapticBaseHeader *)&vibration), + (std::string("Failed to apply haptic action \"") + action_name + "\".").data()); + } + } + else { + for (const XrPath &subaction_path : m_subaction_paths) { + haptic_info.subactionPath = subaction_path; + CHECK_XR( + xrApplyHapticFeedback(session, &haptic_info, (const XrHapticBaseHeader *)&vibration), + (std::string("Failed to apply haptic action \"") + action_name + "\".").data()); + } } } -void GHOST_XrAction::stopHapticFeedback(XrSession session, const char *action_name) +void GHOST_XrAction::stopHapticFeedback(XrSession session, + const char *action_name, + const char **subaction_path_str) { XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO}; haptic_info.action = m_action; - for (std::vector::iterator it = m_subaction_paths.begin(); it != m_subaction_paths.end(); - ++it) { - haptic_info.subactionPath = *it; - CHECK_XR(xrStopHapticFeedback(session, &haptic_info), - (std::string("Failed to stop haptic action \"") + action_name + "\".").data()); + if (subaction_path_str != nullptr) { + 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), + (std::string("Failed to stop haptic action \"") + action_name + "\".").data()); + } + } + else { + for (const XrPath &subaction_path : m_subaction_paths) { + haptic_info.subactionPath = subaction_path; + CHECK_XR(xrStopHapticFeedback(session, &haptic_info), + (std::string("Failed to stop haptic action \"") + action_name + "\".").data()); + } } } @@ -511,6 +536,19 @@ void *GHOST_XrActionSet::getCustomdata() return m_custom_data_->custom_data_; } +uint32_t GHOST_XrActionSet::getActionCount() const +{ + return (uint32_t)m_actions.size(); +} + +void GHOST_XrActionSet::getActionCustomdataArray(void **r_customdata_array) +{ + uint32_t i = 0; + for (auto &[name, action] : m_actions) { + r_customdata_array[i++] = action.getCustomdata(); + } +} + void GHOST_XrActionSet::getBindings( std::map> &r_bindings) const { diff --git a/intern/ghost/intern/GHOST_XrAction.h b/intern/ghost/intern/GHOST_XrAction.h index e2a89e87278..73a1cd9cd6a 100644 --- a/intern/ghost/intern/GHOST_XrAction.h +++ b/intern/ghost/intern/GHOST_XrAction.h @@ -103,17 +103,21 @@ class GHOST_XrAction { const XrTime &predicted_display_time); void applyHapticFeedback(XrSession session, const char *action_name, + const char **subaction_path, const int64_t &duration, const float &frequency, const float &litude); - void stopHapticFeedback(XrSession session, const char *action_name); + void stopHapticFeedback(XrSession session, const char *action_name, const char **subaction_path); void *getCustomdata(); void getBindings(std::map> &r_bindings) const; private: + using SubactionIndexMap = std::map; + XrAction m_action = XR_NULL_HANDLE; GHOST_XrActionType m_type; + SubactionIndexMap m_subaction_indices; std::vector m_subaction_paths; /** States for each subaction path. */ void *m_states; @@ -145,6 +149,8 @@ class GHOST_XrActionSet { XrActionSet getActionSet() const; void *getCustomdata(); + uint32_t getActionCount() const; + void getActionCustomdataArray(void **r_customdata_array); void getBindings(std::map> &r_bindings) const; private: diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index a29ec1cc560..a63ce5c9344 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -754,6 +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 int64_t &duration, const float &frequency, const float &litude) @@ -768,12 +769,15 @@ bool GHOST_XrSession::applyHapticAction(const char *action_set_name, return false; } - action->applyHapticFeedback(m_oxr->session, action_name, duration, frequency, amplitude); + action->applyHapticFeedback( + m_oxr->session, action_name, subaction_path, duration, frequency, amplitude); return true; } -void GHOST_XrSession::stopHapticAction(const char *action_set_name, const char *action_name) +void GHOST_XrSession::stopHapticAction(const char *action_set_name, + const char *action_name, + const char **subaction_path) { GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); if (action_set == nullptr) { @@ -785,7 +789,7 @@ void GHOST_XrSession::stopHapticAction(const char *action_set_name, const char * return; } - action->stopHapticFeedback(m_oxr->session, action_name); + action->stopHapticFeedback(m_oxr->session, action_name, subaction_path); } void *GHOST_XrSession::getActionSetCustomdata(const char *action_set_name) @@ -813,4 +817,25 @@ void *GHOST_XrSession::getActionCustomdata(const char *action_set_name, const ch return action->getCustomdata(); } +uint32_t GHOST_XrSession::getActionCount(const char *action_set_name) +{ + GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); + if (action_set == nullptr) { + return 0; + } + + return action_set->getActionCount(); +} + +void GHOST_XrSession::getActionCustomdataArray(const char *action_set_name, + void **r_customdata_array) +{ + GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name); + if (action_set == nullptr) { + return; + } + + action_set->getActionCustomdataArray(r_customdata_array); +} + /** \} */ /* Actions */ diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h index cdeef153fb1..ec15897058f 100644 --- a/intern/ghost/intern/GHOST_XrSession.h +++ b/intern/ghost/intern/GHOST_XrSession.h @@ -76,14 +76,19 @@ 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 int64_t &duration, const float &frequency, const float &litude); - void stopHapticAction(const char *action_set_name, const char *action_name); + void stopHapticAction(const char *action_set_name, + const char *action_name, + const char **subaction_path); /* Custom data (owned by Blender, not GHOST) accessors. */ void *getActionSetCustomdata(const char *action_set_name); void *getActionCustomdata(const char *action_set_name, const char *action_name); + uint32_t getActionCount(const char *action_set_name); + void getActionCustomdataArray(const char *action_set_name, void **r_customdata_array); private: /** Pointer back to context managing this session. Would be nice to avoid, but needed to access diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index c0928e1519f..2e348ce6855 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -26,6 +26,8 @@ extern "C" { #endif +/* -------------------------------------------------------------------- */ + typedef struct XrSessionSettings { /** Shading settings, struct shared with 3D-View so settings are the same. */ struct View3DShading shading; @@ -68,21 +70,122 @@ typedef enum eXrActionType { XR_VIBRATION_OUTPUT = 100, } eXrActionType; +/** Determines how XR action operators are executed. */ typedef enum eXrOpFlag { XR_OP_PRESS = 0, XR_OP_RELEASE = 1, XR_OP_MODAL = 2, } eXrOpFlag; +typedef enum eXrActionFlag { + /** Action depends on two subaction paths (i.e. two-handed/bimanual action). */ + XR_ACTION_BIMANUAL = (1 << 0), +} eXrActionFlag; + +typedef enum eXrHapticFlag { + /** Whether to apply haptics to corresponding user paths for an action and its haptic action. */ + XR_HAPTIC_MATCHUSERPATHS = (1 << 0), + /** Determines how haptics will be applied ("repeat" is mutually exclusive with + "press"/"release"). */ + XR_HAPTIC_PRESS = (1 << 1), + XR_HAPTIC_RELEASE = (1 << 2), + XR_HAPTIC_REPEAT = (1 << 3), +} eXrHapticFlag; + +/** For axis-based inputs (thumbstick/trackpad/etc). Determines the region for action execution + * (mutually exclusive per axis). */ typedef enum eXrAxisFlag { - /** For axis-based inputs (thumbstick/trackpad/etc). Determines the region for action execution - (mutually exclusive per axis). */ XR_AXIS0_POS = (1 << 0), XR_AXIS0_NEG = (1 << 1), XR_AXIS1_POS = (1 << 2), XR_AXIS1_NEG = (1 << 3), } eXrAxisFlag; +typedef enum eXrPoseFlag { + /* Pose represents controller grip/aim. */ + XR_POSE_GRIP = (1 << 0), + XR_POSE_AIM = (1 << 1), +} eXrPoseFlag; + +/* -------------------------------------------------------------------- */ + +typedef struct XrActionMapBinding { + struct XrActionMapBinding *next, *prev; + + /** Unique name. */ + char name[64]; /* MAX_NAME */ + + /** OpenXR interaction profile path. */ + char profile[256]; + /** OpenXR component paths. */ + char component_path0[192]; + char component_path1[192]; + + /** Input threshold/region. */ + float float_threshold; + short axis_flag; /* eXrAxisFlag */ + char _pad[2]; + + /** Pose action properties. */ + float pose_location[3]; + float pose_rotation[3]; +} XrActionMapBinding; + +/* -------------------------------------------------------------------- */ + +typedef struct XrActionMapItem { + struct XrActionMapItem *next, *prev; + + /** Unique name. */ + char name[64]; /* MAX_NAME */ + /** Type. */ + char type; /** eXrActionType */ + char _pad[7]; + + /** OpenXR user paths. */ + char user_path0[64]; + char user_path1[64]; + + /** Operator to be called on XR events. */ + char op[64]; /* OP_MAX_TYPENAME */ + /** Operator properties, assigned to ptr->data and can be written to a file. */ + IDProperty *op_properties; + /** RNA pointer to access properties. */ + struct PointerRNA *op_properties_ptr; + + short op_flag; /* eXrOpFlag */ + short action_flag; /* eXrActionFlag */ + short haptic_flag; /* eXrHapticFlag */ + + /** Pose action properties. */ + short pose_flag; /* eXrPoseFlag */ + + /** Haptic properties. */ + char haptic_name[64]; /* MAX_NAME */ + float haptic_duration; + float haptic_frequency; + float haptic_amplitude; + + short selbinding; + char _pad3[2]; + ListBase bindings; /* XrActionMapBinding */ +} XrActionMapItem; + +/* -------------------------------------------------------------------- */ + +typedef struct XrActionMap { + struct XrActionMap *next, *prev; + + /** Unique name. */ + char name[64]; /* MAX_NAME */ + + ListBase items; /* XrActionMapItem */ + short selitem; + char _pad[6]; +} XrActionMap; + +/* -------------------------------------------------------------------- */ + #ifdef __cplusplus } #endif diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 56e8418972c..358db14c298 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -18,9 +18,14 @@ * \ingroup RNA */ +#include "BLI_math.h" + +#include "DNA_space_types.h" #include "DNA_view3d_types.h" +#include "DNA_windowmanager_types.h" #include "DNA_xr_types.h" +#include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -30,10 +35,462 @@ #ifdef RNA_RUNTIME -# include "BLI_math.h" +# include "BLI_listbase.h" # include "WM_api.h" +/* -------------------------------------------------------------------- */ +/** \name XR Action Map + * \{ */ + +static XrActionMapBinding *rna_XrActionMapBinding_new(XrActionMapItem *ami, + const char *name, + bool replace_existing) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_binding_new(ami, name, replace_existing); +# else + UNUSED_VARS(ami, name, replace_existing); + return NULL; +# endif +} + +static XrActionMapBinding *rna_XrActionMapBinding_new_from_binding(XrActionMapItem *ami, + XrActionMapBinding *amb_src) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_binding_add_copy(ami, amb_src); +# else + UNUSED_VARS(ami, amb_src); + return NULL; +# endif +} + +static void rna_XrActionMapBinding_remove(XrActionMapItem *ami, + ReportList *reports, + PointerRNA *amb_ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = amb_ptr->data; + if (WM_xr_actionmap_binding_remove(ami, amb) == false) { + BKE_reportf(reports, + RPT_ERROR, + "ActionMapBinding '%s' cannot be removed from '%s'", + amb->name, + ami->name); + return; + } + RNA_POINTER_INVALIDATE(amb_ptr); +# else + UNUSED_VARS(ami, reports, amb_ptr); +# endif +} + +static XrActionMapBinding *rna_XrActionMapBinding_find(XrActionMapItem *ami, const char *name) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_binding_find(ami, name); +# else + UNUSED_VARS(ami, name); + return NULL; +# endif +} + +static int rna_XrActionMapBinding_axis0_flag_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = ptr->data; + if ((amb->axis_flag & XR_AXIS0_POS) != 0) { + return XR_AXIS0_POS; + } + if ((amb->axis_flag & XR_AXIS0_NEG) != 0) { + return XR_AXIS0_NEG; + } +# else + UNUSED_VARS(ptr); +# endif + return 0; +} + +static void rna_XrActionMapBinding_axis0_flag_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = ptr->data; + amb->axis_flag &= ~(XR_AXIS0_POS | XR_AXIS0_NEG); + amb->axis_flag |= value; +# else + UNUSED_VARS(ptr, value); +# endif +} + +static int rna_XrActionMapBinding_axis1_flag_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = ptr->data; + if ((amb->axis_flag & XR_AXIS1_POS) != 0) { + return XR_AXIS1_POS; + } + if ((amb->axis_flag & XR_AXIS1_NEG) != 0) { + return XR_AXIS1_NEG; + } +# else + UNUSED_VARS(ptr); +# endif + return 0; +} + +static void rna_XrActionMapBinding_axis1_flag_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = ptr->data; + amb->axis_flag &= ~(XR_AXIS1_POS | XR_AXIS1_NEG); + amb->axis_flag |= value; +# else + UNUSED_VARS(ptr, value); +# endif +} + +static void rna_XrActionMapBinding_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = bmain->wm.first; + if (wm && wm->xr.runtime) { + ListBase *actionmaps = WM_xr_actionmaps_get(wm->xr.runtime); + short idx = WM_xr_actionmap_selected_index_get(wm->xr.runtime); + XrActionMap *actionmap = BLI_findlink(actionmaps, idx); + if (actionmap) { + XrActionMapItem *ami = BLI_findlink(&actionmap->items, actionmap->selitem); + if (ami) { + XrActionMapBinding *amb = ptr->data; + WM_xr_actionmap_binding_ensure_unique(ami, amb); + } + } + } +# else + UNUSED_VARS(bmain, ptr); +# endif +} + +static XrActionMapItem *rna_XrActionMapItem_new(XrActionMap *am, + const char *name, + bool replace_existing) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_item_new(am, name, replace_existing); +# else + UNUSED_VARS(am, name, replace_existing); + return NULL; +# endif +} + +static XrActionMapItem *rna_XrActionMapItem_new_from_item(XrActionMap *am, + XrActionMapItem *ami_src) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_item_add_copy(am, ami_src); +# else + UNUSED_VARS(am, ami_src); + return NULL; +# endif +} + +static void rna_XrActionMapItem_remove(XrActionMap *am, ReportList *reports, PointerRNA *ami_ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ami_ptr->data; + if (WM_xr_actionmap_item_remove(am, ami) == false) { + BKE_reportf( + reports, RPT_ERROR, "ActionMapItem '%s' cannot be removed from '%s'", ami->name, am->name); + return; + } + RNA_POINTER_INVALIDATE(ami_ptr); +# else + UNUSED_VARS(am, reports, ami_ptr); +# endif +} + +static XrActionMapItem *rna_XrActionMapItem_find(XrActionMap *am, const char *name) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_item_find(am, name); +# else + UNUSED_VARS(am, name); + return NULL; +# endif +} + +static void rna_XrActionMapItem_op_name_get(PointerRNA *ptr, char *value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if (ami->op[0]) { + if (ami->op_properties_ptr) { + wmOperatorType *ot = WM_operatortype_find(ami->op, 1); + if (ot) { + strcpy(value, WM_operatortype_name(ot, ami->op_properties_ptr)); + return; + } + } + strcpy(value, ami->op); + return; + } +# else + UNUSED_VARS(ptr); +# endif + value[0] = '\0'; +} + +static int rna_XrActionMapItem_op_name_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if (ami->op[0]) { + if (ami->op_properties_ptr) { + wmOperatorType *ot = WM_operatortype_find(ami->op, 1); + if (ot) { + return strlen(WM_operatortype_name(ot, ami->op_properties_ptr)); + } + } + return strlen(ami->op); + } +# else + UNUSED_VARS(ptr); +# endif + return 0; +} + +static PointerRNA rna_XrActionMapItem_op_properties_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if (ami->op_properties_ptr) { + return *(ami->op_properties_ptr); + } +# else + UNUSED_VARS(ptr); +# endif + return PointerRNA_NULL; +} + +static bool rna_XrActionMapItem_bimanual_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->action_flag & XR_ACTION_BIMANUAL) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +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; + } +# else + UNUSED_VARS(ptr, value); +# endif +} + +static bool rna_XrActionMapItem_haptic_match_user_paths_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->haptic_flag & XR_HAPTIC_MATCHUSERPATHS) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +static void rna_XrActionMapItem_haptic_match_user_paths_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if (value) { + ami->haptic_flag |= XR_HAPTIC_MATCHUSERPATHS; + } + else { + ami->haptic_flag &= ~XR_HAPTIC_MATCHUSERPATHS; + } +# else + UNUSED_VARS(ptr, value); +# endif +} + +static int rna_XrActionMapItem_haptic_flag_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->haptic_flag & XR_HAPTIC_RELEASE) != 0) { + return ((ami->haptic_flag & XR_HAPTIC_PRESS) != 0) ? (XR_HAPTIC_PRESS | XR_HAPTIC_RELEASE) : + XR_HAPTIC_RELEASE; + } + if ((ami->haptic_flag & XR_HAPTIC_REPEAT) != 0) { + return XR_HAPTIC_REPEAT; + } +# else + UNUSED_VARS(ptr); +# endif + return XR_HAPTIC_PRESS; +} + +static void rna_XrActionMapItem_haptic_flag_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + ami->haptic_flag &= ~(XR_HAPTIC_PRESS | XR_HAPTIC_RELEASE | XR_HAPTIC_REPEAT); + ami->haptic_flag |= value; +# else + UNUSED_VARS(ptr, value); +# endif +} + +static bool rna_XrActionMapItem_pose_is_controller_grip_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->pose_flag & XR_POSE_GRIP) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +static void rna_XrActionMapItem_pose_is_controller_grip_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + SET_FLAG_FROM_TEST(ami->pose_flag, value, XR_POSE_GRIP); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static bool rna_XrActionMapItem_pose_is_controller_aim_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->pose_flag & XR_POSE_AIM) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +static void rna_XrActionMapItem_pose_is_controller_aim_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + SET_FLAG_FROM_TEST(ami->pose_flag, value, XR_POSE_AIM); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static void rna_XrActionMapItem_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = bmain->wm.first; + if (wm && wm->xr.runtime) { + ListBase *actionmaps = WM_xr_actionmaps_get(wm->xr.runtime); + short idx = WM_xr_actionmap_selected_index_get(wm->xr.runtime); + XrActionMap *actionmap = BLI_findlink(actionmaps, idx); + if (actionmap) { + XrActionMapItem *ami = ptr->data; + WM_xr_actionmap_item_ensure_unique(actionmap, ami); + } + } +# else + UNUSED_VARS(bmain, ptr); +# endif +} + +static void rna_XrActionMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + WM_xr_actionmap_item_properties_update_ot(ami); +# else + UNUSED_VARS(ptr); +# endif +} + +static XrActionMap *rna_XrActionMap_new(wmXrData *xr, const char *name, bool replace_existing) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_new(xr->runtime, name, replace_existing); +# else + UNUSED_VARS(xr, name, replace_existing); + return NULL; +# endif +} + +static XrActionMap *rna_XrActionMap_new_from_actionmap(wmXrData *xr, XrActionMap *am_src) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_add_copy(xr->runtime, am_src); +# else + UNUSED_VARS(xr, am_src); + return NULL; +# endif +} + +static void rna_XrActionMap_remove(wmXrData *xr, ReportList *reports, PointerRNA *actionmap_ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMap *actionmap = actionmap_ptr->data; + if (WM_xr_actionmap_remove(xr->runtime, actionmap) == false) { + BKE_reportf(reports, RPT_ERROR, "ActionMap '%s' cannot be removed", actionmap->name); + return; + } + RNA_POINTER_INVALIDATE(actionmap_ptr); +# else + UNUSED_VARS(xr, reports, actionmap_ptr); +# endif +} + +static XrActionMap *rna_XrActionMap_find(wmXrData *xr, const char *name) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_find(xr->runtime, name); +# else + UNUSED_VARS(xr, name); + return NULL; +# endif +} + +static void rna_XrActionMap_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = bmain->wm.first; + if (wm && wm->xr.runtime) { + XrActionMap *actionmap = ptr->data; + WM_xr_actionmap_ensure_unique(wm->xr.runtime, actionmap); + } +# else + UNUSED_VARS(bmain, ptr); +# endif +} + +/** \} */ + +/* -------------------------------------------------------------------- */ + # ifdef WITH_XR_OPENXR static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr) { @@ -49,93 +506,879 @@ static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr) } # endif -static bool rna_XrSessionSettings_use_positional_tracking_get(PointerRNA *ptr) +/* -------------------------------------------------------------------- */ +/** \name XR Session Settings + * \{ */ + +static bool rna_XrSessionSettings_use_positional_tracking_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + return (xr->session_settings.flag & XR_SESSION_USE_POSITION_TRACKING) != 0; +# else + UNUSED_VARS(ptr); + return false; +# endif +} + +static void rna_XrSessionSettings_use_positional_tracking_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + SET_FLAG_FROM_TEST(xr->session_settings.flag, value, XR_SESSION_USE_POSITION_TRACKING); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static bool rna_XrSessionSettings_use_absolute_tracking_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + return (xr->session_settings.flag & XR_SESSION_USE_ABSOLUTE_TRACKING) != 0; +# else + UNUSED_VARS(ptr); + return false; +# endif +} + +static void rna_XrSessionSettings_use_absolute_tracking_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + SET_FLAG_FROM_TEST(xr->session_settings.flag, value, XR_SESSION_USE_ABSOLUTE_TRACKING); +# else + UNUSED_VARS(ptr, value); +# endif +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Session State + * \{ */ + +static bool rna_XrSessionState_is_running(bContext *C) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_session_exists(&wm->xr); +# else + UNUSED_VARS(C); + return false; +# endif +} + +static void rna_XrSessionState_reset_to_base_pose(bContext *C) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_base_pose_reset(&wm->xr); +# else + UNUSED_VARS(C); +# endif +} + +static bool rna_XrSessionState_action_set_create(bContext *C, XrActionMap *actionmap) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_action_set_create(&wm->xr, actionmap->name); +# else + UNUSED_VARS(C, actionmap); + return false; +# endif +} + +static bool rna_XrSessionState_action_create(bContext *C, + XrActionMap *actionmap, + XrActionMapItem *ami) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + unsigned int count_subaction_paths = 0; + const char *subaction_paths[2]; + + if (ami->user_path0[0]) { + subaction_paths[0] = ami->user_path0; + ++count_subaction_paths; + + if (ami->user_path1[0]) { + subaction_paths[1] = ami->user_path1; + ++count_subaction_paths; + } + } + else { + if (ami->user_path1[0]) { + subaction_paths[0] = ami->user_path1; + ++count_subaction_paths; + } + else { + return false; + } + } + + const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT); + 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) { + if (ami->op[0]) { + char idname[OP_MAX_TYPENAME]; + WM_operator_bl_idname(idname, ami->op); + ot = WM_operatortype_find(idname, true); + if (ot) { + op_properties = ami->op_properties; + } + } + + haptic_name = &ami->haptic_name[0]; + haptic_duration_msec = (int64_t)(ami->haptic_duration * 1000.0f); + } + + return WM_xr_action_create(&wm->xr, + actionmap->name, + ami->name, + ami->type, + count_subaction_paths, + subaction_paths, + ot, + op_properties, + is_button_action ? &haptic_name : NULL, + is_button_action ? &haptic_duration_msec : NULL, + is_button_action ? &ami->haptic_frequency : NULL, + is_button_action ? &ami->haptic_amplitude : NULL, + ami->op_flag, + ami->action_flag, + ami->haptic_flag); +# else + UNUSED_VARS(C, actionmap, ami); + return false; +# endif +} + +static bool rna_XrSessionState_action_binding_create(bContext *C, + XrActionMap *actionmap, + XrActionMapItem *ami, + XrActionMapBinding *amb) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + unsigned int count_subaction_paths = 0; + const char *subaction_paths[2]; + const char *component_paths[2]; + + if (ami->user_path0[0]) { + subaction_paths[0] = ami->user_path0; + component_paths[0] = amb->component_path0; + ++count_subaction_paths; + + if (ami->user_path1[0]) { + subaction_paths[1] = ami->user_path1; + component_paths[1] = amb->component_path1; + ++count_subaction_paths; + } + } + else { + if (ami->user_path1[0]) { + subaction_paths[0] = ami->user_path1; + component_paths[0] = amb->component_path1; + ++count_subaction_paths; + } + else { + return false; + } + } + + const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT); + const bool is_button_action = (is_float_action || ami->type == XR_BOOLEAN_INPUT); + const bool is_pose_action = (ami->type == XR_POSE_INPUT); + float float_thresholds[2]; + eXrAxisFlag axis_flags[2]; + wmXrPose poses[2]; + + if (is_float_action) { + float_thresholds[0] = float_thresholds[1] = amb->float_threshold; + } + if (is_button_action) { + axis_flags[0] = axis_flags[1] = amb->axis_flag; + } + if (is_pose_action) { + copy_v3_v3(poses[0].position, amb->pose_location); + eul_to_quat(poses[0].orientation_quat, amb->pose_rotation); + normalize_qt(poses[0].orientation_quat); + memcpy(&poses[1], &poses[0], sizeof(poses[1])); + } + + return WM_xr_action_binding_create(&wm->xr, + actionmap->name, + ami->name, + amb->profile, + count_subaction_paths, + subaction_paths, + component_paths, + is_float_action ? float_thresholds : NULL, + is_button_action ? axis_flags : NULL, + is_pose_action ? poses : NULL); +# else + UNUSED_VARS(C, actionmap, ami, amb); + return false; +# endif +} + +bool rna_XrSessionState_active_action_set_set(bContext *C, const char *action_set_name) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_active_action_set_set(&wm->xr, action_set_name); +# else + UNUSED_VARS(C, action_set_name); + return false; +# endif +} + +bool rna_XrSessionState_controller_pose_actions_set(bContext *C, + const char *action_set_name, + const char *grip_action_name, + const char *aim_action_name) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_controller_pose_actions_set( + &wm->xr, action_set_name, grip_action_name, aim_action_name); +# else + UNUSED_VARS(C, action_set_name, grip_action_name, aim_action_name); + return false; +# endif +} + +void rna_XrSessionState_action_state_get(bContext *C, + const char *action_set_name, + const char *action_name, + const char *user_path, + float r_state[2]) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + wmXrActionState state; + if (WM_xr_action_state_get(&wm->xr, action_set_name, action_name, user_path, &state)) { + switch (state.type) { + case XR_BOOLEAN_INPUT: + r_state[0] = (float)state.state_boolean; + r_state[1] = 0.0f; + return; + case XR_FLOAT_INPUT: + r_state[0] = state.state_float; + r_state[1] = 0.0f; + return; + case XR_VECTOR2F_INPUT: + copy_v2_v2(r_state, state.state_vector2f); + return; + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + } +# else + UNUSED_VARS(C, action_set_name, action_name, user_path); +# endif + zero_v2(r_state); +} + +bool rna_XrSessionState_haptic_action_apply(bContext *C, + const char *action_set_name, + const char *action_name, + const char *user_path, + float duration, + float frequency, + float amplitude) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + int64_t duration_msec = (int64_t)(duration * 1000.0f); + return WM_xr_haptic_action_apply(&wm->xr, + action_set_name, + action_name, + user_path[0] ? &user_path : NULL, + &duration_msec, + &frequency, + &litude); +# else + UNUSED_VARS(C, action_set_name, action_name, user_path, duration, frequency, amplitude); + return false; +# endif +} + +void rna_XrSessionState_haptic_action_stop(bContext *C, + const char *action_set_name, + const char *action_name, + const char *user_path) +{ +# 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); +# else + UNUSED_VARS(C, action_set_name, action_name, user_path); +# endif +} + +static void rna_XrSessionState_controller_grip_location_get(bContext *C, + int *index, + float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_controller_grip_location_get(&wm->xr, *index, r_values); +# else + UNUSED_VARS(C, index); + zero_v3(r_values); +# endif +} + +static void rna_XrSessionState_controller_grip_rotation_get(bContext *C, + int index, + float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_controller_grip_rotation_get(&wm->xr, index, r_values); +# else + UNUSED_VARS(C, index); + unit_qt(r_values); +# endif +} + +static void rna_XrSessionState_controller_aim_location_get(bContext *C, + int *index, + float *r_values) { # ifdef WITH_XR_OPENXR - const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - return (xr->session_settings.flag & XR_SESSION_USE_POSITION_TRACKING) != 0; + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_controller_aim_location_get(&wm->xr, *index, r_values); # else - UNUSED_VARS(ptr); - return false; + UNUSED_VARS(C, index); + zero_v3(r_values); # endif } -static void rna_XrSessionSettings_use_positional_tracking_set(PointerRNA *ptr, bool value) +static void rna_XrSessionState_controller_aim_rotation_get(bContext *C, int index, float *r_values) { # ifdef WITH_XR_OPENXR - wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - SET_FLAG_FROM_TEST(xr->session_settings.flag, value, XR_SESSION_USE_POSITION_TRACKING); + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_controller_aim_rotation_get(&wm->xr, index, r_values); # else - UNUSED_VARS(ptr, value); + UNUSED_VARS(C, index); + unit_qt(r_values); # endif } -static bool rna_XrSessionSettings_use_absolute_tracking_get(PointerRNA *ptr) +static void rna_XrSessionState_viewer_pose_location_get(PointerRNA *ptr, float *r_values) { # ifdef WITH_XR_OPENXR const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - return (xr->session_settings.flag & XR_SESSION_USE_ABSOLUTE_TRACKING) != 0; + WM_xr_session_state_viewer_pose_location_get(xr, r_values); # else UNUSED_VARS(ptr); - return false; + zero_v3(r_values); # endif } -static void rna_XrSessionSettings_use_absolute_tracking_set(PointerRNA *ptr, bool value) +static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float *r_values) { # ifdef WITH_XR_OPENXR - wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - SET_FLAG_FROM_TEST(xr->session_settings.flag, value, XR_SESSION_USE_ABSOLUTE_TRACKING); + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_viewer_pose_rotation_get(xr, r_values); # else - UNUSED_VARS(ptr, value); + UNUSED_VARS(ptr); + unit_qt(r_values); # endif } -static bool rna_XrSessionState_is_running(bContext *C) +static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + ListBase *lb = WM_xr_actionmaps_get(xr->runtime); + rna_iterator_listbase_begin(iter, lb, NULL); +} + +static int rna_XrSessionState_active_actionmap_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR - const wmWindowManager *wm = CTX_wm_manager(C); - return WM_xr_session_exists(&wm->xr); + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + return WM_xr_actionmap_active_index_get(xr->runtime); # else - UNUSED_VARS(C); - return false; + UNUSED_VARS(ptr); + return -1; # endif } -static void rna_XrSessionState_reset_to_base_pose(bContext *C) +static void rna_XrSessionState_active_actionmap_set(PointerRNA *ptr, int value) { # ifdef WITH_XR_OPENXR - wmWindowManager *wm = CTX_wm_manager(C); - WM_xr_session_base_pose_reset(&wm->xr); + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_actionmap_active_index_set(xr->runtime, (short)value); # else - UNUSED_VARS(C); + UNUSED_VARS(ptr, value); # endif } -static void rna_XrSessionState_viewer_pose_location_get(PointerRNA *ptr, float *r_values) +static int rna_XrSessionState_selected_actionmap_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - WM_xr_session_state_viewer_pose_location_get(xr, r_values); + return WM_xr_actionmap_selected_index_get(xr->runtime); # else UNUSED_VARS(ptr); - zero_v3(r_values); + return -1; # endif } -static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float *r_values) +static void rna_XrSessionState_selected_actionmap_set(PointerRNA *ptr, int value) { # ifdef WITH_XR_OPENXR - const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); - WM_xr_session_state_viewer_pose_rotation_get(xr, r_values); + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_actionmap_selected_index_set(xr->runtime, (short)value); # else - UNUSED_VARS(ptr); - unit_qt(r_values); + UNUSED_VARS(ptr, value); # endif } +/** \} */ + #else /* RNA_RUNTIME */ +/* -------------------------------------------------------------------- */ + +static const EnumPropertyItem rna_enum_xr_action_types[] = { + {XR_FLOAT_INPUT, + "FLOAT", + 0, + "Float", + "Float action, representing either a digital or analog button"}, + {XR_VECTOR2F_INPUT, + "VECTOR2D", + 0, + "Vector2D", + "2D float vector action, representing a thumbstick or trackpad"}, + {XR_POSE_INPUT, + "POSE", + 0, + "Pose", + "3D pose action, representing a controller's location and rotation"}, + {XR_VIBRATION_OUTPUT, + "VIBRATION", + 0, + "Vibration", + "Haptic vibration output action, to be applied with a duration, frequency, and amplitude"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_xr_op_flags[] = { + {XR_OP_PRESS, + "PRESS", + 0, + "Press", + "Execute operator on button press (non-modal operators only)"}, + {XR_OP_RELEASE, + "RELEASE", + 0, + "Release", + "Execute operator on button release (non-modal operators only)"}, + {XR_OP_MODAL, "MODAL", 0, "Modal", "Use modal execution (modal operators only)"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_xr_haptic_flags[] = { + {XR_HAPTIC_PRESS, "PRESS", 0, "Press", "Apply haptics on button press"}, + {XR_HAPTIC_RELEASE, "RELEASE", 0, "Release", "Apply haptics on button release"}, + {XR_HAPTIC_PRESS | XR_HAPTIC_RELEASE, + "PRESS_RELEASE", + 0, + "Press Release", + "Apply haptics on button press and release"}, + {XR_HAPTIC_REPEAT, + "REPEAT", + 0, + "Repeat", + "Apply haptics repeatedly for the duration of the button press"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_xr_axis0_flags[] = { + {0, "ANY", 0, "Any", "Use any axis region for operator execution"}, + {XR_AXIS0_POS, + "POSITIVE", + 0, + "Positive", + "Use positive axis region only for operator execution"}, + {XR_AXIS0_NEG, + "NEGATIVE", + 0, + "Negative", + "Use negative axis region only for operator execution"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_xr_axis1_flags[] = { + {0, "ANY", 0, "Any", "Use any axis region for operator execution"}, + {XR_AXIS1_POS, + "POSITIVE", + 0, + "Positive", + "Use positive axis region only for operator execution"}, + {XR_AXIS1_NEG, + "NEGATIVE", + 0, + "Negative", + "Use negative axis region only for operator execution"}, + {0, NULL, 0, NULL, NULL}, +}; + +/* -------------------------------------------------------------------- */ +/** \name XR Action Map + * \{ */ + +static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrActionMapBindings"); + srna = RNA_def_struct(brna, "XrActionMapBindings", NULL); + RNA_def_struct_sdna(srna, "XrActionMapItem"); + 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", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, + "replace_existing", + true, + "Replace Existing", + "Replace any existing binding with the same name"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "binding", "XrActionMapBinding", "Binding", "Added action map binding"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "new_from_binding", "rna_XrActionMapBinding_new_from_binding"); + parm = RNA_def_pointer( + func, "binding", "XrActionMapBinding", "Binding", "Binding to use as a reference"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "result", "XrActionMapBinding", "Binding", "Added action map binding"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrActionMapBinding_remove"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "binding", "XrActionMapBinding", "Binding", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "find", "rna_XrActionMapBinding_find"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, + "binding", + "XrActionMapBinding", + "Binding", + "The action map binding with the given name"); + RNA_def_function_return(func, parm); +} + +static void rna_def_xr_actionmap_items(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrActionMapItems"); + srna = RNA_def_struct(brna, "XrActionMapItems", NULL); + RNA_def_struct_sdna(srna, "XrActionMap"); + 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", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, + "replace_existing", + true, + "Replace Existing", + "Replace any existing item with the same name"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", "Added action map item"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "new_from_item", "rna_XrActionMapItem_new_from_item"); + parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", "Item to use as a reference"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "result", "XrActionMapItem", "Item", "Added action map item"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrActionMapItem_remove"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "find", "rna_XrActionMapItem_find"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "item", "XrActionMapItem", "Item", "The action map item with the given name"); + RNA_def_function_return(func, parm); +} + +static void rna_def_xr_actionmaps(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrActionMaps"); + srna = RNA_def_struct(brna, "XrActionMaps", NULL); + RNA_def_struct_sdna(srna, "wmXrData"); + RNA_def_struct_ui_text(srna, "XR Action Maps", "Collection of XR action maps"); + + func = RNA_def_function(srna, "new", "rna_XrActionMap_new"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, + "replace_existing", + true, + "Replace Existing", + "Replace any existing actionmap with the same name"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "Action Map", "Added action map"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "new_from_actionmap", "rna_XrActionMap_new_from_actionmap"); + parm = RNA_def_pointer( + func, "actionmap", "XrActionMap", "Action Map", "Action map to use as a reference"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "result", "XrActionMap", "Action Map", "Added action map"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrActionMap_remove"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "Action Map", "Removed action map"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "find", "rna_XrActionMap_find"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "actionmap", "XrActionMap", "Action Map", "The action map with the given name"); + RNA_def_function_return(func, parm); +} + +static void rna_def_xr_actionmap(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* XrActionMap */ + srna = RNA_def_struct(brna, "XrActionMap", NULL); + RNA_def_struct_sdna(srna, "XrActionMap"); + RNA_def_struct_ui_text(srna, "XR Action Map", ""); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the action map"); + RNA_def_property_update(prop, 0, "rna_XrActionMap_name_update"); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "actionmap_items", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "items", NULL); + RNA_def_property_struct_type(prop, "XrActionMapItem"); + RNA_def_property_ui_text( + prop, + "Items", + "Items in the action map, mapping an XR event to an operator, pose, or haptic output"); + rna_def_xr_actionmap_items(brna, prop); + + prop = RNA_def_property(srna, "selected_item", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "selitem"); + RNA_def_property_ui_text(prop, "Selected Item", ""); + + /* XrActionMapItem */ + srna = RNA_def_struct(brna, "XrActionMapItem", NULL); + RNA_def_struct_sdna(srna, "XrActionMapItem"); + RNA_def_struct_ui_text(srna, "XR Action Map Item", ""); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the action map item"); + RNA_def_property_update(prop, 0, "rna_XrActionMapItem_name_update"); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_xr_action_types); + RNA_def_property_ui_text(prop, "Type", "Action type"); + RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update"); + + prop = RNA_def_property(srna, "user_path0", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 64); + RNA_def_property_ui_text(prop, "User Path 0", "OpenXR user path"); + + prop = RNA_def_property(srna, "user_path1", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 64); + RNA_def_property_ui_text(prop, "User Path 1", "OpenXR user path"); + + prop = RNA_def_property(srna, "op", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); + RNA_def_property_ui_text(prop, "Operator", "Identifier of operator to call on action event"); + RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update"); + + prop = RNA_def_property(srna, "op_name", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, "Operator Name", "Name of operator (translated) to call on action event"); + RNA_def_property_string_funcs( + prop, "rna_XrActionMapItem_op_name_get", "rna_XrActionMapItem_op_name_length", NULL); + + prop = RNA_def_property(srna, "op_properties", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "OperatorProperties"); + RNA_def_property_pointer_funcs(prop, "rna_XrActionMapItem_op_properties_get", NULL, NULL, NULL); + RNA_def_property_ui_text( + prop, "Operator Properties", "Properties to set when the operator is called"); + RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update"); + + prop = RNA_def_property(srna, "op_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "op_flag"); + RNA_def_property_enum_items(prop, rna_enum_xr_op_flags); + RNA_def_property_ui_text(prop, "Operator Mode", "Operator execution mode"); + + prop = RNA_def_property(srna, "bimanual", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs( + prop, "rna_XrActionMapItem_bimanual_get", "rna_XrActionMapItem_bimanual_set"); + RNA_def_property_ui_text( + prop, "Bimanual", "The action depends on the states/poses of both user paths"); + + prop = RNA_def_property(srna, "pose_is_controller_grip", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, + "rna_XrActionMapItem_pose_is_controller_grip_get", + "rna_XrActionMapItem_pose_is_controller_grip_set"); + RNA_def_property_ui_text( + prop, "Is Controller Grip", "The action poses will be used for the VR controller grips"); + + prop = RNA_def_property(srna, "pose_is_controller_aim", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, + "rna_XrActionMapItem_pose_is_controller_aim_get", + "rna_XrActionMapItem_pose_is_controller_aim_set"); + RNA_def_property_ui_text( + prop, "Is Controller Aim", "The action poses will be used for the VR controller aims"); + + prop = RNA_def_property(srna, "haptic_name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text( + prop, "Haptic Name", "Name of the haptic action to apply when executing this action"); + + prop = RNA_def_property(srna, "haptic_match_user_paths", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, + "rna_XrActionMapItem_haptic_match_user_paths_get", + "rna_XrActionMapItem_haptic_match_user_paths_set"); + RNA_def_property_ui_text( + prop, + "Haptic Match User Paths", + "Apply haptics to the same user paths for the haptic action and this action"); + + prop = RNA_def_property(srna, "haptic_duration", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_text(prop, + "Haptic Duration", + "Haptic duration in seconds. 0.0 is the minimum supported duration"); + + prop = RNA_def_property(srna, "haptic_frequency", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_text(prop, + "Haptic Frequency", + "Frequency of the haptic vibration in hertz. 0.0 specifies the OpenXR " + "runtime's default frequency"); + + prop = RNA_def_property(srna, "haptic_amplitude", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text( + prop, "Haptic Amplitude", "Intensity of the haptic vibration, ranging from 0.0 to 1.0"); + + 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); + RNA_def_property_ui_text(prop, "Haptic mode", "Haptic application mode"); + + prop = RNA_def_property(srna, "bindings", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "XrActionMapBinding"); + RNA_def_property_ui_text( + prop, "Bindings", "Bindings for the action map item, mapping the action to an XR input"); + rna_def_xr_actionmap_bindings(brna, prop); + + prop = RNA_def_property(srna, "selected_binding", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "selbinding"); + RNA_def_property_ui_text(prop, "Selected Binding", "Currently selected binding"); + + /* XrActionMapBinding */ + srna = RNA_def_struct(brna, "XrActionMapBinding", NULL); + RNA_def_struct_sdna(srna, "XrActionMapBinding"); + RNA_def_struct_ui_text(srna, "XR Action Map Binding", "Binding in an XR action map item"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the action map binding"); + RNA_def_property_update(prop, 0, "rna_XrActionMapBinding_name_update"); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "profile", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 256); + RNA_def_property_ui_text(prop, "Profile", "OpenXR interaction profile path"); + + prop = RNA_def_property(srna, "component_path0", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 192); + RNA_def_property_ui_text(prop, "Component Path 0", "OpenXR component path"); + + prop = RNA_def_property(srna, "component_path1", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 192); + RNA_def_property_ui_text(prop, "Component Path 1", "OpenXR component path"); + + prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "float_threshold"); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Threshold", "Input threshold for button/axis actions"); + + 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", + NULL); + RNA_def_property_ui_text( + prop, "Axis 0 Region", "Action execution region for the first input axis"); + + 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", + NULL); + RNA_def_property_ui_text( + prop, "Axis 1 Region", "Action execution region for the second input axis"); + + prop = RNA_def_property(srna, "pose_location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_ui_text(prop, "Pose Location Offset", ""); + + prop = RNA_def_property(srna, "pose_rotation", PROP_FLOAT, PROP_EULER); + RNA_def_property_ui_text(prop, "Pose Rotation Offset", ""); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Session Settings + * \{ */ + static void rna_def_xr_session_settings(BlenderRNA *brna) { StructRNA *srna; @@ -241,6 +1484,12 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Session State + * \{ */ + static void rna_def_xr_session_state(BlenderRNA *brna) { StructRNA *srna; @@ -265,6 +1514,260 @@ static void rna_def_xr_session_state(BlenderRNA *brna) parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + func = RNA_def_function(srna, "action_set_create", "rna_XrSessionState_action_set_create"); + RNA_def_function_ui_description(func, "Create a VR action set"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "action_create", "rna_XrSessionState_action_create"); + RNA_def_function_ui_description(func, "Create a VR action"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap_item", "XrActionMapItem", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function( + srna, "action_binding_create", "rna_XrSessionState_action_binding_create"); + RNA_def_function_ui_description(func, "Create a VR action binding"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap_item", "XrActionMapItem", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap_binding", "XrActionMapBinding", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function( + srna, "active_action_set_set", "rna_XrSessionState_active_action_set_set"); + RNA_def_function_ui_description(func, "Set the active VR action set"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function( + srna, "controller_pose_actions_set", "rna_XrSessionState_controller_pose_actions_set"); + RNA_def_function_ui_description(func, "Set the actions that determine the VR controller poses"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, + "grip_action", + NULL, + 64, + "Grip Action", + "Name of the action representing the controller grips"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, + "aim_action", + NULL, + 64, + "Aim Action", + "Name of the action representing the controller aims"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "action_state_get", "rna_XrSessionState_action_state_get"); + RNA_def_function_ui_description(func, "Get the current state of a VR action"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "user_path", NULL, 64, "User Path", "OpenXR user path"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_float_array( + func, + "state", + 2, + NULL, + -FLT_MAX, + FLT_MAX, + "Action State", + "Current state of the VR action. Second float value is only set for 2D vector type actions", + -FLT_MAX, + FLT_MAX); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function(srna, "haptic_action_apply", "rna_XrSessionState_haptic_action_apply"); + RNA_def_function_ui_description(func, "Apply a VR haptic action"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string( + func, + "user_path", + NULL, + 64, + "User Path", + "Optional OpenXR user path. If not set, the action will be applied to all paths"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_float(func, + "duration", + 0.0f, + 0.0f, + FLT_MAX, + "Duration", + "Haptic duration in seconds. 0.0 is the minimum supported duration", + 0.0f, + FLT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float(func, + "frequency", + 0.0f, + 0.0f, + FLT_MAX, + "Frequency", + "Frequency of the haptic vibration in hertz. 0.0 specifies the OpenXR " + "runtime's default frequency", + 0.0f, + FLT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float(func, + "amplitude", + 1.0f, + 0.0f, + 1.0f, + "Amplitude", + "Haptic amplitude, ranging from 0.0 to 1.0", + 0.0f, + 1.0f); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "haptic_action_stop", "rna_XrSessionState_haptic_action_stop"); + RNA_def_function_ui_description(func, "Stop a VR haptic action"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string( + func, + "user_path", + NULL, + 64, + "User Path", + "Optional OpenXR user path. If not set, the action will be stopped for all paths"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + + func = RNA_def_function( + srna, "controller_grip_location_get", "rna_XrSessionState_controller_grip_location_get"); + RNA_def_function_ui_description(func, + "Get the last known controller grip location in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + 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); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function( + srna, "controller_grip_rotation_get", "rna_XrSessionState_controller_grip_rotation_get"); + RNA_def_function_ui_description( + func, "Get the last known controller grip rotation (quaternion) in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + 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); + parm = RNA_def_float_vector(func, + "rotation", + 4, + NULL, + -FLT_MAX, + FLT_MAX, + "Rotation", + "Controller grip quaternion rotation", + -FLT_MAX, + FLT_MAX); + parm->subtype = PROP_QUATERNION; + RNA_def_property_ui_range(parm, -FLT_MAX, FLT_MAX, 1, 5); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function( + srna, "controller_aim_location_get", "rna_XrSessionState_controller_aim_location_get"); + RNA_def_function_ui_description(func, + "Get the last known controller aim location in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + 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); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function( + srna, "controller_aim_rotation_get", "rna_XrSessionState_controller_aim_rotation_get"); + RNA_def_function_ui_description( + func, "Get the last known controller aim rotation (quaternion) in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + 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); + parm = RNA_def_float_vector(func, + "rotation", + 4, + NULL, + -FLT_MAX, + FLT_MAX, + "Rotation", + "Controller aim quaternion rotation", + -FLT_MAX, + FLT_MAX); + parm->subtype = PROP_QUATERNION; + RNA_def_property_ui_range(parm, -FLT_MAX, FLT_MAX, 1, 5); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + prop = RNA_def_property(srna, "viewer_pose_location", PROP_FLOAT, PROP_TRANSLATION); RNA_def_property_array(prop, 3); RNA_def_property_float_funcs(prop, "rna_XrSessionState_viewer_pose_location_get", NULL, NULL); @@ -282,12 +1785,43 @@ static void rna_def_xr_session_state(BlenderRNA *brna) prop, "Viewer Pose Rotation", "Last known rotation of the viewer pose (center between the eyes) in world space"); + + prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, + "rna_XrSessionState_actionmaps_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + NULL, + NULL, + NULL, + NULL); + RNA_def_property_struct_type(prop, "XrActionMap"); + RNA_def_property_ui_text(prop, "XR Action Maps", ""); + rna_def_xr_actionmaps(brna, prop); + + prop = RNA_def_property(srna, "active_actionmap", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs(prop, + "rna_XrSessionState_active_actionmap_get", + "rna_XrSessionState_active_actionmap_set", + NULL); + RNA_def_property_ui_text(prop, "Active Action Map", ""); + + prop = RNA_def_property(srna, "selected_actionmap", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs(prop, + "rna_XrSessionState_selected_actionmap_get", + "rna_XrSessionState_selected_actionmap_set", + NULL); + RNA_def_property_ui_text(prop, "Selected Action Map", ""); } +/** \} */ + void RNA_def_xr(BlenderRNA *brna) { RNA_define_animate_sdna(false); + rna_def_xr_actionmap(brna); rna_def_xr_session_settings(brna); rna_def_xr_session_state(brna); diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 8b0ceb02b5f..b7fbb9bb82b 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -204,6 +204,7 @@ if(WITH_XR_OPENXR) list(APPEND SRC xr/intern/wm_xr.c xr/intern/wm_xr_action.c + xr/intern/wm_xr_actionmap.c xr/intern/wm_xr_draw.c xr/intern/wm_xr_session.c diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 7e26d921bd7..02e8d42e0ff 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -74,8 +74,7 @@ struct wmNDOFMotionData; #endif #ifdef WITH_XR_OPENXR -struct wmXrActionState; -struct wmXrPose; +struct wmXrRuntimeData; #endif typedef struct wmGizmo wmGizmo; @@ -988,7 +987,13 @@ bool WM_xr_action_create(wmXrData *xr, const char **subaction_paths, struct wmOperatorType *ot, struct IDProperty *op_properties, - eXrOpFlag op_flag); + const char **haptic_name, + const int64_t *haptic_duration, + const float *haptic_frequency, + const float *haptic_amplitude, + eXrOpFlag op_flag, + eXrActionFlag action_flag, + eXrHapticFlag haptic_flag); void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name); bool WM_xr_action_binding_create(wmXrData *xr, const char *action_set_name, @@ -1022,10 +1027,48 @@ 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 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); +void WM_xr_haptic_action_stop(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char **subaction_path); + +/* wm_xr_actionmap.c */ +XrActionMap *WM_xr_actionmap_new(struct wmXrRuntimeData *runtime, + const char *name, + bool replace_existing); +void WM_xr_actionmap_ensure_unique(struct wmXrRuntimeData *runtime, XrActionMap *actionmap); +XrActionMap *WM_xr_actionmap_add_copy(struct wmXrRuntimeData *runtime, XrActionMap *am_src); +bool WM_xr_actionmap_remove(struct wmXrRuntimeData *runtime, XrActionMap *actionmap); +XrActionMap *WM_xr_actionmap_find(struct wmXrRuntimeData *runtime, const char *name); +void WM_xr_actionmap_clear(XrActionMap *actionmap); +void WM_xr_actionmaps_clear(struct wmXrRuntimeData *runtime); +ListBase *WM_xr_actionmaps_get(struct wmXrRuntimeData *runtime); +short WM_xr_actionmap_active_index_get(const struct wmXrRuntimeData *runtime); +void WM_xr_actionmap_active_index_set(struct wmXrRuntimeData *runtime, short idx); +short WM_xr_actionmap_selected_index_get(const struct wmXrRuntimeData *runtime); +void WM_xr_actionmap_selected_index_set(struct wmXrRuntimeData *runtime, short idx); + +XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap, + const char *name, + bool replace_existing); +void WM_xr_actionmap_item_ensure_unique(XrActionMap *actionmap, XrActionMapItem *ami); +XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionMapItem *ami_src); +bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami); +XrActionMapItem *WM_xr_actionmap_item_find(XrActionMap *actionmap, const char *name); +void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami); + +XrActionMapBinding *WM_xr_actionmap_binding_new(XrActionMapItem *ami, + const char *name, + bool replace_existing); +void WM_xr_actionmap_binding_ensure_unique(XrActionMapItem *ami, XrActionMapBinding *amb); +XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami, + XrActionMapBinding *amb_src); +bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb); +XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const char *name); #endif /* WITH_XR_OPENXR */ #ifdef __cplusplus diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c index 2a67c2bee9f..716a0936a24 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr.c +++ b/source/blender/windowmanager/xr/intern/wm_xr.c @@ -115,6 +115,7 @@ bool wm_xr_init(wmWindowManager *wm) void wm_xr_exit(wmWindowManager *wm) { if (wm->xr.runtime != NULL) { + WM_xr_actionmaps_clear(wm->xr.runtime); wm_xr_runtime_data_free(&wm->xr.runtime); } if (wm->xr.session_settings.shading.prop) { diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c index 8f2de4bbbad..2712fde51a8 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_action.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -23,6 +23,7 @@ * All functions are designed to be usable by RNA / the Python API. */ +#include "BLI_listbase.h" #include "BLI_math.h" #include "GHOST_C-api.h" @@ -56,6 +57,9 @@ static void action_set_destroy(void *val) MEM_SAFE_FREE(action_set->name); + BLI_freelistN(&action_set->active_modal_actions); + BLI_freelistN(&action_set->active_haptic_actions); + MEM_freeN(action_set); } @@ -70,7 +74,13 @@ static wmXrAction *action_create(const char *action_name, const char **subaction_paths, wmOperatorType *ot, IDProperty *op_properties, - eXrOpFlag op_flag) + const char **haptic_name, + const int64_t *haptic_duration, + const float *haptic_frequency, + const float *haptic_amplitude, + eXrOpFlag op_flag, + eXrActionFlag action_flag, + eXrHapticFlag haptic_flag) { wmXrAction *action = MEM_callocN(sizeof(*action), __func__); action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name"); @@ -121,7 +131,19 @@ static wmXrAction *action_create(const char *action_name, action->ot = ot; action->op_properties = op_properties; + + 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_duration = *haptic_duration; + action->haptic_frequency = *haptic_frequency; + action->haptic_amplitude = *haptic_amplitude; + } + action->op_flag = op_flag; + action->action_flag = action_flag; + action->haptic_flag = haptic_flag; return action; } @@ -147,6 +169,8 @@ static void action_destroy(void *val) MEM_SAFE_FREE(action->float_thresholds); MEM_SAFE_FREE(action->axis_flags); + MEM_SAFE_FREE(action->haptic_name); + MEM_freeN(action); } @@ -190,9 +214,10 @@ void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name) wm_xr_session_controller_data_clear(session_state); action_set->controller_grip_action = action_set->controller_aim_action = NULL; } - if (action_set->active_modal_action) { - action_set->active_modal_action = NULL; - } + + BLI_freelistN(&action_set->active_modal_actions); + BLI_freelistN(&action_set->active_haptic_actions); + session_state->active_action_set = NULL; } @@ -207,14 +232,31 @@ bool WM_xr_action_create(wmXrData *xr, const char **subaction_paths, wmOperatorType *ot, IDProperty *op_properties, - eXrOpFlag op_flag) + const char **haptic_name, + const int64_t *haptic_duration, + const float *haptic_frequency, + const float *haptic_amplitude, + eXrOpFlag op_flag, + eXrActionFlag action_flag, + eXrHapticFlag haptic_flag) { if (action_find(xr, action_set_name, action_name)) { return false; } - wmXrAction *action = action_create( - action_name, type, count_subaction_paths, subaction_paths, ot, op_properties, op_flag); + wmXrAction *action = action_create(action_name, + type, + count_subaction_paths, + subaction_paths, + ot, + op_properties, + haptic_name, + haptic_duration, + haptic_frequency, + haptic_amplitude, + op_flag, + action_flag, + haptic_flag); GHOST_XrActionInfo info = { .name = action_name, @@ -274,9 +316,18 @@ void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char action_set->controller_grip_action = action_set->controller_aim_action = NULL; } - if (action_set->active_modal_action && - STREQ(action_set->active_modal_action->name, action_name)) { - action_set->active_modal_action = NULL; + LISTBASE_FOREACH (LinkData *, ld, &action_set->active_modal_actions) { + wmXrAction *active_modal_action = ld->data; + if (STREQ(active_modal_action->name, action_name)) { + BLI_freelinkN(&action_set->active_modal_actions, ld); + break; + } + } + + LISTBASE_FOREACH_MUTABLE (wmXrHapticAction *, ha, &action_set->active_haptic_actions) { + if (STREQ(ha->action->name, action_name)) { + BLI_freelinkN(&action_set->active_haptic_actions, ha); + } } GHOST_XrDestroyActions(xr->runtime->context, action_set_name, 1, &action_name); @@ -342,16 +393,11 @@ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name) } { - /* Unset active modal action (if any). */ + /* Clear any active modal/haptic actions. */ wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set; if (active_action_set) { - wmXrAction *active_modal_action = active_action_set->active_modal_action; - if (active_modal_action) { - if (active_modal_action->active_modal_path) { - active_modal_action->active_modal_path = NULL; - } - active_action_set->active_modal_action = NULL; - } + BLI_freelistN(&active_action_set->active_modal_actions); + BLI_freelistN(&active_action_set->active_haptic_actions); } } @@ -456,19 +502,28 @@ 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 int64_t *duration, const float *frequency, const float *amplitude) { - return GHOST_XrApplyHapticAction( - xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ? + return GHOST_XrApplyHapticAction(xr->runtime->context, + action_set_name, + action_name, + subaction_path, + duration, + frequency, + amplitude) ? true : false; } -void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name) +void WM_xr_haptic_action_stop(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char **subaction_path) { - GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name); + GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name, subaction_path); } /** \} */ /* XR-Action API */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c new file mode 100644 index 00000000000..7673f2aa212 --- /dev/null +++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c @@ -0,0 +1,565 @@ +/* + * 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. + */ + +/** \file + * \ingroup wm + * + * \name Window-Manager XR Action Maps + * + * XR actionmap API, similar to WM keymap API. + */ + +#include +#include + +#include "BKE_context.h" +#include "BKE_idprop.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "GHOST_Types.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_xr_intern.h" + +#define WM_XR_ACTIONMAP_STR_DEFAULT "actionmap" +#define WM_XR_ACTIONMAP_ITEM_STR_DEFAULT "action" +#define WM_XR_ACTIONMAP_BINDING_STR_DEFAULT "binding" + +/* -------------------------------------------------------------------- */ +/** \name Action Map Binding + * + * Binding in an XR action map item, that maps an action to an XR input. + * \{ */ + +XrActionMapBinding *WM_xr_actionmap_binding_new(XrActionMapItem *ami, + const char *name, + bool replace_existing) +{ + XrActionMapBinding *amb_prev = WM_xr_actionmap_binding_find(ami, name); + if (amb_prev && replace_existing) { + return amb_prev; + } + + XrActionMapBinding *amb = MEM_callocN(sizeof(XrActionMapBinding), __func__); + BLI_strncpy(amb->name, name, MAX_NAME); + if (amb_prev) { + WM_xr_actionmap_binding_ensure_unique(ami, amb); + } + + BLI_addtail(&ami->bindings, amb); + + /* Set non-zero threshold by default. */ + amb->float_threshold = 0.3f; + + return amb; +} + +static XrActionMapBinding *wm_xr_actionmap_binding_find_except(XrActionMapItem *ami, + const char *name, + XrActionMapBinding *ambexcept) +{ + LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) { + if (STREQLEN(name, amb->name, MAX_NAME) && (amb != ambexcept)) { + return amb; + } + } + return NULL; +} + +/** + * Ensure unique name among all action map bindings. + */ +void WM_xr_actionmap_binding_ensure_unique(XrActionMapItem *ami, XrActionMapBinding *amb) +{ + char name[MAX_NAME]; + char *suffix; + size_t baselen; + size_t idx = 0; + + BLI_strncpy(name, amb->name, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + + while (wm_xr_actionmap_binding_find_except(ami, name, amb)) { + if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) { + /* Use default base name. */ + BLI_strncpy(name, WM_XR_ACTIONMAP_BINDING_STR_DEFAULT, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + idx = 0; + } + else { + BLI_snprintf(suffix, MAX_NAME, "%zu", idx); + } + } + + BLI_strncpy(amb->name, name, MAX_NAME); +} + +static XrActionMapBinding *wm_xr_actionmap_binding_copy(XrActionMapBinding *amb_src) +{ + XrActionMapBinding *amb_dst = MEM_dupallocN(amb_src); + + amb_dst->prev = amb_dst->next = NULL; + + return amb_dst; +} + +XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami, + XrActionMapBinding *amb_src) +{ + XrActionMapBinding *amb_dst = wm_xr_actionmap_binding_copy(amb_src); + + WM_xr_actionmap_binding_ensure_unique(ami, amb_dst); + + BLI_addtail(&ami->bindings, amb_dst); + + return amb_dst; +} + +bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb) +{ + int idx = BLI_findindex(&ami->bindings, amb); + + if (idx != -1) { + BLI_freelinkN(&ami->bindings, amb); + + if (BLI_listbase_is_empty(&ami->bindings)) { + ami->selbinding = -1; + } + else { + if (idx <= ami->selbinding) { + if (--ami->selbinding < 0) { + ami->selbinding = 0; + } + } + } + + return true; + } + + return false; +} + +XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const char *name) +{ + LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) { + if (STREQLEN(name, amb->name, MAX_NAME)) { + return amb; + } + } + return NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Action Map Item + * + * Item in an XR action map, that maps an XR event to an operator, pose, or haptic output. + * \{ */ + +static void wm_xr_actionmap_item_properties_set(XrActionMapItem *ami) +{ + WM_operator_properties_alloc(&(ami->op_properties_ptr), &(ami->op_properties), ami->op); + WM_operator_properties_sanitize(ami->op_properties_ptr, 1); +} + +static void wm_xr_actionmap_item_properties_free(XrActionMapItem *ami) +{ + if (ami->op_properties_ptr) { + WM_operator_properties_free(ami->op_properties_ptr); + MEM_freeN(ami->op_properties_ptr); + ami->op_properties_ptr = NULL; + ami->op_properties = NULL; + } + else { + BLI_assert(ami->op_properties == NULL); + } +} + +/** + * Similar to #wm_xr_actionmap_item_properties_set() + * but checks for the #eXrActionType and #wmOperatorType having changed. + */ +void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami) +{ + switch (ami->type) { + case XR_BOOLEAN_INPUT: + case XR_FLOAT_INPUT: + case XR_VECTOR2F_INPUT: + break; + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + wm_xr_actionmap_item_properties_free(ami); + memset(ami->op, 0, sizeof(ami->op)); + return; + } + + if (ami->op[0] == 0) { + wm_xr_actionmap_item_properties_free(ami); + return; + } + + if (ami->op_properties_ptr == NULL) { + wm_xr_actionmap_item_properties_set(ami); + } + else { + wmOperatorType *ot = WM_operatortype_find(ami->op, 0); + if (ot) { + if (ot->srna != ami->op_properties_ptr->type) { + /* Matches wm_xr_actionmap_item_properties_set() but doesn't alloc new ptr. */ + WM_operator_properties_create_ptr(ami->op_properties_ptr, ot); + if (ami->op_properties) { + ami->op_properties_ptr->data = ami->op_properties; + } + WM_operator_properties_sanitize(ami->op_properties_ptr, 1); + } + } + else { + wm_xr_actionmap_item_properties_free(ami); + } + } +} + +XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap, + const char *name, + bool replace_existing) +{ + XrActionMapItem *ami_prev = WM_xr_actionmap_item_find(actionmap, name); + if (ami_prev && replace_existing) { + wm_xr_actionmap_item_properties_free(ami_prev); + return ami_prev; + } + + XrActionMapItem *ami = MEM_callocN(sizeof(XrActionMapItem), __func__); + BLI_strncpy(ami->name, name, MAX_NAME); + if (ami_prev) { + WM_xr_actionmap_item_ensure_unique(actionmap, ami); + } + + BLI_addtail(&actionmap->items, ami); + + /* Set type to float (button) input by default. */ + ami->type = XR_FLOAT_INPUT; + + return ami; +} + +static XrActionMapItem *wm_xr_actionmap_item_find_except(XrActionMap *actionmap, + const char *name, + const XrActionMapItem *amiexcept) +{ + LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { + if (STREQLEN(name, ami->name, MAX_NAME) && (ami != amiexcept)) { + return ami; + } + } + return NULL; +} + +/** + * Ensure unique name among all action map items. + */ +void WM_xr_actionmap_item_ensure_unique(XrActionMap *actionmap, XrActionMapItem *ami) +{ + char name[MAX_NAME]; + char *suffix; + size_t baselen; + size_t idx = 0; + + BLI_strncpy(name, ami->name, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + + while (wm_xr_actionmap_item_find_except(actionmap, name, ami)) { + if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) { + /* Use default base name. */ + BLI_strncpy(name, WM_XR_ACTIONMAP_ITEM_STR_DEFAULT, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + idx = 0; + } + else { + BLI_snprintf(suffix, MAX_NAME, "%zu", idx); + } + } + + BLI_strncpy(ami->name, name, MAX_NAME); +} + +static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami) +{ + XrActionMapItem *amin = MEM_dupallocN(ami); + + amin->prev = amin->next = NULL; + + if (amin->op_properties) { + amin->op_properties_ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr"); + WM_operator_properties_create(amin->op_properties_ptr, amin->op); + + amin->op_properties = IDP_CopyProperty(amin->op_properties); + amin->op_properties_ptr->data = amin->op_properties; + } + else { + amin->op_properties = NULL; + amin->op_properties_ptr = NULL; + } + + return amin; +} + +XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionMapItem *ami_src) +{ + XrActionMapItem *ami_dst = wm_xr_actionmap_item_copy(ami_src); + + WM_xr_actionmap_item_ensure_unique(actionmap, ami_dst); + + BLI_addtail(&actionmap->items, ami_dst); + + return ami_dst; +} + +bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami) +{ + int idx = BLI_findindex(&actionmap->items, ami); + + if (idx != -1) { + if (ami->op_properties_ptr) { + WM_operator_properties_free(ami->op_properties_ptr); + MEM_freeN(ami->op_properties_ptr); + } + BLI_freelinkN(&actionmap->items, ami); + + if (BLI_listbase_is_empty(&actionmap->items)) { + actionmap->selitem = -1; + } + else { + if (idx <= actionmap->selitem) { + if (--actionmap->selitem < 0) { + actionmap->selitem = 0; + } + } + } + + return true; + } + + return false; +} + +XrActionMapItem *WM_xr_actionmap_item_find(XrActionMap *actionmap, const char *name) +{ + LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { + if (STREQLEN(name, ami->name, MAX_NAME)) { + return ami; + } + } + return NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Action Map + * + * List of XR action map items. + * \{ */ + +XrActionMap *WM_xr_actionmap_new(wmXrRuntimeData *runtime, const char *name, bool replace_existing) +{ + XrActionMap *am_prev = WM_xr_actionmap_find(runtime, name); + if (am_prev && replace_existing) { + WM_xr_actionmap_clear(am_prev); + return am_prev; + } + + XrActionMap *am = MEM_callocN(sizeof(struct XrActionMap), __func__); + BLI_strncpy(am->name, name, MAX_NAME); + if (am_prev) { + WM_xr_actionmap_ensure_unique(runtime, am); + } + + BLI_addtail(&runtime->actionmaps, am); + + return am; +} + +static XrActionMap *wm_xr_actionmap_find_except(wmXrRuntimeData *runtime, + const char *name, + const XrActionMap *am_except) +{ + LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { + if (STREQLEN(name, am->name, MAX_NAME) && (am != am_except)) { + return am; + } + } + + return NULL; +} + +/** + * Ensure unique name among all action maps. + */ +void WM_xr_actionmap_ensure_unique(wmXrRuntimeData *runtime, XrActionMap *actionmap) +{ + char name[MAX_NAME]; + char *suffix; + size_t baselen; + size_t idx = 0; + + BLI_strncpy(name, actionmap->name, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + + while (wm_xr_actionmap_find_except(runtime, name, actionmap)) { + if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) { + /* Use default base name. */ + BLI_strncpy(name, WM_XR_ACTIONMAP_STR_DEFAULT, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + idx = 0; + } + else { + BLI_snprintf(suffix, MAX_NAME, "%zu", idx); + } + } + + BLI_strncpy(actionmap->name, name, MAX_NAME); +} + +static XrActionMap *wm_xr_actionmap_copy(XrActionMap *am_src) +{ + XrActionMap *am_dst = MEM_dupallocN(am_src); + + am_dst->prev = am_dst->next = NULL; + BLI_listbase_clear(&am_dst->items); + + LISTBASE_FOREACH (XrActionMapItem *, ami, &am_src->items) { + XrActionMapItem *ami_new = wm_xr_actionmap_item_copy(ami); + BLI_addtail(&am_dst->items, ami_new); + } + + return am_dst; +} + +XrActionMap *WM_xr_actionmap_add_copy(wmXrRuntimeData *runtime, XrActionMap *am_src) +{ + XrActionMap *am_dst = wm_xr_actionmap_copy(am_src); + + WM_xr_actionmap_ensure_unique(runtime, am_dst); + + BLI_addtail(&runtime->actionmaps, am_dst); + + return am_dst; +} + +bool WM_xr_actionmap_remove(wmXrRuntimeData *runtime, XrActionMap *actionmap) +{ + int idx = BLI_findindex(&runtime->actionmaps, actionmap); + + if (idx != -1) { + WM_xr_actionmap_clear(actionmap); + BLI_freelinkN(&runtime->actionmaps, actionmap); + + if (BLI_listbase_is_empty(&runtime->actionmaps)) { + runtime->actactionmap = runtime->selactionmap = -1; + } + else { + if (idx <= runtime->actactionmap) { + if (--runtime->actactionmap < 0) { + runtime->actactionmap = 0; + } + } + if (idx <= runtime->selactionmap) { + if (--runtime->selactionmap < 0) { + runtime->selactionmap = 0; + } + } + } + + return true; + } + + return false; +} + +XrActionMap *WM_xr_actionmap_find(wmXrRuntimeData *runtime, const char *name) +{ + LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { + if (STREQLEN(name, am->name, MAX_NAME)) { + return am; + } + } + return NULL; +} + +void WM_xr_actionmap_clear(XrActionMap *actionmap) +{ + LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { + wm_xr_actionmap_item_properties_free(ami); + } + + BLI_freelistN(&actionmap->items); + + actionmap->selitem = -1; +} + +void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime) +{ + LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { + WM_xr_actionmap_clear(am); + } + + BLI_freelistN(&runtime->actionmaps); + + runtime->actactionmap = runtime->selactionmap = -1; +} + +ListBase *WM_xr_actionmaps_get(wmXrRuntimeData *runtime) +{ + return &runtime->actionmaps; +} + +short WM_xr_actionmap_active_index_get(const wmXrRuntimeData *runtime) +{ + return runtime->actactionmap; +} + +void WM_xr_actionmap_active_index_set(wmXrRuntimeData *runtime, short idx) +{ + BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps)); + runtime->actactionmap = idx; +} + +short WM_xr_actionmap_selected_index_get(const wmXrRuntimeData *runtime) +{ + return runtime->selactionmap; +} + +void WM_xr_actionmap_selected_index_set(wmXrRuntimeData *runtime, short idx) +{ + BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps)); + runtime->selactionmap = idx; +} + +/** \} */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index b6aff1f71f9..4b1605d0f68 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -70,6 +70,10 @@ typedef struct wmXrRuntimeData { /** Although this struct is internal, RNA gets a handle to this for state information queries. */ wmXrSessionState session_state; wmXrSessionExitFn exit_fn; + + ListBase actionmaps; /* XrActionMap */ + short actactionmap; + short selactionmap; } wmXrRuntimeData; typedef struct wmXrViewportPair { @@ -99,6 +103,22 @@ typedef struct wmXrDrawData { float eye_position_ofs[3]; /* Local/view space. */ } wmXrDrawData; +typedef struct wmXrController { + struct wmXrController *next, *prev; + /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256). + This subaction path will later be combined with a component path, and that combined path should + also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path = + /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value). + */ + char subaction_path[64]; + /* Pose (in world space) that represents the user's hand when holding the controller.*/ + GHOST_XrPose grip_pose; + float grip_mat[4][4]; + /* Pose (in world space) that represents the controller's aiming source. */ + GHOST_XrPose aim_pose; + float aim_mat[4][4]; +} wmXrController; + typedef struct wmXrAction { char *name; eXrActionType type; @@ -119,32 +139,37 @@ typedef struct wmXrAction { /** Operator to be called on XR events. */ struct wmOperatorType *ot; IDProperty *op_properties; + + /** Haptics. */ + char *haptic_name; + int64_t haptic_duration; + float haptic_frequency; + float haptic_amplitude; + + /** Flags. */ eXrOpFlag op_flag; + eXrActionFlag action_flag; + eXrHapticFlag haptic_flag; } wmXrAction; -typedef struct wmXrController { - struct wmXrController *next, *prev; - /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256). - This subaction path will later be combined with a component path, and that combined path should - also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path = - /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value). - */ - char subaction_path[64]; - /* Pose (in world space) that represents the user's hand when holding the controller.*/ - GHOST_XrPose grip_pose; - float grip_mat[4][4]; - /* Pose (in world space) that represents the controller's aiming source. */ - GHOST_XrPose aim_pose; - float aim_mat[4][4]; -} wmXrController; +typedef struct wmXrHapticAction { + struct wmXrHapticAction *next, *prev; + wmXrAction *action; + const char **subaction_path; + int64_t time_start; +} wmXrHapticAction; typedef struct wmXrActionSet { char *name; + /** XR pose actions that determine the controller grip/aim transforms. */ wmXrAction *controller_grip_action; wmXrAction *controller_aim_action; - /** The currently active modal action (if any). */ - wmXrAction *active_modal_action; + + /** Currently active modal actions. */ + ListBase active_modal_actions; + /** Currently active haptic actions. */ + ListBase active_haptic_actions; } wmXrActionSet; wmXrRuntimeData *wm_xr_runtime_data_create(void); -- cgit v1.2.3 From cc4e674e41f9ddca9d3b6aee8c21ddcb3fdfea26 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 3 Aug 2021 13:39:30 -0300 Subject: DRW: New Select Debug Engine This is a simple engine used only to debug the texture of select ids. It is only used when the `WITH_DRAW_DEBUG` option is enabled and the debug value is 31. Reviewed By: fclem Differential Revision: https://developer.blender.org/D5490 --- CMakeLists.txt | 4 + source/blender/blenkernel/BKE_global.h | 1 + source/blender/draw/CMakeLists.txt | 7 ++ .../draw/engines/select/select_debug_engine.c | 135 +++++++++++++++++++++ source/blender/draw/engines/select/select_engine.h | 6 + source/blender/draw/intern/draw_manager.c | 9 ++ 6 files changed, 162 insertions(+) create mode 100644 source/blender/draw/engines/select/select_debug_engine.c diff --git a/CMakeLists.txt b/CMakeLists.txt index b7dfb56ff02..3baebba4678 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -428,6 +428,10 @@ mark_as_advanced(WITH_CYCLES_NETWORK) option(WITH_CUDA_DYNLOAD "Dynamically load CUDA libraries at runtime" ON) mark_as_advanced(WITH_CUDA_DYNLOAD) +# Draw Manager +option(WITH_DRAW_DEBUG "Add extra debug capabilities to Draw Manager" OFF) +mark_as_advanced(WITH_DRAW_DEBUG) + # LLVM option(WITH_LLVM "Use LLVM" OFF) if(APPLE) diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 69c950a86dc..31928b5e80a 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -70,6 +70,7 @@ typedef struct Global { * * -16384 and below: Reserved for python (add-ons) usage. * * -1: Disable faster motion paths computation (since 08/2018). * * 1 - 30: EEVEE debug/stats values (01/2018). + * * 31: Enable the Select Debug Engine. Only available with #WITH_DRAW_DEBUG (08/2021). * * 101: Enable UI debug drawing of fullscreen area's corner widget (10/2014). * * 666: Use quicker batch delete for outliners' delete hierarchy (01/2019). * * 777: Enable UI node panel's sockets polling (11/2011). diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 930d82fa225..257eb80ae0b 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -483,6 +483,13 @@ data_to_c_simple(engines/image/shaders/engine_image_vert.glsl SRC) list(APPEND INC ) +if(WITH_DRAW_DEBUG) + list(APPEND SRC + engines/select/select_debug_engine.c + ) + add_definitions(-DWITH_DRAW_DEBUG) +endif() + if(WITH_MOD_FLUID) list(APPEND INC ../../../intern/mantaflow/extern diff --git a/source/blender/draw/engines/select/select_debug_engine.c b/source/blender/draw/engines/select/select_debug_engine.c new file mode 100644 index 00000000000..ded96be23f3 --- /dev/null +++ b/source/blender/draw/engines/select/select_debug_engine.c @@ -0,0 +1,135 @@ +/* + * 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. + * + * Copyright 2019, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + * + * Engine for debuging the selection map drawing. + */ + +#include "DNA_ID.h" +#include "DNA_vec_types.h" + +#include "DRW_engine.h" +#include "DRW_select_buffer.h" + +#include "draw_cache.h" +#include "draw_manager.h" + +#include "select_engine.h" + +#define SELECT_DEBUG_ENGINE "SELECT_DEBUG_ENGINE" + +/* -------------------------------------------------------------------- */ +/** \name Structs and static variables + * \{ */ + +typedef struct SELECTIDDEBUG_PassList { + struct DRWPass *debug_pass; +} SELECTIDDEBUG_PassList; + +typedef struct SELECTIDDEBUG_Data { + void *engine_type; + DRWViewportEmptyList *fbl; + DRWViewportEmptyList *txl; + SELECTIDDEBUG_PassList *psl; + DRWViewportEmptyList *stl; +} SELECTIDDEBUG_Data; + +static struct { + struct GPUShader *select_debug_sh; +} e_data = {{NULL}}; /* Engine data */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Engine Functions + * \{ */ + +static void select_debug_engine_init(void *vedata) +{ + SELECTIDDEBUG_PassList *psl = ((SELECTIDDEBUG_Data *)vedata)->psl; + + if (!e_data.select_debug_sh) { + e_data.select_debug_sh = DRW_shader_create_fullscreen( + "uniform usampler2D image;" + "in vec4 uvcoordsvar;" + "out vec4 fragColor;" + "void main() {" + " uint px = texture(image, uvcoordsvar.xy).r;" + " fragColor = vec4(1.0, 1.0, 1.0, 0.0);" + " if (px != 0u) {" + " fragColor.a = 1.0;" + " px &= 0x3Fu;" + " fragColor.r = ((px >> 0) & 0x3u) / float(0x3u);" + " fragColor.g = ((px >> 2) & 0x3u) / float(0x3u);" + " fragColor.b = ((px >> 4) & 0x3u) / float(0x3u);" + " }" + "}\n", + NULL); + } + + psl->debug_pass = DRW_pass_create("Debug Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA); + GPUTexture *texture_u32 = DRW_engine_select_texture_get(); + if (texture_u32) { + DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.select_debug_sh, psl->debug_pass); + DRW_shgroup_uniform_texture(shgrp, "image", texture_u32); + DRW_shgroup_call_procedural_triangles(shgrp, NULL, 1); + } +} + +static void select_debug_draw_scene(void *vedata) +{ + SELECTIDDEBUG_PassList *psl = ((SELECTIDDEBUG_Data *)vedata)->psl; + DRW_draw_pass(psl->debug_pass); +} + +static void select_debug_engine_free(void) +{ + DRW_SHADER_FREE_SAFE(e_data.select_debug_sh); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Engine Type + * \{ */ + +static const DrawEngineDataSize select_debug_data_size = DRW_VIEWPORT_DATA_SIZE( + SELECTIDDEBUG_Data); + +DrawEngineType draw_engine_debug_select_type = { + NULL, + NULL, + N_("Select ID Debug"), + &select_debug_data_size, + &select_debug_engine_init, + &select_debug_engine_free, + NULL, + NULL, + NULL, + &select_debug_draw_scene, + NULL, + NULL, + NULL, + NULL, +}; + +/** \} */ + +#undef SELECT_DEBUG_ENGINE diff --git a/source/blender/draw/engines/select/select_engine.h b/source/blender/draw/engines/select/select_engine.h index 2b35cf6bee5..d6192103178 100644 --- a/source/blender/draw/engines/select/select_engine.h +++ b/source/blender/draw/engines/select/select_engine.h @@ -22,9 +22,15 @@ #pragma once +/* select_engine.c */ extern DrawEngineType draw_engine_select_type; extern RenderEngineType DRW_engine_viewport_select_type; +#ifdef WITH_DRAW_DEBUG +/* select_debug_engine.c */ +extern DrawEngineType draw_engine_debug_select_type; +#endif + struct SELECTID_Context *DRW_select_engine_context_get(void); struct GPUFrameBuffer *DRW_engine_select_framebuffer_get(void); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 11e2856147f..027ab8ce32b 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1281,6 +1281,12 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer), use_drw_engine(&draw_engine_gpencil_type); } drw_engines_enable_overlays(); + +#ifdef WITH_DRAW_DEBUG + if (G.debug_value == 31) { + use_drw_engine(&draw_engine_debug_select_type); + } +#endif } static void drw_engines_disable(void) @@ -2940,6 +2946,9 @@ void DRW_engines_register(void) DRW_engine_register(&draw_engine_overlay_type); DRW_engine_register(&draw_engine_select_type); DRW_engine_register(&draw_engine_basic_type); +#ifdef WITH_DRAW_DEBUG + DRW_engine_register(&draw_engine_debug_select_type); +#endif DRW_engine_register(&draw_engine_image_type); DRW_engine_register(DRW_engine_viewport_external_type.draw_engine); -- cgit v1.2.3 From b4f950cbbe5d1306f7bdcff6a288381759e04aeb Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 5 Aug 2021 17:06:21 +0200 Subject: GPencil: New Caps icons These icons are used to define the type of caps. Designed by: Matias Mendiola Reviewed by: Pablo Vazquez --- release/datafiles/blender_icons.svg | 15 +++++++++++++++ .../datafiles/blender_icons16/icon16_gp_caps_flat.dat | Bin 0 -> 1048 bytes .../datafiles/blender_icons16/icon16_gp_caps_round.dat | Bin 0 -> 1048 bytes .../datafiles/blender_icons32/icon32_gp_caps_flat.dat | Bin 0 -> 4120 bytes .../datafiles/blender_icons32/icon32_gp_caps_round.dat | Bin 0 -> 4120 bytes source/blender/editors/datafiles/CMakeLists.txt | 2 ++ source/blender/editors/include/UI_icons.h | 4 ++-- source/blender/makesrna/intern/rna_brush.c | 4 ++-- 8 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 release/datafiles/blender_icons16/icon16_gp_caps_flat.dat create mode 100644 release/datafiles/blender_icons16/icon16_gp_caps_round.dat create mode 100644 release/datafiles/blender_icons32/icon32_gp_caps_flat.dat create mode 100644 release/datafiles/blender_icons32/icon32_gp_caps_round.dat diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index 54357550847..c3461fd1bea 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -17306,6 +17306,21 @@ d="m 418.85321,140.04954 -2.82,-2.82 a 0.62,0.62 0 0 0 -0.4,-0.18 0.6,0.6 0 0 0 -0.6,0.6 0.62,0.62 0 0 0 0.18,0.43 l 1,1 -9.18,9.12 -1,-1 a 0.62,0.62 0 0 0 -0.4,-0.15 0.6,0.6 0 0 0 -0.6,0.6 0.62,0.62 0 0 0 0.18,0.4 l 2.82,2.82 a 0.6,0.6 0 0 0 0.82,-0.82 l -1,-1 9.18,-9.15 1,1 a 0.6,0.6 0 0 0 0.82,-0.85 z" id="path3261" inkscape:connector-curvature="0" /> + + + + Date: Fri, 6 Aug 2021 00:08:56 +0900 Subject: Fix build error when WITH_XR_OPENXR not defined --- source/blender/makesrna/intern/rna_xr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 358db14c298..57133ebe1c1 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -901,9 +901,13 @@ static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float * static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { +# ifdef WITH_XR_OPENXR wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); ListBase *lb = WM_xr_actionmaps_get(xr->runtime); rna_iterator_listbase_begin(iter, lb, NULL); +# else + UNUSED_VARS(iter, ptr); +# endif } static int rna_XrSessionState_active_actionmap_get(PointerRNA *ptr) -- cgit v1.2.3 From 27b9cb7a1e675930d348300fb11446a0c6d35fde Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 5 Aug 2021 17:12:11 +0200 Subject: GPencil: New Merge Layer keymap: Shift+Ctrl+M The keymap is available in: Draw, Edit, Sculpt, Weight Paint and Vertex Paint modes. The keymap is not available in Object mode to avoid any conflict. Reviewed By: pepeland Differential Revision: https://developer.blender.org/D12128 --- .../scripts/presets/keyconfig/keymap_data/blender_default.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index a37577e898b..a67c5244611 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -3329,6 +3329,8 @@ def km_grease_pencil_stroke_edit_mode(params): ("gpencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None), # Move to layer op_menu("GPENCIL_MT_move_to_layer", {"type": 'M', "value": 'PRESS'}), + # Merge Layer + ("gpencil.layer_merge", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), # Transform tools ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), @@ -3427,6 +3429,8 @@ def km_grease_pencil_stroke_paint_mode(params): {"properties": [("unselected", True)]}), # Active layer op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}), + # Merge Layer + ("gpencil.layer_merge", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), # Active material op_menu("GPENCIL_MT_material_active", {"type": 'U', "value": 'PRESS'}), # Keyframe menu @@ -3592,6 +3596,8 @@ def km_grease_pencil_stroke_sculpt_mode(params): ("gpencil.active_frames_delete_all", {"type": 'DEL', "value": 'PRESS', "shift": True}, None), # Active layer op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}), + # Merge Layer + ("gpencil.layer_merge", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), # Keyframe menu op_menu("VIEW3D_MT_gpencil_animation", {"type": 'I', "value": 'PRESS'}), # Context menu @@ -3809,6 +3815,8 @@ def km_grease_pencil_stroke_weight_mode(params): ("gpencil.active_frames_delete_all", {"type": 'DEL', "value": 'PRESS', "shift": True}, None), # Active layer op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}), + # Merge Layer + ("gpencil.layer_merge", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), # Keyframe menu op_menu("VIEW3D_MT_gpencil_animation", {"type": 'I', "value": 'PRESS'}), # Context menu @@ -3873,6 +3881,8 @@ def km_grease_pencil_stroke_vertex_mode(params): ("gpencil.active_frames_delete_all", {"type": 'DEL', "value": 'PRESS', "shift": True}, None), # Active layer op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}), + # Merge Layer + ("gpencil.layer_merge", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), # Keyframe menu op_menu("VIEW3D_MT_gpencil_animation", {"type": 'I', "value": 'PRESS'}), # Vertex Paint context menu -- cgit v1.2.3 From 834523e2391b3c8dab393daa040e135d7b729f86 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 5 Aug 2021 16:48:16 +0200 Subject: Cleanup/Fix RNA array length accessors returning non-zero values in invalid cases. This was apparently done in two places only, with a very cryptic comment (`/* for raw_access, untested */`), and... I cannot see how returning a non-zero length value for an array that does not exist or is not accessible at least, would be anything but an obvious source of issues. Note that both commits adding those lines are from stone ages (2009): rBcbc2c1886dee and rB50e3bb7f5f34. --- source/blender/makesrna/intern/rna_constraint.c | 2 +- source/blender/makesrna/intern/rna_fcurve.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 3064703b02e..a0a0a41b58d 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -715,7 +715,7 @@ static int rna_SplineIKConstraint_joint_bindings_get_length(PointerRNA *ptr, length[0] = ikData->numpoints; } else { - length[0] = 256; /* for raw_access, untested */ + length[0] = 0; } return length[0]; diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 1f187382980..a38bbd3d6d2 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -809,7 +809,7 @@ static int rna_FModifierGenerator_coefficients_get_length(PointerRNA *ptr, length[0] = gen->arraysize; } else { - length[0] = 100; /* for raw_access, untested */ + length[0] = 0; } return length[0]; -- cgit v1.2.3 From 6c326ba0a24f24763b751483a0ee0cc98abdd921 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 5 Aug 2021 17:17:15 +0200 Subject: Fix T83164: Spline IK `joint_bindings` parameter is broken. Code freeing the array would not properly reset its length value to zero. Note that this corrupted data could also be saved in .blend files, so had to bump fileversion and add some doversion code too. Fix T90166: crash when creating a liboverride. --- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/intern/constraint.c | 4 +- source/blender/blenloader/intern/versioning_300.c | 51 +++++++++++++++++------ source/blender/editors/object/object_constraint.c | 7 +--- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 0d5835a5eed..5ef56fab9cb 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 16 +#define BLENDER_FILE_SUBVERSION 17 /* 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/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 022073b0f12..b7e02f06571 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -4616,9 +4616,7 @@ static void splineik_free(bConstraint *con) bSplineIKConstraint *data = con->data; /* binding array */ - if (data->points) { - MEM_freeN(data->points); - } + MEM_SAFE_FREE(data->points); } static void splineik_copy(bConstraint *con, bConstraint *srccon) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 7df4d2d93ec..42af8f4bda2 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -29,6 +29,7 @@ #include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_collection_types.h" +#include "DNA_constraint_types.h" #include "DNA_curve_types.h" #include "DNA_genfile.h" #include "DNA_listBase.h" @@ -383,6 +384,19 @@ static void do_version_bones_bbone_len_scale(ListBase *lb) } } +static void do_version_constraints_spline_ik_joint_bindings(ListBase *lb) +{ + /* Binding array data could be freed without properly resetting its size data. */ + LISTBASE_FOREACH (bConstraint *, con, lb) { + if (con->type == CONSTRAINT_TYPE_SPLINEIK) { + bSplineIKConstraint *data = (bSplineIKConstraint *)con->data; + if (data->points == NULL) { + data->numpoints = 0; + } + } + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -697,18 +711,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) BKE_main_id_repair_duplicate_names_listbase(lb); } - /** - * 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, 17)) { if (!DNA_struct_elem_find( fd->filesdna, "View3DOverlay", "float", "normals_constant_screen_size")) { LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { @@ -722,5 +725,29 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Fix SplineIK constraint's inconsistency between binding points array and its stored size. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + /* NOTE: Objects should never have SplineIK constraint, so no need to apply this fix on + * their constraints. */ + if (ob->pose) { + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + do_version_constraints_spline_ik_joint_bindings(&pchan->constraints); + } + } + } + } + + /** + * 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/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 4970338973d..3d0213f1830 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -432,11 +432,8 @@ static void test_constraint( * free the points array and request a rebind... */ if ((data->points == NULL) || (data->numpoints != data->chainlen + 1)) { - /* free the points array */ - if (data->points) { - MEM_freeN(data->points); - data->points = NULL; - } + MEM_SAFE_FREE(data->points); + data->numpoints = 0; /* clear the bound flag, forcing a rebind next time this is evaluated */ data->flag &= ~CONSTRAINT_SPLINEIK_BOUND; -- cgit v1.2.3 From ca64bd0aacdaa9fcf75d693321d4d73c4a6a991a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 4 Aug 2021 19:43:40 +0200 Subject: Render: move Cycles visibility, holdout and shadow catcher properties to Blender The immediate reason for this is that we want to be able to initialize them to different defaults for light objects, which is hard with Python properties. But in general it is useful to be able to share these with other renderers. As a side effect, Eevee now supports a per-object holdout instead of only per-collection. Differential Revision: https://developer.blender.org/D12133 --- intern/cycles/blender/addon/properties.py | 21 --------- intern/cycles/blender/addon/ui.py | 19 ++++---- intern/cycles/blender/blender_object.cpp | 6 +-- intern/cycles/blender/blender_util.h | 13 +++--- release/scripts/startup/bl_ui/properties_object.py | 4 ++ source/blender/blenkernel/intern/layer.c | 2 +- source/blender/blenkernel/intern/object.c | 6 +++ .../blender/blenloader/intern/versioning_cycles.c | 37 +++++++++++++++ source/blender/makesdna/DNA_object_types.h | 16 +++++-- source/blender/makesrna/intern/rna_object.c | 53 ++++++++++++++++++++++ 10 files changed, 129 insertions(+), 48 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 70efb1054a2..124223635d1 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1164,12 +1164,6 @@ class CyclesVisibilitySettings(bpy.types.PropertyGroup): @classmethod def register(cls): - bpy.types.Object.cycles_visibility = PointerProperty( - name="Cycles Visibility Settings", - description="Cycles visibility settings", - type=cls, - ) - bpy.types.World.cycles_visibility = PointerProperty( name="Cycles Visibility Settings", description="Cycles visibility settings", @@ -1178,7 +1172,6 @@ class CyclesVisibilitySettings(bpy.types.PropertyGroup): @classmethod def unregister(cls): - del bpy.types.Object.cycles_visibility del bpy.types.World.cycles_visibility @@ -1276,20 +1269,6 @@ class CyclesObjectSettings(bpy.types.PropertyGroup): subtype='DISTANCE', ) - is_shadow_catcher: BoolProperty( - name="Shadow Catcher", - description="Only render shadows on this object, for compositing renders into real footage", - default=False, - ) - - is_holdout: BoolProperty( - name="Holdout", - description="Render objects as a holdout or matte, creating a " - "hole in the image with zero alpha, to fill out in " - "compositing with real footage or another render", - default=False, - ) - @classmethod def register(cls): bpy.types.Object.cycles = PointerProperty( diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index e804f697571..47f7b4c6d73 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1270,10 +1270,9 @@ class CYCLES_OBJECT_PT_visibility(CyclesButtonsPanel, Panel): col.prop(ob, "hide_render", text="Renders", invert_checkbox=True, toggle=False) if has_geometry_visibility(ob): - cob = ob.cycles col = layout.column(heading="Mask") - col.prop(cob, "is_shadow_catcher") - col.prop(cob, "is_holdout") + col.prop(ob, "is_shadow_catcher") + col.prop(ob, "is_holdout") class CYCLES_OBJECT_PT_visibility_ray_visibility(CyclesButtonsPanel, Panel): @@ -1293,19 +1292,17 @@ class CYCLES_OBJECT_PT_visibility_ray_visibility(CyclesButtonsPanel, Panel): scene = context.scene ob = context.object - cob = ob.cycles - visibility = ob.cycles_visibility col = layout.column() - col.prop(visibility, "camera") - col.prop(visibility, "diffuse") - col.prop(visibility, "glossy") - col.prop(visibility, "transmission") - col.prop(visibility, "scatter") + col.prop(ob, "visible_camera", text="Camera") + col.prop(ob, "visible_diffuse", text="Diffuse") + col.prop(ob, "visible_glossy", text="Glossy") + col.prop(ob, "visible_transmission", text="Transmission") + col.prop(ob, "visible_volume_scatter", text="Volume Scatter") if ob.type != 'LIGHT': sub = col.column() - sub.prop(visibility, "shadow") + sub.prop(ob, "visible_shadow", text="Shadow") class CYCLES_OBJECT_PT_visibility_culling(CyclesButtonsPanel, Panel): diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 65b5ac2c58f..4711e0cbe76 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -199,8 +199,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, /* Visibility flags for both parent and child. */ PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles"); - bool use_holdout = get_boolean(cobject, "is_holdout") || - b_parent.holdout_get(PointerRNA_NULL, b_view_layer); + bool use_holdout = b_parent.holdout_get(PointerRNA_NULL, b_view_layer); uint visibility = object_ray_visibility(b_ob) & PATH_RAY_ALL_VISIBILITY; if (b_parent.ptr.data != b_ob.ptr.data) { @@ -287,8 +286,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, object->set_visibility(visibility); - bool is_shadow_catcher = get_boolean(cobject, "is_shadow_catcher"); - object->set_is_shadow_catcher(is_shadow_catcher); + object->set_is_shadow_catcher(b_ob.is_shadow_catcher()); float shadow_terminator_shading_offset = get_float(cobject, "shadow_terminator_offset"); object->set_shadow_terminator_shading_offset(shadow_terminator_shading_offset); diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 43dbb4105df..2b2188b023d 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -596,15 +596,14 @@ static inline Mesh::SubdivisionType object_subdivision_type(BL::Object &b_ob, static inline uint object_ray_visibility(BL::Object &b_ob) { - PointerRNA cvisibility = RNA_pointer_get(&b_ob.ptr, "cycles_visibility"); uint flag = 0; - flag |= get_boolean(cvisibility, "camera") ? PATH_RAY_CAMERA : 0; - flag |= get_boolean(cvisibility, "diffuse") ? PATH_RAY_DIFFUSE : 0; - flag |= get_boolean(cvisibility, "glossy") ? PATH_RAY_GLOSSY : 0; - flag |= get_boolean(cvisibility, "transmission") ? PATH_RAY_TRANSMIT : 0; - flag |= get_boolean(cvisibility, "shadow") ? PATH_RAY_SHADOW : 0; - flag |= get_boolean(cvisibility, "scatter") ? PATH_RAY_VOLUME_SCATTER : 0; + flag |= b_ob.visible_camera() ? PATH_RAY_CAMERA : 0; + flag |= b_ob.visible_diffuse() ? PATH_RAY_DIFFUSE : 0; + flag |= b_ob.visible_glossy() ? PATH_RAY_GLOSSY : 0; + flag |= b_ob.visible_transmission() ? PATH_RAY_TRANSMIT : 0; + flag |= b_ob.visible_shadow() ? PATH_RAY_SHADOW : 0; + flag |= b_ob.visible_volume_scatter() ? PATH_RAY_VOLUME_SCATTER : 0; return flag; } diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py index 4ea1ec26738..46a16a70dca 100644 --- a/release/scripts/startup/bl_ui/properties_object.py +++ b/release/scripts/startup/bl_ui/properties_object.py @@ -399,6 +399,10 @@ class OBJECT_PT_visibility(ObjectButtonsPanel, Panel): col = layout.column(heading="Grease Pencil") col.prop(ob, "use_grease_pencil_lights", toggle=False) + layout.separator() + col = layout.column(heading="Mask") + col.prop(ob, "is_holdout") + class OBJECT_PT_custom_props(ObjectButtonsPanel, PropertyPanel, Panel): COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 5940569ba76..e7d83c668c8 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -1020,7 +1020,7 @@ static void layer_collection_objects_sync(ViewLayer *view_layer, } /* Holdout and indirect only */ - if (layer->flag & LAYER_COLLECTION_HOLDOUT) { + if ((layer->flag & LAYER_COLLECTION_HOLDOUT) || (base->object->visibility_flag & OB_HOLDOUT)) { base->flag_from_collection |= BASE_HOLDOUT; } if (layer->flag & LAYER_COLLECTION_INDIRECT_ONLY) { diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 6a309e040c0..f969ca0ff1c 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2081,6 +2081,12 @@ static void object_init(Object *ob, const short ob_type) if (ob->type == OB_GPENCIL) { ob->dtx |= OB_USE_GPENCIL_LIGHTS; } + + if (ob->type == OB_LAMP) { + /* Lights are invisible to camera rays and are assumed to be a + * shadow catcher by default. */ + ob->visibility_flag |= OB_HIDE_CAMERA | OB_SHADOW_CATCHER; + } } void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index 94ee89c5120..90e6b43f02e 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -78,6 +78,12 @@ static IDProperty *cycles_properties_from_ID(ID *id) return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : NULL; } +static IDProperty *cycles_visibility_properties_from_ID(ID *id) +{ + IDProperty *idprop = IDP_GetProperties(id, false); + return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles_visibility", IDP_GROUP) : NULL; +} + static IDProperty *cycles_properties_from_view_layer(ViewLayer *view_layer) { IDProperty *idprop = view_layer->id_properties; @@ -1600,4 +1606,35 @@ void do_versions_after_linking_cycles(Main *bmain) } } } + + /* Move visibility from Cycles to Blender. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 17)) { + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + IDProperty *cvisibility = cycles_visibility_properties_from_ID(&object->id); + int flag = 0; + + if (cvisibility) { + flag |= cycles_property_boolean(cvisibility, "camera", true) ? 0 : OB_HIDE_CAMERA; + flag |= cycles_property_boolean(cvisibility, "diffuse", true) ? 0 : OB_HIDE_DIFFUSE; + flag |= cycles_property_boolean(cvisibility, "glossy", true) ? 0 : OB_HIDE_GLOSSY; + flag |= cycles_property_boolean(cvisibility, "transmission", true) ? 0 : + OB_HIDE_TRANSMISSION; + flag |= cycles_property_boolean(cvisibility, "scatter", true) ? 0 : OB_HIDE_VOLUME_SCATTER; + flag |= cycles_property_boolean(cvisibility, "shadow", true) ? 0 : OB_HIDE_SHADOW; + } + + IDProperty *cobject = cycles_properties_from_ID(&object->id); + if (cobject) { + flag |= cycles_property_boolean(cobject, "is_holdout", false) ? OB_HOLDOUT : 0; + flag |= cycles_property_boolean(cobject, "is_shadow_catcher", false) ? OB_SHADOW_CATCHER : + 0; + } + + if (object->type == OB_LAMP) { + flag |= OB_HIDE_CAMERA | OB_SHADOW_CATCHER; + } + + object->visibility_flag |= flag; + } + } } diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index a37536f7022..e7091c78f71 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -385,14 +385,14 @@ typedef struct Object { short softflag; /** For restricting view, select, render etc. accessible in outliner. */ - char visibility_flag; + short visibility_flag; - /** Flag for pinning. */ - char shapeflag; /** Current shape key for menu or pinned. */ short shapenr; + /** Flag for pinning. */ + char shapeflag; - char _pad3[2]; + char _pad3[1]; /** Object constraints. */ ListBase constraints; @@ -675,6 +675,14 @@ enum { OB_HIDE_VIEWPORT = 1 << 0, OB_HIDE_SELECT = 1 << 1, OB_HIDE_RENDER = 1 << 2, + OB_HIDE_CAMERA = 1 << 3, + OB_HIDE_DIFFUSE = 1 << 4, + OB_HIDE_GLOSSY = 1 << 5, + OB_HIDE_TRANSMISSION = 1 << 6, + OB_HIDE_VOLUME_SCATTER = 1 << 7, + OB_HIDE_SHADOW = 1 << 8, + OB_HOLDOUT = 1 << 9, + OB_SHADOW_CATCHER = 1 << 10 }; /* ob->shapeflag */ diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index f0b83f3eb46..bef430b0314 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -2963,6 +2963,59 @@ static void rna_def_object_visibility(StructRNA *srna) RNA_def_property_ui_text(prop, "Display Instancer", "Make instancer visible in the viewport"); RNA_def_property_update( prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update"); + + /* Ray visibility. */ + prop = RNA_def_property(srna, "visible_camera", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_CAMERA); + RNA_def_property_ui_text(prop, "Camera Visibility", "Object visibility to camera rays"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); + + prop = RNA_def_property(srna, "visible_diffuse", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_DIFFUSE); + RNA_def_property_ui_text(prop, "Diffuse Visibility", "Object visibility to diffuse rays"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); + + prop = RNA_def_property(srna, "visible_glossy", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_GLOSSY); + RNA_def_property_ui_text(prop, "Glossy Visibility", "Object visibility to glossy rays"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); + + prop = RNA_def_property(srna, "visible_transmission", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_TRANSMISSION); + RNA_def_property_ui_text( + prop, "Transmission Visibility", "Object visibility to transmission rays"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); + + prop = RNA_def_property(srna, "visible_volume_scatter", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_VOLUME_SCATTER); + RNA_def_property_ui_text( + prop, "Volume Scatter Visibility", "Object visibility to volume scattering rays"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); + + prop = RNA_def_property(srna, "visible_shadow", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_SHADOW); + RNA_def_property_ui_text(prop, "Shadow Visibility", "Object visibility to shadow rays"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); + + /* Holdout and shadow catcher. */ + prop = RNA_def_property(srna, "is_holdout", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_HOLDOUT); + RNA_def_property_ui_text( + prop, + "Holdout", + "Render objects as a holdout or matte, creating a hole in the image with zero alpha, to " + "fill out in compositing with real footage or another render"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); + + prop = RNA_def_property(srna, "is_shadow_catcher", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_SHADOW_CATCHER); + RNA_def_property_ui_text( + prop, + "Shadow Catcher", + "Only render shadows and reflections on this object, for compositing renders into real " + "footage. Objects with this setting are considered to already exist in the footage, " + "objects without it are synthetic objects being composited into it"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); } static void rna_def_object(BlenderRNA *brna) -- cgit v1.2.3 From 8158211198b7a27812ddf90bf0d1df0cd9e55afb Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 6 Aug 2021 01:32:04 +1000 Subject: Cleanup: quiet array-parameter warning --- source/blender/makesrna/intern/rna_xr.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 57133ebe1c1..9a512e351cb 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -829,7 +829,7 @@ void rna_XrSessionState_haptic_action_stop(bContext *C, static void rna_XrSessionState_controller_grip_location_get(bContext *C, int *index, - float *r_values) + float r_values[3]) { # ifdef WITH_XR_OPENXR const wmWindowManager *wm = CTX_wm_manager(C); @@ -842,7 +842,7 @@ static void rna_XrSessionState_controller_grip_location_get(bContext *C, static void rna_XrSessionState_controller_grip_rotation_get(bContext *C, int index, - float *r_values) + float r_values[4]) { # ifdef WITH_XR_OPENXR const wmWindowManager *wm = CTX_wm_manager(C); @@ -855,7 +855,7 @@ static void rna_XrSessionState_controller_grip_rotation_get(bContext *C, static void rna_XrSessionState_controller_aim_location_get(bContext *C, int *index, - float *r_values) + float r_values[3]) { # ifdef WITH_XR_OPENXR const wmWindowManager *wm = CTX_wm_manager(C); @@ -866,7 +866,9 @@ static void rna_XrSessionState_controller_aim_location_get(bContext *C, # endif } -static void rna_XrSessionState_controller_aim_rotation_get(bContext *C, int index, float *r_values) +static void rna_XrSessionState_controller_aim_rotation_get(bContext *C, + int index, + float r_values[4]) { # ifdef WITH_XR_OPENXR const wmWindowManager *wm = CTX_wm_manager(C); -- cgit v1.2.3 From ff2265f0a9269ad5e95f77ded3c9692d656955c7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 6 Aug 2021 01:36:43 +1000 Subject: Cleanup: comment blocks & spelling --- intern/ghost/intern/GHOST_XrAction.h | 8 ++++---- .../compositor/operations/COM_DilateErodeOperation.cc | 2 +- .../blender/depsgraph/intern/builder/deg_builder_rna.cc | 4 ++-- source/blender/makesdna/DNA_xr_types.h | 16 ++++++++++------ 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/intern/ghost/intern/GHOST_XrAction.h b/intern/ghost/intern/GHOST_XrAction.h index 73a1cd9cd6a..70eaa694ae9 100644 --- a/intern/ghost/intern/GHOST_XrAction.h +++ b/intern/ghost/intern/GHOST_XrAction.h @@ -18,8 +18,8 @@ * \ingroup GHOST */ -/* Note: Requires OpenXR headers to be included before this one for OpenXR types (XrSpace, XrPath, - * etc.). */ +/* NOTE: Requires OpenXR headers to be included before this one for OpenXR types + * (XrSpace, XrPath, etc.). */ #pragma once @@ -78,9 +78,9 @@ class GHOST_XrActionProfile { private: XrPath m_profile = XR_NULL_PATH; - /** Subaction data identified by user (subaction) path. */ + /** Sub-action data identified by user `subaction` path. */ std::map m_subaction_data; - /** Bindings identified by interaction (user (subaction) + component) path. */ + /** Bindings identified by interaction (user `subaction` + component) path. */ std::map m_bindings; }; diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index e9305e0e192..c67a35b686c 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -370,7 +370,7 @@ void *DilateStepOperation::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/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc index bdabd67cc07..40e59875832 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc @@ -400,8 +400,8 @@ RNANodeQueryIDData *RNANodeQuery::ensure_id_data(const ID *id) bool rna_prop_affects_parameters_node(const PointerRNA *ptr, const PropertyRNA *prop) { return prop != nullptr && RNA_property_is_idprop(prop) && - /* ID properties in the geometry nodes modifier don't affect that parameters node. Instead - they affect the modifier and therefore the geometry node directly. */ + /* ID properties in the geometry nodes modifier don't affect that parameters node. + * Instead they affect the modifier and therefore the geometry node directly. */ !RNA_struct_is_a(ptr->type, &RNA_NodesModifier); } diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index 2e348ce6855..a9d427777f7 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -34,7 +34,7 @@ typedef struct XrSessionSettings { char _pad[7]; - char base_pose_type; /* eXRSessionBasePoseType */ + char base_pose_type; /* #eXRSessionBasePoseType */ /** Object to take the location and rotation as base position from. */ Object *base_pose_object; float base_pose_location[3]; @@ -78,22 +78,26 @@ typedef enum eXrOpFlag { } eXrOpFlag; typedef enum eXrActionFlag { - /** Action depends on two subaction paths (i.e. two-handed/bimanual action). */ + /** Action depends on two sub-action paths (i.e. two-handed/bi-manual action). */ XR_ACTION_BIMANUAL = (1 << 0), } eXrActionFlag; typedef enum eXrHapticFlag { /** Whether to apply haptics to corresponding user paths for an action and its haptic action. */ XR_HAPTIC_MATCHUSERPATHS = (1 << 0), - /** Determines how haptics will be applied ("repeat" is mutually exclusive with - "press"/"release"). */ + /** + * Determines how haptics will be applied + * ("repeat" is mutually exclusive with "press"/"release"). + */ XR_HAPTIC_PRESS = (1 << 1), XR_HAPTIC_RELEASE = (1 << 2), XR_HAPTIC_REPEAT = (1 << 3), } eXrHapticFlag; -/** For axis-based inputs (thumbstick/trackpad/etc). Determines the region for action execution - * (mutually exclusive per axis). */ +/** + * For axis-based inputs (thumb-stick/track-pad/etc). + * Determines the region for action execution (mutually exclusive per axis). + */ typedef enum eXrAxisFlag { XR_AXIS0_POS = (1 << 0), XR_AXIS0_NEG = (1 << 1), -- cgit v1.2.3 From 04c24bec07c1671b78d94bfd530230ec533fae8e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 6 Aug 2021 01:42:01 +1000 Subject: Cleanup: replace short with boolean for zero area array Also remove redundant fabsf on the area of a quad/tri & reduce indentation using continue in for loop. --- .../blender/bmesh/operators/bmo_smooth_laplacian.c | 314 +++++++++++---------- .../blender/modifiers/intern/MOD_laplaciansmooth.c | 24 +- 2 files changed, 171 insertions(+), 167 deletions(-) diff --git a/source/blender/bmesh/operators/bmo_smooth_laplacian.c b/source/blender/bmesh/operators/bmo_smooth_laplacian.c index 1d72bb893b2..eb25923d1d1 100644 --- a/source/blender/bmesh/operators/bmo_smooth_laplacian.c +++ b/source/blender/bmesh/operators/bmo_smooth_laplacian.c @@ -44,7 +44,7 @@ struct BLaplacianSystem { int numEdges; /* Number of edges. */ int numFaces; /* Number of faces. */ int numVerts; /* Number of verts. */ - short *zerola; /* Is zero area or length. */ + bool *zerola; /* Is zero area or length. */ /* Pointers to data. */ BMesh *bm; @@ -98,7 +98,7 @@ static void memset_laplacian_system(LaplacianSystem *sys, int val) memset(sys->ring_areas, val, sizeof(float) * sys->numVerts); memset(sys->vlengths, val, sizeof(float) * sys->numVerts); memset(sys->vweights, val, sizeof(float) * sys->numVerts); - memset(sys->zerola, val, sizeof(short) * sys->numVerts); + memset(sys->zerola, val, sizeof(bool) * sys->numVerts); } static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, int a_numVerts) @@ -139,7 +139,7 @@ static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, in return NULL; } - sys->zerola = MEM_callocN(sizeof(short) * sys->numVerts, "ModLaplSmoothZeloa"); + sys->zerola = MEM_callocN(sizeof(bool) * sys->numVerts, "ModLaplSmoothZeloa"); if (!sys->zerola) { delete_laplacian_system(sys); return NULL; @@ -181,104 +181,107 @@ static void init_laplacian_matrix(LaplacianSystem *sys) BMVert *vf[4]; BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) { - if (!BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_boundary(e)) { - v1 = e->v1->co; - v2 = e->v2->co; - idv1 = BM_elem_index_get(e->v1); - idv2 = BM_elem_index_get(e->v2); - - w1 = len_v3v3(v1, v2); - if (w1 > sys->min_area) { - w1 = 1.0f / w1; - sys->eweights[i] = w1; - sys->vlengths[idv1] += w1; - sys->vlengths[idv2] += w1; - } - else { - sys->zerola[idv1] = 1; - sys->zerola[idv2] = 1; - } + if (BM_elem_flag_test(e, BM_ELEM_SELECT) || !BM_edge_is_boundary(e)) { + continue; + } + + v1 = e->v1->co; + v2 = e->v2->co; + idv1 = BM_elem_index_get(e->v1); + idv2 = BM_elem_index_get(e->v2); + + w1 = len_v3v3(v1, v2); + if (w1 > sys->min_area) { + w1 = 1.0f / w1; + sys->eweights[i] = w1; + sys->vlengths[idv1] += w1; + sys->vlengths[idv2] += w1; + } + else { + sys->zerola[idv1] = true; + sys->zerola[idv2] = true; } } BM_ITER_MESH_INDEX (f, &fiter, sys->bm, BM_FACES_OF_MESH, i) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - - BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, j) { - vf[j] = vn; - } - has_4_vert = (j == 4) ? 1 : 0; - idv1 = BM_elem_index_get(vf[0]); - idv2 = BM_elem_index_get(vf[1]); - idv3 = BM_elem_index_get(vf[2]); - idv4 = has_4_vert ? BM_elem_index_get(vf[3]) : 0; + if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { + continue; + } - v1 = vf[0]->co; - v2 = vf[1]->co; - v3 = vf[2]->co; - v4 = has_4_vert ? vf[3]->co : NULL; + BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, j) { + vf[j] = vn; + } + has_4_vert = (j == 4) ? 1 : 0; + idv1 = BM_elem_index_get(vf[0]); + idv2 = BM_elem_index_get(vf[1]); + idv3 = BM_elem_index_get(vf[2]); + idv4 = has_4_vert ? BM_elem_index_get(vf[3]) : 0; + + v1 = vf[0]->co; + v2 = vf[1]->co; + v3 = vf[2]->co; + v4 = has_4_vert ? vf[3]->co : NULL; + + if (has_4_vert) { + areaf = area_quad_v3(v1, v2, v3, v4); + } + else { + areaf = area_tri_v3(v1, v2, v3); + } + if (areaf < sys->min_area) { + sys->zerola[idv1] = true; + sys->zerola[idv2] = true; + sys->zerola[idv3] = true; if (has_4_vert) { - areaf = area_quad_v3(v1, v2, v3, v4); - } - else { - areaf = area_tri_v3(v1, v2, v3); - } - - if (fabsf(areaf) < sys->min_area) { - sys->zerola[idv1] = 1; - sys->zerola[idv2] = 1; - sys->zerola[idv3] = 1; - if (has_4_vert) { - sys->zerola[idv4] = 1; - } + sys->zerola[idv4] = true; } + } - sys->ring_areas[idv1] += areaf; - sys->ring_areas[idv2] += areaf; - sys->ring_areas[idv3] += areaf; - if (has_4_vert) { - sys->ring_areas[idv4] += areaf; - } + sys->ring_areas[idv1] += areaf; + sys->ring_areas[idv2] += areaf; + sys->ring_areas[idv3] += areaf; + if (has_4_vert) { + sys->ring_areas[idv4] += areaf; + } - if (has_4_vert) { + if (has_4_vert) { - idv[0] = idv1; - idv[1] = idv2; - idv[2] = idv3; - idv[3] = idv4; + idv[0] = idv1; + idv[1] = idv2; + idv[2] = idv3; + idv[3] = idv4; - for (j = 0; j < 4; j++) { - idv1 = idv[j]; - idv2 = idv[(j + 1) % 4]; - idv3 = idv[(j + 2) % 4]; - idv4 = idv[(j + 3) % 4]; + for (j = 0; j < 4; j++) { + idv1 = idv[j]; + idv2 = idv[(j + 1) % 4]; + idv3 = idv[(j + 2) % 4]; + idv4 = idv[(j + 3) % 4]; - v1 = vf[j]->co; - v2 = vf[(j + 1) % 4]->co; - v3 = vf[(j + 2) % 4]->co; - v4 = vf[(j + 3) % 4]->co; + v1 = vf[j]->co; + v2 = vf[(j + 1) % 4]->co; + v3 = vf[(j + 2) % 4]->co; + v4 = vf[(j + 3) % 4]->co; - w2 = cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2); - w3 = cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3); - w4 = cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(v3, v4, v1); + w2 = cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2); + w3 = cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3); + w4 = cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(v3, v4, v1); - sys->vweights[idv1] += (w2 + w3 + w4) / 4.0f; - } + sys->vweights[idv1] += (w2 + w3 + w4) / 4.0f; } - else { - w1 = cotangent_tri_weight_v3(v1, v2, v3); - w2 = cotangent_tri_weight_v3(v2, v3, v1); - w3 = cotangent_tri_weight_v3(v3, v1, v2); + } + else { + w1 = cotangent_tri_weight_v3(v1, v2, v3); + w2 = cotangent_tri_weight_v3(v2, v3, v1); + w3 = cotangent_tri_weight_v3(v3, v1, v2); - sys->fweights[i][0] += w1; - sys->fweights[i][1] += w2; - sys->fweights[i][2] += w3; + sys->fweights[i][0] += w1; + sys->fweights[i][1] += w2; + sys->fweights[i][2] += w3; - sys->vweights[idv1] += w2 + w3; - sys->vweights[idv2] += w1 + w3; - sys->vweights[idv3] += w1 + w2; - } + sys->vweights[idv1] += w2 + w3; + sys->vweights[idv2] += w1 + w3; + sys->vweights[idv3] += w1 + w2; } } } @@ -300,82 +303,83 @@ static void fill_laplacian_matrix(LaplacianSystem *sys) BMVert *vf[4]; BM_ITER_MESH_INDEX (f, &fiter, sys->bm, BM_FACES_OF_MESH, i) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, j) { - vf[j] = vn; - } - has_4_vert = (j == 4) ? 1 : 0; - if (has_4_vert) { - idv[0] = BM_elem_index_get(vf[0]); - idv[1] = BM_elem_index_get(vf[1]); - idv[2] = BM_elem_index_get(vf[2]); - idv[3] = BM_elem_index_get(vf[3]); - for (j = 0; j < 4; j++) { - idv1 = idv[j]; - idv2 = idv[(j + 1) % 4]; - idv3 = idv[(j + 2) % 4]; - idv4 = idv[(j + 3) % 4]; - - v1 = vf[j]->co; - v2 = vf[(j + 1) % 4]->co; - v3 = vf[(j + 2) % 4]->co; - v4 = vf[(j + 3) % 4]->co; - - w2 = cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2); - w3 = cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3); - w4 = cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(v3, v4, v1); - - w2 = w2 / 4.0f; - w3 = w3 / 4.0f; - w4 = w4 / 4.0f; - - if (!vert_is_boundary(vf[j]) && sys->zerola[idv1] == 0) { - EIG_linear_solver_matrix_add(sys->context, idv1, idv2, w2 * sys->vweights[idv1]); - EIG_linear_solver_matrix_add(sys->context, idv1, idv3, w3 * sys->vweights[idv1]); - EIG_linear_solver_matrix_add(sys->context, idv1, idv4, w4 * sys->vweights[idv1]); - } - } - } - else { - idv1 = BM_elem_index_get(vf[0]); - idv2 = BM_elem_index_get(vf[1]); - idv3 = BM_elem_index_get(vf[2]); - /* Is ring if number of faces == number of edges around vertice. */ - if (!vert_is_boundary(vf[0]) && sys->zerola[idv1] == 0) { - EIG_linear_solver_matrix_add( - sys->context, idv1, idv2, sys->fweights[i][2] * sys->vweights[idv1]); - EIG_linear_solver_matrix_add( - sys->context, idv1, idv3, sys->fweights[i][1] * sys->vweights[idv1]); - } - if (!vert_is_boundary(vf[1]) && sys->zerola[idv2] == 0) { - EIG_linear_solver_matrix_add( - sys->context, idv2, idv1, sys->fweights[i][2] * sys->vweights[idv2]); - EIG_linear_solver_matrix_add( - sys->context, idv2, idv3, sys->fweights[i][0] * sys->vweights[idv2]); - } - if (!vert_is_boundary(vf[2]) && sys->zerola[idv3] == 0) { - EIG_linear_solver_matrix_add( - sys->context, idv3, idv1, sys->fweights[i][1] * sys->vweights[idv3]); - EIG_linear_solver_matrix_add( - sys->context, idv3, idv2, sys->fweights[i][0] * sys->vweights[idv3]); + if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { + continue; + } + + BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, j) { + vf[j] = vn; + } + has_4_vert = (j == 4) ? 1 : 0; + if (has_4_vert) { + idv[0] = BM_elem_index_get(vf[0]); + idv[1] = BM_elem_index_get(vf[1]); + idv[2] = BM_elem_index_get(vf[2]); + idv[3] = BM_elem_index_get(vf[3]); + for (j = 0; j < 4; j++) { + idv1 = idv[j]; + idv2 = idv[(j + 1) % 4]; + idv3 = idv[(j + 2) % 4]; + idv4 = idv[(j + 3) % 4]; + + v1 = vf[j]->co; + v2 = vf[(j + 1) % 4]->co; + v3 = vf[(j + 2) % 4]->co; + v4 = vf[(j + 3) % 4]->co; + + w2 = cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2); + w3 = cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3); + w4 = cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(v3, v4, v1); + + w2 = w2 / 4.0f; + w3 = w3 / 4.0f; + w4 = w4 / 4.0f; + + if (!vert_is_boundary(vf[j]) && sys->zerola[idv1] == false) { + EIG_linear_solver_matrix_add(sys->context, idv1, idv2, w2 * sys->vweights[idv1]); + EIG_linear_solver_matrix_add(sys->context, idv1, idv3, w3 * sys->vweights[idv1]); + EIG_linear_solver_matrix_add(sys->context, idv1, idv4, w4 * sys->vweights[idv1]); } } } - } - BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) { - if (!BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_boundary(e)) { - v1 = e->v1->co; - v2 = e->v2->co; - idv1 = BM_elem_index_get(e->v1); - idv2 = BM_elem_index_get(e->v2); - if (sys->zerola[idv1] == 0 && sys->zerola[idv2] == 0) { + else { + idv1 = BM_elem_index_get(vf[0]); + idv2 = BM_elem_index_get(vf[1]); + idv3 = BM_elem_index_get(vf[2]); + /* Is ring if number of faces == number of edges around vertice. */ + if (!vert_is_boundary(vf[0]) && sys->zerola[idv1] == false) { + EIG_linear_solver_matrix_add( + sys->context, idv1, idv2, sys->fweights[i][2] * sys->vweights[idv1]); + EIG_linear_solver_matrix_add( + sys->context, idv1, idv3, sys->fweights[i][1] * sys->vweights[idv1]); + } + if (!vert_is_boundary(vf[1]) && sys->zerola[idv2] == false) { + EIG_linear_solver_matrix_add( + sys->context, idv2, idv1, sys->fweights[i][2] * sys->vweights[idv2]); EIG_linear_solver_matrix_add( - sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]); + sys->context, idv2, idv3, sys->fweights[i][0] * sys->vweights[idv2]); + } + if (!vert_is_boundary(vf[2]) && sys->zerola[idv3] == false) { + EIG_linear_solver_matrix_add( + sys->context, idv3, idv1, sys->fweights[i][1] * sys->vweights[idv3]); EIG_linear_solver_matrix_add( - sys->context, idv2, idv1, sys->eweights[i] * sys->vlengths[idv2]); + sys->context, idv3, idv2, sys->fweights[i][0] * sys->vweights[idv3]); } } } + BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT) || !BM_edge_is_boundary(e)) { + continue; + } + idv1 = BM_elem_index_get(e->v1); + idv2 = BM_elem_index_get(e->v2); + if (sys->zerola[idv1] == false && sys->zerola[idv2] == false) { + EIG_linear_solver_matrix_add( + sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]); + EIG_linear_solver_matrix_add( + sys->context, idv2, idv1, sys->eweights[i] * sys->vlengths[idv2]); + } + } } static bool vert_is_boundary(BMVert *v) @@ -448,8 +452,8 @@ static void validate_solution( lene = len_v3v3(ve1, ve2); if (lene > leni * SMOOTH_LAPLACIAN_MAX_EDGE_PERCENTAGE || lene < leni * SMOOTH_LAPLACIAN_MIN_EDGE_PERCENTAGE) { - sys->zerola[idv1] = 1; - sys->zerola[idv2] = 1; + sys->zerola[idv1] = true; + sys->zerola[idv2] = true; } } @@ -458,7 +462,7 @@ static void validate_solution( } BMO_ITER (v, &siter, sys->op->slots_in, "verts", BM_VERT) { m_vertex_id = BM_elem_index_get(v); - if (sys->zerola[m_vertex_id] == 0) { + if (sys->zerola[m_vertex_id] == false) { if (usex) { v->co[0] = EIG_linear_solver_variable_get(sys->context, 0, m_vertex_id); } @@ -528,7 +532,7 @@ void bmo_smooth_laplacian_vert_exec(BMesh *bm, BMOperator *op) EIG_linear_solver_right_hand_side_add(sys->context, 1, m_vertex_id, v->co[1]); EIG_linear_solver_right_hand_side_add(sys->context, 2, m_vertex_id, v->co[2]); i = m_vertex_id; - if ((sys->zerola[i] == 0) && + if ((sys->zerola[i] == false) && /* Non zero check is to account for vertices that aren't connected to a selected face. * Without this wire edges become `nan`, see T89214. */ (sys->ring_areas[i] != 0.0f)) { diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c index 63495c4104e..a36a8c386b4 100644 --- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -66,7 +66,7 @@ struct BLaplacianSystem { int numVerts; /* Number of verts. */ short *numNeFa; /* Number of neighbors faces around vertice. */ short *numNeEd; /* Number of neighbors Edges around vertice. */ - short *zerola; /* Is zero area or length. */ + bool *zerola; /* Is zero area or length. */ /* Pointers to data. */ float (*vertexCos)[3]; @@ -130,7 +130,7 @@ static void memset_laplacian_system(LaplacianSystem *sys, int val) memset(sys->ring_areas, val, sizeof(float) * sys->numVerts); memset(sys->vlengths, val, sizeof(float) * sys->numVerts); memset(sys->vweights, val, sizeof(float) * sys->numVerts); - memset(sys->zerola, val, sizeof(short) * sys->numVerts); + memset(sys->zerola, val, sizeof(bool) * sys->numVerts); } static LaplacianSystem *init_laplacian_system(int a_numEdges, @@ -152,7 +152,7 @@ static LaplacianSystem *init_laplacian_system(int a_numEdges, sys->ring_areas = MEM_calloc_arrayN(sys->numVerts, sizeof(float), __func__); sys->vlengths = MEM_calloc_arrayN(sys->numVerts, sizeof(float), __func__); sys->vweights = MEM_calloc_arrayN(sys->numVerts, sizeof(float), __func__); - sys->zerola = MEM_calloc_arrayN(sys->numVerts, sizeof(short), __func__); + sys->zerola = MEM_calloc_arrayN(sys->numVerts, sizeof(bool), __func__); return sys; } @@ -225,8 +225,8 @@ static void init_laplacian_matrix(LaplacianSystem *sys) sys->numNeEd[idv2] = sys->numNeEd[idv2] + 1; w1 = len_v3v3(v1, v2); if (w1 < sys->min_area) { - sys->zerola[idv1] = 1; - sys->zerola[idv2] = 1; + sys->zerola[idv1] = true; + sys->zerola[idv2] = true; } else { w1 = 1.0f / w1; @@ -253,7 +253,7 @@ static void init_laplacian_matrix(LaplacianSystem *sys) areaf = area_tri_v3(v_prev, v_curr, v_next); if (areaf < sys->min_area) { - sys->zerola[l_curr->v] = 1; + sys->zerola[l_curr->v] = true; } sys->ring_areas[l_prev->v] += areaf; @@ -300,7 +300,7 @@ static void fill_laplacian_matrix(LaplacianSystem *sys) const uint l_curr_index = l_curr - sys->mloop; /* Is ring if number of faces == number of edges around vertice. */ - if (sys->numNeEd[l_curr->v] == sys->numNeFa[l_curr->v] && sys->zerola[l_curr->v] == 0) { + if (sys->numNeEd[l_curr->v] == sys->numNeFa[l_curr->v] && sys->zerola[l_curr->v] == false) { EIG_linear_solver_matrix_add(sys->context, l_curr->v, l_next->v, @@ -310,7 +310,7 @@ static void fill_laplacian_matrix(LaplacianSystem *sys) l_prev->v, sys->fweights[l_curr_index][1] * sys->vweights[l_curr->v]); } - if (sys->numNeEd[l_next->v] == sys->numNeFa[l_next->v] && sys->zerola[l_next->v] == 0) { + if (sys->numNeEd[l_next->v] == sys->numNeFa[l_next->v] && sys->zerola[l_next->v] == false) { EIG_linear_solver_matrix_add(sys->context, l_next->v, l_curr->v, @@ -320,7 +320,7 @@ static void fill_laplacian_matrix(LaplacianSystem *sys) l_prev->v, sys->fweights[l_curr_index][0] * sys->vweights[l_next->v]); } - if (sys->numNeEd[l_prev->v] == sys->numNeFa[l_prev->v] && sys->zerola[l_prev->v] == 0) { + if (sys->numNeEd[l_prev->v] == sys->numNeFa[l_prev->v] && sys->zerola[l_prev->v] == false) { EIG_linear_solver_matrix_add(sys->context, l_prev->v, l_curr->v, @@ -338,7 +338,7 @@ static void fill_laplacian_matrix(LaplacianSystem *sys) idv2 = sys->medges[i].v2; /* Is boundary */ if (sys->numNeEd[idv1] != sys->numNeFa[idv1] && sys->numNeEd[idv2] != sys->numNeFa[idv2] && - sys->zerola[idv1] == 0 && sys->zerola[idv2] == 0) { + sys->zerola[idv1] == false && sys->zerola[idv2] == false) { EIG_linear_solver_matrix_add( sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]); EIG_linear_solver_matrix_add( @@ -358,7 +358,7 @@ static void validate_solution(LaplacianSystem *sys, short flag, float lambda, fl sys->vert_centroid, sys->vertexCos, sys->mpoly, sys->numPolys, sys->mloop); } for (i = 0; i < sys->numVerts; i++) { - if (sys->zerola[i] == 0) { + if (sys->zerola[i] == false) { lam = sys->numNeEd[i] == sys->numNeFa[i] ? (lambda >= 0.0f ? 1.0f : -1.0f) : (lambda_border >= 0.0f ? 1.0f : -1.0f); if (flag & MOD_LAPLACIANSMOOTH_X) { @@ -442,7 +442,7 @@ static void laplaciansmoothModifier_do( wpaint = 1.0f; } - if (sys->zerola[i] == 0) { + if (sys->zerola[i] == false) { if (smd->flag & MOD_LAPLACIANSMOOTH_NORMALIZED) { w = sys->vweights[i]; sys->vweights[i] = (w == 0.0f) ? 0.0f : -fabsf(smd->lambda) * wpaint / w; -- cgit v1.2.3 From c15635bd8d5483a56107b5c31d8dc0b6a691a767 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 6 Aug 2021 01:42:02 +1000 Subject: BMesh: support laplacian smooth for n-gons Follow the same logic already used by the modifier. --- .../blender/bmesh/operators/bmo_smooth_laplacian.c | 258 ++++++++------------- source/blender/editors/mesh/editmesh_tools.c | 23 -- 2 files changed, 100 insertions(+), 181 deletions(-) diff --git a/source/blender/bmesh/operators/bmo_smooth_laplacian.c b/source/blender/bmesh/operators/bmo_smooth_laplacian.c index eb25923d1d1..94856701e72 100644 --- a/source/blender/bmesh/operators/bmo_smooth_laplacian.c +++ b/source/blender/bmesh/operators/bmo_smooth_laplacian.c @@ -37,12 +37,12 @@ struct BLaplacianSystem { float *eweights; /* Length weights per Edge. */ - float (*fweights)[3]; /* Cotangent weights per face. */ + float (*fweights)[3]; /* Cotangent weights per loop. */ float *ring_areas; /* Total area per ring. */ float *vlengths; /* Total sum of lengths(edges) per vertex. */ float *vweights; /* Total sum of weights per vertex. */ int numEdges; /* Number of edges. */ - int numFaces; /* Number of faces. */ + int numLoops; /* Number of loops. */ int numVerts; /* Number of verts. */ bool *zerola; /* Is zero area or length. */ @@ -57,7 +57,7 @@ struct BLaplacianSystem { typedef struct BLaplacianSystem LaplacianSystem; static bool vert_is_boundary(BMVert *v); -static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, int a_numVerts); +static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numLoops, int a_numVerts); static void init_laplacian_matrix(LaplacianSystem *sys); static void delete_laplacian_system(LaplacianSystem *sys); static void delete_void_pointer(void *data); @@ -94,19 +94,19 @@ static void delete_laplacian_system(LaplacianSystem *sys) static void memset_laplacian_system(LaplacianSystem *sys, int val) { memset(sys->eweights, val, sizeof(float) * sys->numEdges); - memset(sys->fweights, val, sizeof(float) * sys->numFaces * 3); + memset(sys->fweights, val, sizeof(float[3]) * sys->numLoops); memset(sys->ring_areas, val, sizeof(float) * sys->numVerts); memset(sys->vlengths, val, sizeof(float) * sys->numVerts); memset(sys->vweights, val, sizeof(float) * sys->numVerts); memset(sys->zerola, val, sizeof(bool) * sys->numVerts); } -static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, int a_numVerts) +static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numLoops, int a_numVerts) { LaplacianSystem *sys; sys = MEM_callocN(sizeof(LaplacianSystem), "ModLaplSmoothSystem"); sys->numEdges = a_numEdges; - sys->numFaces = a_numFaces; + sys->numLoops = a_numLoops; sys->numVerts = a_numVerts; sys->eweights = MEM_callocN(sizeof(float) * sys->numEdges, "ModLaplSmoothEWeight"); @@ -115,7 +115,7 @@ static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, in return NULL; } - sys->fweights = MEM_callocN(sizeof(float[3]) * sys->numFaces, "ModLaplSmoothFWeight"); + sys->fweights = MEM_callocN(sizeof(float[3]) * sys->numLoops, "ModLaplSmoothFWeight"); if (!sys->fweights) { delete_laplacian_system(sys); return NULL; @@ -166,31 +166,23 @@ static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, in static void init_laplacian_matrix(LaplacianSystem *sys) { - float areaf; - float *v1, *v2, *v3, *v4; - float w1, w2, w3, w4; - int i, j; - bool has_4_vert; - uint idv1, idv2, idv3, idv4, idv[4]; BMEdge *e; BMFace *f; BMIter eiter; BMIter fiter; - BMIter vi; - BMVert *vn; - BMVert *vf[4]; + uint i; BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) { if (BM_elem_flag_test(e, BM_ELEM_SELECT) || !BM_edge_is_boundary(e)) { continue; } - v1 = e->v1->co; - v2 = e->v2->co; - idv1 = BM_elem_index_get(e->v1); - idv2 = BM_elem_index_get(e->v2); + const float *v1 = e->v1->co; + const float *v2 = e->v2->co; + const int idv1 = BM_elem_index_get(e->v1); + const int idv2 = BM_elem_index_get(e->v2); - w1 = len_v3v3(v1, v2); + float w1 = len_v3v3(v1, v2); if (w1 > sys->min_area) { w1 = 1.0f / w1; sys->eweights[i] = w1; @@ -203,176 +195,126 @@ static void init_laplacian_matrix(LaplacianSystem *sys) } } - BM_ITER_MESH_INDEX (f, &fiter, sys->bm, BM_FACES_OF_MESH, i) { + uint l_curr_index = 0; + + BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) { if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { + l_curr_index += f->len; continue; } - BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, j) { - vf[j] = vn; - } - has_4_vert = (j == 4) ? 1 : 0; - idv1 = BM_elem_index_get(vf[0]); - idv2 = BM_elem_index_get(vf[1]); - idv3 = BM_elem_index_get(vf[2]); - idv4 = has_4_vert ? BM_elem_index_get(vf[3]) : 0; - - v1 = vf[0]->co; - v2 = vf[1]->co; - v3 = vf[2]->co; - v4 = has_4_vert ? vf[3]->co : NULL; - - if (has_4_vert) { - areaf = area_quad_v3(v1, v2, v3, v4); - } - else { - areaf = area_tri_v3(v1, v2, v3); - } + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter; - if (areaf < sys->min_area) { - sys->zerola[idv1] = true; - sys->zerola[idv2] = true; - sys->zerola[idv3] = true; - if (has_4_vert) { - sys->zerola[idv4] = true; - } - } + l_iter = l_first; + do { + const int vi_prev = BM_elem_index_get(l_iter->prev->v); + const int vi_curr = BM_elem_index_get(l_iter->v); + const int vi_next = BM_elem_index_get(l_iter->next->v); - sys->ring_areas[idv1] += areaf; - sys->ring_areas[idv2] += areaf; - sys->ring_areas[idv3] += areaf; - if (has_4_vert) { - sys->ring_areas[idv4] += areaf; - } - - if (has_4_vert) { - - idv[0] = idv1; - idv[1] = idv2; - idv[2] = idv3; - idv[3] = idv4; + const float *co_prev = l_iter->prev->v->co; + const float *co_curr = l_iter->v->co; + const float *co_next = l_iter->next->v->co; - for (j = 0; j < 4; j++) { - idv1 = idv[j]; - idv2 = idv[(j + 1) % 4]; - idv3 = idv[(j + 2) % 4]; - idv4 = idv[(j + 3) % 4]; + const float areaf = area_tri_v3(co_prev, co_curr, co_next); - v1 = vf[j]->co; - v2 = vf[(j + 1) % 4]->co; - v3 = vf[(j + 2) % 4]->co; - v4 = vf[(j + 3) % 4]->co; + if (areaf < sys->min_area) { + sys->zerola[vi_curr] = true; + } - w2 = cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2); - w3 = cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3); - w4 = cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(v3, v4, v1); + sys->ring_areas[vi_prev] += areaf; + sys->ring_areas[vi_curr] += areaf; + sys->ring_areas[vi_next] += areaf; - sys->vweights[idv1] += (w2 + w3 + w4) / 4.0f; - } - } - else { - w1 = cotangent_tri_weight_v3(v1, v2, v3); - w2 = cotangent_tri_weight_v3(v2, v3, v1); - w3 = cotangent_tri_weight_v3(v3, v1, v2); + const float w1 = cotangent_tri_weight_v3(co_curr, co_next, co_prev) / 2.0f; + const float w2 = cotangent_tri_weight_v3(co_next, co_prev, co_curr) / 2.0f; + const float w3 = cotangent_tri_weight_v3(co_prev, co_curr, co_next) / 2.0f; - sys->fweights[i][0] += w1; - sys->fweights[i][1] += w2; - sys->fweights[i][2] += w3; + sys->fweights[l_curr_index][0] += w1; + sys->fweights[l_curr_index][1] += w2; + sys->fweights[l_curr_index][2] += w3; - sys->vweights[idv1] += w2 + w3; - sys->vweights[idv2] += w1 + w3; - sys->vweights[idv3] += w1 + w2; - } + sys->vweights[vi_prev] += w1 + w2; + sys->vweights[vi_curr] += w2 + w3; + sys->vweights[vi_next] += w1 + w3; + } while (((void)(l_curr_index += 1), (l_iter = l_iter->next) != l_first)); } } static void fill_laplacian_matrix(LaplacianSystem *sys) { - float *v1, *v2, *v3, *v4; - float w2, w3, w4; - int i, j; - bool has_4_vert; - uint idv1, idv2, idv3, idv4, idv[4]; - BMEdge *e; BMFace *f; BMIter eiter; BMIter fiter; - BMIter vi; - BMVert *vn; - BMVert *vf[4]; + int i; - BM_ITER_MESH_INDEX (f, &fiter, sys->bm, BM_FACES_OF_MESH, i) { + uint l_curr_index = 0; + + BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) { if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { + l_curr_index += f->len; continue; } - BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, j) { - vf[j] = vn; - } - has_4_vert = (j == 4) ? 1 : 0; - if (has_4_vert) { - idv[0] = BM_elem_index_get(vf[0]); - idv[1] = BM_elem_index_get(vf[1]); - idv[2] = BM_elem_index_get(vf[2]); - idv[3] = BM_elem_index_get(vf[3]); - for (j = 0; j < 4; j++) { - idv1 = idv[j]; - idv2 = idv[(j + 1) % 4]; - idv3 = idv[(j + 2) % 4]; - idv4 = idv[(j + 3) % 4]; - - v1 = vf[j]->co; - v2 = vf[(j + 1) % 4]->co; - v3 = vf[(j + 2) % 4]->co; - v4 = vf[(j + 3) % 4]->co; - - w2 = cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2); - w3 = cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3); - w4 = cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(v3, v4, v1); - - w2 = w2 / 4.0f; - w3 = w3 / 4.0f; - w4 = w4 / 4.0f; - - if (!vert_is_boundary(vf[j]) && sys->zerola[idv1] == false) { - EIG_linear_solver_matrix_add(sys->context, idv1, idv2, w2 * sys->vweights[idv1]); - EIG_linear_solver_matrix_add(sys->context, idv1, idv3, w3 * sys->vweights[idv1]); - EIG_linear_solver_matrix_add(sys->context, idv1, idv4, w4 * sys->vweights[idv1]); - } + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + + int vi_prev = BM_elem_index_get(l_iter->prev->v); + int vi_curr = BM_elem_index_get(l_iter->v); + + bool ok_prev = (sys->zerola[vi_prev] == false) && !vert_is_boundary(l_iter->prev->v); + bool ok_curr = (sys->zerola[vi_curr] == false) && !vert_is_boundary(l_iter->v); + + do { + const int vi_next = BM_elem_index_get(l_iter->next->v); + const bool ok_next = (sys->zerola[vi_next] == false) && !vert_is_boundary(l_iter->next->v); + + if (ok_prev) { + EIG_linear_solver_matrix_add(sys->context, + vi_prev, + vi_curr, + sys->fweights[l_curr_index][1] * sys->vweights[vi_prev]); + EIG_linear_solver_matrix_add(sys->context, + vi_prev, + vi_next, + sys->fweights[l_curr_index][0] * sys->vweights[vi_prev]); } - } - else { - idv1 = BM_elem_index_get(vf[0]); - idv2 = BM_elem_index_get(vf[1]); - idv3 = BM_elem_index_get(vf[2]); - /* Is ring if number of faces == number of edges around vertice. */ - if (!vert_is_boundary(vf[0]) && sys->zerola[idv1] == false) { - EIG_linear_solver_matrix_add( - sys->context, idv1, idv2, sys->fweights[i][2] * sys->vweights[idv1]); - EIG_linear_solver_matrix_add( - sys->context, idv1, idv3, sys->fweights[i][1] * sys->vweights[idv1]); + if (ok_curr) { + EIG_linear_solver_matrix_add(sys->context, + vi_curr, + vi_next, + sys->fweights[l_curr_index][2] * sys->vweights[vi_curr]); + EIG_linear_solver_matrix_add(sys->context, + vi_curr, + vi_prev, + sys->fweights[l_curr_index][1] * sys->vweights[vi_curr]); } - if (!vert_is_boundary(vf[1]) && sys->zerola[idv2] == false) { - EIG_linear_solver_matrix_add( - sys->context, idv2, idv1, sys->fweights[i][2] * sys->vweights[idv2]); - EIG_linear_solver_matrix_add( - sys->context, idv2, idv3, sys->fweights[i][0] * sys->vweights[idv2]); + if (ok_next) { + EIG_linear_solver_matrix_add(sys->context, + vi_next, + vi_curr, + sys->fweights[l_curr_index][2] * sys->vweights[vi_next]); + EIG_linear_solver_matrix_add(sys->context, + vi_next, + vi_prev, + sys->fweights[l_curr_index][0] * sys->vweights[vi_next]); } - if (!vert_is_boundary(vf[2]) && sys->zerola[idv3] == false) { - EIG_linear_solver_matrix_add( - sys->context, idv3, idv1, sys->fweights[i][1] * sys->vweights[idv3]); - EIG_linear_solver_matrix_add( - sys->context, idv3, idv2, sys->fweights[i][0] * sys->vweights[idv3]); - } - } + + vi_prev = vi_curr; + vi_curr = vi_next; + + ok_prev = ok_curr; + ok_curr = ok_next; + + } while (((void)(l_curr_index += 1), (l_iter = l_iter->next) != l_first)); } BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) { if (BM_elem_flag_test(e, BM_ELEM_SELECT) || !BM_edge_is_boundary(e)) { continue; } - idv1 = BM_elem_index_get(e->v1); - idv2 = BM_elem_index_get(e->v2); + const uint idv1 = BM_elem_index_get(e->v1); + const uint idv2 = BM_elem_index_get(e->v2); if (sys->zerola[idv1] == false && sys->zerola[idv2] == false) { EIG_linear_solver_matrix_add( sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]); @@ -494,7 +436,7 @@ void bmo_smooth_laplacian_vert_exec(BMesh *bm, BMOperator *op) if (bm->totface == 0) { return; } - sys = init_laplacian_system(bm->totedge, bm->totface, bm->totvert); + sys = init_laplacian_system(bm->totedge, bm->totloop, bm->totvert); if (!sys) { return; } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 215ce0185f1..1b6643da1aa 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -2748,9 +2748,6 @@ void MESH_OT_vertices_smooth(wmOperatorType *ot) static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) { - BMIter fiter; - BMFace *f; - int tot_invalid = 0; int tot_unselected = 0; ViewLayer *view_layer = CTX_data_view_layer(C); @@ -2777,22 +2774,6 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) if (em->bm->totvertsel == 0) { tot_unselected++; - tot_invalid++; - continue; - } - - bool is_invalid = false; - /* Check if select faces are triangles. */ - BM_ITER_MESH (f, &fiter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - if (f->len > 4) { - tot_invalid++; - is_invalid = true; - break; - } - } - } - if (is_invalid) { continue; } @@ -2841,10 +2822,6 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_WARNING, "No selected vertex"); return OPERATOR_CANCELLED; } - if (tot_invalid == objects_len) { - BKE_report(op->reports, RPT_WARNING, "Selected faces must be triangles or quads"); - return OPERATOR_CANCELLED; - } return OPERATOR_FINISHED; } -- cgit v1.2.3 From bd44e82b255a231242a1b7ddd59cee7830af20ea Mon Sep 17 00:00:00 2001 From: Eitan Date: Thu, 5 Aug 2021 10:44:59 -0500 Subject: Geometry Nodes: Add more warnings for out of bounds parameters Add warning(info) to nodes that don't work when an input value is out of range. For example, the grid node doesn't work with Vertices X or Verices Y less than 2. These are purposefully added as "Info" warnings, because they don't show in the modifier and they aren't printed to the terminal. Differential Revision: https://developer.blender.org/D11923 --- .../blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc | 1 + source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc | 1 + .../nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc | 1 + source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc | 6 ++++++ .../nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc | 6 ++++++ 5 files changed, 15 insertions(+) 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 667e1c931bd..96c6f073ab3 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 @@ -211,6 +211,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) const float radius = params.extract_input("Radius"); const int verts_num = params.extract_input("Vertices"); if (verts_num < 3) { + params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); params.set_output("Geometry", GeometrySet()); return; } 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 d46ea2d2050..790a518e584 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 @@ -551,6 +551,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) const int verts_num = params.extract_input("Vertices"); if (verts_num < 3) { + params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); params.set_output("Geometry", GeometrySet()); return; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index 1767f765da4..b40cb478b03 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -70,6 +70,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) const float depth = params.extract_input("Depth"); const int verts_num = params.extract_input("Vertices"); if (verts_num < 3) { + params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); params.set_output("Geometry", GeometrySet()); return; } 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 ac2f5a23a4d..7a97ae8e318 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 @@ -162,6 +162,12 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params) const int verts_x = params.extract_input("Vertices X"); const int verts_y = params.extract_input("Vertices Y"); if (verts_x < 2 || verts_y < 2) { + if (verts_x < 2) { + params.error_message_add(NodeWarningType::Info, TIP_("Vertices X must be at least 2")); + } + if (verts_y < 2) { + params.error_message_add(NodeWarningType::Info, TIP_("Vertices Y must be at least 2")); + } params.set_output("Geometry", GeometrySet()); return; } 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 599c59e4a2e..fe456dc4564 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 @@ -291,6 +291,12 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) const int segments_num = params.extract_input("Segments"); const int rings_num = params.extract_input("Rings"); if (segments_num < 3 || rings_num < 2) { + if (segments_num < 3) { + params.error_message_add(NodeWarningType::Info, TIP_("Segments must be at least 3")); + } + if (rings_num < 3) { + params.error_message_add(NodeWarningType::Info, TIP_("Rings must be at least 3")); + } params.set_output("Geometry", GeometrySet()); return; } -- cgit v1.2.3 From 6844f7bedb71883f383bdeff130fd2200750cc54 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Thu, 5 Aug 2021 13:04:59 -0400 Subject: PyDoc: document how parameter are used for 3D and 2D textures Improves on rB171433e841379e7efad069bbda9880fb271e2fc4 --- source/blender/makesrna/intern/rna_texture_api.c | 28 ++++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/source/blender/makesrna/intern/rna_texture_api.c b/source/blender/makesrna/intern/rna_texture_api.c index c46b9acf986..83c1efd55bc 100644 --- a/source/blender/makesrna/intern/rna_texture_api.c +++ b/source/blender/makesrna/intern/rna_texture_api.c @@ -59,19 +59,23 @@ void RNA_api_texture(StructRNA *srna) PropertyRNA *parm; func = RNA_def_function(srna, "evaluate", "texture_evaluate"); - RNA_def_function_ui_description(func, "Evaluate the texture at the coordinates given"); - - parm = RNA_def_float_vector(func, - "value", - 3, - NULL, - -FLT_MAX, - FLT_MAX, - "The object coordinates (x,y,z) used to generate/map the texture", - "", - -1e4, - 1e4); + RNA_def_function_ui_description( + func, "Evaluate the texture at the a given coordinate and returns the result"); + + parm = RNA_def_float_vector( + func, + "value", + 3, + NULL, + -FLT_MAX, + FLT_MAX, + "The coordinates (x,y,z) of the texture, in case of a 3D texture, the z value is the slice " + "of the texture that is evaluated. For 2D textures such as images, the z value is ignored", + "", + -1e4, + 1e4); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + /* return location and normal */ parm = RNA_def_float_vector( func, -- cgit v1.2.3 From 4dd6c9ad450f773a4bfaaad2d54feee815399ce4 Mon Sep 17 00:00:00 2001 From: Pablo Dobarro Date: Tue, 27 Jul 2021 20:50:26 +0200 Subject: Fix T90236: Sculpt automasking failing when the stroke does not start over the mesh The active geometry element are usually updated by the cursor drawing code (as they are needed for the cursor preview) and when an sculpt operator starts. For brushes, this was not happening. This was making brushes rely by default on the last cursor drawing update, which can be incorrect if the mouse moved after starting the stroke without hovering the active geometry. Reviewed By: JacquesLucke Maniphest Tasks: T90236 Differential Revision: https://developer.blender.org/D12045 --- source/blender/editors/sculpt_paint/sculpt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 83388c1aef2..8264affc465 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -7887,6 +7887,9 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f sculpt_update_cache_invariants(C, sd, ss, op, mouse); + SculptCursorGeometryInfo sgi; + SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_undo_push_begin(ob, sculpt_tool_name(sd)); return true; -- cgit v1.2.3 From d01781129fab7846a6a03815b0c0b48b2a809c39 Mon Sep 17 00:00:00 2001 From: Pablo Dobarro Date: Tue, 27 Jul 2021 20:26:38 +0200 Subject: Fix T90235: Smooth Brush not working with interior vertices with two adjacent edges The exception to automatically pin vertices of grid corners also has to take into account that the vertex is in a boundary. Reviewed By: JacquesLucke Maniphest Tasks: T90235 Differential Revision: https://developer.blender.org/D12044 --- source/blender/editors/sculpt_paint/sculpt_smooth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index eabbfe43e03..38165b7622f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -88,7 +88,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); /* Do not modify corner vertices. */ - if (neighbor_count <= 2) { + if (neighbor_count <= 2 && is_boundary) { copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); return; } -- cgit v1.2.3 From cf10eb54cc4bc89e763dc48644c402f7578fa6b2 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 2 Aug 2021 19:08:12 +0300 Subject: Action Constraint: add Split Channels Mix choices from Copy Transforms Practice shows that when combining actions and direct animation it is usually best to combine location, rotation and scale separately, which is implemented by the Split Channels modes recently introduced in D9469 for Copy Transforms. This completes the same set of 6 choices for the Action Constraint. The default for new constraints is changed to the newly added Before Original (Split Channels) mode. The original patch is motivated by Loic Pinsard, who created an addon that does the equivalent of this feature by splitting the action into two, separating location and rotation+scale. Differential Revision: https://developer.blender.org/D7547 --- source/blender/blenkernel/intern/constraint.c | 30 +++++++++++++- .../blender/editors/transform/transform_convert.c | 5 ++- source/blender/makesdna/DNA_constraint_types.h | 6 +++ source/blender/makesrna/intern/rna_constraint.c | 48 ++++++++++++++++------ 4 files changed, 73 insertions(+), 16 deletions(-) diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index b7e02f06571..0da29ded13d 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -2982,6 +2982,16 @@ static void actcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targ if (VALID_CONS_TARGET(ct) || data->flag & ACTCON_USE_EVAL_TIME) { switch (data->mix_mode) { + /* Simple matrix multiplication. */ + case ACTCON_MIX_BEFORE_FULL: + mul_m4_m4m4(cob->matrix, ct->matrix, cob->matrix); + break; + + case ACTCON_MIX_AFTER_FULL: + mul_m4_m4m4(cob->matrix, cob->matrix, ct->matrix); + break; + + /* Aligned Inherit Scale emulation. */ case ACTCON_MIX_BEFORE: mul_m4_m4m4_aligned_scale(cob->matrix, ct->matrix, cob->matrix); break; @@ -2990,8 +3000,13 @@ static void actcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targ mul_m4_m4m4_aligned_scale(cob->matrix, cob->matrix, ct->matrix); break; - case ACTCON_MIX_AFTER_FULL: - mul_m4_m4m4(cob->matrix, cob->matrix, ct->matrix); + /* Fully separate handling of channels. */ + case ACTCON_MIX_BEFORE_SPLIT: + mul_m4_m4m4_split_channels(cob->matrix, ct->matrix, cob->matrix); + break; + + case ACTCON_MIX_AFTER_SPLIT: + mul_m4_m4m4_split_channels(cob->matrix, cob->matrix, ct->matrix); break; default: @@ -5767,6 +5782,17 @@ static bConstraint *add_new_constraint(Object *ob, } break; } + case CONSTRAINT_TYPE_ACTION: { + /* The Before or Split modes require computing in local space, but + * for objects the Local space doesn't make sense (T78462, D6095 etc). + * So only default to Before (Split) if the constraint is on a bone. */ + if (pchan) { + bActionConstraint *data = con->data; + data->mix_mode = ACTCON_MIX_BEFORE_SPLIT; + con->ownspace = CONSTRAINT_SPACE_LOCAL; + } + break; + } } return con; diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 00fd008151d..e77fedfe143 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -861,10 +861,13 @@ bool constraints_list_needinv(TransInfo *t, ListBase *list) /* The Action constraint only does this in the Before mode. */ bActionConstraint *data = (bActionConstraint *)con->data; - if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE) && + if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE, ACTCON_MIX_BEFORE_FULL) && ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION)) { return true; } + if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE_SPLIT) && ELEM(t->mode, TFM_ROTATION)) { + return true; + } } else if (con->type == CONSTRAINT_TYPE_TRANSFORM) { /* Transform constraint needs it for rotation at least (r.57309), diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index a77fbc9e45e..822b8705c9b 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -901,10 +901,16 @@ typedef enum eActionConstraint_Flags { typedef enum eActionConstraint_MixMode { /* Multiply the action transformation on the right. */ ACTCON_MIX_AFTER_FULL = 0, + /* Multiply the action transformation on the left. */ + ACTCON_MIX_BEFORE_FULL = 3, /* Multiply the action transformation on the right, with anti-shear scale handling. */ ACTCON_MIX_AFTER = 1, /* Multiply the action transformation on the left, with anti-shear scale handling. */ ACTCON_MIX_BEFORE = 2, + /* Separately combine Translation, Rotation and Scale, with rotation on the right. */ + ACTCON_MIX_AFTER_SPLIT = 4, + /* Separately combine Translation, Rotation and Scale, with rotation on the left. */ + ACTCON_MIX_BEFORE_SPLIT = 5, } eActionConstraint_MixMode; /* Locked-Axis Values (Locked Track) */ diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index a0a0a41b58d..e36d052d27c 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -679,7 +679,7 @@ static void rna_ActionConstraint_mix_mode_set(PointerRNA *ptr, int value) acon->mix_mode = value; /* The After mode can be computed in world space for efficiency - * and backward compatibility, while Before requires Local. */ + * and backward compatibility, while Before or Split requires Local. */ if (ELEM(value, ACTCON_MIX_AFTER, ACTCON_MIX_AFTER_FULL)) { con->ownspace = CONSTRAINT_SPACE_WORLD; } @@ -1773,25 +1773,47 @@ static void rna_def_constraint_action(BlenderRNA *brna) }; static const EnumPropertyItem mix_mode_items[] = { + {ACTCON_MIX_BEFORE_FULL, + "BEFORE_FULL", + 0, + "Before Original (Full)", + "Apply the action channels before the original transformation, as if applied to an " + "imaginary parent in Full Inherit Scale mode. Will create shear when combining rotation " + "and non-uniform scale"}, {ACTCON_MIX_BEFORE, "BEFORE", 0, - "Before Original", - "Apply the action channels before the original transformation, " - "as if applied to an imaginary parent with Aligned Inherit Scale"}, - {ACTCON_MIX_AFTER, - "AFTER", + "Before Original (Aligned)", + "Apply the action channels before the original transformation, as if applied to an " + "imaginary parent in Aligned Inherit Scale mode. This effectively uses Full for location " + "and Split Channels for rotation and scale"}, + {ACTCON_MIX_BEFORE_SPLIT, + "BEFORE_SPLIT", 0, - "After Original", - "Apply the action channels after the original transformation, " - "as if applied to an imaginary child with Aligned Inherit Scale"}, + "Before Original (Split Channels)", + "Apply the action channels before the original transformation, handling location, rotation " + "and scale separately"}, + {0, "", 0, NULL, NULL}, {ACTCON_MIX_AFTER_FULL, "AFTER_FULL", 0, - "After Original (Full Scale)", - "Apply the action channels after the original transformation, as if " - "applied to an imaginary child with Full Inherit Scale. This mode " - "can create shear and is provided only for backward compatibility"}, + "After Original (Full)", + "Apply the action channels after the original transformation, as if applied to an " + "imaginary child in Full Inherit Scale mode. Will create shear when combining rotation " + "and non-uniform scale"}, + {ACTCON_MIX_AFTER, + "AFTER", + 0, + "After Original (Aligned)", + "Apply the action channels after the original transformation, as if applied to an " + "imaginary child in Aligned Inherit Scale mode. This effectively uses Full for location " + "and Split Channels for rotation and scale"}, + {ACTCON_MIX_AFTER_SPLIT, + "AFTER_SPLIT", + 0, + "After Original (Split Channels)", + "Apply the action channels after the original transformation, handling location, rotation " + "and scale separately"}, {0, NULL, 0, NULL, NULL}, }; -- cgit v1.2.3 From 89014b51f12779efbd9499f91a86b96c7407b2f7 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 5 Aug 2021 12:49:55 -0300 Subject: Xcode: support cmake options for grouping in folders The Xcode IDE can also benefit from the options: - WINDOWS_USE_VISUAL_STUDIO_SOURCE_FOLDERS - WINDOWS_USE_VISUAL_STUDIO_PROJECT_FOLDERS So add suport to these options and also renames them as they are no longer limited to just Windows and Visual Studio. Reviewed By: brecht, ankitm Differential Revision: https://developer.blender.org/D12132 --- CMakeLists.txt | 18 ++++++++++++------ build_files/cmake/macros.cmake | 6 +++--- build_files/cmake/platform/platform_win32.cmake | 2 -- intern/cycles/cmake/macros.cmake | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3baebba4678..2868324bf46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -612,12 +612,6 @@ if(WIN32) option(WITH_WINDOWS_FIND_MODULES "Use find_package to locate libraries" OFF) mark_as_advanced(WITH_WINDOWS_FIND_MODULES) - option(WINDOWS_USE_VISUAL_STUDIO_PROJECT_FOLDERS "Organize the visual studio projects according to source folder structure." ON) - mark_as_advanced(WINDOWS_USE_VISUAL_STUDIO_PROJECT_FOLDERS) - - option(WINDOWS_USE_VISUAL_STUDIO_SOURCE_FOLDERS "Organize the source files in filters matching the source folders." ON) - mark_as_advanced(WINDOWS_USE_VISUAL_STUDIO_SOURCE_FOLDERS) - option(WINDOWS_PYTHON_DEBUG "Include the files needed for debugging python scripts with visual studio 2017+." OFF) mark_as_advanced(WINDOWS_PYTHON_DEBUG) @@ -635,6 +629,18 @@ if(WIN32) endif() +if(WIN32 OR XCODE) + option(IDE_GROUP_SOURCES_IN_FOLDERS "Organize the source files in filters matching the source folders." ON) + mark_as_advanced(IDE_GROUP_SOURCES_IN_FOLDERS) + + option(IDE_GROUP_PROJECTS_IN_FOLDERS "Organize the projects according to source folder structure." ON) + mark_as_advanced(IDE_GROUP_PROJECTS_IN_FOLDERS) + + if (IDE_GROUP_PROJECTS_IN_FOLDERS) + set_property(GLOBAL PROPERTY USE_FOLDERS ON) + endif() +endif() + if(UNIX) # See WITH_WINDOWS_SCCACHE for Windows. option(WITH_COMPILER_CCACHE "Use ccache to improve rebuild times (Works with Ninja, Makefiles and Xcode)" OFF) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 8ad3f77c7d3..1471aa21505 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -208,7 +208,7 @@ function(blender_source_group ) # if enabled, use the sources directories as filters. - if(WINDOWS_USE_VISUAL_STUDIO_SOURCE_FOLDERS) + if(IDE_GROUP_SOURCES_IN_FOLDERS) foreach(_SRC ${sources}) # remove ../'s get_filename_component(_SRC_DIR ${_SRC} REALPATH) @@ -240,8 +240,8 @@ function(blender_source_group endforeach() endif() - # if enabled, set the FOLDER property for visual studio projects - if(WINDOWS_USE_VISUAL_STUDIO_PROJECT_FOLDERS) + # if enabled, set the FOLDER property for the projects + if(IDE_GROUP_PROJECTS_IN_FOLDERS) get_filename_component(FolderDir ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY) string(REPLACE ${CMAKE_SOURCE_DIR} "" FolderDir ${FolderDir}) set_target_properties(${name} PROPERTIES FOLDER ${FolderDir}) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index 113c41c545b..3773aaaffed 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -57,8 +57,6 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang") endif() endif() -set_property(GLOBAL PROPERTY USE_FOLDERS ${WINDOWS_USE_VISUAL_STUDIO_PROJECT_FOLDERS}) - if(NOT WITH_PYTHON_MODULE) set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT blender) endif() diff --git a/intern/cycles/cmake/macros.cmake b/intern/cycles/cmake/macros.cmake index ff62b816e6c..47196dfd1ce 100644 --- a/intern/cycles/cmake/macros.cmake +++ b/intern/cycles/cmake/macros.cmake @@ -13,7 +13,7 @@ # limitations under the License. function(cycles_set_solution_folder target) - if(WINDOWS_USE_VISUAL_STUDIO_FOLDERS) + if(IDE_GROUP_PROJECTS_IN_FOLDERS) get_filename_component(folderdir ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY) string(REPLACE ${CMAKE_SOURCE_DIR} "" folderdir ${folderdir}) set_target_properties(${target} PROPERTIES FOLDER ${folderdir}) -- cgit v1.2.3 From 92edf37997c25444fc2628c8057a87b7e87c5eff Mon Sep 17 00:00:00 2001 From: Himanshi Kalra Date: Fri, 6 Aug 2021 00:03:16 +0530 Subject: Add custom data comparison for generic attributes Generic attributes CD_PROP_* comparison is added in customdata_compare Checks for built-in as well as user created attributes. Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D12137 --- source/blender/blenkernel/intern/mesh.c | 95 +++++++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 8d74002ad79..b00670aad4c 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -389,6 +389,7 @@ enum { MESHCMP_EDGEUNKNOWN, MESHCMP_VERTCOMISMATCH, MESHCMP_CDLAYERS_MISMATCH, + MESHCMP_ATTRIBUTE_VALUE_MISMATCH, }; static const char *cmpcode_to_str(int code) @@ -416,6 +417,8 @@ static const char *cmpcode_to_str(int code) return "Vertex Coordinate Mismatch"; case MESHCMP_CDLAYERS_MISMATCH: return "CustomData Layer Count Mismatch"; + case MESHCMP_ATTRIBUTE_VALUE_MISMATCH: + return "Attribute Value Mismatch"; default: return "Mesh Comparison Code Unknown"; } @@ -423,13 +426,13 @@ static const char *cmpcode_to_str(int code) /** Thresh is threshold for comparing vertices, UV's, vertex colors, weights, etc. */ static int customdata_compare( - CustomData *c1, CustomData *c2, Mesh *m1, Mesh *m2, const float thresh) + CustomData *c1, CustomData *c2, const int total_length, Mesh *m1, Mesh *m2, const float thresh) { const float thresh_sq = thresh * thresh; CustomDataLayer *l1, *l2; - int i, i1 = 0, i2 = 0, tot, j; + int i1 = 0, i2 = 0, tot, j; - for (i = 0; i < c1->totlayer; i++) { + for (int i = 0; i < c1->totlayer; i++) { if (ELEM(c1->layers[i].type, CD_MVERT, CD_MEDGE, @@ -441,7 +444,7 @@ static int customdata_compare( } } - for (i = 0; i < c2->totlayer; i++) { + for (int i = 0; i < c2->totlayer; i++) { if (ELEM(c2->layers[i].type, CD_MVERT, CD_MEDGE, @@ -457,12 +460,86 @@ static int customdata_compare( return MESHCMP_CDLAYERS_MISMATCH; } + l1 = c1->layers; + l2 = c2->layers; + + for (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++) { + 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_PROP_FLOAT: { + const float *l1_data = l1->data; + const float *l2_data = l2->data; + + for (int i = 0; i < total_length; i++) { + if (fabsf(l1_data[i] - l2_data[i]) > thresh) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } + } + break; + } + case CD_PROP_FLOAT2: { + const float(*l1_data)[2] = l1->data; + const float(*l2_data)[2] = l2->data; + + for (int i = 0; i < total_length; i++) { + if (len_squared_v2v2(l1_data[i], l2_data[i]) > thresh_sq) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } + } + break; + } + case CD_PROP_FLOAT3: { + const float(*l1_data)[3] = l1->data; + const float(*l2_data)[3] = l2->data; + + for (int i = 0; i < total_length; i++) { + if (len_squared_v3v3(l1_data[i], l2_data[i]) > thresh_sq) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } + } + break; + } + default: { + int element_size = CustomData_sizeof(l1->type); + 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))) { + 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 (i = 0; i < tot; i++) { + 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)) { @@ -626,19 +703,19 @@ const char *BKE_mesh_cmp(Mesh *me1, Mesh *me2, float thresh) return "Number of loops don't match"; } - if ((c = customdata_compare(&me1->vdata, &me2->vdata, me1, me2, thresh))) { + if ((c = customdata_compare(&me1->vdata, &me2->vdata, me1->totvert, me1, me2, thresh))) { return cmpcode_to_str(c); } - if ((c = customdata_compare(&me1->edata, &me2->edata, me1, me2, thresh))) { + if ((c = customdata_compare(&me1->edata, &me2->edata, me1->totedge, me1, me2, thresh))) { return cmpcode_to_str(c); } - if ((c = customdata_compare(&me1->ldata, &me2->ldata, me1, me2, thresh))) { + if ((c = customdata_compare(&me1->ldata, &me2->ldata, me1->totloop, me1, me2, thresh))) { return cmpcode_to_str(c); } - if ((c = customdata_compare(&me1->pdata, &me2->pdata, me1, me2, thresh))) { + if ((c = customdata_compare(&me1->pdata, &me2->pdata, me1->totpoly, me1, me2, thresh))) { return cmpcode_to_str(c); } -- cgit v1.2.3 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 From 3f0d785d23bf246abcb6f9cf86e106cdc9ac3636 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Sun, 15 Aug 2021 17:54:59 -0300 Subject: Fix T90658: selection of some 3D gizmos failing Small error due to wrong variable usage. Introduced in rBfcd2d63b644e. --- source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index b90e288776e..6f6a2402d89 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -627,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(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d_origin); + GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d); 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 899935d5d02463187617d1adc5f0c0f41fe56265 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Sun, 15 Aug 2021 20:07:25 -0300 Subject: Fix wrong usage of 'sizeof' The intention was to use `ARRAY_SIZE`. No functional changes. --- source/blender/draw/intern/draw_cache_impl_mesh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 86c14330409..52b76733b78 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -215,7 +215,7 @@ static const DRWBatchFlag g_buffer_deps[] = { }; #ifndef NDEBUG -static DRWBatchFlag g_buffer_deps_d[sizeof(g_buffer_deps)] = {0}; +static DRWBatchFlag g_buffer_deps_d[ARRAY_SIZE(g_buffer_deps)] = {0}; #endif static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache); -- cgit v1.2.3 From eb278f5e12cf9db9664a89ed10902fb891846afe Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Mon, 16 Aug 2021 11:46:09 +0900 Subject: XR: Color Depth Adjustments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This addresses reduced visibility of scenes (as displayed in the VR headset) that can result from the 8-bit color depth format currently used for XR swapchain images. By switching to a swapchain format with higher color depth (RGB10_A2, RGBA16, RGBA16F) for supported runtimes, visibility in VR should be noticeably improved. However, current limitations are lack of support for these higher color depth formats by some XR runtimes, especially for OpenGL. Also important to note that GPU_offscreen_create() now explicitly takes in the texture format (eGPUTextureFormat) instead of a "high_bitdepth" boolean. Reviewed By: Julian Eisel, Clément Foucault Differential Revision: http://developer.blender.org/D9842 --- intern/ghost/GHOST_Types.h | 9 ++ intern/ghost/intern/GHOST_ContextD3D.cpp | 15 ++- intern/ghost/intern/GHOST_ContextD3D.h | 8 +- intern/ghost/intern/GHOST_IXrGraphicsBinding.h | 1 + intern/ghost/intern/GHOST_XrGraphicsBinding.cpp | 103 +++++++++++++++++++-- intern/ghost/intern/GHOST_XrSession.cpp | 1 + intern/ghost/intern/GHOST_XrSwapchain.cpp | 12 ++- intern/ghost/intern/GHOST_XrSwapchain.h | 4 +- source/blender/editors/gpencil/gpencil_fill.c | 3 +- source/blender/editors/render/render_opengl.c | 2 +- source/blender/editors/screen/screen_draw.c | 2 +- source/blender/editors/space_view3d/view3d_draw.c | 2 +- source/blender/gpu/GPU_framebuffer.h | 2 +- source/blender/gpu/intern/gpu_framebuffer.cc | 5 +- source/blender/python/gpu/gpu_py_offscreen.c | 2 +- source/blender/windowmanager/intern/wm_draw.c | 4 +- .../windowmanager/xr/intern/wm_xr_session.c | 26 +++++- 17 files changed, 168 insertions(+), 33 deletions(-) diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index fb19b9535ad..e46f712cb64 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -669,6 +669,14 @@ typedef struct { void *exit_customdata; } GHOST_XrSessionBeginInfo; +/** Texture format for XR swapchain. */ +typedef enum GHOST_TXrSwapchainFormat { + GHOST_kXrSwapchainFormatRGBA8, + GHOST_kXrSwapchainFormatRGBA16, + GHOST_kXrSwapchainFormatRGBA16F, + GHOST_kXrSwapchainFormatRGB10_A2, +} GHOST_TXrSwapchainFormat; + typedef struct GHOST_XrDrawViewInfo { int ofsx, ofsy; int width, height; @@ -681,6 +689,7 @@ typedef struct GHOST_XrDrawViewInfo { float angle_up, angle_down; } fov; + GHOST_TXrSwapchainFormat swapchain_format; /** Set if the buffer should be submitted with a SRGB transfer applied. */ char expects_srgb_buffer; diff --git a/intern/ghost/intern/GHOST_ContextD3D.cpp b/intern/ghost/intern/GHOST_ContextD3D.cpp index ad948578d53..73f6c12e100 100644 --- a/intern/ghost/intern/GHOST_ContextD3D.cpp +++ b/intern/ghost/intern/GHOST_ContextD3D.cpp @@ -132,6 +132,7 @@ class GHOST_SharedOpenGLResource { ID3D11DeviceContext *device_ctx, unsigned int width, unsigned int height, + DXGI_FORMAT format, ID3D11RenderTargetView *render_target = nullptr) : m_device(device), m_device_ctx(device_ctx), m_cur_width(width), m_cur_height(height) { @@ -144,7 +145,7 @@ class GHOST_SharedOpenGLResource { texDesc.Width = width; texDesc.Height = height; - texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + texDesc.Format = format; texDesc.SampleDesc.Count = 1; texDesc.ArraySize = 1; texDesc.MipLevels = 1; @@ -321,7 +322,10 @@ class GHOST_SharedOpenGLResource { }; GHOST_SharedOpenGLResource *GHOST_ContextD3D::createSharedOpenGLResource( - unsigned int width, unsigned int height, ID3D11RenderTargetView *render_target) + unsigned int width, + unsigned int height, + DXGI_FORMAT format, + ID3D11RenderTargetView *render_target) { if (!(WGL_NV_DX_interop && WGL_NV_DX_interop2)) { fprintf(stderr, @@ -330,14 +334,15 @@ GHOST_SharedOpenGLResource *GHOST_ContextD3D::createSharedOpenGLResource( return nullptr; } GHOST_SharedOpenGLResource *shared_res = new GHOST_SharedOpenGLResource( - m_device, m_device_ctx, width, height, render_target); + m_device, m_device_ctx, width, height, format, render_target); return shared_res; } GHOST_SharedOpenGLResource *GHOST_ContextD3D::createSharedOpenGLResource(unsigned int width, - unsigned int height) + unsigned int height, + DXGI_FORMAT format) { - return createSharedOpenGLResource(width, height, nullptr); + return createSharedOpenGLResource(width, height, format, nullptr); } void GHOST_ContextD3D::disposeSharedOpenGLResource(GHOST_SharedOpenGLResource *shared_res) diff --git a/intern/ghost/intern/GHOST_ContextD3D.h b/intern/ghost/intern/GHOST_ContextD3D.h index 8b9537ca439..c18c9d3c286 100644 --- a/intern/ghost/intern/GHOST_ContextD3D.h +++ b/intern/ghost/intern/GHOST_ContextD3D.h @@ -106,9 +106,13 @@ class GHOST_ContextD3D : public GHOST_Context { } class GHOST_SharedOpenGLResource *createSharedOpenGLResource( - unsigned int width, unsigned int height, ID3D11RenderTargetView *render_target); + unsigned int width, + unsigned int height, + DXGI_FORMAT format, + ID3D11RenderTargetView *render_target); class GHOST_SharedOpenGLResource *createSharedOpenGLResource(unsigned int width, - unsigned int height); + unsigned int height, + DXGI_FORMAT format); void disposeSharedOpenGLResource(class GHOST_SharedOpenGLResource *shared_res); GHOST_TSuccess blitFromOpenGLContext(class GHOST_SharedOpenGLResource *shared_res, unsigned int width, diff --git a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h index a7339158dc4..bfdf0cac633 100644 --- a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h +++ b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h @@ -60,6 +60,7 @@ class GHOST_IXrGraphicsBinding { std::string *r_requirement_info) const = 0; virtual void initFromGhostContext(class GHOST_Context &ghost_ctx) = 0; virtual std::optional chooseSwapchainFormat(const std::vector &runtime_formats, + GHOST_TXrSwapchainFormat &r_format, bool &r_is_rgb_format) const = 0; virtual std::vector createSwapchainImages( uint32_t image_count) = 0; diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp index dd0205ea867..70567e02cb9 100644 --- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp +++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp @@ -38,6 +38,7 @@ # include "GHOST_SystemWin32.h" #endif #include "GHOST_C-api.h" +#include "GHOST_XrException.h" #include "GHOST_Xr_intern.h" #include "GHOST_IXrGraphicsBinding.h" @@ -160,16 +161,41 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { } std::optional chooseSwapchainFormat(const std::vector &runtime_formats, + GHOST_TXrSwapchainFormat &r_format, bool &r_is_srgb_format) const override { std::vector gpu_binding_formats = { + GL_RGB10_A2, + GL_RGBA16, + GL_RGBA16F, GL_RGBA8, GL_SRGB8_ALPHA8, }; std::optional result = choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats); - r_is_srgb_format = result ? (*result == GL_SRGB8_ALPHA8) : false; + if (result) { + switch (*result) { + case GL_RGB10_A2: + r_format = GHOST_kXrSwapchainFormatRGB10_A2; + break; + case GL_RGBA16: + r_format = GHOST_kXrSwapchainFormatRGBA16; + break; + case GL_RGBA16F: + r_format = GHOST_kXrSwapchainFormatRGBA16F; + break; + case GL_RGBA8: + case GL_SRGB8_ALPHA8: + r_format = GHOST_kXrSwapchainFormatRGBA8; + break; + } + r_is_srgb_format = (*result == GL_SRGB8_ALPHA8); + } + else { + r_format = GHOST_kXrSwapchainFormatRGBA8; + r_is_srgb_format = false; + } return result; } @@ -228,6 +254,33 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { }; #ifdef WIN32 +static void ghost_format_to_dx_format(GHOST_TXrSwapchainFormat ghost_format, + bool expects_srgb_buffer, + DXGI_FORMAT &r_dx_format) +{ + r_dx_format = DXGI_FORMAT_UNKNOWN; + + switch (ghost_format) { + case GHOST_kXrSwapchainFormatRGBA8: + r_dx_format = expects_srgb_buffer ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : + DXGI_FORMAT_R8G8B8A8_UNORM; + break; + case GHOST_kXrSwapchainFormatRGBA16: + r_dx_format = DXGI_FORMAT_R16G16B16A16_UNORM; + break; + case GHOST_kXrSwapchainFormatRGBA16F: + r_dx_format = DXGI_FORMAT_R16G16B16A16_FLOAT; + break; + case GHOST_kXrSwapchainFormatRGB10_A2: + r_dx_format = DXGI_FORMAT_R10G10B10A2_UNORM; + break; + } + + if (r_dx_format == DXGI_FORMAT_UNKNOWN) { + throw GHOST_XrException("No supported DirectX swapchain format found."); + } +} + class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding { public: GHOST_XrGraphicsBindingD3D(GHOST_Context &ghost_ctx) @@ -284,16 +337,48 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding { } std::optional chooseSwapchainFormat(const std::vector &runtime_formats, + GHOST_TXrSwapchainFormat &r_format, bool &r_is_srgb_format) const override { std::vector gpu_binding_formats = { - DXGI_FORMAT_R8G8B8A8_UNORM, - DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, +# if 0 /* RGB10A2 doesn't seem to work with Oculus headsets, so move it after RGB16AF for the \ + time being. */ + DXGI_FORMAT_R10G10B10A2_UNORM, +# endif + DXGI_FORMAT_R16G16B16A16_UNORM, + DXGI_FORMAT_R16G16B16A16_FLOAT, +# if 1 + DXGI_FORMAT_R10G10B10A2_UNORM, +# endif + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, }; std::optional result = choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats); - r_is_srgb_format = result ? (*result == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) : false; + if (result) { + switch (*result) { + case DXGI_FORMAT_R10G10B10A2_UNORM: + r_format = GHOST_kXrSwapchainFormatRGB10_A2; + break; + case DXGI_FORMAT_R16G16B16A16_UNORM: + r_format = GHOST_kXrSwapchainFormatRGBA16; + break; + case DXGI_FORMAT_R16G16B16A16_FLOAT: + r_format = GHOST_kXrSwapchainFormatRGBA16F; + break; + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + r_format = GHOST_kXrSwapchainFormatRGBA8; + break; + } + r_is_srgb_format = (*result == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB); + } + else { + r_format = GHOST_kXrSwapchainFormatRGBA8; + r_is_srgb_format = false; + } + return result; } @@ -334,14 +419,18 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding { m_ghost_ctx->m_device->CreateRenderTargetView(d3d_swapchain_image.texture, &rtv_desc, &rtv); if (!m_shared_resource) { + DXGI_FORMAT format; + ghost_format_to_dx_format(draw_info.swapchain_format, draw_info.expects_srgb_buffer, format); m_shared_resource = m_ghost_ctx->createSharedOpenGLResource( - draw_info.width, draw_info.height, rtv); + draw_info.width, draw_info.height, format, rtv); } m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info.width, draw_info.height); # else if (!m_shared_resource) { - m_shared_resource = m_ghost_d3d_ctx->createSharedOpenGLResource(draw_info.width, - draw_info.height); + DXGI_FORMAT format; + ghost_format_to_dx_format(draw_info.swapchain_format, draw_info.expects_srgb_buffer, format); + m_shared_resource = m_ghost_d3d_ctx->createSharedOpenGLResource( + draw_info.width, draw_info.height, format); } m_ghost_d3d_ctx->blitFromOpenGLContext(m_shared_resource, draw_info.width, draw_info.height); diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index 4cab22ee676..8b0320ef358 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -422,6 +422,7 @@ void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain, assert(view_idx < 256); draw_view_info.view_idx = (char)view_idx; + draw_view_info.swapchain_format = swapchain.getFormat(); draw_view_info.expects_srgb_buffer = swapchain.isBufferSRGB(); draw_view_info.ofsx = r_proj_layer_view.subImage.imageRect.offset.x; draw_view_info.ofsy = r_proj_layer_view.subImage.imageRect.offset.y; diff --git a/intern/ghost/intern/GHOST_XrSwapchain.cpp b/intern/ghost/intern/GHOST_XrSwapchain.cpp index 9973d99cc37..f89b7227ab1 100644 --- a/intern/ghost/intern/GHOST_XrSwapchain.cpp +++ b/intern/ghost/intern/GHOST_XrSwapchain.cpp @@ -67,8 +67,8 @@ GHOST_XrSwapchain::GHOST_XrSwapchain(GHOST_IXrGraphicsBinding &gpu_binding, "Failed to get swapchain image formats."); assert(swapchain_formats.size() == format_count); - std::optional chosen_format = gpu_binding.chooseSwapchainFormat(swapchain_formats, - m_is_srgb_buffer); + std::optional chosen_format = gpu_binding.chooseSwapchainFormat( + swapchain_formats, m_format, m_is_srgb_buffer); if (!chosen_format) { throw GHOST_XrException( "Error: No format matching OpenXR runtime supported swapchain formats found."); @@ -97,6 +97,7 @@ GHOST_XrSwapchain::GHOST_XrSwapchain(GHOST_XrSwapchain &&other) : m_oxr(std::move(other.m_oxr)), m_image_width(other.m_image_width), m_image_height(other.m_image_height), + m_format(other.m_format), m_is_srgb_buffer(other.m_is_srgb_buffer) { /* Prevent xrDestroySwapchain call for the moved out item. */ @@ -134,7 +135,12 @@ void GHOST_XrSwapchain::updateCompositionLayerProjectViewSubImage(XrSwapchainSub r_sub_image.imageRect.extent = {m_image_width, m_image_height}; } -bool GHOST_XrSwapchain::isBufferSRGB() +GHOST_TXrSwapchainFormat GHOST_XrSwapchain::getFormat() const +{ + return m_format; +} + +bool GHOST_XrSwapchain::isBufferSRGB() const { return m_is_srgb_buffer; } diff --git a/intern/ghost/intern/GHOST_XrSwapchain.h b/intern/ghost/intern/GHOST_XrSwapchain.h index 33a1c17b993..0c6592e2db6 100644 --- a/intern/ghost/intern/GHOST_XrSwapchain.h +++ b/intern/ghost/intern/GHOST_XrSwapchain.h @@ -37,10 +37,12 @@ class GHOST_XrSwapchain { void updateCompositionLayerProjectViewSubImage(XrSwapchainSubImage &r_sub_image); - bool isBufferSRGB(); + GHOST_TXrSwapchainFormat getFormat() const; + bool isBufferSRGB() const; private: std::unique_ptr m_oxr; /* Could use stack, but PImpl is preferable. */ int32_t m_image_width, m_image_height; + GHOST_TXrSwapchainFormat m_format; bool m_is_srgb_buffer = false; }; diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 67e1bd5294b..0c88d678ef4 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -646,7 +646,8 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) tgpf->sizey = (int)tgpf->region->winy; char err_out[256] = "unknown"; - GPUOffScreen *offscreen = GPU_offscreen_create(tgpf->sizex, tgpf->sizey, true, false, err_out); + GPUOffScreen *offscreen = GPU_offscreen_create( + tgpf->sizex, tgpf->sizey, true, GPU_RGBA8, err_out); if (offscreen == NULL) { printf("GPencil - Fill - Unable to create fill buffer\n"); return false; diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index d3307ebf274..749010a5ba3 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -768,7 +768,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) /* corrects render size with actual size, not every card supports non-power-of-two dimensions */ DRW_opengl_context_enable(); /* Off-screen creation needs to be done in DRW context. */ - ofs = GPU_offscreen_create(sizex, sizey, true, true, err_out); + ofs = GPU_offscreen_create(sizex, sizey, true, GPU_RGBA16F, err_out); DRW_opengl_context_disable(); if (!ofs) { diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index dca464bbf22..ab50e327de3 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -451,7 +451,7 @@ static void screen_preview_draw(const bScreen *screen, int size_x, int size_y) void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uint *r_rect) { char err_out[256] = "unknown"; - GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, true, false, err_out); + GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, true, GPU_RGBA8, err_out); GPU_offscreen_bind(offscreen, true); GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index d87c14b9844..ec99affe43b 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1876,7 +1876,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph, if (own_ofs) { /* bind */ - ofs = GPU_offscreen_create(sizex, sizey, true, false, err_out); + ofs = GPU_offscreen_create(sizex, sizey, true, GPU_RGBA8, err_out); if (ofs == NULL) { DRW_opengl_context_disable(); return NULL; diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index 6482d9a9d3e..bf0ab3dc533 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -223,7 +223,7 @@ uint GPU_framebuffer_stack_level_get(void); */ GPUOffScreen *GPU_offscreen_create( - int width, int height, bool depth, bool high_bitdepth, char err_out[256]); + int width, int height, bool depth, eGPUTextureFormat format, char err_out[256]); void GPU_offscreen_free(GPUOffScreen *ofs); void GPU_offscreen_bind(GPUOffScreen *ofs, bool save); void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore); diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index a6f7d43e563..9099a6e4245 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -591,7 +591,7 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs) } GPUOffScreen *GPU_offscreen_create( - int width, int height, bool depth, bool high_bitdepth, char err_out[256]) + int width, int height, bool depth, eGPUTextureFormat format, char err_out[256]) { GPUOffScreen *ofs = (GPUOffScreen *)MEM_callocN(sizeof(GPUOffScreen), __func__); @@ -600,8 +600,7 @@ GPUOffScreen *GPU_offscreen_create( height = max_ii(1, height); width = max_ii(1, width); - ofs->color = GPU_texture_create_2d( - "ofs_color", width, height, 1, (high_bitdepth) ? GPU_RGBA16F : GPU_RGBA8, nullptr); + ofs->color = GPU_texture_create_2d("ofs_color", width, height, 1, format, nullptr); if (depth) { ofs->depth = GPU_texture_create_2d( diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index 32053df5e97..6f23c2213e2 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -227,7 +227,7 @@ static PyObject *pygpu_offscreen__tp_new(PyTypeObject *UNUSED(self), } if (GPU_context_active_get()) { - ofs = GPU_offscreen_create(width, height, true, false, err_out); + ofs = GPU_offscreen_create(width, height, true, GPU_RGBA8, err_out); } else { STRNCPY(err_out, "No active GPU context found"); diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index f01e28f8822..dcb918747f3 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -455,7 +455,7 @@ static void wm_draw_region_buffer_create(ARegion *region, bool stereo, bool use_ * depth or multisample buffers. 3D view creates own buffers with * the data it needs. */ GPUOffScreen *offscreen = GPU_offscreen_create( - region->winx, region->winy, false, false, NULL); + region->winx, region->winy, false, GPU_RGBA8, NULL); if (!offscreen) { WM_report(RPT_ERROR, "Region could not be drawn!"); return; @@ -888,7 +888,7 @@ static void wm_draw_window(bContext *C, wmWindow *win) * stereo methods, but it's less efficient than drawing directly. */ const int width = WM_window_pixels_x(win); const int height = WM_window_pixels_y(win); - GPUOffScreen *offscreen = GPU_offscreen_create(width, height, false, false, NULL); + GPUOffScreen *offscreen = GPU_offscreen_create(width, height, false, GPU_RGBA8, NULL); if (offscreen) { GPUTexture *texture = GPU_offscreen_color_texture(offscreen); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index dd9cac2bb16..ba30b0dd864 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -657,9 +657,6 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, GPUViewport *viewport = vp->viewport; const bool size_changed = offscreen && (GPU_offscreen_width(offscreen) != draw_view->width) && (GPU_offscreen_height(offscreen) != draw_view->height); - char err_out[256] = "unknown"; - bool failure = false; - if (offscreen) { BLI_assert(viewport); @@ -670,8 +667,29 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, GPU_offscreen_free(offscreen); } + char err_out[256] = "unknown"; + bool failure = false; + eGPUTextureFormat format = + GPU_R8; /* Initialize with some unsupported format to check following switch statement. */ + + switch (draw_view->swapchain_format) { + case GHOST_kXrSwapchainFormatRGBA8: + format = GPU_RGBA8; + break; + case GHOST_kXrSwapchainFormatRGBA16: + format = GPU_RGBA16; + break; + case GHOST_kXrSwapchainFormatRGBA16F: + format = GPU_RGBA16F; + break; + case GHOST_kXrSwapchainFormatRGB10_A2: + format = GPU_RGB10_A2; + break; + } + BLI_assert(format != GPU_R8); + offscreen = vp->offscreen = GPU_offscreen_create( - draw_view->width, draw_view->height, true, false, err_out); + draw_view->width, draw_view->height, true, format, err_out); if (offscreen) { viewport = vp->viewport = GPU_viewport_create(); if (!viewport) { -- cgit v1.2.3 From 6aebbe6a0a2a52df3ff127b6ad28da494661b992 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 16 Aug 2021 13:44:02 +1000 Subject: Cleanup: replace macros with inline functions for font drawing Also assert blf_font_ensure_ascii_kerning has been called in blf_kerning_step_fast. --- source/blender/blenfont/intern/blf_font.c | 140 ++++++++++++++++-------------- 1 file changed, 77 insertions(+), 63 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 28039ff11ac..a0c146a974d 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -329,23 +329,29 @@ static void blf_font_ensure_ascii_kerning(FontBLF *font, * characters. */ -#define BLF_UTF8_NEXT_FAST(_font, _gc, _g, _str, _i, _c) \ - if (((_c) = (_str)[_i]) < GLYPH_ASCII_TABLE_SIZE) { \ - 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) { \ - if ((_g = blf_glyph_search(_gc, _c)) == NULL) { \ - _g = blf_glyph_add(_font, _gc, FT_Get_Char_Index((_font)->face, _c), _c); \ - } \ - } \ - else { \ - _g = NULL; \ - } \ - (void)0 +BLI_INLINE GlyphBLF *blf_utf8_next_fast( + FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t *i_p, uint *r_c) +{ + GlyphBLF *g; + if ((*r_c = str[*i_p]) < GLYPH_ASCII_TABLE_SIZE) { + g = (gc->glyph_ascii_table)[*r_c]; + if (UNLIKELY(g == NULL)) { + g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c); + gc->glyph_ascii_table[*r_c] = g; + } + (*i_p)++; + } + else if ((*r_c = BLI_str_utf8_as_unicode_step(str, i_p)) != BLI_UTF8_ERR) { + g = blf_glyph_search(gc, *r_c); + if (UNLIKELY(g == NULL)) { + g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c); + } + } + else { + g = NULL; + } + return g; +} #define BLF_KERNING_VARS(_font, _has_kerning, _kern_mode) \ const bool _has_kerning = FT_HAS_KERNING((_font)->face); \ @@ -353,33 +359,40 @@ static void blf_font_ensure_ascii_kerning(FontBLF *font, FT_KERNING_UNFITTED : \ FT_KERNING_DEFAULT; -/* NOTE: `blf_font_ensure_ascii_kerning(font, gc, kern_mode);` must be called before this macro. */ - -#define BLF_KERNING_STEP_FAST(_font, _kern_mode, _g_prev, _g, _c_prev, _c, _pen_x) \ - { \ - if (_g_prev) { \ - FT_Vector _delta; \ - 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)) == \ - 0) { \ - _pen_x += (int)_delta.x >> 6; \ - } \ - } \ - } \ - (void)0 - -#define BLF_KERNING_STEP(_font, _kern_mode, _g_prev, _g, _delta, _pen_x) \ - { \ - if (_g_prev) { \ - _delta.x = _delta.y = 0; \ - if (FT_Get_Kerning((_font)->face, (_g_prev)->idx, (_g)->idx, _kern_mode, &(_delta)) == 0) { \ - _pen_x += (int)_delta.x >> 6; \ - } \ - } \ - } \ - (void)0 +BLI_INLINE void blf_kerning_step_fast(FontBLF *font, + const FT_UInt kern_mode, + const GlyphBLF *g_prev, + const GlyphBLF *g, + const uint c_prev, + const uint c, + int *pen_x_p) +{ + /* `blf_font_ensure_ascii_kerning(font, gc, kern_mode);` must be called before this function. */ + BLI_assert((font->kerning_mode == kern_mode) && (font->kerning_cache != NULL)); + + if (g_prev != NULL) { + if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { + *pen_x_p += font->kerning_cache->table[c][c_prev]; + } + else { + FT_Vector delta; + if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, kern_mode, &delta) == 0) { + *pen_x_p += (int)delta.x >> 6; + } + } + } +} + +BLI_INLINE void blf_kerning_step( + FontBLF *font, const FT_UInt kern_mode, const GlyphBLF *g_prev, const GlyphBLF *g, int *pen_x) +{ + if (g_prev != NULL) { + FT_Vector delta; + if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, kern_mode, &delta) == 0) { + *pen_x += (int)delta.x >> 6; + } + } +} static void blf_font_draw_ex(FontBLF *font, GlyphCacheBLF *gc, @@ -405,7 +418,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); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -414,7 +427,7 @@ static void blf_font_draw_ex(FontBLF *font, continue; } if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); + blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, &pen_x); } /* do not return this loop if clipped, we want every character tested */ @@ -457,14 +470,16 @@ static void blf_font_draw_ascii_ex( while ((c = *(str++)) && len--) { BLI_assert(c < GLYPH_ASCII_TABLE_SIZE); - if ((g = gc->glyph_ascii_table[c]) == NULL) { + g = gc->glyph_ascii_table[c]; + if (UNLIKELY(g == NULL)) { g = blf_glyph_add(font, gc, FT_Get_Char_Index((font)->face, c), c); - if ((gc->glyph_ascii_table[c] = g) == NULL) { + gc->glyph_ascii_table[c] = g; + if (UNLIKELY(g == NULL)) { continue; } } if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); + blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, &pen_x); } /* do not return this loop if clipped, we want every character tested */ @@ -504,7 +519,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) blf_batch_draw_begin(font); while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -559,7 +574,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); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -568,7 +583,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, continue; } if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); + blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, &pen_x); } chx = pen_x + ((int)g->pos[0]); @@ -699,7 +714,7 @@ static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, return false; /* continue the calling loop. */ } if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, *pen_x); + blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, pen_x); } *pen_x += g->advance_i; @@ -726,7 +741,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); + g = blf_utf8_next_fast(font, gc, 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)) { @@ -767,7 +782,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); + g = blf_utf8_next_fast(font, gc, 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); @@ -775,7 +790,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); + g_prev = blf_utf8_next_fast(font, gc, str, &i_tmp, &c_prev); BLI_assert(i_tmp == i); } @@ -817,7 +832,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); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -826,7 +841,7 @@ static void blf_font_boundbox_ex(FontBLF *font, continue; } if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); + blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, &pen_x); } gbox.xmin = (float)pen_x; @@ -900,7 +915,6 @@ static void blf_font_wrap_apply(FontBLF *font, { unsigned int c; GlyphBLF *g, *g_prev = NULL; - FT_Vector delta; int pen_x = 0, pen_y = 0; size_t i = 0; int lines = 0; @@ -922,7 +936,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); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -931,7 +945,7 @@ static void blf_font_wrap_apply(FontBLF *font, continue; } if (has_kerning) { - BLF_KERNING_STEP(font, kern_mode, g_prev, g, delta, pen_x); + blf_kerning_step(font, kern_mode, g_prev, g, &pen_x); } /** @@ -1168,7 +1182,7 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, while ((i < len) && str[i]) { i_curr = i; - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -1177,7 +1191,7 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, continue; } if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); + blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, &pen_x); } gbox.xmin = pen_x; -- cgit v1.2.3 From 87adcbc94fc78d2c227aff992d045b287fcf2337 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 16 Aug 2021 13:57:54 +1000 Subject: BLF: use fast ASCII kerning for word-wrap calculations While this wasn't a bottleneck, using the fast version of this function removes some duplicate code that doesn't use the look-up table. --- source/blender/blenfont/intern/blf_font.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index a0c146a974d..512e2babf74 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -383,17 +383,6 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, } } -BLI_INLINE void blf_kerning_step( - FontBLF *font, const FT_UInt kern_mode, const GlyphBLF *g_prev, const GlyphBLF *g, int *pen_x) -{ - if (g_prev != NULL) { - FT_Vector delta; - if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, kern_mode, &delta) == 0) { - *pen_x += (int)delta.x >> 6; - } - } -} - static void blf_font_draw_ex(FontBLF *font, GlyphCacheBLF *gc, const char *str, @@ -913,7 +902,7 @@ static void blf_font_wrap_apply(FontBLF *font, void *userdata), void *userdata) { - unsigned int c; + unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0, pen_y = 0; size_t i = 0; @@ -924,6 +913,8 @@ static void blf_font_wrap_apply(FontBLF *font, BLF_KERNING_VARS(font, has_kerning, kern_mode); + blf_font_ensure_ascii_kerning(font, gc, kern_mode); + struct WordWrapVars { int wrap_width; size_t start, last[2]; @@ -945,7 +936,7 @@ static void blf_font_wrap_apply(FontBLF *font, continue; } if (has_kerning) { - blf_kerning_step(font, kern_mode, g_prev, g, &pen_x); + blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, &pen_x); } /** @@ -986,12 +977,14 @@ static void blf_font_wrap_apply(FontBLF *font, pen_x = 0; pen_y -= gc->glyph_height_max; g_prev = NULL; + c_prev = BLI_UTF8_ERR; lines += 1; continue; } pen_x = pen_x_next; g_prev = g; + c_prev = c; } // printf("done! lines: %d, width, %d\n", lines, pen_x_next); -- cgit v1.2.3 From 4300050e20a7fa5c06a990c1f7df3792b7fed656 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 16 Aug 2021 14:13:53 +1000 Subject: Cleanup: rename kerning table to ascii_table It wasn't obvious this was only for ASCII characters. --- source/blender/blenfont/intern/blf_font.c | 2 +- source/blender/blenfont/intern/blf_glyph.c | 4 ++-- source/blender/blenfont/intern/blf_internal_types.h | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 512e2babf74..d9396bd0f90 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -372,7 +372,7 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, if (g_prev != NULL) { if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - *pen_x_p += font->kerning_cache->table[c][c_prev]; + *pen_x_p += font->kerning_cache->ascii_table[c][c_prev]; } else { FT_Vector delta; diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 35938a7d5c3..a2571860c94 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -95,10 +95,10 @@ KerningCacheBLF *blf_kerning_cache_new(FontBLF *font, GlyphCacheBLF *gc) .y = 0, }; if (g && g_prev && FT_Get_Kerning(font->face, g_prev->idx, g->idx, kc->mode, &delta) == 0) { - kc->table[i][j] = (int)delta.x >> 6; + kc->ascii_table[i][j] = (int)delta.x >> 6; } else { - kc->table[i][j] = 0; + kc->ascii_table[i][j] = 0; } } } diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 6bcacc62a30..ece9a5ffae4 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -55,9 +55,11 @@ typedef struct KerningCacheBLF { /* kerning mode. */ FT_UInt mode; - /* only cache a ascii glyph pairs. Only store the x - * offset we are interested in, instead of the full FT_Vector. */ - int table[KERNING_CACHE_TABLE_SIZE][KERNING_CACHE_TABLE_SIZE]; + /** + * Cache a ascii glyph pairs. Only store the x offset we are interested in, + * instead of the full #FT_Vector since it's not used for drawing at the moment. + */ + int ascii_table[KERNING_CACHE_TABLE_SIZE][KERNING_CACHE_TABLE_SIZE]; } KerningCacheBLF; typedef struct GlyphCacheBLF { -- cgit v1.2.3 From c0016a8581f3124d34dc8cf0a2a5c3374e72356a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 16 Aug 2021 14:28:10 +1000 Subject: BLF: avoid unnecessary lookups in blf_kerning_cache_new blf_kerning_cache_new was performing many unnecessary hash lookups, calling blf_glyph_search 32768 times. Use a lookup table to reduce this to the number of ASCII characters (128 calls). --- source/blender/blenfont/intern/blf_glyph.c | 44 ++++++++++++++++-------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index a2571860c94..c5abc5982e8 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -72,34 +72,36 @@ KerningCacheBLF *blf_kerning_cache_find(FontBLF *font) /* Create a new glyph cache for the current kerning mode. */ KerningCacheBLF *blf_kerning_cache_new(FontBLF *font, GlyphCacheBLF *gc) { - KerningCacheBLF *kc; - - kc = (KerningCacheBLF *)MEM_callocN(sizeof(KerningCacheBLF), "blf_kerning_cache_new"); + KerningCacheBLF *kc = MEM_mallocN(sizeof(KerningCacheBLF), __func__); kc->next = NULL; kc->prev = NULL; kc->mode = font->kerning_mode; - unsigned int i, 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); - g = blf_glyph_add(font, gc, glyph_index, i); + GlyphBLF *g_table[KERNING_CACHE_TABLE_SIZE]; + for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { + GlyphBLF *g = blf_glyph_search(gc, i); + if (UNLIKELY(g == NULL)) { + FT_UInt glyph_index = FT_Get_Char_Index(font->face, i); + g = blf_glyph_add(font, gc, glyph_index, i); + } + g_table[i] = g; + } + + memset(kc->ascii_table, 0, sizeof(kc->ascii_table)); + for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { + GlyphBLF *g = g_table[i]; + if (g == NULL) { + continue; + } + for (uint j = 0; j < KERNING_CACHE_TABLE_SIZE; j++) { + GlyphBLF *g_prev = g_table[j]; + if (g_prev == NULL) { + continue; } - /* Can fail on certain fonts */ - GlyphBLF *g_prev = blf_glyph_search(gc, j); - - FT_Vector delta = { - .x = 0, - .y = 0, - }; - if (g && g_prev && FT_Get_Kerning(font->face, g_prev->idx, g->idx, kc->mode, &delta) == 0) { + FT_Vector delta; + if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, kc->mode, &delta) == 0) { kc->ascii_table[i][j] = (int)delta.x >> 6; } - else { - kc->ascii_table[i][j] = 0; - } } } -- cgit v1.2.3 From c48a01a88add5247f08de6f8af8d223ebc4e28c9 Mon Sep 17 00:00:00 2001 From: Himanshi Kalra Date: Mon, 16 Aug 2021 12:17:30 +0530 Subject: Add cutom data color property for mesh comparison Add color data type comparison for meshes, adding it as part of comparing meshes with geometry nodes applied. Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D12192 --- source/blender/blenkernel/intern/mesh.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index b32d58de1e2..b1292a1c94d 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -638,6 +638,19 @@ static int customdata_compare( } break; } + case CD_PROP_COLOR: { + const MPropCol *l1_data = l1->data; + const MPropCol *l2_data = l2->data; + + for (int i = 0; i < total_length; i++) { + for (int j = 0; j < 4; j++) { + if (fabsf(l1_data[i].color[j] - l2_data[i].color[j]) > thresh) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } + } + } + break; + } default: { break; } -- cgit v1.2.3 From ddecd7aaca880586c2762c5a85d90ee8b44cd0a1 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 16 Aug 2021 18:06:10 +1000 Subject: Cleanup: shadow variable warning --- source/blender/blenkernel/intern/mesh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index b1292a1c94d..e99670fc488 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -643,7 +643,7 @@ static int customdata_compare( const MPropCol *l2_data = l2->data; for (int i = 0; i < total_length; i++) { - for (int j = 0; j < 4; j++) { + for (j = 0; j < 4; j++) { if (fabsf(l1_data[i].color[j] - l2_data[i].color[j]) > thresh) { return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; } -- cgit v1.2.3 From fecec1644ce54ea386eaeed5ca6748d4a7b2737b Mon Sep 17 00:00:00 2001 From: Eitan Date: Mon, 16 Aug 2021 14:25:10 +0530 Subject: Geometry Nodes: Add UV Smooth, Boundary Smooth options to subdivision node Replaces the boolean option with enum menus for consistency with the subdivision modifier (rB66151b5de3ff,rB3d3b6d94e6e). Adds all UV interpolation options. Original patch by Eitan. Updated by Himanshi Kalra . {F9883204} Reviewed By: HooglyBoogly Differential Revision: https://developer.blender.org/D10417 --- source/blender/makesdna/DNA_node_types.h | 7 ++ source/blender/makesrna/RNA_enum_types.h | 2 + source/blender/makesrna/intern/rna_modifier.c | 85 ++++++++++------------ source/blender/makesrna/intern/rna_nodetree.c | 20 +++++ source/blender/nodes/NOD_static_types.h | 2 +- .../geometry/nodes/node_geo_subdivision_surface.cc | 43 +++++++++-- 6 files changed, 106 insertions(+), 53 deletions(-) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index ad7722d3ed0..fd794ed1b21 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1328,6 +1328,13 @@ typedef struct NodeAttributeConvert { int8_t domain; } NodeAttributeConvert; +typedef struct NodeGeometrySubdivisionSurface { + /* eSubsurfUVSmooth. */ + uint8_t uv_smooth; + /* eSubsurfBoundarySmooth. */ + uint8_t boundary_smooth; +} NodeGeometrySubdivisionSurface; + typedef struct NodeGeometryMeshCircle { /* GeometryNodeMeshCircleFillType. */ uint8_t fill_type; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index d544083a749..7e3f279b251 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -245,6 +245,8 @@ extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bo extern const EnumPropertyItem rna_enum_collection_color_items[]; +extern const EnumPropertyItem rna_enum_subdivision_uv_smooth_items[]; +extern const EnumPropertyItem rna_enum_subdivision_boundary_smooth_items[]; /** * For ID filters (#FILTER_ID_AC, #FILTER_ID_AR, ...) an int isn't enough. This version allows 64 * bit integers. So can't use the regular #EnumPropertyItem. Would be nice if RNA supported this diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index b424a575094..486d8d13564 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -595,6 +595,44 @@ const EnumPropertyItem rna_enum_axis_flag_xyz_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_subdivision_uv_smooth_items[] = { + {SUBSURF_UV_SMOOTH_NONE, "NONE", 0, "None", "UVs are not smoothed, boundaries are kept sharp"}, + {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS, + "PRESERVE_CORNERS", + 0, + "Keep Corners", + "UVs are smoothed, corners on discontinuous boundary are kept sharp"}, + {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_AND_JUNCTIONS, + "PRESERVE_CORNERS_AND_JUNCTIONS", + 0, + "Keep Corners, Junctions", + "UVs are smoothed, corners on discontinuous boundary and " + "junctions of 3 or more regions are kept sharp"}, + {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE, + "PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE", + 0, + "Keep Corners, Junctions, Concave", + "UVs are smoothed, corners on discontinuous boundary, " + "junctions of 3 or more regions and darts and concave corners are kept sharp"}, + {SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES, + "PRESERVE_BOUNDARIES", + 0, + "Keep Boundaries", + "UVs are smoothed, boundaries are kept sharp"}, + {SUBSURF_UV_SMOOTH_ALL, "SMOOTH_ALL", 0, "All", "UVs and boundaries are smoothed"}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_subdivision_boundary_smooth_items[] = { + {SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS, + "PRESERVE_CORNERS", + 0, + "Keep Corners", + "Smooth boundaries, but corners are kept sharp"}, + {SUBSURF_BOUNDARY_SMOOTH_ALL, "ALL", 0, "All", "Smooth boundaries, including corners"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include "DNA_curve_types.h" # include "DNA_fluid_types.h" @@ -1631,55 +1669,12 @@ static IDProperty **rna_NodesModifier_properties(PointerRNA *ptr) static void rna_def_property_subdivision_common(StructRNA *srna) { - static const EnumPropertyItem prop_uv_smooth_items[] = { - {SUBSURF_UV_SMOOTH_NONE, - "NONE", - 0, - "None", - "UVs are not smoothed, boundaries are kept sharp"}, - {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS, - "PRESERVE_CORNERS", - 0, - "Keep Corners", - "UVs are smoothed, corners on discontinuous boundary are kept sharp"}, - {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_AND_JUNCTIONS, - "PRESERVE_CORNERS_AND_JUNCTIONS", - 0, - "Keep Corners, Junctions", - "UVs are smoothed, corners on discontinuous boundary and " - "junctions of 3 or more regions are kept sharp"}, - {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE, - "PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE", - 0, - "Keep Corners, Junctions, Concave", - "UVs are smoothed, corners on discontinuous boundary, " - "junctions of 3 or more regions and darts and concave corners are kept sharp"}, - {SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES, - "PRESERVE_BOUNDARIES", - 0, - "Keep Boundaries", - "UVs are smoothed, boundaries are kept sharp"}, - {SUBSURF_UV_SMOOTH_ALL, "SMOOTH_ALL", 0, "All", "UVs and boundaries are smoothed"}, - {0, NULL, 0, NULL, NULL}, - }; - - static const EnumPropertyItem prop_boundary_smooth_items[] = { - {SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS, - "PRESERVE_CORNERS", - 0, - "Keep Corners", - "Smooth boundaries, but corners are kept sharp"}, - {SUBSURF_BOUNDARY_SMOOTH_ALL, "ALL", 0, "All", "Smooth boundaries, including corners"}, - {0, NULL, 0, NULL, NULL}, - }; - PropertyRNA *prop; - RNA_define_lib_overridable(true); prop = RNA_def_property(srna, "uv_smooth", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "uv_smooth"); - RNA_def_property_enum_items(prop, prop_uv_smooth_items); + RNA_def_property_enum_items(prop, rna_enum_subdivision_uv_smooth_items); RNA_def_property_ui_text(prop, "UV Smooth", "Controls how smoothing is applied to UVs"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); @@ -1693,7 +1688,7 @@ static void rna_def_property_subdivision_common(StructRNA *srna) prop = RNA_def_property(srna, "boundary_smooth", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "boundary_smooth"); - RNA_def_property_enum_items(prop, prop_boundary_smooth_items); + RNA_def_property_enum_items(prop, rna_enum_subdivision_boundary_smooth_items); RNA_def_property_ui_text(prop, "Boundary Smooth", "Controls how open boundaries are smoothed"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b360d3b6672..7d5fffcc6c9 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9095,6 +9095,26 @@ static void def_geo_triangulate(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_subdivision_surface(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometrySubdivisionSurface", "storage"); + prop = RNA_def_property(srna, "uv_smooth", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "uv_smooth"); + RNA_def_property_enum_items(prop, rna_enum_subdivision_uv_smooth_items); + RNA_def_property_enum_default(prop, SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES); + RNA_def_property_ui_text(prop, "UV Smooth", "Controls how smoothing is applied to UVs"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "boundary_smooth", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "boundary_smooth"); + RNA_def_property_enum_items(prop, rna_enum_subdivision_boundary_smooth_items); + RNA_def_property_enum_default(prop, SUBSURF_BOUNDARY_SMOOTH_ALL); + RNA_def_property_ui_text(prop, "Boundary Smooth", "Controls how open boundaries are smoothed"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_attribute_randomize(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 3852819746e..4da8648173d 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -337,7 +337,7 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POIN DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") -DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") +DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index a2c10af9c4d..7882cd04845 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -20,15 +20,13 @@ #include "UI_interface.h" #include "UI_resources.h" - +#include "DNA_modifier_types.h" #include "node_geometry_util.hh" static bNodeSocketTemplate geo_node_subdivision_surface_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, {SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6}, {SOCK_BOOLEAN, N_("Use Creases")}, - {SOCK_BOOLEAN, N_("Boundary Smooth"), true}, - {SOCK_BOOLEAN, N_("Smooth UVs")}, {-1, ""}, }; @@ -37,6 +35,29 @@ static bNodeSocketTemplate geo_node_subdivision_surface_out[] = { {-1, ""}, }; +static void geo_node_subdivision_surface_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ +#ifndef WITH_OPENSUBDIV + uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR); +#else + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "uv_smooth", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "boundary_smooth", 0, nullptr, ICON_NONE); +#endif +} + +static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometrySubdivisionSurface *data = (NodeGeometrySubdivisionSurface *)MEM_callocN( + sizeof(NodeGeometrySubdivisionSurface), __func__); + data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; + data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; + node->storage = data; +} + namespace blender::nodes { static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) { @@ -53,6 +74,9 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenSubdiv")); #else + const NodeGeometrySubdivisionSurface &storage = *(const NodeGeometrySubdivisionSurface *)params.node().storage; + const int uv_smooth = storage.uv_smooth; + const int boundary_smooth = storage.boundary_smooth; const int subdiv_level = clamp_i(params.extract_input("Level"), 0, 30); /* Only process subdivision if level is greater than 0. */ @@ -62,8 +86,6 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) } const bool use_crease = params.extract_input("Use Creases"); - const bool boundary_smooth = params.extract_input("Boundary Smooth"); - const bool smooth_uvs = params.extract_input("Smooth UVs"); const Mesh *mesh_in = geometry_set.get_mesh_for_read(); /* Initialize mesh settings. */ @@ -79,9 +101,9 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) subdiv_settings.level = subdiv_level; subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf( - !boundary_smooth); + boundary_smooth); subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( - smooth_uvs); + uv_smooth); /* Apply subdivision to mesh. */ Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); @@ -117,5 +139,12 @@ void register_node_type_geo_subdivision_surface() node_type_socket_templates( &ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out); ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec; + ntype.draw_buttons = geo_node_subdivision_surface_layout; + node_type_init(&ntype, geo_node_subdivision_surface_init); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_storage( + &ntype, "NodeGeometrySubdivisionSurface", node_free_standard_storage, node_copy_standard_storage); + node_type_socket_templates( + &ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out); nodeRegisterType(&ntype); } -- cgit v1.2.3 From 08af3e6e926bf1d448bfdae8ffa2593d0850e016 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Mon, 5 Jul 2021 14:16:02 +0200 Subject: VSE: Flush audio encode after finishing video export We didn't flush audio after encoding finished which lead to audio packets being lost. In addition to this the audio timestamps were wrong because we incremented the current audio time before using it. Reviewed By: Richard Antalik Differential Revision: http://developer.blender.org/D11916 --- source/blender/blenkernel/intern/writeffmpeg.c | 37 +++++++++++++------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 32057709c38..9f3f50febe8 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -149,7 +149,6 @@ static int write_audio_frame(FFMpegContext *context) AUD_Device_read( context->audio_mixdown_device, context->audio_input_buffer, context->audio_input_samples); - context->audio_time += (double)context->audio_input_samples / (double)c->sample_rate; frame = av_frame_alloc(); frame->pts = context->audio_time / av_q2d(c->time_base); @@ -184,7 +183,7 @@ static int write_audio_frame(FFMpegContext *context) context->audio_input_samples * c->channels * context->audio_sample_size, 1); - int success = 0; + int success = 1; int ret = avcodec_send_frame(c, frame); if (ret < 0) { @@ -369,7 +368,7 @@ static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, R return success; } -/* read and encode a frame of audio from the buffer */ +/* read and encode a frame of video from the buffer */ static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixels) { AVCodecParameters *codec = context->video_stream->codecpar; @@ -1226,9 +1225,8 @@ fail: * parameter. *

*/ -static void flush_ffmpeg(FFMpegContext *context) +static void flush_ffmpeg(AVCodecContext *c, AVStream *stream, AVFormatContext *outfile) { - AVCodecContext *c = context->video_codec; AVPacket *packet = av_packet_alloc(); avcodec_send_frame(c, NULL); @@ -1247,13 +1245,13 @@ static void flush_ffmpeg(FFMpegContext *context) break; } - packet->stream_index = context->video_stream->index; - av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); + packet->stream_index = stream->index; + av_packet_rescale_ts(packet, c->time_base, stream->time_base); # ifdef FFMPEG_USE_DURATION_WORKAROUND - my_guess_pkt_duration(context->outfile, context->video_stream, packet); + my_guess_pkt_duration(context->outfile, stream, packet); # endif - int write_ret = av_interleaved_write_frame(context->outfile, packet); + int write_ret = av_interleaved_write_frame(outfile, packet); if (write_ret != 0) { fprintf(stderr, "Error writing delayed frame: %s\n", av_err2str(write_ret)); break; @@ -1396,12 +1394,13 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit); # ifdef WITH_AUDASPACE static void write_audio_frames(FFMpegContext *context, double to_pts) { - int finished = 0; + AVCodecContext *c = context->audio_codec; - while (context->audio_stream && !finished) { - if ((context->audio_time >= to_pts) || (write_audio_frame(context))) { - finished = 1; + while (context->audio_stream) { + if ((context->audio_time >= to_pts) || !write_audio_frame(context)) { + break; } + context->audio_time += (double)context->audio_input_samples / (double)c->sample_rate; } } # endif @@ -1422,9 +1421,6 @@ int BKE_ffmpeg_append(void *context_v, PRINT("Writing frame %i, render width=%d, render height=%d\n", frame, rectx, recty); - /* why is this done before writing the video frame and again at end_ffmpeg? */ - // write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base)); - if (context->video_stream) { avframe = generate_video_frame(context, (unsigned char *)pixels); success = (avframe && write_video_frame(context, frame - start_frame, avframe, reports)); @@ -1461,8 +1457,13 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit) # endif if (context->video_stream) { - PRINT("Flushing delayed frames...\n"); - flush_ffmpeg(context); + PRINT("Flushing delayed video frames...\n"); + flush_ffmpeg(context->video_codec, context->video_stream, context->outfile); + } + + if (context->audio_stream) { + PRINT("Flushing delayed audio frames...\n"); + flush_ffmpeg(context->audio_codec, context->audio_stream, context->outfile); } if (context->outfile) { -- cgit v1.2.3 From e314260fa778b5d0874cadab1f70f91bddd18434 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Fri, 9 Jul 2021 15:06:06 +0200 Subject: VSE: Fix "off by one" error when encoding audio Before we didn't encode the audio up until the current frame. This lead to us not encoding the last video frame of audio. Reviewed By: Richard Antalik Differential Revision: http://developer.blender.org/D11918 --- source/blender/blenkernel/intern/writeffmpeg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 9f3f50febe8..323da7473b5 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -1435,8 +1435,9 @@ int BKE_ffmpeg_append(void *context_v, } # ifdef WITH_AUDASPACE - write_audio_frames(context, - (frame - start_frame) / (((double)rd->frs_sec) / (double)rd->frs_sec_base)); + /* Add +1 frame because we want to encode audio up until the next video frame. */ + write_audio_frames( + context, (frame - start_frame + 1) / (((double)rd->frs_sec) / (double)rd->frs_sec_base)); # endif return success; } -- cgit v1.2.3 From 43ad345caa0ac156f585eab55e335dfca2465ce4 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Mon, 12 Jul 2021 15:38:25 +0200 Subject: VSE: Fix memory leak when adding bad image/movie strips If the add strip operator errored out, we wouldn't free custom data allocated Reviewed By: Richard Antalik Differential Revision: http://developer.blender.org/D11919 --- source/blender/editors/space_sequencer/sequencer_add.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 265a52ed1a6..47495eaa57a 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -707,13 +707,13 @@ static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op) } else { if (!sequencer_add_movie_single_strip(C, op, &load_data)) { + sequencer_add_cancel(C, op); return OPERATOR_CANCELLED; } } - if (op->customdata) { - MEM_freeN(op->customdata); - } + /* Free custom data. */ + sequencer_add_cancel(C, op); DEG_relations_tag_update(bmain); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -1040,6 +1040,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) load_data.image.len = sequencer_add_image_strip_calculate_length( op, load_data.start_frame, &minframe, &numdigits); if (load_data.image.len == 0) { + sequencer_add_cancel(C, op); return OPERATOR_CANCELLED; } @@ -1062,9 +1063,8 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - if (op->customdata) { - MEM_freeN(op->customdata); - } + /* Free custom data. */ + sequencer_add_cancel(C, op); return OPERATOR_FINISHED; } -- cgit v1.2.3 From a01cf90fd8ad978d8b6c7fd2ece64872c940f3a1 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Mon, 12 Jul 2021 16:00:43 +0200 Subject: VSE: Fix video strip duration calculation The video duration was not read correctly from the video file. It would use the global duration of the file which does in some cases not line up with the actual duration of the video stream. Now we take the video stream duration and start time into account when calculating the strip duration. Reviewed By: Richard Antalik Differential Revision: http://developer.blender.org/D11920 --- source/blender/imbuf/intern/anim_movie.c | 46 +++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 47514308ae4..c08df2889de 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -616,10 +616,50 @@ static int startffmpeg(struct anim *anim) } } } - /* Fall back to the container. */ + /* Fall back to manually estimating the video stream duration. + * This is because the video stream duration can be shorter than the pFormatCtx->duration. + */ if (anim->duration_in_frames == 0) { - anim->duration_in_frames = (int)(pFormatCtx->duration * av_q2d(frame_rate) / AV_TIME_BASE + - 0.5f); + double pts_time_base = av_q2d(video_stream->time_base); + double stream_dur; + + if (video_stream->duration != AV_NOPTS_VALUE) { + stream_dur = video_stream->duration * pts_time_base; + } + else { + double video_start = 0; + double audio_start = 0; + + if (video_stream->start_time != AV_NOPTS_VALUE) { + video_start = video_stream->start_time * pts_time_base; + } + + /* Find audio stream to guess the duration of the video. + * Sometimes the audio AND the video stream have a start offset. + * The difference between these is the offset we want to use to + * calculate the video duration. + */ + for (i = 0; i < pFormatCtx->nb_streams; i++) { + if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + AVStream *audio_stream = pFormatCtx->streams[i]; + if (audio_stream->start_time != AV_NOPTS_VALUE) { + audio_start = audio_stream->start_time * av_q2d(audio_stream->time_base); + } + break; + } + } + + if (video_start > audio_start) { + stream_dur = (double)pFormatCtx->duration / AV_TIME_BASE - (video_start - audio_start); + } + else { + /* The video stream starts before or at the same time as the audio stream! + * We have to assume that the video stream is as long as the full pFormatCtx->duration. + */ + stream_dur = (double)pFormatCtx->duration / AV_TIME_BASE; + } + } + anim->duration_in_frames = (int)(stream_dur * av_q2d(frame_rate) + 0.5f); } frs_num = frame_rate.num; -- cgit v1.2.3 From 6df81ddb84c60876ac3ebd87d1d134109f34eabd Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Mon, 12 Jul 2021 19:13:15 +0200 Subject: VSE: Fix seeking issues. The seek pts was not correctly calculated. In addition to that we were not seeking in the video pts time base. Reviewed By: Richard Antalik Differential Revision: http://developer.blender.org/D11921 --- source/blender/imbuf/intern/anim_movie.c | 87 +++++++++++++++----------------- source/blender/imbuf/intern/indexer.c | 2 +- 2 files changed, 42 insertions(+), 47 deletions(-) diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index c08df2889de..fd96110b59e 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -1059,33 +1059,21 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx) return false; } -static int64_t ffmpeg_get_seek_pos(struct anim *anim, int position) +static int64_t ffmpeg_get_seek_pts(struct anim *anim, int64_t pts_to_search) { AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); - int64_t st_time = anim->pFormatCtx->start_time; - int64_t pos = (int64_t)(position)*AV_TIME_BASE; - /* Step back half a time base position to make sure that we get the requested - * frame and not the one after it. + AVRational frame_rate = v_st->r_frame_rate; + AVRational time_base = v_st->time_base; + double steps_per_frame = (double)(frame_rate.den * time_base.den) / + (double)(frame_rate.num * time_base.num); + /* Step back half a frame position to make sure that we get the requested + * frame and not the one after it. This is a workaround as ffmpeg will + * sometimes not seek to a frame after the requested pts even if + * AVSEEK_FLAG_BACKWARD is specified. */ - pos -= (AV_TIME_BASE / 2); - pos /= frame_rate; + int64_t pts = pts_to_search - (steps_per_frame / 2); - av_log(anim->pFormatCtx, - AV_LOG_DEBUG, - "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n", - pos, - (st_time != AV_NOPTS_VALUE) ? st_time : 0); - - if (pos < 0) { - pos = 0; - } - - if (st_time != AV_NOPTS_VALUE) { - pos += st_time; - } - - return pos; + return pts; } /* This gives us an estimate of which pts our requested frame will have. @@ -1102,17 +1090,18 @@ static int64_t ffmpeg_get_pts_to_search(struct anim *anim, pts_to_search = IMB_indexer_get_pts(tc_index, new_frame_index); } else { - int64_t st_time = anim->pFormatCtx->start_time; AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, NULL); + int64_t start_pts = v_st->start_time; + AVRational frame_rate = v_st->r_frame_rate; AVRational time_base = v_st->time_base; - int64_t steps_per_frame = (frame_rate.den * time_base.den) / (frame_rate.num * time_base.num); - pts_to_search = position * steps_per_frame; + double steps_per_frame = (double)(frame_rate.den * time_base.den) / + (double)(frame_rate.num * time_base.num); + + pts_to_search = round(position * steps_per_frame); - if (st_time != AV_NOPTS_VALUE && st_time != 0) { - int64_t start_frame = (double)st_time / AV_TIME_BASE * av_q2d(frame_rate); - pts_to_search += start_frame * steps_per_frame; + if (start_pts != AV_NOPTS_VALUE) { + pts_to_search += start_pts; } } return pts_to_search; @@ -1196,23 +1185,29 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea * decoded will be read. See https://trac.ffmpeg.org/ticket/1607 and * https://developer.blender.org/T86944. */ static int ffmpeg_generic_seek_workaround(struct anim *anim, - int64_t *requested_pos, + int64_t *requested_pts, int64_t pts_to_search) { AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); - int64_t current_pos = *requested_pos; + AVRational frame_rate = v_st->r_frame_rate; + AVRational time_base = v_st->time_base; + + double steps_per_frame = (double)(frame_rate.den * time_base.den) / + (double)(frame_rate.num * time_base.num); + + int64_t current_pts = *requested_pts; int64_t offset = 0; int64_t cur_pts, prev_pts = -1; /* Step backward frame by frame until we find the key frame we are looking for. */ - while (current_pos != 0) { - current_pos = *requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate); - current_pos = max_ii(current_pos, 0); + while (current_pts != 0) { + current_pts = *requested_pts - (int64_t)round(offset * steps_per_frame); + current_pts = max_ii(current_pts, 0); /* Seek to timestamp. */ - if (av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD) < 0) { + if (av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD) < + 0) { break; } @@ -1249,10 +1244,10 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, offset++; } - *requested_pos = current_pos; + *requested_pts = current_pts; /* Re-seek to timestamp that gave I-frame, so it can be read by decode function. */ - return av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD); + return av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD); } /* Seek to last necessary key frame. */ @@ -1300,13 +1295,13 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, else { /* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from. */ - pos = ffmpeg_get_seek_pos(anim, position); + pos = ffmpeg_get_seek_pts(anim, pts_to_search); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos); AVFormatContext *format_ctx = anim->pFormatCtx; if (format_ctx->iformat->read_seek2 || format_ctx->iformat->read_seek) { - ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); + ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD); } else { ret = ffmpeg_generic_seek_workaround(anim, &pos, pts_to_search); @@ -1351,7 +1346,7 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, anim->cur_key_frame_pts = gop_pts; /* Seek back so we are at the correct position after we decoded a frame. */ - av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); + av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD); } } @@ -1391,18 +1386,18 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ struct anim_index *tc_index = IMB_anim_open_index(anim, tc); int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position); AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); + double frame_rate = av_q2d(v_st->r_frame_rate); double pts_time_base = av_q2d(v_st->time_base); - int64_t st_time = anim->pFormatCtx->start_time; + int64_t start_pts = v_st->start_time; av_log(anim->pFormatCtx, AV_LOG_DEBUG, - "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, st_time=%" PRId64 + "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, start_pts=%" PRId64 ")\n", (int64_t)pts_to_search, pts_time_base, frame_rate, - st_time); + start_pts); if (ffmpeg_pts_matches_last_frame(anim, pts_to_search)) { av_log(anim->pFormatCtx, diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 27195b294d6..bbb0f3b5b22 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -1022,7 +1022,7 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, stream_size = avio_size(context->iFormatCtx->pb); - context->frame_rate = av_q2d(av_guess_frame_rate(context->iFormatCtx, context->iStream, NULL)); + context->frame_rate = av_q2d(context->iStream->r_frame_rate); context->pts_time_base = av_q2d(context->iStream->time_base); while (av_read_frame(context->iFormatCtx, next_packet) >= 0) { -- cgit v1.2.3 From ded68fb10275c9f9a66e7019171b83cab0e9485d Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Tue, 6 Jul 2021 19:48:06 +0200 Subject: VSE: Fix audaspace not reading ffmpeg files with start offset correctly The duration and start time for audio strips were not correctly read in audaspace. Some video files have a "lead in" section of audio that plays before the video starts playing back. Before this patch, we would play this lead in audio at the same time as the video started and thus the audio would not be in sync anymore. Now the lead in audio is cut off and the duration should be correctly calculated with this in mind. If the audio starts after the video, the audio strip is shifted to account for this, but it will also lead to cut off audio which might not be wanted. However we don't have a simple way to solve this at this point. Differential Revision: http://developer.blender.org/D11917 --- extern/audaspace/bindings/C/AUD_Special.cpp | 2 + extern/audaspace/bindings/C/AUD_Types.h | 1 + extern/audaspace/include/IReader.h | 6 ++ extern/audaspace/include/fx/VolumeReader.h | 2 +- extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp | 80 +++++++++++++++------- extern/audaspace/plugins/ffmpeg/FFMPEGReader.h | 17 +++++ extern/audaspace/src/fx/VolumeReader.cpp | 2 +- source/blender/blenkernel/BKE_sound.h | 9 ++- source/blender/blenkernel/intern/sound.c | 12 ++-- .../editors/space_sequencer/sequencer_add.c | 16 +++-- .../editors/space_sequencer/sequencer_draw.c | 10 +-- source/blender/imbuf/IMB_imbuf.h | 5 ++ source/blender/imbuf/intern/IMB_anim.h | 1 + source/blender/imbuf/intern/anim_movie.c | 27 ++++++-- source/blender/makesdna/DNA_sound_types.h | 1 + source/blender/makesrna/intern/rna_sequencer_api.c | 5 +- source/blender/sequencer/SEQ_add.h | 6 +- source/blender/sequencer/intern/sound.c | 7 +- source/blender/sequencer/intern/strip_add.c | 46 +++++++++++-- source/blender/sequencer/intern/strip_time.c | 4 +- 20 files changed, 197 insertions(+), 62 deletions(-) diff --git a/extern/audaspace/bindings/C/AUD_Special.cpp b/extern/audaspace/bindings/C/AUD_Special.cpp index ac876a01eb3..97e5f5540de 100644 --- a/extern/audaspace/bindings/C/AUD_Special.cpp +++ b/extern/audaspace/bindings/C/AUD_Special.cpp @@ -86,6 +86,7 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound) info.specs.channels = AUD_CHANNELS_INVALID; info.specs.rate = AUD_RATE_INVALID; info.length = 0.0f; + info.start_offset = 0.0f; try { @@ -95,6 +96,7 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound) { info.specs = convSpecToC(reader->getSpecs()); info.length = reader->getLength() / (float) info.specs.rate; + info.start_offset = reader->getStartOffset(); } } catch(Exception&) diff --git a/extern/audaspace/bindings/C/AUD_Types.h b/extern/audaspace/bindings/C/AUD_Types.h index 75e4ffae18c..c6a96d30d3f 100644 --- a/extern/audaspace/bindings/C/AUD_Types.h +++ b/extern/audaspace/bindings/C/AUD_Types.h @@ -176,4 +176,5 @@ typedef struct { AUD_Specs specs; float length; + double start_offset; } AUD_SoundInfo; diff --git a/extern/audaspace/include/IReader.h b/extern/audaspace/include/IReader.h index c29900ca579..f6070b0f23b 100644 --- a/extern/audaspace/include/IReader.h +++ b/extern/audaspace/include/IReader.h @@ -70,6 +70,12 @@ public: */ virtual int getPosition() const=0; + /** + * Returns the start offset the sound should have to line up with related sources. + * \return The required start offset in seconds. + */ + virtual double getStartOffset() const { return 0.0;} + /** * Returns the specification of the reader. * \return The Specs structure. diff --git a/extern/audaspace/include/fx/VolumeReader.h b/extern/audaspace/include/fx/VolumeReader.h index 13b6845e931..f7169f4c78b 100644 --- a/extern/audaspace/include/fx/VolumeReader.h +++ b/extern/audaspace/include/fx/VolumeReader.h @@ -67,4 +67,4 @@ public: virtual void read(int& length, bool& eos, sample_t* buffer); }; -AUD_NAMESPACE_END \ No newline at end of file +AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp index b46f65eddbf..afdc7fcfcc6 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp @@ -68,7 +68,7 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) for(int i = 0; i < m_frame->nb_samples; i++) { std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size, - m_frame->data[channel] + i * single_size, single_size); + m_frame->data[channel] + i * single_size, single_size); } } } @@ -109,7 +109,7 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) for(int i = 0; i < m_frame->nb_samples; i++) { std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size, - m_frame->data[channel] + i * single_size, single_size); + m_frame->data[channel] + i * single_size, single_size); } } } @@ -126,7 +126,10 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) void FFMPEGReader::init() { m_position = 0; + m_start_offset = 0.0f; m_pkgbuf_left = 0; + m_st_time = 0; + m_duration = 0; if(avformat_find_stream_info(m_formatCtx, nullptr) < 0) AUD_THROW(FileException, "File couldn't be read, ffmpeg couldn't find the stream info."); @@ -134,15 +137,41 @@ void FFMPEGReader::init() // find audio stream and codec m_stream = -1; + double dur_sec = 0; + for(unsigned int i = 0; i < m_formatCtx->nb_streams; i++) { #ifdef FFMPEG_OLD_CODE - if((m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) + if(m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) #else - if((m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) + if(m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) #endif - && (m_stream < 0)) { + AVStream *audio_stream = m_formatCtx->streams[i]; + double audio_timebase = av_q2d(audio_stream->time_base); + + if (audio_stream->start_time != AV_NOPTS_VALUE) + { + m_st_time = audio_stream->start_time; + } + + int64_t ctx_start_time = 0; + if (m_formatCtx->start_time != AV_NOPTS_VALUE) { + ctx_start_time = m_formatCtx->start_time; + } + + m_start_offset = m_st_time * audio_timebase - (double)ctx_start_time / AV_TIME_BASE; + + if(audio_stream->duration != AV_NOPTS_VALUE) + { + dur_sec = audio_stream->duration * audio_timebase; + } + else + { + /* If the audio starts after the stream start time, subract this from the total duration. */ + dur_sec = (double)m_formatCtx->duration / AV_TIME_BASE - m_start_offset; + } + m_stream=i; break; } @@ -213,6 +242,7 @@ void FFMPEGReader::init() } m_specs.rate = (SampleRate) m_codecCtx->sample_rate; + m_duration = lround(dur_sec * m_codecCtx->sample_rate); } FFMPEGReader::FFMPEGReader(std::string filename) : @@ -338,21 +368,17 @@ void FFMPEGReader::seek(int position) { if(position >= 0) { - uint64_t st_time = m_formatCtx->start_time; - uint64_t seek_pos = ((uint64_t)position) * ((uint64_t)AV_TIME_BASE) / ((uint64_t)m_specs.rate); + double pts_time_base = + av_q2d(m_formatCtx->streams[m_stream]->time_base); - if(st_time != AV_NOPTS_VALUE) { - seek_pos += st_time; - } + uint64_t seek_pts = (((uint64_t)position) / ((uint64_t)m_specs.rate)) / pts_time_base; - double pts_time_base = - av_q2d(m_formatCtx->streams[m_stream]->time_base); - uint64_t pts_st_time = - ((st_time != AV_NOPTS_VALUE) ? st_time : 0) - / pts_time_base / (uint64_t) AV_TIME_BASE; + if(m_st_time != AV_NOPTS_VALUE) { + seek_pts += m_st_time; + } // a value < 0 tells us that seeking failed - if(av_seek_frame(m_formatCtx, -1, seek_pos, + if(av_seek_frame(m_formatCtx, m_stream, seek_pts, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0) { avcodec_flush_buffers(m_codecCtx); @@ -374,7 +400,7 @@ void FFMPEGReader::seek(int position) if(packet.pts != AV_NOPTS_VALUE) { // calculate real position, and read to frame! - m_position = (packet.pts - pts_st_time) * pts_time_base * m_specs.rate; + m_position = (packet.pts - m_st_time) * pts_time_base * m_specs.rate; if(m_position < position) { @@ -405,8 +431,7 @@ void FFMPEGReader::seek(int position) int FFMPEGReader::getLength() const { // return approximated remaning size - return (int)((m_formatCtx->duration * m_codecCtx->sample_rate) - / AV_TIME_BASE)-m_position; + return m_duration - m_position; } int FFMPEGReader::getPosition() const @@ -414,6 +439,11 @@ int FFMPEGReader::getPosition() const return m_position; } +double FFMPEGReader::getStartOffset() const +{ + return m_start_offset; +} + Specs FFMPEGReader::getSpecs() const { return m_specs.specs; @@ -450,11 +480,13 @@ void FFMPEGReader::read(int& length, bool& eos, sample_t* buffer) // decode the package pkgbuf_pos = decode(packet, m_pkgbuf); - // copy to output buffer - data_size = std::min(pkgbuf_pos, left * sample_size); - m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format)); - buf += data_size / AUD_FORMAT_SIZE(m_specs.format); - left -= data_size / sample_size; + if (packet.pts >= m_st_time) { + // copy to output buffer + data_size = std::min(pkgbuf_pos, left * sample_size); + m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format)); + buf += data_size / AUD_FORMAT_SIZE(m_specs.format); + left -= data_size / sample_size; + } } av_packet_unref(&packet); } diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h index f62436ac83b..d613457c220 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h @@ -54,6 +54,22 @@ private: */ int m_position; + /** + * The start offset in seconds relative to the media container start time. + * IE how much the sound should be delayed to be kept in sync with the rest of the containter streams. + */ + double m_start_offset; + + /** + * The start time pts of the stream. All packets before this timestamp shouldn't be played back (only decoded). + */ + int64_t m_st_time; + + /** + * The duration of the audio stream in samples. + */ + int64_t m_duration; + /** * The specification of the audio data. */ @@ -182,6 +198,7 @@ public: virtual void seek(int position); virtual int getLength() const; virtual int getPosition() const; + virtual double getStartOffset() const; virtual Specs getSpecs() const; virtual void read(int& length, bool& eos, sample_t* buffer); }; diff --git a/extern/audaspace/src/fx/VolumeReader.cpp b/extern/audaspace/src/fx/VolumeReader.cpp index ac1d4882a87..627acbac9ef 100644 --- a/extern/audaspace/src/fx/VolumeReader.cpp +++ b/extern/audaspace/src/fx/VolumeReader.cpp @@ -57,4 +57,4 @@ void VolumeReader::read(int& length, bool& eos, sample_t* buffer) buffer[i] = buffer[i] * m_volumeStorage->getVolume(); } -AUD_NAMESPACE_END \ No newline at end of file +AUD_NAMESPACE_END diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h index 57ce33a239f..4b257b3b8ab 100644 --- a/source/blender/blenkernel/BKE_sound.h +++ b/source/blender/blenkernel/BKE_sound.h @@ -97,6 +97,7 @@ typedef struct SoundInfo { eSoundChannels channels; } specs; float length; + double start_offset; } SoundInfo; /* Get information about given sound. Returns truth on success., false if sound can not be loaded @@ -139,8 +140,12 @@ void BKE_sound_remove_scene_sound(struct Scene *scene, void *handle); void BKE_sound_mute_scene_sound(void *handle, char mute); -void BKE_sound_move_scene_sound( - struct Scene *scene, void *handle, int startframe, int endframe, int frameskip); +void BKE_sound_move_scene_sound(struct Scene *scene, + void *handle, + int startframe, + int endframe, + int frameskip, + double audio_offset); void BKE_sound_move_scene_sound_defaults(struct Scene *scene, struct Sequence *sequence); void BKE_sound_update_scene_sound(void *handle, struct bSound *sound); diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index fcb992e1535..9d287377545 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -709,7 +709,7 @@ void *BKE_sound_scene_add_scene_sound( sequence->scene->sound_scene, startframe / fps, endframe / fps, - frameskip / fps); + frameskip / fps + sequence->sound->offset_time); } return NULL; } @@ -737,7 +737,7 @@ void *BKE_sound_add_scene_sound( sequence->sound->playback_handle, startframe / fps, endframe / fps, - frameskip / fps); + frameskip / fps + sequence->sound->offset_time); AUD_SequenceEntry_setMuted(handle, (sequence->flag & SEQ_MUTE) != 0); AUD_SequenceEntry_setAnimationData(handle, AUD_AP_VOLUME, CFRA, &sequence->volume, 0); AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PITCH, CFRA, &sequence->pitch, 0); @@ -765,11 +765,11 @@ void BKE_sound_mute_scene_sound(void *handle, char mute) } void BKE_sound_move_scene_sound( - Scene *scene, void *handle, int startframe, int endframe, int frameskip) + Scene *scene, void *handle, int startframe, int endframe, int frameskip, double audio_offset) { sound_verify_evaluated_id(&scene->id); const double fps = FPS; - AUD_SequenceEntry_move(handle, startframe / fps, endframe / fps, frameskip / fps); + AUD_SequenceEntry_move(handle, startframe / fps, endframe / fps, frameskip / fps + audio_offset); } void BKE_sound_move_scene_sound_defaults(Scene *scene, Sequence *sequence) @@ -780,7 +780,8 @@ void BKE_sound_move_scene_sound_defaults(Scene *scene, Sequence *sequence) sequence->scene_sound, sequence->startdisp, sequence->enddisp, - sequence->startofs + sequence->anim_startofs); + sequence->startofs + sequence->anim_startofs, + sequence->sound->offset_time); } } @@ -1213,6 +1214,7 @@ static bool sound_info_from_playback_handle(void *playback_handle, SoundInfo *so AUD_SoundInfo info = AUD_getInfo(playback_handle); sound_info->specs.channels = (eSoundChannels)info.specs.channels; sound_info->length = info.length; + sound_info->start_offset = info.start_offset; return true; } diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 47495eaa57a..ff8cbdb1a59 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -643,15 +643,17 @@ static void sequencer_add_movie_multiple_strips(bContext *C, BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; + double video_start_offset; + load_data->channel++; - seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data); + seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset); load_data->channel--; if (seq_movie == NULL) { BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); } else { if (RNA_boolean_get(op->ptr, "sound")) { - seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, video_start_offset); } load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; seq_load_apply_generic_options(C, op, seq_sound); @@ -670,8 +672,10 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; + double video_start_offset; + load_data->channel++; - seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data); + seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset); load_data->channel--; if (seq_movie == NULL) { @@ -679,7 +683,7 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa return false; } if (RNA_boolean_get(op->ptr, "sound")) { - seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, video_start_offset); } seq_load_apply_generic_options(C, op, seq_sound); seq_load_apply_generic_options(C, op, seq_movie); @@ -822,7 +826,7 @@ static void sequencer_add_sound_multiple_strips(bContext *C, RNA_string_get(&itemptr, "name", file_only); BLI_join_dirfile(load_data->path, sizeof(load_data->path), dir_only, file_only); BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); - Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, 0.0f); if (seq == NULL) { BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); } @@ -840,7 +844,7 @@ static bool sequencer_add_sound_single_strip(bContext *C, wmOperator *op, SeqLoa Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene, true); - Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, 0.0f); if (seq == NULL) { BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); return false; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 0472e1264ce..e49a88c88d2 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -284,10 +284,12 @@ static void draw_seq_waveform_overlay(View2D *v2d, return; } - startsample = floor((seq->startofs + seq->anim_startofs) / FPS * - SOUND_WAVE_SAMPLES_PER_SECOND); - endsample = ceil((seq->startofs + seq->anim_startofs + seq->enddisp - seq->startdisp) / FPS * - SOUND_WAVE_SAMPLES_PER_SECOND); + startsample = (seq->startofs + seq->anim_startofs) / FPS * SOUND_WAVE_SAMPLES_PER_SECOND; + startsample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND; + + endsample = (seq->enddisp - seq->startdisp) / FPS * SOUND_WAVE_SAMPLES_PER_SECOND; + endsample += startsample; + samplestep = (endsample - startsample) * stepsize / (x2 - x1); length = min_ii( diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index d527aca184c..4ad7aa98484 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -366,6 +366,11 @@ void IMB_anim_index_rebuild_finish(struct IndexBuildContext *context, short stop */ int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc); +/** + * Return the encoded start offset (in seconds) of the given \a anim. + */ +double IMD_anim_get_offset(struct anim *anim); + /** * Return the fps contained in movie files (function rval is false, * and frs_sec and frs_sec_base untouched if none available!) diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index cfeffcca0ea..c4e2ad9da7f 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -91,6 +91,7 @@ struct anim { int duration_in_frames; int frs_sec; double frs_sec_base; + double start_offset; int x, y; /* for number */ diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index fd96110b59e..2998c4781b6 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -425,6 +425,7 @@ static int startavi(struct anim *anim) } anim->duration_in_frames = anim->avi->header->TotalFrames; + anim->start_offset = 0.0f; anim->params = NULL; anim->x = anim->avi->header->Width; @@ -597,6 +598,13 @@ static int startffmpeg(struct anim *anim) return -1; } + double video_start = 0; + double pts_time_base = av_q2d(video_stream->time_base); + + if (video_stream->start_time != AV_NOPTS_VALUE) { + video_start = video_stream->start_time * pts_time_base; + } + frame_rate = av_guess_frame_rate(pFormatCtx, video_stream, NULL); anim->duration_in_frames = 0; @@ -620,20 +628,14 @@ static int startffmpeg(struct anim *anim) * This is because the video stream duration can be shorter than the pFormatCtx->duration. */ if (anim->duration_in_frames == 0) { - double pts_time_base = av_q2d(video_stream->time_base); double stream_dur; if (video_stream->duration != AV_NOPTS_VALUE) { stream_dur = video_stream->duration * pts_time_base; } else { - double video_start = 0; double audio_start = 0; - if (video_stream->start_time != AV_NOPTS_VALUE) { - video_start = video_stream->start_time * pts_time_base; - } - /* Find audio stream to guess the duration of the video. * Sometimes the audio AND the video stream have a start offset. * The difference between these is the offset we want to use to @@ -662,6 +664,11 @@ static int startffmpeg(struct anim *anim) anim->duration_in_frames = (int)(stream_dur * av_q2d(frame_rate) + 0.5f); } + double ctx_start = 0; + if (pFormatCtx->start_time != AV_NOPTS_VALUE) { + ctx_start = (double)pFormatCtx->start_time / AV_TIME_BASE; + } + frs_num = frame_rate.num; frs_den = frame_rate.den; @@ -674,6 +681,9 @@ static int startffmpeg(struct anim *anim) anim->frs_sec = frs_num; anim->frs_sec_base = frs_den; + /* Save the relative start time for the video. IE the start time in relation to where playback + * starts. */ + anim->start_offset = video_start - ctx_start; anim->params = 0; @@ -1672,6 +1682,11 @@ int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc) return IMB_indexer_get_duration(idx); } +double IMD_anim_get_offset(struct anim *anim) +{ + return anim->start_offset; +} + bool IMB_anim_get_fps(struct anim *anim, short *frs_sec, float *frs_sec_base, bool no_av_base) { double frs_sec_base_double; diff --git a/source/blender/makesdna/DNA_sound_types.h b/source/blender/makesdna/DNA_sound_types.h index b2bb50c56a2..e6394f0a56a 100644 --- a/source/blender/makesdna/DNA_sound_types.h +++ b/source/blender/makesdna/DNA_sound_types.h @@ -67,6 +67,7 @@ typedef struct bSound { /** Runtime only, always reset in readfile. */ short tags; char _pad[4]; + double offset_time; /* Unused currently. */ // int type; diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 4895ab11618..264ccccd350 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -310,7 +310,8 @@ static Sequence *rna_Sequences_new_movie(ID *id, SEQ_add_load_data_init(&load_data, name, file, frame_start, channel); load_data.fit_method = fit_method; load_data.allow_invalid_file = true; - Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data); + double video_start_offset; + Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data, &video_start_offset); DEG_relations_tag_update(bmain); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -359,7 +360,7 @@ static Sequence *rna_Sequences_new_sound(ID *id, SeqLoadData load_data; SEQ_add_load_data_init(&load_data, name, file, frame_start, channel); load_data.allow_invalid_file = true; - Sequence *seq = SEQ_add_sound_strip(bmain, scene, seqbase, &load_data); + Sequence *seq = SEQ_add_sound_strip(bmain, scene, seqbase, &load_data, 0.0f); if (seq == NULL) { BKE_report(reports, RPT_ERROR, "Sequences.new_sound: unable to open sound file"); diff --git a/source/blender/sequencer/SEQ_add.h b/source/blender/sequencer/SEQ_add.h index 2941eb6f4c0..4025f1a4a04 100644 --- a/source/blender/sequencer/SEQ_add.h +++ b/source/blender/sequencer/SEQ_add.h @@ -79,14 +79,16 @@ struct Sequence *SEQ_add_image_strip(struct Main *bmain, struct Sequence *SEQ_add_sound_strip(struct Main *bmain, struct Scene *scene, struct ListBase *seqbase, - struct SeqLoadData *load_data); + struct SeqLoadData *load_data, + const double audio_offset); struct Sequence *SEQ_add_meta_strip(struct Scene *scene, struct ListBase *seqbase, struct SeqLoadData *load_data); struct Sequence *SEQ_add_movie_strip(struct Main *bmain, struct Scene *scene, struct ListBase *seqbase, - struct SeqLoadData *load_data); + struct SeqLoadData *load_data, + double *r_video_start_offset); struct Sequence *SEQ_add_scene_strip(struct Scene *scene, struct ListBase *seqbase, struct SeqLoadData *load_data); diff --git a/source/blender/sequencer/intern/sound.c b/source/blender/sequencer/intern/sound.c index 1054dbeeba6..c53aacddcfe 100644 --- a/source/blender/sequencer/intern/sound.c +++ b/source/blender/sequencer/intern/sound.c @@ -111,7 +111,12 @@ void SEQ_sound_update_bounds(Scene *scene, Sequence *seq) /* We have to take into account start frame of the sequence's scene! */ int startofs = seq->startofs + seq->anim_startofs + seq->scene->r.sfra; - BKE_sound_move_scene_sound(scene, seq->scene_sound, seq->startdisp, seq->enddisp, startofs); + BKE_sound_move_scene_sound(scene, + seq->scene_sound, + seq->startdisp, + seq->enddisp, + startofs, + seq->sound->offset_time); } } else { diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index dab5593be37..cc70cc38ebb 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -382,9 +382,14 @@ Sequence *SEQ_add_image_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL * \return created strip */ -Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data) +Sequence *SEQ_add_sound_strip(Main *bmain, + Scene *scene, + ListBase *seqbase, + SeqLoadData *load_data, + const double audio_offset) { bSound *sound = BKE_sound_new_file(bmain, load_data->path); /* Handles relative paths. */ + sound->offset_time = audio_offset; SoundInfo info; bool sound_loaded = BKE_sound_info_get(bmain, sound, &info); @@ -398,14 +403,36 @@ Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL return NULL; } - Sequence *seq = SEQ_sequence_alloc( - seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_SOUND_RAM); + /* If this sound it part of a video, then the sound might start after the video. + * In this case we need to then offset the start frame of the audio so it syncs up + * properly with the video. + */ + int start_frame_offset = info.start_offset * FPS; + double start_frame_offset_remainer = (info.start_offset * FPS - start_frame_offset) / FPS; + + if (start_frame_offset_remainer > FLT_EPSILON) { + /* We can't represent a fraction of a frame, so skip the first frame fraction of sound so we + * start on a "whole" frame. + */ + start_frame_offset++; + } + + sound->offset_time += start_frame_offset_remainer; + + Sequence *seq = SEQ_sequence_alloc(seqbase, + load_data->start_frame + start_frame_offset, + load_data->channel, + SEQ_TYPE_SOUND_RAM); seq->sound = sound; seq->scene_sound = NULL; - /* We add a very small negative offset here, because - * ceil(132.0) == 133.0, not nice with videos, see T47135. */ - seq->len = MAX2(1, (int)ceil((double)info.length * FPS - 1e-4)); + /* We round the frame duration as the audio sample lenghts usually does not + * line up with the video frames. Therefore we round this number to the + * nearsest frame as the audio track usually overshoots or undershoots the + * end frame ofthe video by a little bit. + * See T47135 for under shoot example. + */ + seq->len = MAX2(1, round((info.length - sound->offset_time) * FPS)); Strip *strip = seq->strip; /* We only need 1 element to store the filename. */ @@ -477,7 +504,11 @@ Sequence *SEQ_add_meta_strip(Scene *scene, ListBase *seqbase, SeqLoadData *load_ * \param load_data: SeqLoadData with information necessary to create strip * \return created strip */ -Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data) +Sequence *SEQ_add_movie_strip(Main *bmain, + Scene *scene, + ListBase *seqbase, + SeqLoadData *load_data, + double *r_video_start_offset) { char path[sizeof(load_data->path)]; BLI_strncpy(path, load_data->path, sizeof(path)); @@ -552,6 +583,7 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL if (anim_arr[0] != NULL) { seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); + *r_video_start_offset = IMD_anim_get_offset(anim_arr[0]); IMB_anim_load_metadata(anim_arr[0]); diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index 68128690773..b73ac631693 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -34,6 +34,7 @@ #include "BKE_scene.h" #include "BKE_sound.h" +#include "DNA_sound_types.h" #include "IMB_imbuf.h" #include "SEQ_iterator.h" @@ -134,7 +135,8 @@ static void seq_update_sound_bounds_recursive_impl(Scene *scene, seq->scene_sound, seq->start + startofs, seq->start + seq->len - endofs, - startofs + seq->anim_startofs); + startofs + seq->anim_startofs, + seq->sound->offset_time); } } } -- cgit v1.2.3 From 2946f72a2a1f4afc4967ceda28df4294de304b81 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Thu, 1 Jul 2021 18:51:26 +0200 Subject: VSE: Use lines to draw waveform Refactor and improve waveform drawing. Drawing now can use line strips to draw waveforms instead of only triangle strips. This makes us able to properly visualize thin waveforms as they would not be visible before. We now also draw the RMS value of the waveform. The waveform drawing is now also properly aligned to the screen pixels to avoid flickering when transforming the strip. Reviewed By: Richard Antalik Differential Revision: https://developer.blender.org/D11184 --- extern/audaspace/bindings/C/AUD_Special.cpp | 2 +- .../editors/space_sequencer/sequencer_draw.c | 328 ++++++++++++++++----- 2 files changed, 259 insertions(+), 71 deletions(-) diff --git a/extern/audaspace/bindings/C/AUD_Special.cpp b/extern/audaspace/bindings/C/AUD_Special.cpp index 97e5f5540de..5cc33525d1d 100644 --- a/extern/audaspace/bindings/C/AUD_Special.cpp +++ b/extern/audaspace/bindings/C/AUD_Special.cpp @@ -247,7 +247,7 @@ AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, int sampl buffer[i * 3] = min; buffer[i * 3 + 1] = max; - buffer[i * 3 + 2] = sqrt(power) / len; + buffer[i * 3 + 2] = sqrt(power / len); // RMS if(overallmax < max) overallmax = max; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index e49a88c88d2..888e232ce45 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -228,9 +228,93 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3]) } } +typedef struct WaveVizData { + float pos[2]; + float rms_pos; + bool clip; + bool end; +} WaveVizData; + +static int get_section_len(WaveVizData *start, WaveVizData *end) +{ + int len = 0; + while (start != end) { + len++; + if (start->end) { + return len; + } + start++; + } + return len; +} + +static void draw_waveform(WaveVizData *iter, WaveVizData *end, GPUPrimType prim_type, bool use_rms) +{ + int strip_len = get_section_len(iter, end); + if (strip_len > 1) { + GPU_blend(GPU_BLEND_ALPHA); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(prim_type, strip_len); + + while (iter != end) { + if (iter->clip) { + immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f); + } + else if (use_rms) { + immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f); + } + else { + immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f); + } + + if (use_rms) { + immVertex2f(pos, iter->pos[0], iter->rms_pos); + } + else { + immVertex2f(pos, iter->pos[0], iter->pos[1]); + } + + if (iter->end) { + /* End of line. */ + iter++; + strip_len = get_section_len(iter, end); + if (strip_len != 0) { + immEnd(); + immUnbindProgram(); + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(prim_type, strip_len); + } + } + else { + iter++; + } + } + immEnd(); + immUnbindProgram(); + + GPU_blend(GPU_BLEND_NONE); + } +} + +static float clamp_frame_coord_to_pixel(float frame_coord, + float pixel_frac, + float frames_per_pixel) +{ + float cur_pixel = (frame_coord / frames_per_pixel); + float new_pixel = (int)(frame_coord / frames_per_pixel) + pixel_frac; + if (cur_pixel > new_pixel) { + new_pixel += 1.0f; + } + return new_pixel * frames_per_pixel; +} + /** * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2. - * \param stepsize: The width of a pixel. + * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction). */ static void draw_seq_waveform_overlay(View2D *v2d, const bContext *C, @@ -241,29 +325,34 @@ static void draw_seq_waveform_overlay(View2D *v2d, float y1, float x2, float y2, - float stepsize) + float frames_per_pixel) { - /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ - int x1_offset = max_ff(v2d->cur.xmin, x1); - int x2_offset = min_ff(v2d->cur.xmax + 1.0f, x2); - if (seq->sound && ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { - int length = floor((x2_offset - x1_offset) / stepsize) + 1; - float ymid = (y1 + y2) / 2.0f; - float yscale = (y2 - y1) / 2.0f; - float samplestep; - float startsample, endsample; - float volume = seq->volume; - float value1, value2; - bSound *sound = seq->sound; - SoundWaveform *waveform; - - if (length < 2) { + /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid + * flickering whem moving around the strip. + * To do this we figure out the fractional offset in pixel space by checking where the + * window starts. + * We then append this pixel offset to our strip start coordiate to ensure we are aligned to + * the screen pixel grid. */ + float pixel_frac = v2d->cur.xmin / frames_per_pixel - floor(v2d->cur.xmin / frames_per_pixel); + float x1_adj = clamp_frame_coord_to_pixel(x1, pixel_frac, frames_per_pixel); + + /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ + float x1_offset = max_ff(v2d->cur.xmin, x1_adj); + float x2_offset = min_ff(v2d->cur.xmax, x2); + + /* Calculate how long the strip that is in view is in pixels. */ + int pix_strip_len = round((x2_offset - x1_offset) / frames_per_pixel); + + if (pix_strip_len < 2) { return; } + bSound *sound = seq->sound; + BLI_spin_lock(sound->spinlock); if (!sound->waveform) { + /* Load the waveform data if it hasn't been loaded and cached already. */ if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) { /* Prevent sounds from reloading. */ sound->tags |= SOUND_TAGS_WAVEFORM_LOADING; @@ -277,89 +366,188 @@ static void draw_seq_waveform_overlay(View2D *v2d, } BLI_spin_unlock(sound->spinlock); - waveform = sound->waveform; + SoundWaveform *waveform = sound->waveform; /* Waveform could not be built. */ if (waveform->length == 0) { return; } - startsample = (seq->startofs + seq->anim_startofs) / FPS * SOUND_WAVE_SAMPLES_PER_SECOND; - startsample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND; + /* F-curve lookup is quite expensive, so do this after precondition. */ + FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL); - endsample = (seq->enddisp - seq->startdisp) / FPS * SOUND_WAVE_SAMPLES_PER_SECOND; - endsample += startsample; + WaveVizData *tri_strip_arr = MEM_callocN(sizeof(*tri_strip_arr) * pix_strip_len * 2, + "tri_strip"); + WaveVizData *line_strip_arr = MEM_callocN(sizeof(*line_strip_arr) * pix_strip_len, + "line_strip"); - samplestep = (endsample - startsample) * stepsize / (x2 - x1); + WaveVizData *tri_strip_iter = tri_strip_arr; + WaveVizData *line_strip_iter = line_strip_arr; - length = min_ii( - floor((waveform->length - startsample) / samplestep - (x1_offset - x1) / stepsize), - length); + /* The y coordinate for the middle of the strip. */ + float y_mid = (y1 + y2) / 2.0f; + /* The lenght from the middle of the strip to the top/bottom. */ + float y_scale = (y2 - y1) / 2.0f; + float volume = seq->volume; - if (length < 2) { - return; - } + /* Value to keep track if the previous item to be drawn was a line strip. */ + int8_t was_line_strip = -1; /* -1 == no previous value. */ - /* F-curve lookup is quite expensive, so do this after precondition. */ - FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL); + float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS; - GPU_blend(GPU_BLEND_ALPHA); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); - immBegin(GPU_PRIM_TRI_STRIP, length * 2); + /* How many samples do we have for each pixel? */ + float samples_per_pix = samples_per_frame * frames_per_pixel; - for (int i = 0; i < length; i++) { - float sampleoffset = startsample + ((x1_offset - x1) / stepsize + i) * samplestep; - int p = sampleoffset; + float strip_start_offset = seq->startofs + seq->anim_startofs; + float start_sample = 0; - value1 = waveform->data[p * 3]; - value2 = waveform->data[p * 3 + 1]; + if (strip_start_offset != 0) { + /* If start offset is not zero, we need to make sure that we pick the same start sample as if + * we simply scrolled the start of the strip offscreen. Otherwise we will get flickering when + * changing start offset as the pixel alignment will not be the same for the drawn samples. + */ + strip_start_offset = clamp_frame_coord_to_pixel( + x1 - strip_start_offset, pixel_frac, frames_per_pixel); + start_sample = fabsf(strip_start_offset - x1_adj) * samples_per_frame; + } - if (samplestep > 1.0f) { - for (int j = p + 1; (j < waveform->length) && (j < p + samplestep); j++) { - if (value1 > waveform->data[j * 3]) { - value1 = waveform->data[j * 3]; - } + start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND; + /* If we scrolled the start off-screen, then the start sample should be at the first visible + * sample. */ + start_sample += (x1_offset - x1_adj) * samples_per_frame; - if (value2 < waveform->data[j * 3 + 1]) { - value2 = waveform->data[j * 3 + 1]; - } - } + for (int i = 0; i < pix_strip_len; i++) { + float sample_offset = start_sample + i * samples_per_pix; + int p = sample_offset; + + if (p >= waveform->length) { + break; } - else if (p + 1 < waveform->length) { + + float value_min = waveform->data[p * 3]; + float value_max = waveform->data[p * 3 + 1]; + float rms = waveform->data[p * 3 + 2]; + + if (p + 1 < waveform->length) { /* Use simple linear interpolation. */ - float f = sampleoffset - p; - value1 = (1.0f - f) * value1 + f * waveform->data[p * 3 + 3]; - value2 = (1.0f - f) * value2 + f * waveform->data[p * 3 + 4]; + float f = sample_offset - p; + value_min = (1.0f - f) * value_min + f * waveform->data[p * 3 + 3]; + value_max = (1.0f - f) * value_max + f * waveform->data[p * 3 + 4]; + rms = (1.0f - f) * rms + f * waveform->data[p * 3 + 5]; + if (samples_per_pix > 1.0f) { + /* We need to sum up the values we skip over until the next step. */ + float next_pos = sample_offset + samples_per_pix; + int end_idx = next_pos; + + for (int j = p + 1; (j < waveform->length) && (j < end_idx); j++) { + value_min = min_ff(value_min, waveform->data[j * 3]); + value_max = max_ff(value_max, waveform->data[j * 3 + 1]); + rms = max_ff(rms, waveform->data[j * 3 + 2]); + } + } } if (fcu && !BKE_fcurve_is_empty(fcu)) { - float evaltime = x1_offset + (i * stepsize); + float evaltime = x1_offset + (i * frames_per_pixel); volume = evaluate_fcurve(fcu, evaltime); CLAMP_MIN(volume, 0.0f); } - value1 *= volume; - value2 *= volume; - if (value2 > 1 || value1 < -1) { - immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f); + value_min *= volume; + value_max *= volume; + rms *= volume; + + bool clipping = false; + + if (value_max > 1 || value_min < -1) { + clipping = true; - CLAMP_MAX(value2, 1.0f); - CLAMP_MIN(value1, -1.0f); + CLAMP_MAX(value_max, 1.0f); + CLAMP_MIN(value_min, -1.0f); } - else { - immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f); + + bool is_line_strip = (value_max - value_min < 0.05f); + + if (was_line_strip != -1 && is_line_strip != was_line_strip) { + /* If the previously added strip type isn't the same as the current one, + * add transision areas so they transistion smoothly between each other. + */ + if (is_line_strip) { + /* This will be a line strip, end the tri strip. */ + tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; + tri_strip_iter->pos[1] = y_mid + value_min * y_scale; + tri_strip_iter->clip = clipping; + tri_strip_iter->rms_pos = tri_strip_iter->pos[1]; + tri_strip_iter->end = true; + + /* End of section. */ + tri_strip_iter++; + + /* Check if we are at the end. + * If so, skip one point line. */ + if (i + 1 == pix_strip_len) { + continue; + } + } + else { + /* This will be a tri strip. */ + line_strip_iter--; + tri_strip_iter->pos[0] = line_strip_iter->pos[0]; + tri_strip_iter->pos[1] = line_strip_iter->pos[1]; + tri_strip_iter->clip = line_strip_iter->clip; + tri_strip_iter->rms_pos = line_strip_iter->pos[1]; + tri_strip_iter++; + + /* Check if line had only one point. */ + line_strip_iter--; + if (line_strip_iter < line_strip_arr || line_strip_iter->end) { + /* Only one point, skip it. */ + line_strip_iter++; + } + else { + /* End of section. */ + line_strip_iter++; + line_strip_iter->end = true; + line_strip_iter++; + } + } } - immVertex2f(pos, x1_offset + i * stepsize, ymid + value1 * yscale); - immVertex2f(pos, x1_offset + i * stepsize, ymid + value2 * yscale); + was_line_strip = is_line_strip; + + if (is_line_strip) { + line_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; + line_strip_iter->pos[1] = y_mid + value_min * y_scale; + line_strip_iter->clip = clipping; + line_strip_iter++; + } + else { + tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; + tri_strip_iter->pos[1] = y_mid + value_min * y_scale; + tri_strip_iter->clip = clipping; + tri_strip_iter->rms_pos = y_mid + max_ff(-rms, value_min) * y_scale; + tri_strip_iter++; + + tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; + tri_strip_iter->pos[1] = y_mid + value_max * y_scale; + tri_strip_iter->clip = clipping; + tri_strip_iter->rms_pos = y_mid + min_ff(rms, value_max) * y_scale; + tri_strip_iter++; + } } - immEnd(); - immUnbindProgram(); - GPU_blend(GPU_BLEND_NONE); + WaveVizData *tri_strip_end = tri_strip_iter; + WaveVizData *line_strip_end = line_strip_iter; + + tri_strip_iter = tri_strip_arr; + line_strip_iter = line_strip_arr; + + draw_waveform(line_strip_iter, line_strip_end, GPU_PRIM_LINE_STRIP, false); + draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, false); + draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, true); + + MEM_freeN(tri_strip_arr); + MEM_freeN(line_strip_arr); } } @@ -1127,7 +1315,7 @@ static void draw_seq_strip(const bContext *C, } else { text_margin_y = y2; - y_threshold = 1; + y_threshold = false; } uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); -- cgit v1.2.3 From 118946d1953fb4e1bfd978d5ecef3151b98880a1 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 16 Aug 2021 14:35:23 +0200 Subject: Fix T87967: M2T video seeking is broken Bug caused by integer overflow in ffmpeg_generic_seek_workaround(). Function max_ii() was used to limit int_64tvalue. After fixing the issue there was another issue, where near-infinite loop was caused by requested_pos being very large and stream being cut in a way, that it was missing keyframe at beginning. This was fixed by checking if we are reading beyond file content. Reviewed By: zeddb Differential Revision: https://developer.blender.org/D11888 --- source/blender/imbuf/intern/anim_movie.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 2998c4781b6..dbca16ca82b 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -1213,7 +1213,7 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, /* Step backward frame by frame until we find the key frame we are looking for. */ while (current_pts != 0) { current_pts = *requested_pts - (int64_t)round(offset * steps_per_frame); - current_pts = max_ii(current_pts, 0); + current_pts = MAX2(current_pts, 0); /* Seek to timestamp. */ if (av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD) < @@ -1243,11 +1243,12 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, /* We found the I-frame we were looking for! */ break; } - if (cur_pts == prev_pts) { - /* We got the same key frame packet twice. - * This probably means that we have hit the beginning of the stream. */ - break; - } + } + + if (cur_pts == prev_pts) { + /* We got the same key frame packet twice. + * This probably means that we have hit the beginning of the stream. */ + break; } prev_pts = cur_pts; -- cgit v1.2.3 From 394a0b0da5b899944d33a38c9ff01e03dc370f2a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 16 Aug 2021 23:19:54 +1000 Subject: Fix building without audaspace --- source/blender/blenkernel/intern/sound.c | 3 ++- source/blender/sequencer/intern/strip_add.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 9d287377545..bd0fbd840ff 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -1312,7 +1312,8 @@ void BKE_sound_move_scene_sound(Scene *UNUSED(scene), void *UNUSED(handle), int UNUSED(startframe), int UNUSED(endframe), - int UNUSED(frameskip)) + int UNUSED(frameskip), + double UNUSED(audio_offset)) { } void BKE_sound_move_scene_sound_defaults(Scene *UNUSED(scene), Sequence *UNUSED(sequence)) diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index cc70cc38ebb..27e92550bbb 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -463,7 +463,8 @@ Sequence *SEQ_add_sound_strip(Main *bmain, Sequence *SEQ_add_sound_strip(Main *UNUSED(bmain), Scene *UNUSED(scene), ListBase *UNUSED(seqbase), - SeqLoadData *UNUSED(load_data)) + SeqLoadData *UNUSED(load_data), + const double UNUSED(audio_offset)) { return NULL; } -- cgit v1.2.3 From 7db3746033437783411b0eb4b77f0804c25e2614 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 16 Aug 2021 23:46:28 +1000 Subject: Cleanup: spelling --- intern/ghost/intern/GHOST_XrGraphicsBinding.cpp | 4 ++-- source/blender/editors/space_sequencer/sequencer_draw.c | 15 +++++++-------- source/blender/editors/space_sequencer/sequencer_edit.c | 2 +- source/blender/sequencer/intern/strip_add.c | 9 ++++----- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp index 70567e02cb9..936b973c97e 100644 --- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp +++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp @@ -341,8 +341,8 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding { bool &r_is_srgb_format) const override { std::vector gpu_binding_formats = { -# if 0 /* RGB10A2 doesn't seem to work with Oculus headsets, so move it after RGB16AF for the \ - time being. */ +# if 0 /* RGB10A2 doesn't seem to work with Oculus head-sets, \ + * so move it after RGB16AF for the time being. */ DXGI_FORMAT_R10G10B10A2_UNORM, # endif DXGI_FORMAT_R16G16B16A16_UNORM, diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 888e232ce45..b3c39e2fa6f 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -329,10 +329,10 @@ static void draw_seq_waveform_overlay(View2D *v2d, { if (seq->sound && ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid - * flickering whem moving around the strip. + * flickering when moving around the strip. * To do this we figure out the fractional offset in pixel space by checking where the * window starts. - * We then append this pixel offset to our strip start coordiate to ensure we are aligned to + * We then append this pixel offset to our strip start coordinate to ensure we are aligned to * the screen pixel grid. */ float pixel_frac = v2d->cur.xmin / frames_per_pixel - floor(v2d->cur.xmin / frames_per_pixel); float x1_adj = clamp_frame_coord_to_pixel(x1, pixel_frac, frames_per_pixel); @@ -386,7 +386,7 @@ static void draw_seq_waveform_overlay(View2D *v2d, /* The y coordinate for the middle of the strip. */ float y_mid = (y1 + y2) / 2.0f; - /* The lenght from the middle of the strip to the top/bottom. */ + /* The length from the middle of the strip to the top/bottom. */ float y_scale = (y2 - y1) / 2.0f; float volume = seq->volume; @@ -403,9 +403,9 @@ static void draw_seq_waveform_overlay(View2D *v2d, if (strip_start_offset != 0) { /* If start offset is not zero, we need to make sure that we pick the same start sample as if - * we simply scrolled the start of the strip offscreen. Otherwise we will get flickering when - * changing start offset as the pixel alignment will not be the same for the drawn samples. - */ + * we simply scrolled the start of the strip off-screen. Otherwise we will get flickering + * when changing start offset as the pixel alignment will not be the same for the drawn + * samples. */ strip_start_offset = clamp_frame_coord_to_pixel( x1 - strip_start_offset, pixel_frac, frames_per_pixel); start_sample = fabsf(strip_start_offset - x1_adj) * samples_per_frame; @@ -470,8 +470,7 @@ static void draw_seq_waveform_overlay(View2D *v2d, if (was_line_strip != -1 && is_line_strip != was_line_strip) { /* If the previously added strip type isn't the same as the current one, - * add transision areas so they transistion smoothly between each other. - */ + * add transition areas so they transition smoothly between each other. */ if (is_line_strip) { /* This will be a line strip, end the tri strip. */ tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 4b26469aad3..afad8999e88 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -2943,7 +2943,7 @@ static int seq_cmp_time_startdisp_channel(const void *a, const void *b) int seq_a_start = SEQ_transform_get_left_handle_frame(seq_a); int seq_b_start = SEQ_transform_get_left_handle_frame(seq_b); - /** If strips have the same start frame favor the one with a higher channel. **/ + /* If strips have the same start frame favor the one with a higher channel. */ if (seq_a_start == seq_b_start) { return seq_a->machine > seq_b->machine; } diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 27e92550bbb..7b383bcb330 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -426,12 +426,11 @@ Sequence *SEQ_add_sound_strip(Main *bmain, seq->sound = sound; seq->scene_sound = NULL; - /* We round the frame duration as the audio sample lenghts usually does not + /* We round the frame duration as the audio sample lengths usually does not * line up with the video frames. Therefore we round this number to the - * nearsest frame as the audio track usually overshoots or undershoots the - * end frame ofthe video by a little bit. - * See T47135 for under shoot example. - */ + * nearest frame as the audio track usually overshoots or undershoots the + * end frame of the video by a little bit. + * See T47135 for under shoot example. */ seq->len = MAX2(1, round((info.length - sound->offset_time) * FPS)); Strip *strip = seq->strip; -- cgit v1.2.3 From df3884d51244a8a08c62308a3d7e84011090df8b Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 16 Aug 2021 15:40:18 +0200 Subject: Fix T90689, T90705: Cycles math node with 3 inputs broken after recent changes Thanks Charlie Jolly for finding the fix. --- intern/cycles/blender/blender_shader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 1404c58290d..24819bacbb5 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -1024,6 +1024,9 @@ static ShaderInput *node_find_input_by_name(ShaderNode *node, BL::NodeSocket &b_ if (string_endswith(name, "_001")) { string_replace(name, "_001", "2"); } + else if (string_endswith(name, "_002")) { + string_replace(name, "_002", "3"); + } else { name += "1"; } -- cgit v1.2.3 From 035d4c28abaf351b36ff5bb6057f865888790331 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Mon, 16 Aug 2021 16:50:54 +0200 Subject: Add sanity NULL checks when loading sound sequences Would cause crashes in files that had lingering invalid sound sequences around. For example our tests/render/volume/fire.blend test file. --- source/blender/blenkernel/intern/sound.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index bd0fbd840ff..8730d2758e6 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -703,7 +703,7 @@ void *BKE_sound_scene_add_scene_sound( Scene *scene, Sequence *sequence, int startframe, int endframe, int frameskip) { sound_verify_evaluated_id(&scene->id); - if (sequence->scene && scene != sequence->scene) { + if (sequence->scene && scene != sequence->scene && sequence->sound) { const double fps = FPS; return AUD_Sequence_add(scene->sound_scene, sequence->scene->sound_scene, @@ -775,7 +775,7 @@ void BKE_sound_move_scene_sound( void BKE_sound_move_scene_sound_defaults(Scene *scene, Sequence *sequence) { sound_verify_evaluated_id(&scene->id); - if (sequence->scene_sound) { + if (sequence->scene_sound && sequence->sound) { BKE_sound_move_scene_sound(scene, sequence->scene_sound, sequence->startdisp, -- cgit v1.2.3 From b5117660da27d9d13c48c1d2f1afd64083e120bb Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 16 Aug 2021 13:49:06 -0300 Subject: PyAPI: GPU Buffer: Buffer protocol support The code was commented due to lack of testing and short release deadline. --- source/blender/python/gpu/gpu_py_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c index a1fc89e772e..6e6aef4204d 100644 --- a/source/blender/python/gpu/gpu_py_buffer.c +++ b/source/blender/python/gpu/gpu_py_buffer.c @@ -37,7 +37,7 @@ #include "gpu_py_buffer.h" -//#define PYGPU_BUFFER_PROTOCOL +#define PYGPU_BUFFER_PROTOCOL #define MAX_DIMENSIONS 64 /* -------------------------------------------------------------------- */ -- cgit v1.2.3 From 4dba2060118b43699a40ae04a66f75ba5f9c745e Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 16 Aug 2021 13:53:56 -0300 Subject: PyAPI: GPUShader: make 'uniform_vector_*' less restricted Buffers larger than required may be allowed without restriction. --- source/blender/python/gpu/gpu_py_shader.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index 41c40fdeb96..145586d8ab0 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -211,8 +211,9 @@ static bool pygpu_shader_uniform_vector_impl(PyObject *args, return false; } - if (r_pybuffer->len != (*r_length * *r_count * elem_size)) { - PyErr_SetString(PyExc_BufferError, "GPUShader.uniform_vector_*: buffer size does not match."); + if (r_pybuffer->len < (*r_length * *r_count * elem_size)) { + PyErr_SetString(PyExc_OverflowError, + "GPUShader.uniform_vector_*: buffer size smaller than required."); return false; } -- cgit v1.2.3 From eaa152738523bdd2163330fda7ffb1ad62e559cd Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Mon, 16 Aug 2021 21:19:39 -0700 Subject: UDIM: Fix tile number calculation when adding a range of image tiles When adding a range of tiles, the operator could incorrectly calculate the end_tile. It would not account for the start_tile itself and the IMA_UDIM_MAX value was 1 too small. This is most noticeable when attempting to fill the entire supported range of tiles. Differential Revision: https://developer.blender.org/D11857 --- source/blender/blenkernel/BKE_image.h | 2 +- source/blender/editors/space_image/image_ops.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index d298e5dcf6d..ac73bd2b595 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -45,7 +45,7 @@ struct StampData; struct anim; #define IMA_MAX_SPACE 64 -#define IMA_UDIM_MAX 1999 +#define IMA_UDIM_MAX 2000 void BKE_images_init(void); void BKE_images_exit(void); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 613042a2ab9..999d2956fef 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -3922,7 +3922,7 @@ static int tile_add_exec(bContext *C, wmOperator *op) Image *ima = CTX_data_edit_image(C); int start_tile = RNA_int_get(op->ptr, "number"); - int end_tile = start_tile + RNA_int_get(op->ptr, "count"); + int end_tile = start_tile + RNA_int_get(op->ptr, "count") - 1; if (start_tile < 1001 || end_tile > IMA_UDIM_MAX) { BKE_report(op->reports, RPT_ERROR, "Invalid UDIM index range was specified"); @@ -3933,7 +3933,7 @@ static int tile_add_exec(bContext *C, wmOperator *op) char *label = RNA_string_get_alloc(op->ptr, "label", NULL, 0); bool created_tile = false; - for (int tile_number = start_tile; tile_number < end_tile; tile_number++) { + for (int tile_number = start_tile; tile_number <= end_tile; tile_number++) { ImageTile *tile = BKE_image_add_tile(ima, tile_number, label); if (tile != NULL) { @@ -3949,6 +3949,7 @@ static int tile_add_exec(bContext *C, wmOperator *op) MEM_freeN(label); if (!created_tile) { + BKE_report(op->reports, RPT_WARNING, "No UDIM tiles were created"); return OPERATOR_CANCELLED; } -- cgit v1.2.3 From 869b84452a8c06c505fcd265510727248b059a59 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Aug 2021 14:42:12 +1000 Subject: Cleanup: compiler warnings --- source/blender/python/gpu/gpu_py_buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c index 6e6aef4204d..0fef59d6352 100644 --- a/source/blender/python/gpu/gpu_py_buffer.c +++ b/source/blender/python/gpu/gpu_py_buffer.c @@ -608,7 +608,7 @@ static void pygpu_buffer_strides_calc(const eGPUDataFormat format, } /* Here is the buffer interface function */ -static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int flags) +static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int UNUSED(flags)) { if (view == NULL) { PyErr_SetString(PyExc_ValueError, "NULL view in getbuffer"); @@ -620,7 +620,7 @@ static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int f view->len = bpygpu_Buffer_size(self); view->readonly = 0; view->itemsize = GPU_texture_dataformat_size(self->format); - view->format = pygpu_buffer_formatstr(self->format); + view->format = (char *)pygpu_buffer_formatstr(self->format); view->ndim = self->shape_len; view->shape = self->shape; view->strides = MEM_mallocN(view->ndim * sizeof(*view->strides), "BPyGPUBuffer strides"); -- cgit v1.2.3 From d60e28093f8b3145d099ca696e0ddc2d0c9850ed Mon Sep 17 00:00:00 2001 From: nutti Date: Tue, 17 Aug 2021 14:44:28 +1000 Subject: Docs: add API docs for gpu.platform Adds Python API documentations for gpu.platform module. Ref D12222 --- doc/python_api/sphinx_doc_gen.py | 2 ++ source/blender/python/gpu/gpu_py_platform.c | 30 ++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 43d0319b73f..3b13f119229 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -254,6 +254,7 @@ else: "gpu.shader", "gpu.state", "gpu.texture", + "gpu.platform", "gpu_extras", "idprop.types", "mathutils", @@ -2000,6 +2001,7 @@ def write_rst_importable_modules(basepath): "gpu.shader": "GPU Shader Utilities", "gpu.state": "GPU State Utilities", "gpu.texture": "GPU Texture Utilities", + "gpu.platform": "GPU Platform Utilities", "bmesh": "BMesh Module", "bmesh.ops": "BMesh Operators", "bmesh.types": "BMesh Types", diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c index 132052b6f1d..7d10f0e9b43 100644 --- a/source/blender/python/gpu/gpu_py_platform.c +++ b/source/blender/python/gpu/gpu_py_platform.c @@ -33,16 +33,37 @@ /** \name Functions * \{ */ +PyDoc_STRVAR(pygpu_platform_vendor_get_doc, + ".. function:: vendor_get()\n" + "\n" + " Get GPU vendor.\n" + "\n" + " :return: Vendor name.\n" + " :rtype: str\n"); static PyObject *pygpu_platform_vendor_get(PyObject *UNUSED(self)) { return PyUnicode_FromString(GPU_platform_vendor()); } +PyDoc_STRVAR(pygpu_platform_renderer_get_doc, + ".. function:: renderer_get()\n" + "\n" + " Get GPU to be used for rendering.\n" + "\n" + " :return: GPU name.\n" + " :rtype: str\n"); static PyObject *pygpu_platform_renderer_get(PyObject *UNUSED(self)) { return PyUnicode_FromString(GPU_platform_renderer()); } +PyDoc_STRVAR(pygpu_platform_version_get_doc, + ".. function:: version_get()\n" + "\n" + " Get GPU driver version.\n" + "\n" + " :return: Driver version.\n" + " :rtype: str\n"); static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self)) { return PyUnicode_FromString(GPU_platform_version()); @@ -55,9 +76,12 @@ static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self)) * \{ */ static struct PyMethodDef pygpu_platform__tp_methods[] = { - {"vendor_get", (PyCFunction)pygpu_platform_vendor_get, METH_NOARGS, NULL}, - {"renderer_get", (PyCFunction)pygpu_platform_renderer_get, METH_NOARGS, NULL}, - {"version_get", (PyCFunction)pygpu_platform_version_get, METH_NOARGS, NULL}, + {"vendor_get", (PyCFunction)pygpu_platform_vendor_get, METH_NOARGS, + pygpu_platform_vendor_get_doc}, + {"renderer_get", (PyCFunction)pygpu_platform_renderer_get, METH_NOARGS, + pygpu_platform_renderer_get_doc}, + {"version_get", (PyCFunction)pygpu_platform_version_get, METH_NOARGS, + pygpu_platform_version_get_doc}, {NULL, NULL, 0, NULL}, }; -- cgit v1.2.3 From ba055493a05a1dbf609dece130cc1ac334f50228 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Aug 2021 14:46:46 +1000 Subject: Cleanup: clang-format --- source/blender/gpu/GPU_texture.h | 9 +++++++-- .../nodes/geometry/nodes/node_geo_subdivision_surface.cc | 11 +++++++---- source/blender/python/gpu/gpu_py_platform.c | 12 +++++++++--- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index ee4d08d4059..9a1885160b6 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -192,8 +192,13 @@ GPUTexture *GPU_texture_create_1d_array( 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 mip_len, eGPUTextureFormat format, const float *data); -GPUTexture *GPU_texture_create_2d_array( - const char *name, int w, int h, int d, int mip_len, eGPUTextureFormat format, const float *data); +GPUTexture *GPU_texture_create_2d_array(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, diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 7882cd04845..e8dd36e528c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -18,9 +18,9 @@ #include "BKE_subdiv.h" #include "BKE_subdiv_mesh.h" +#include "DNA_modifier_types.h" #include "UI_interface.h" #include "UI_resources.h" -#include "DNA_modifier_types.h" #include "node_geometry_util.hh" static bNodeSocketTemplate geo_node_subdivision_surface_in[] = { @@ -74,7 +74,8 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenSubdiv")); #else - const NodeGeometrySubdivisionSurface &storage = *(const NodeGeometrySubdivisionSurface *)params.node().storage; + const NodeGeometrySubdivisionSurface &storage = + *(const NodeGeometrySubdivisionSurface *)params.node().storage; const int uv_smooth = storage.uv_smooth; const int boundary_smooth = storage.boundary_smooth; const int subdiv_level = clamp_i(params.extract_input("Level"), 0, 30); @@ -142,8 +143,10 @@ void register_node_type_geo_subdivision_surface() ntype.draw_buttons = geo_node_subdivision_surface_layout; node_type_init(&ntype, geo_node_subdivision_surface_init); node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_storage( - &ntype, "NodeGeometrySubdivisionSurface", node_free_standard_storage, node_copy_standard_storage); + node_type_storage(&ntype, + "NodeGeometrySubdivisionSurface", + node_free_standard_storage, + node_copy_standard_storage); node_type_socket_templates( &ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out); nodeRegisterType(&ntype); diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c index 7d10f0e9b43..62310a83642 100644 --- a/source/blender/python/gpu/gpu_py_platform.c +++ b/source/blender/python/gpu/gpu_py_platform.c @@ -76,11 +76,17 @@ static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self)) * \{ */ static struct PyMethodDef pygpu_platform__tp_methods[] = { - {"vendor_get", (PyCFunction)pygpu_platform_vendor_get, METH_NOARGS, + {"vendor_get", + (PyCFunction)pygpu_platform_vendor_get, + METH_NOARGS, pygpu_platform_vendor_get_doc}, - {"renderer_get", (PyCFunction)pygpu_platform_renderer_get, METH_NOARGS, + {"renderer_get", + (PyCFunction)pygpu_platform_renderer_get, + METH_NOARGS, pygpu_platform_renderer_get_doc}, - {"version_get", (PyCFunction)pygpu_platform_version_get, METH_NOARGS, + {"version_get", + (PyCFunction)pygpu_platform_version_get, + METH_NOARGS, pygpu_platform_version_get_doc}, {NULL, NULL, 0, NULL}, }; -- cgit v1.2.3 From 69fdcea9788caa1c94e2baf848eeb6650b274c6b Mon Sep 17 00:00:00 2001 From: nutti Date: Tue, 17 Aug 2021 14:55:05 +1000 Subject: Docs: add API docs for gpu.capabilities Adds Python API documentations for gpu.capabilities module. Ref D12226 --- doc/python_api/sphinx_doc_gen.py | 2 + source/blender/python/gpu/gpu_py_capabilities.c | 162 ++++++++++++++++++++++-- 2 files changed, 151 insertions(+), 13 deletions(-) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 3b13f119229..48cbb5f197c 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -255,6 +255,7 @@ else: "gpu.state", "gpu.texture", "gpu.platform", + "gpu.capabilities", "gpu_extras", "idprop.types", "mathutils", @@ -2002,6 +2003,7 @@ def write_rst_importable_modules(basepath): "gpu.state": "GPU State Utilities", "gpu.texture": "GPU Texture Utilities", "gpu.platform": "GPU Platform Utilities", + "gpu.capabilities": "GPU Capabilities Utilities", "bmesh": "BMesh Module", "bmesh.ops": "BMesh Operators", "bmesh.types": "BMesh Types", diff --git a/source/blender/python/gpu/gpu_py_capabilities.c b/source/blender/python/gpu/gpu_py_capabilities.c index f3fb93021b2..11e7d48f096 100644 --- a/source/blender/python/gpu/gpu_py_capabilities.c +++ b/source/blender/python/gpu/gpu_py_capabilities.c @@ -33,66 +33,166 @@ /** \name Functions * \{ */ +PyDoc_STRVAR(pygpu_max_texture_size_get_doc, + ".. function:: max_texture_size_get()\n" + "\n" + " Get estimated maximum texture size to be able to handle.\n" + "\n" + " :return: Texture size.\n" + " :rtype: int\n"); static PyObject *pygpu_max_texture_size_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_texture_size()); } +PyDoc_STRVAR(pygpu_max_texture_layers_get_doc, + ".. function:: max_texture_layers_get()\n" + "\n" + " Get maximum number of layers in texture.\n" + "\n" + " :return: Number of layers.\n" + " :rtype: int\n"); static PyObject *pygpu_max_texture_layers_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_texture_layers()); } +PyDoc_STRVAR(pygpu_max_textures_get_doc, + ".. function:: max_textures_get()\n" + "\n" + " Get maximum supported texture image units used for\n" + " accessing texture maps from the vertex shader and the\n" + " fragment processor.\n" + "\n" + " :return: Texture image units.\n" + " :rtype: int\n"); static PyObject *pygpu_max_textures_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_textures()); } +PyDoc_STRVAR(pygpu_max_textures_vert_get_doc, + ".. function:: max_textures_vert_get()\n" + "\n" + " Get maximum supported texture image units used for\n" + " accessing texture maps from the vertex shader.\n" + "\n" + " :return: Texture image units.\n" + " :rtype: int\n"); static PyObject *pygpu_max_textures_vert_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_textures_vert()); } +PyDoc_STRVAR(pygpu_max_textures_geom_get_doc, + ".. function:: max_textures_geom_get()\n" + "\n" + " Get maximum supported texture image units used for\n" + " accessing texture maps from the geometry shader.\n" + "\n" + " :return: Texture image units.\n" + " :rtype: int\n"); static PyObject *pygpu_max_textures_geom_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_textures_geom()); } +PyDoc_STRVAR(pygpu_max_textures_frag_get_doc, + ".. function:: max_textures_frag_get()\n" + "\n" + " Get maximum supported texture image units used for\n" + " accessing texture maps from the fragment shader.\n" + "\n" + " :return: Texture image units.\n" + " :rtype: int\n"); static PyObject *pygpu_max_textures_frag_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_textures_frag()); } +PyDoc_STRVAR(pygpu_max_uniforms_vert_get_doc, + ".. function:: max_uniforms_vert_get()\n" + "\n" + " Get maximum number of values held in uniform variable\n" + " storage for a vertex shader.\n" + "\n" + " :return: Number of values.\n" + " :rtype: int\n"); static PyObject *pygpu_max_uniforms_vert_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_uniforms_vert()); } +PyDoc_STRVAR(pygpu_max_uniforms_frag_get_doc, + ".. function:: max_uniforms_frag_get()\n" + "\n" + " Get maximum number of values held in uniform variable\n" + " storage for a fragment shader.\n" + "\n" + " :return: Number of values.\n" + " :rtype: int\n"); static PyObject *pygpu_max_uniforms_frag_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_uniforms_frag()); } +PyDoc_STRVAR(pygpu_max_batch_indices_get_doc, + ".. function:: max_batch_indices_get()\n" + "\n" + " Get maximum number of vertex array indices.\n" + "\n" + " :return: Number of indices.\n" + " :rtype: int\n"); static PyObject *pygpu_max_batch_indices_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_batch_indices()); } +PyDoc_STRVAR(pygpu_max_batch_vertices_get_doc, + ".. function:: max_batch_vertices_get()\n" + "\n" + " Get maximum number of vertex array vertices.\n" + "\n" + " :return: Number of vertices.\n" + " :rtype: int\n"); static PyObject *pygpu_max_batch_vertices_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_batch_vertices()); } +PyDoc_STRVAR(pygpu_max_vertex_attribs_get_doc, + ".. function:: max_vertex_attribs_get()\n" + "\n" + " Get maximum number of vertex attributes accessible to\n" + " a vertex shader.\n" + "\n" + " :return: Number of attributes.\n" + " :rtype: int\n"); static PyObject *pygpu_max_vertex_attribs_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_vertex_attribs()); } +PyDoc_STRVAR(pygpu_max_varying_floats_get_doc, + ".. function:: max_varying_floats_get()\n" + "\n" + " Get maximum number of varying variables used by\n" + " vertex and fragment shaders.\n" + "\n" + " :return: Number of variables.\n" + " :rtype: int\n"); static PyObject *pygpu_max_varying_floats_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_varying_floats()); } +PyDoc_STRVAR(pygpu_extensions_get_doc, + ".. function:: extensions_get()\n" + "\n" + " Get supported extensions in the current context.\n" + "\n" + " :return: Extensions.\n" + " :rtype: tuple of string\n"); static PyObject *pygpu_extensions_get(PyObject *UNUSED(self)) { int extensions_len = GPU_extensions_len(); @@ -112,19 +212,55 @@ static PyObject *pygpu_extensions_get(PyObject *UNUSED(self)) * \{ */ static struct PyMethodDef pygpu_capabilities__tp_methods[] = { - {"max_texture_size_get", (PyCFunction)pygpu_max_texture_size_get, METH_NOARGS, NULL}, - {"max_texture_layers_get", (PyCFunction)pygpu_max_texture_layers_get, METH_NOARGS, NULL}, - {"max_textures_get", (PyCFunction)pygpu_max_textures_get, METH_NOARGS, NULL}, - {"max_textures_vert_get", (PyCFunction)pygpu_max_textures_vert_get, METH_NOARGS, NULL}, - {"max_textures_geom_get", (PyCFunction)pygpu_max_textures_geom_get, METH_NOARGS, NULL}, - {"max_textures_frag_get", (PyCFunction)pygpu_max_textures_frag_get, METH_NOARGS, NULL}, - {"max_uniforms_vert_get", (PyCFunction)pygpu_max_uniforms_vert_get, METH_NOARGS, NULL}, - {"max_uniforms_frag_get", (PyCFunction)pygpu_max_uniforms_frag_get, METH_NOARGS, NULL}, - {"max_batch_indices_get", (PyCFunction)pygpu_max_batch_indices_get, METH_NOARGS, NULL}, - {"max_batch_vertices_get", (PyCFunction)pygpu_max_batch_vertices_get, METH_NOARGS, NULL}, - {"max_vertex_attribs_get", (PyCFunction)pygpu_max_vertex_attribs_get, METH_NOARGS, NULL}, - {"max_varying_floats_get", (PyCFunction)pygpu_max_varying_floats_get, METH_NOARGS, NULL}, - {"extensions_get", (PyCFunction)pygpu_extensions_get, METH_NOARGS, NULL}, + {"max_texture_size_get", + (PyCFunction)pygpu_max_texture_size_get, + METH_NOARGS, + pygpu_max_texture_size_get_doc}, + {"max_texture_layers_get", + (PyCFunction)pygpu_max_texture_layers_get, + METH_NOARGS, + pygpu_max_texture_layers_get_doc}, + {"max_textures_get", + (PyCFunction)pygpu_max_textures_get, + METH_NOARGS, + pygpu_max_textures_get_doc}, + {"max_textures_vert_get", + (PyCFunction)pygpu_max_textures_vert_get, + METH_NOARGS, + pygpu_max_textures_vert_get_doc}, + {"max_textures_geom_get", + (PyCFunction)pygpu_max_textures_geom_get, + METH_NOARGS, + pygpu_max_textures_geom_get_doc}, + {"max_textures_frag_get", + (PyCFunction)pygpu_max_textures_frag_get, + METH_NOARGS, + pygpu_max_textures_frag_get_doc}, + {"max_uniforms_vert_get", + (PyCFunction)pygpu_max_uniforms_vert_get, + METH_NOARGS, + pygpu_max_uniforms_vert_get_doc}, + {"max_uniforms_frag_get", + (PyCFunction)pygpu_max_uniforms_frag_get, + METH_NOARGS, + pygpu_max_uniforms_frag_get_doc}, + {"max_batch_indices_get", + (PyCFunction)pygpu_max_batch_indices_get, + METH_NOARGS, + pygpu_max_batch_indices_get_doc}, + {"max_batch_vertices_get", + (PyCFunction)pygpu_max_batch_vertices_get, + METH_NOARGS, + pygpu_max_batch_vertices_get_doc}, + {"max_vertex_attribs_get", + (PyCFunction)pygpu_max_vertex_attribs_get, + METH_NOARGS, + pygpu_max_vertex_attribs_get_doc}, + {"max_varying_floats_get", + (PyCFunction)pygpu_max_varying_floats_get, + METH_NOARGS, + pygpu_max_varying_floats_get_doc}, + {"extensions_get", (PyCFunction)pygpu_extensions_get, METH_NOARGS, pygpu_extensions_get_doc}, {NULL, NULL, 0, NULL}, }; -- cgit v1.2.3 From 736b6a70a492e35c2be9e1f6bb6b26a057fbdf58 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Aug 2021 15:11:54 +1000 Subject: Docs: improve word wrap comment --- source/blender/blenkernel/intern/font.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index de838f1b3cd..72add476bfe 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -716,8 +716,10 @@ typedef struct VFontToCurveIter { } 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. + * Wrap words that extends beyond the text-box width (enabled by default). + * + * Currently only disabled when scale-to-fit is enabled, + * so floating-point error doesn't cause unexpected wrapping, see T89241. */ bool word_wrap; int status; -- cgit v1.2.3 From 4c8d68c03293a36fd28b3401833a465f24020ce5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Aug 2021 15:14:09 +1000 Subject: Cleanup: replace degenerate check with assert Use an assert since this should never happen. --- source/blender/bmesh/intern/bmesh_mesh_normals.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index a5e41b74ee1..186c85abe58 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -85,10 +85,9 @@ BLI_INLINE void bm_vert_calc_normals_accum_loop(const BMLoop *l_iter, 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); - } + /* Shouldn't happen as normalizing edge-vectors cause degenerate values to be zeroed out. */ + BLI_assert(!isnan(fac)); + madd_v3_v3fl(v_no, f_no, fac); } static void bm_vert_calc_normals_impl(BMVert *v) -- cgit v1.2.3 From cb40c7ca1f4bbbbee5efc4fee6e1dcf8709a8bc4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Aug 2021 16:46:09 +1000 Subject: Fix memory leak in edit-mesh dissolve degenerate --- source/blender/editors/mesh/editmesh_tools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 1b6643da1aa..186c98eb8da 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -6314,7 +6314,7 @@ static int edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op) BMesh *bm = em->bm; if (!EDBM_op_callf(em, op, "dissolve_degenerate edges=%he dist=%f", BM_ELEM_SELECT, thresh)) { - return OPERATOR_CANCELLED; + continue; } /* tricky to maintain correct selection here, so just flush up from verts */ -- cgit v1.2.3 From 6baa62245f2bc18cc6d12cfcd16575823028bcab Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Aug 2021 17:10:59 +1000 Subject: Edit Mesh: skip flipping custom normals for meshes with no selection Also split out normal calculation into functions. --- source/blender/editors/mesh/editmesh_tools.c | 111 +++++++++++++++------------ 1 file changed, 63 insertions(+), 48 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 186c98eb8da..b62eee67600 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -2183,6 +2183,61 @@ static bool flip_custom_normals(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr) /* -------------------------------------------------------------------- */ /** \name Flip Normals Operator * \{ */ + +static void edbm_flip_normals_custom_loop_normals(Object *obedit, BMEditMesh *em) +{ + if (!CustomData_has_layer(&em->bm->ldata, CD_CUSTOMLOOPNORMAL)) { + return; + } + + /* The mesh has custom normal data, flip them. */ + BMesh *bm = em->bm; + + BM_lnorspace_update(bm); + BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, false); + BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; + + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + negate_v3(lnor_ed->nloc); + + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data); + } + BM_loop_normal_editdata_array_free(lnors_ed_arr); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); +} + +static void edbm_flip_normals_face_winding(wmOperator *op, Object *obedit, BMEditMesh *em) +{ + + bool has_flipped_faces = false; + + /* See if we have any custom normals to flip. */ + BMLoopNorEditDataArray *lnors_ed_arr = flip_custom_normals_init_data(em->bm); + + if (EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true)) { + has_flipped_faces = true; + } + + if (flip_custom_normals(em->bm, lnors_ed_arr) || has_flipped_faces) { + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); + } + + if (lnors_ed_arr != NULL) { + BM_loop_normal_editdata_array_free(lnors_ed_arr); + } +} + static int edbm_flip_normals_exec(bContext *C, wmOperator *op) { const bool only_clnors = RNA_boolean_get(op->ptr, "only_clnors"); @@ -2197,56 +2252,16 @@ static int edbm_flip_normals_exec(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(obedit); if (only_clnors) { - if (CustomData_has_layer(&em->bm->ldata, CD_CUSTOMLOOPNORMAL)) { - /* The mesh has custom normal data, flip them. */ - BMesh *bm = em->bm; - - BM_lnorspace_update(bm); - BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, false); - BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; - - for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { - negate_v3(lnor_ed->nloc); - - BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], - lnor_ed->nloc, - lnor_ed->clnors_data); - } - BM_loop_normal_editdata_array_free(lnors_ed_arr); - EDBM_update(obedit->data, - &(const struct EDBMUpdate_Params){ - .calc_looptri = true, - .calc_normals = false, - .is_destructive = false, - }); + if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) { + continue; } - continue; - } - - if (em->bm->totfacesel == 0) { - continue; - } - - bool has_flipped_faces = false; - - /* See if we have any custom normals to flip. */ - BMLoopNorEditDataArray *lnors_ed_arr = flip_custom_normals_init_data(em->bm); - - if (EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true)) { - has_flipped_faces = true; + edbm_flip_normals_custom_loop_normals(obedit, em); } - - if (flip_custom_normals(em->bm, lnors_ed_arr) || has_flipped_faces) { - EDBM_update(obedit->data, - &(const struct EDBMUpdate_Params){ - .calc_looptri = true, - .calc_normals = false, - .is_destructive = false, - }); - } - - if (lnors_ed_arr != NULL) { - BM_loop_normal_editdata_array_free(lnors_ed_arr); + else { + if (em->bm->totfacesel == 0) { + continue; + } + edbm_flip_normals_face_winding(op, obedit, em); } } -- cgit v1.2.3 From 4443831c6bc264f6017a309b6217beb72943abee Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Aug 2021 17:11:00 +1000 Subject: Edit Mesh: skip normals to vector with unselected meshes for "Delete" Meshes with unselected elements are skipped but still called BM_custom_loop_normals_to_vector_layer. --- source/blender/editors/mesh/editmesh_tools.c | 46 +++++++++++++++++----------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index b62eee67600..101c997fd1c 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -467,40 +467,50 @@ static int edbm_delete_exec(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(obedit); const int type = RNA_enum_get(op->ptr, "type"); - BM_custom_loop_normals_to_vector_layer(em->bm); - switch (type) { case MESH_DELETE_VERT: /* Erase Vertices */ - if (!(em->bm->totvertsel && - EDBM_op_callf(em, op, "delete geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS))) { + if (em->bm->totvertsel == 0) { + continue; + } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_callf(em, op, "delete geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS)) { continue; } break; case MESH_DELETE_EDGE: /* Erase Edges */ - if (!(em->bm->totedgesel && - EDBM_op_callf(em, op, "delete geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES))) { + if (em->bm->totedgesel == 0) { + continue; + } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_callf(em, op, "delete geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES)) { continue; } break; case MESH_DELETE_FACE: /* Erase Faces */ - if (!(em->bm->totfacesel && - EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES))) { + if (em->bm->totfacesel == 0) { + continue; + } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES)) { continue; } break; - case MESH_DELETE_EDGE_FACE: - /* Edges and Faces */ - if (!((em->bm->totedgesel || em->bm->totfacesel) && - EDBM_op_callf( - em, op, "delete geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES))) { + case MESH_DELETE_EDGE_FACE: /* Edges and Faces */ + if ((em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) { + continue; + } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_callf( + em, op, "delete geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES)) { continue; } break; - case MESH_DELETE_ONLY_FACE: - /* Only faces. */ - if (!(em->bm->totfacesel && - EDBM_op_callf( - em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_ONLYFACES))) { + case MESH_DELETE_ONLY_FACE: /* Only faces. */ + if (em->bm->totfacesel == 0) { + continue; + } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_ONLYFACES)) { continue; } break; -- cgit v1.2.3 From 32844d32c1d90e6205145ef3bdde2f9281127c82 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Aug 2021 17:11:02 +1000 Subject: Edit Mesh: skip unselected meshes for "Set Normals from Faces" --- source/blender/editors/mesh/editmesh_tools.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 101c997fd1c..fa0762ee0bf 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -9531,6 +9531,10 @@ static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op) Object *obedit = objects[ob_index]; BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; + if (bm->totfacesel == 0) { + continue; + } + BMFace *f; BMVert *v; BMEdge *e; -- cgit v1.2.3 From 7304541f66a87e8131e9654a7fa1b9e77fb1fa4e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Aug 2021 17:11:03 +1000 Subject: Edit Mesh: skip unselected meshes for "Tris to Quads" Also move property assignment out of the object loop. --- source/blender/editors/mesh/editmesh_tools.c | 33 +++++++++++++++------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index fa0762ee0bf..b8bbf2d3e70 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -5576,24 +5576,24 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( view_layer, CTX_wm_view3d(C), &objects_len); - bool is_face_pair; + const bool do_seam = RNA_boolean_get(op->ptr, "seam"); + const bool do_sharp = RNA_boolean_get(op->ptr, "sharp"); + const bool do_uvs = RNA_boolean_get(op->ptr, "uvs"); + const bool do_vcols = RNA_boolean_get(op->ptr, "vcols"); + const bool do_materials = RNA_boolean_get(op->ptr, "materials"); + float angle_face_threshold, angle_shape_threshold; + bool is_face_pair; { int totelem_sel[3]; EDBM_mesh_stats_multi(objects, objects_len, NULL, totelem_sel); is_face_pair = (totelem_sel[2] == 2); } - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - - BMEditMesh *em = BKE_editmesh_from_object(obedit); - bool do_seam, do_sharp, do_uvs, do_vcols, do_materials; - float angle_face_threshold, angle_shape_threshold; + /* When joining exactly 2 faces, no limit. + * this is useful for one off joins while editing. */ + { PropertyRNA *prop; - - /* When joining exactly 2 faces, no limit. - * this is useful for one off joins while editing. */ prop = RNA_struct_find_property(op->ptr, "face_threshold"); if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) { angle_face_threshold = DEG2RADF(180.0f); @@ -5609,12 +5609,15 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) else { angle_shape_threshold = RNA_property_float_get(op->ptr, prop); } + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - do_seam = RNA_boolean_get(op->ptr, "seam"); - do_sharp = RNA_boolean_get(op->ptr, "sharp"); - do_uvs = RNA_boolean_get(op->ptr, "uvs"); - do_vcols = RNA_boolean_get(op->ptr, "vcols"); - do_materials = RNA_boolean_get(op->ptr, "materials"); + if (em->bm->totfacesel == 0) { + continue; + } BM_custom_loop_normals_to_vector_layer(em->bm); -- cgit v1.2.3 From 6028ac44a1351ef91a2fe3b98ab15f8690d885b5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Aug 2021 17:45:57 +1000 Subject: Cleanup: unused defines --- source/blender/blenlib/tests/BLI_array_store_test.cc | 11 ----------- source/blender/bmesh/operators/bmo_removedoubles.c | 1 - source/blender/makesdna/DNA_object_types.h | 2 -- source/blender/windowmanager/WM_types.h | 9 ++++----- 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/source/blender/blenlib/tests/BLI_array_store_test.cc b/source/blender/blenlib/tests/BLI_array_store_test.cc index 8bbd109fb81..89aeccdc105 100644 --- a/source/blender/blenlib/tests/BLI_array_store_test.cc +++ b/source/blender/blenlib/tests/BLI_array_store_test.cc @@ -187,17 +187,6 @@ static void testbuffer_list_state_from_data__stride_expand(ListBase *lb, ((void)0) /* test in both directions */ -#define TESTBUFFER_STRINGS_EX(bs, ...) \ - { \ - ListBase lb; \ - TESTBUFFER_STRINGS_CREATE(&lb, __VA_ARGS__); \ -\ - testbuffer_run_tests(bs, &lb); \ -\ - testbuffer_list_free(&lb); \ - } \ - ((void)0) - #define TESTBUFFER_STRINGS(stride, chunk_count, ...) \ { \ ListBase lb; \ diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c index 8cc0bfadbda..57760900d45 100644 --- a/source/blender/bmesh/operators/bmo_removedoubles.c +++ b/source/blender/bmesh/operators/bmo_removedoubles.c @@ -327,7 +327,6 @@ void bmo_weld_verts_exec(BMesh *bm, BMOperator *op) } #define VERT_KEEP 8 -#define VERT_IN 32 #define EDGE_MARK 1 diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index c6d6334118f..0250d853898 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -467,8 +467,6 @@ typedef struct ObHook { /* used many places, should be specialized. */ #define SELECT 1 -#define OBJECT_ACTIVE_MODIFIER_NONE -1 - /* type */ enum { OB_EMPTY = 0, diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 485d8e5a162..843ceca7700 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -328,11 +328,10 @@ typedef struct wmNotifier { #define ND_LAYOUTDELETE (2 << 16) #define ND_ANIMPLAY (4 << 16) #define ND_GPENCIL (5 << 16) -#define ND_EDITOR_CHANGED (6 << 16) /* Sent to new editors after switching to them. */ -#define ND_LAYOUTSET (7 << 16) -#define ND_SKETCH (8 << 16) -#define ND_WORKSPACE_SET (9 << 16) -#define ND_WORKSPACE_DELETE (10 << 16) +#define ND_LAYOUTSET (6 << 16) +#define ND_SKETCH (7 << 16) +#define ND_WORKSPACE_SET (8 << 16) +#define ND_WORKSPACE_DELETE (9 << 16) /* NC_SCENE Scene */ #define ND_SCENEBROWSE (1 << 16) -- cgit v1.2.3 From f8dd0080a9c490b017a95857ff750e79d4c6943a Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 17 Aug 2021 11:12:59 +0200 Subject: Cleanup: clang tidy The parameter name was inconsistent between declaratation and implementation. --- source/blender/gpu/intern/gpu_texture.cc | 36 +++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index 6564cbda694..d5d13ea269f 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -241,55 +241,61 @@ static inline GPUTexture *gpu_texture_create(const char *name, } 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) { - return gpu_texture_create(name, w, 0, 0, GPU_TEXTURE_1D, mips, format, GPU_DATA_FLOAT, data); + return gpu_texture_create(name, w, 0, 0, GPU_TEXTURE_1D, mip_len, format, GPU_DATA_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) { return gpu_texture_create( - name, w, h, 0, GPU_TEXTURE_1D_ARRAY, mips, format, GPU_DATA_FLOAT, data); + name, w, h, 0, GPU_TEXTURE_1D_ARRAY, mip_len, format, GPU_DATA_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) { - return gpu_texture_create(name, w, h, 0, GPU_TEXTURE_2D, mips, format, GPU_DATA_FLOAT, data); + return gpu_texture_create(name, w, h, 0, GPU_TEXTURE_2D, mip_len, format, GPU_DATA_FLOAT, data); } -GPUTexture *GPU_texture_create_2d_array( - const char *name, int w, int h, int d, int mips, eGPUTextureFormat format, const float *data) +GPUTexture *GPU_texture_create_2d_array(const char *name, + int w, + int h, + int d, + int mip_len, + eGPUTextureFormat format, + const float *data) { return gpu_texture_create( - name, w, h, d, GPU_TEXTURE_2D_ARRAY, mips, format, GPU_DATA_FLOAT, data); + name, w, h, d, GPU_TEXTURE_2D_ARRAY, mip_len, format, GPU_DATA_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) { return gpu_texture_create( - name, w, h, d, GPU_TEXTURE_3D, mips, texture_format, data_format, data); + name, w, h, d, GPU_TEXTURE_3D, mip_len, texture_format, data_format, 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) { - return gpu_texture_create(name, w, w, 0, GPU_TEXTURE_CUBE, mips, format, GPU_DATA_FLOAT, data); + return gpu_texture_create( + name, w, w, 0, GPU_TEXTURE_CUBE, mip_len, format, GPU_DATA_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) { return gpu_texture_create( - name, w, w, d, GPU_TEXTURE_CUBE_ARRAY, mips, format, GPU_DATA_FLOAT, data); + name, w, w, d, GPU_TEXTURE_CUBE_ARRAY, mip_len, format, GPU_DATA_FLOAT, data); } /* DDS texture loading. Return NULL if support is not available. */ -- cgit v1.2.3 From 0246128b7f9c353641733f38dfafec362c3cf933 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 17 Aug 2021 15:10:50 +0200 Subject: Fix wrong Anim Auto-Snap Ctrl toggle This was not working like elsewhere in both NLA and Graph Editor (meaning that when snapping was already enabled, {key Ctrl} during transform did not disable it). Now use getAnimEdit_SnapMode() for this in NLA and GE as well. Maniphest Tasks: T87173 Differential Revision: https://developer.blender.org/D12244 --- source/blender/editors/transform/transform_convert_graph.c | 11 ++++++----- source/blender/editors/transform/transform_convert_nla.c | 4 +++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index 111f81ff87b..a6cbbb299ac 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -41,6 +41,7 @@ #include "transform.h" #include "transform_convert.h" +#include "transform_mode.h" typedef struct TransDataGraph { float unit_scale; @@ -656,7 +657,6 @@ static bool fcu_test_selected(FCurve *fcu) */ static void flushTransGraphData(TransInfo *t) { - SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; TransData *td; TransData2D *td2d; TransDataGraph *tdg; @@ -680,7 +680,8 @@ static void flushTransGraphData(TransInfo *t) * - Don't do this when canceling, or else these changes won't go away. */ if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0) { - switch (sipo->autosnap) { + const short autosnap = getAnimEdit_SnapMode(t); + switch (autosnap) { case SACTSNAP_FRAME: /* snap to nearest frame */ td2d->loc[0] = floor((double)td2d->loc[0] + 0.5); break; @@ -714,9 +715,9 @@ static void flushTransGraphData(TransInfo *t) * * \note We don't do this when canceling transforms, or else these changes don't go away. */ - if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0 && - ELEM(sipo->autosnap, SACTSNAP_STEP, SACTSNAP_TSTEP)) { - switch (sipo->autosnap) { + if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0) { + const short autosnap = getAnimEdit_SnapMode(t); + switch (autosnap) { case SACTSNAP_STEP: /* frame step */ td2d->loc2d[0] = floor((double)td2d->loc[0] + 0.5); td->loc[0] = floor((double)td->loc[0] + 0.5); diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index b55005673d9..f96f2e93bbc 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -42,6 +42,7 @@ #include "transform.h" #include "transform_convert.h" +#include "transform_mode.h" /** Used for NLA transform (stored in #TransData.extra pointer). */ typedef struct TransDataNla { @@ -411,7 +412,8 @@ void recalcData_nla(TransInfo *t) * NOTE: only do this when transform is still running, or we can't restore */ if (t->state != TRANS_CANCEL) { - switch (snla->autosnap) { + const short autosnap = getAnimEdit_SnapMode(t); + switch (autosnap) { case SACTSNAP_FRAME: /* snap to nearest frame */ case SACTSNAP_STEP: /* frame step - this is basically the same, * since we don't have any remapping going on */ -- cgit v1.2.3 From 7f388725337d185a25b8dd3e90d479da86fb7aa1 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 18 Aug 2021 00:22:23 +1000 Subject: RNA: de-duplicate enums in generated source Reuse existing enums instead of expanding them since it bloats the binary. The icons enum for example contains over 900 items and was being expanded 17 times (once for each function that takes an icon argument). Similar with the event type enum which contains over 200 items and was duplicated 7 times. makesrna.c now matches enum definitions from declarations in RNA_enum_items.h, using their identifiers when found. The overall space saving on my system is 776kb (tested with a stripped release build). Reviewed By: brecht Ref D12245 --- source/blender/makesrna/RNA_enum_items.h | 240 ++++++++++++++++++++++++++++++ source/blender/makesrna/RNA_enum_types.h | 214 +------------------------- source/blender/makesrna/intern/makesrna.c | 93 ++++++++---- 3 files changed, 309 insertions(+), 238 deletions(-) create mode 100644 source/blender/makesrna/RNA_enum_items.h diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h new file mode 100644 index 00000000000..c8f44262020 --- /dev/null +++ b/source/blender/makesrna/RNA_enum_items.h @@ -0,0 +1,240 @@ +/* + * 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. + */ + +/** \file + * \ingroup RNA + */ + +/* NOTE: this is included multiple times with different #defines for DEF_ENUM. */ + +/* use in cases where only dynamic types are used */ +DEF_ENUM(DummyRNA_NULL_items) +DEF_ENUM(DummyRNA_DEFAULT_items) + +/* all others should follow 'rna_enum_*_items' naming */ +DEF_ENUM(rna_enum_id_type_items) + +DEF_ENUM(rna_enum_object_mode_items) +DEF_ENUM(rna_enum_workspace_object_mode_items) +DEF_ENUM(rna_enum_object_empty_drawtype_items) +DEF_ENUM(rna_enum_object_gpencil_type_items) +DEF_ENUM(rna_enum_metaelem_type_items) + +DEF_ENUM(rna_enum_proportional_falloff_items) +DEF_ENUM(rna_enum_proportional_falloff_curve_only_items) +DEF_ENUM(rna_enum_snap_target_items) +DEF_ENUM(rna_enum_snap_element_items) +DEF_ENUM(rna_enum_snap_node_element_items) +DEF_ENUM(rna_enum_curve_fit_method_items) +DEF_ENUM(rna_enum_mesh_select_mode_items) +DEF_ENUM(rna_enum_mesh_select_mode_uv_items) +DEF_ENUM(rna_enum_mesh_delimit_mode_items) +DEF_ENUM(rna_enum_space_graph_mode_items) +DEF_ENUM(rna_enum_space_file_browse_mode_items) +DEF_ENUM(rna_enum_space_sequencer_view_type_items) +DEF_ENUM(rna_enum_space_type_items) +DEF_ENUM(rna_enum_space_image_mode_items) +DEF_ENUM(rna_enum_space_image_mode_all_items) +DEF_ENUM(rna_enum_space_action_mode_items) +DEF_ENUM(rna_enum_fileselect_params_sort_items) +DEF_ENUM(rna_enum_region_type_items) +DEF_ENUM(rna_enum_object_modifier_type_items) +DEF_ENUM(rna_enum_constraint_type_items) +DEF_ENUM(rna_enum_boidrule_type_items) +DEF_ENUM(rna_enum_sequence_modifier_type_items) +DEF_ENUM(rna_enum_object_greasepencil_modifier_type_items) +DEF_ENUM(rna_enum_object_shaderfx_type_items) + +DEF_ENUM(rna_enum_modifier_triangulate_quad_method_items) +DEF_ENUM(rna_enum_modifier_triangulate_ngon_method_items) +DEF_ENUM(rna_enum_modifier_shrinkwrap_mode_items) + +DEF_ENUM(rna_enum_image_type_items) +DEF_ENUM(rna_enum_image_color_mode_items) +DEF_ENUM(rna_enum_image_color_depth_items) +DEF_ENUM(rna_enum_image_generated_type_items) + +DEF_ENUM(rna_enum_normal_space_items) +DEF_ENUM(rna_enum_normal_swizzle_items) +DEF_ENUM(rna_enum_bake_save_mode_items) +DEF_ENUM(rna_enum_bake_target_items) + +DEF_ENUM(rna_enum_views_format_items) +DEF_ENUM(rna_enum_views_format_multilayer_items) +DEF_ENUM(rna_enum_views_format_multiview_items) +DEF_ENUM(rna_enum_stereo3d_display_items) +DEF_ENUM(rna_enum_stereo3d_anaglyph_type_items) +DEF_ENUM(rna_enum_stereo3d_interlace_type_items) + +#ifdef WITH_OPENEXR +DEF_ENUM(rna_enum_exr_codec_items) +#endif +DEF_ENUM(rna_enum_color_sets_items) + +DEF_ENUM(rna_enum_beztriple_keyframe_type_items) +DEF_ENUM(rna_enum_beztriple_interpolation_mode_items) +DEF_ENUM(rna_enum_beztriple_interpolation_easing_items) +DEF_ENUM(rna_enum_fcurve_auto_smoothing_items) +DEF_ENUM(rna_enum_keyframe_handle_type_items) +DEF_ENUM(rna_enum_driver_target_rotation_mode_items) + +DEF_ENUM(rna_enum_keyingset_path_grouping_items) +DEF_ENUM(rna_enum_keying_flag_items) +DEF_ENUM(rna_enum_keying_flag_items_api) + +DEF_ENUM(rna_enum_fmodifier_type_items) + +DEF_ENUM(rna_enum_motionpath_bake_location_items) + +DEF_ENUM(rna_enum_event_value_all_items) +DEF_ENUM(rna_enum_event_value_keymouse_items) +DEF_ENUM(rna_enum_event_value_tweak_items) + +DEF_ENUM(rna_enum_event_type_items) +DEF_ENUM(rna_enum_event_type_mask_items) + +DEF_ENUM(rna_enum_operator_type_flag_items) +DEF_ENUM(rna_enum_operator_return_items) +DEF_ENUM(rna_enum_operator_property_tags) + +DEF_ENUM(rna_enum_brush_sculpt_tool_items) +DEF_ENUM(rna_enum_brush_uv_sculpt_tool_items) +DEF_ENUM(rna_enum_brush_vertex_tool_items) +DEF_ENUM(rna_enum_brush_weight_tool_items) +DEF_ENUM(rna_enum_brush_gpencil_types_items) +DEF_ENUM(rna_enum_brush_gpencil_vertex_types_items) +DEF_ENUM(rna_enum_brush_gpencil_sculpt_types_items) +DEF_ENUM(rna_enum_brush_gpencil_weight_types_items) +DEF_ENUM(rna_enum_brush_image_tool_items) + +DEF_ENUM(rna_enum_axis_xy_items) +DEF_ENUM(rna_enum_axis_xyz_items) + +DEF_ENUM(rna_enum_axis_flag_xyz_items) + +DEF_ENUM(rna_enum_symmetrize_direction_items) + +DEF_ENUM(rna_enum_texture_type_items) + +DEF_ENUM(rna_enum_light_type_items) + +DEF_ENUM(rna_enum_lightprobes_type_items) + +DEF_ENUM(rna_enum_unpack_method_items) + +DEF_ENUM(rna_enum_object_type_items) +DEF_ENUM(rna_enum_object_rotation_mode_items) + +DEF_ENUM(rna_enum_object_type_curve_items) + +DEF_ENUM(rna_enum_rigidbody_object_type_items) +DEF_ENUM(rna_enum_rigidbody_object_shape_items) +DEF_ENUM(rna_enum_rigidbody_constraint_type_items) + +DEF_ENUM(rna_enum_object_axis_items) + +DEF_ENUM(rna_enum_render_pass_type_items) + +DEF_ENUM(rna_enum_bake_pass_type_items) +DEF_ENUM(rna_enum_bake_pass_filter_type_items) + +DEF_ENUM(rna_enum_keymap_propvalue_items) + +DEF_ENUM(rna_enum_operator_context_items) + +DEF_ENUM(rna_enum_wm_report_items) + +DEF_ENUM(rna_enum_property_type_items) +DEF_ENUM(rna_enum_property_subtype_items) +DEF_ENUM(rna_enum_property_unit_items) + +DEF_ENUM(rna_enum_shading_type_items) + +DEF_ENUM(rna_enum_navigation_mode_items) + +DEF_ENUM(rna_enum_node_socket_in_out_items) + +DEF_ENUM(rna_enum_node_math_items) +DEF_ENUM(rna_enum_mapping_type_items) +DEF_ENUM(rna_enum_node_vec_math_items) +DEF_ENUM(rna_enum_node_boolean_math_items) +DEF_ENUM(rna_enum_node_float_compare_items) +DEF_ENUM(rna_enum_node_filter_items) +DEF_ENUM(rna_enum_node_float_to_int_items) +DEF_ENUM(rna_enum_node_map_range_items) +DEF_ENUM(rna_enum_node_clamp_items) + +DEF_ENUM(rna_enum_ramp_blend_items) + +DEF_ENUM(rna_enum_prop_dynamicpaint_type_items) + +DEF_ENUM(rna_enum_clip_editor_mode_items) + +DEF_ENUM(rna_enum_icon_items) +DEF_ENUM(rna_enum_uilist_layout_type_items) + +DEF_ENUM(rna_enum_linestyle_color_modifier_type_items) +DEF_ENUM(rna_enum_linestyle_alpha_modifier_type_items) +DEF_ENUM(rna_enum_linestyle_thickness_modifier_type_items) +DEF_ENUM(rna_enum_linestyle_geometry_modifier_type_items) + +DEF_ENUM(rna_enum_window_cursor_items) + +DEF_ENUM(rna_enum_dt_method_vertex_items) +DEF_ENUM(rna_enum_dt_method_edge_items) +DEF_ENUM(rna_enum_dt_method_loop_items) +DEF_ENUM(rna_enum_dt_method_poly_items) +DEF_ENUM(rna_enum_dt_mix_mode_items) +DEF_ENUM(rna_enum_dt_layers_select_src_items) +DEF_ENUM(rna_enum_dt_layers_select_dst_items) + +DEF_ENUM(rna_enum_context_mode_items) + +DEF_ENUM(rna_enum_preference_section_items) + +DEF_ENUM(rna_enum_attribute_type_items) +DEF_ENUM(rna_enum_attribute_type_with_auto_items) +DEF_ENUM(rna_enum_attribute_domain_items) +DEF_ENUM(rna_enum_attribute_domain_with_auto_items) + +DEF_ENUM(rna_enum_collection_color_items) + +DEF_ENUM(rna_enum_subdivision_uv_smooth_items) +DEF_ENUM(rna_enum_subdivision_boundary_smooth_items) + +DEF_ENUM(rna_enum_transform_orientation_items) + +/* Not available to RNA pre-processing (`makrsrna`). + * Defined in editors for example. */ +#ifndef RNA_MAKESRNA + +DEF_ENUM(rna_enum_particle_edit_hair_brush_items) +DEF_ENUM(rna_enum_particle_edit_disconnected_hair_brush_items) + +DEF_ENUM(rna_enum_keyframe_paste_offset_items) +DEF_ENUM(rna_enum_keyframe_paste_merge_items) + +DEF_ENUM(rna_enum_transform_pivot_items_full) +DEF_ENUM(rna_enum_transform_mode_types) + +/* In the runtime part of RNA, could be removed from this section. */ +DEF_ENUM(rna_enum_nla_mode_extend_items) +DEF_ENUM(rna_enum_nla_mode_blend_items) +DEF_ENUM(rna_enum_keyblock_type_items) + +#endif + +#undef DEF_ENUM diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index 7e3f279b251..d7520834287 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -32,221 +32,11 @@ struct bNodeTreeType; struct bNodeType; /* Types */ +#define DEF_ENUM(id) extern const EnumPropertyItem id[]; +#include "RNA_enum_items.h" -/* use in cases where only dynamic types are used */ -extern const EnumPropertyItem DummyRNA_NULL_items[]; -extern const EnumPropertyItem DummyRNA_DEFAULT_items[]; - -/* all others should follow 'rna_enum_*_items' naming */ -extern const EnumPropertyItem rna_enum_id_type_items[]; - -extern const EnumPropertyItem rna_enum_object_mode_items[]; -extern const EnumPropertyItem rna_enum_workspace_object_mode_items[]; -extern const EnumPropertyItem rna_enum_object_empty_drawtype_items[]; -extern const EnumPropertyItem rna_enum_object_gpencil_type_items[]; -extern const EnumPropertyItem rna_enum_metaelem_type_items[]; - -extern const EnumPropertyItem rna_enum_proportional_falloff_items[]; -extern const EnumPropertyItem rna_enum_proportional_falloff_curve_only_items[]; -extern const EnumPropertyItem rna_enum_snap_target_items[]; -extern const EnumPropertyItem rna_enum_snap_element_items[]; -extern const EnumPropertyItem rna_enum_snap_node_element_items[]; -extern const EnumPropertyItem rna_enum_curve_fit_method_items[]; -extern const EnumPropertyItem rna_enum_mesh_select_mode_items[]; -extern const EnumPropertyItem rna_enum_mesh_select_mode_uv_items[]; -extern const EnumPropertyItem rna_enum_mesh_delimit_mode_items[]; -extern const EnumPropertyItem rna_enum_space_graph_mode_items[]; -extern const EnumPropertyItem rna_enum_space_file_browse_mode_items[]; -extern const EnumPropertyItem rna_enum_space_sequencer_view_type_items[]; -extern const EnumPropertyItem rna_enum_space_type_items[]; -extern const EnumPropertyItem rna_enum_space_image_mode_items[]; -extern const EnumPropertyItem rna_enum_space_image_mode_all_items[]; -extern const EnumPropertyItem rna_enum_space_action_mode_items[]; -extern const EnumPropertyItem rna_enum_fileselect_params_sort_items[]; -extern const EnumPropertyItem rna_enum_region_type_items[]; -extern const EnumPropertyItem rna_enum_object_modifier_type_items[]; -extern const EnumPropertyItem rna_enum_constraint_type_items[]; -extern const EnumPropertyItem rna_enum_boidrule_type_items[]; -extern const EnumPropertyItem rna_enum_sequence_modifier_type_items[]; -extern const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[]; -extern const EnumPropertyItem rna_enum_object_shaderfx_type_items[]; - -extern const EnumPropertyItem rna_enum_modifier_triangulate_quad_method_items[]; -extern const EnumPropertyItem rna_enum_modifier_triangulate_ngon_method_items[]; -extern const EnumPropertyItem rna_enum_modifier_shrinkwrap_mode_items[]; - -extern const EnumPropertyItem rna_enum_image_type_items[]; -extern const EnumPropertyItem rna_enum_image_color_mode_items[]; -extern const EnumPropertyItem rna_enum_image_color_depth_items[]; -extern const EnumPropertyItem rna_enum_image_generated_type_items[]; - -extern const EnumPropertyItem rna_enum_normal_space_items[]; -extern const EnumPropertyItem rna_enum_normal_swizzle_items[]; -extern const EnumPropertyItem rna_enum_bake_save_mode_items[]; -extern const EnumPropertyItem rna_enum_bake_target_items[]; - -extern const EnumPropertyItem rna_enum_views_format_items[]; -extern const EnumPropertyItem rna_enum_views_format_multilayer_items[]; -extern const EnumPropertyItem rna_enum_views_format_multiview_items[]; -extern const EnumPropertyItem rna_enum_stereo3d_display_items[]; -extern const EnumPropertyItem rna_enum_stereo3d_anaglyph_type_items[]; -extern const EnumPropertyItem rna_enum_stereo3d_interlace_type_items[]; - -extern const EnumPropertyItem rna_enum_exr_codec_items[]; -extern const EnumPropertyItem rna_enum_color_sets_items[]; - -extern const EnumPropertyItem rna_enum_beztriple_keyframe_type_items[]; -extern const EnumPropertyItem rna_enum_beztriple_interpolation_mode_items[]; -extern const EnumPropertyItem rna_enum_beztriple_interpolation_easing_items[]; -extern const EnumPropertyItem rna_enum_fcurve_auto_smoothing_items[]; -extern const EnumPropertyItem rna_enum_keyframe_handle_type_items[]; -extern const EnumPropertyItem rna_enum_driver_target_rotation_mode_items[]; - -extern const EnumPropertyItem rna_enum_keyblock_type_items[]; - -extern const EnumPropertyItem rna_enum_keyingset_path_grouping_items[]; -extern const EnumPropertyItem rna_enum_keying_flag_items[]; -extern const EnumPropertyItem rna_enum_keying_flag_items_api[]; - -extern const EnumPropertyItem rna_enum_keyframe_paste_offset_items[]; -extern const EnumPropertyItem rna_enum_keyframe_paste_merge_items[]; - -extern const EnumPropertyItem rna_enum_fmodifier_type_items[]; - -extern const EnumPropertyItem rna_enum_nla_mode_extend_items[]; -extern const EnumPropertyItem rna_enum_nla_mode_blend_items[]; - -extern const EnumPropertyItem rna_enum_motionpath_bake_location_items[]; - -extern const EnumPropertyItem rna_enum_event_value_all_items[]; -extern const EnumPropertyItem rna_enum_event_value_keymouse_items[]; -extern const EnumPropertyItem rna_enum_event_value_tweak_items[]; - -extern const EnumPropertyItem rna_enum_event_type_items[]; -extern const EnumPropertyItem rna_enum_event_type_mask_items[]; - -extern const EnumPropertyItem rna_enum_operator_type_flag_items[]; -extern const EnumPropertyItem rna_enum_operator_return_items[]; -extern const EnumPropertyItem rna_enum_operator_property_tags[]; - -extern const EnumPropertyItem rna_enum_brush_sculpt_tool_items[]; -extern const EnumPropertyItem rna_enum_brush_uv_sculpt_tool_items[]; -extern const EnumPropertyItem rna_enum_brush_vertex_tool_items[]; -extern const EnumPropertyItem rna_enum_brush_weight_tool_items[]; -extern const EnumPropertyItem rna_enum_brush_gpencil_types_items[]; -extern const EnumPropertyItem rna_enum_brush_gpencil_vertex_types_items[]; -extern const EnumPropertyItem rna_enum_brush_gpencil_sculpt_types_items[]; -extern const EnumPropertyItem rna_enum_brush_gpencil_weight_types_items[]; -extern const EnumPropertyItem rna_enum_brush_image_tool_items[]; - -extern const EnumPropertyItem rna_enum_particle_edit_hair_brush_items[]; -extern const EnumPropertyItem rna_enum_particle_edit_disconnected_hair_brush_items[]; - -extern const EnumPropertyItem rna_enum_uv_sculpt_tool_items[]; - -extern const EnumPropertyItem rna_enum_axis_xy_items[]; -extern const EnumPropertyItem rna_enum_axis_xyz_items[]; - -extern const EnumPropertyItem rna_enum_axis_flag_xyz_items[]; - -extern const EnumPropertyItem rna_enum_symmetrize_direction_items[]; - -extern const EnumPropertyItem rna_enum_texture_type_items[]; - -extern const EnumPropertyItem rna_enum_light_type_items[]; - -extern const EnumPropertyItem rna_enum_lightprobes_type_items[]; - -extern const EnumPropertyItem rna_enum_unpack_method_items[]; - -extern const EnumPropertyItem rna_enum_object_type_items[]; -extern const EnumPropertyItem rna_enum_object_rotation_mode_items[]; - -extern const EnumPropertyItem rna_enum_object_type_curve_items[]; - -extern const EnumPropertyItem rna_enum_rigidbody_object_type_items[]; -extern const EnumPropertyItem rna_enum_rigidbody_object_shape_items[]; -extern const EnumPropertyItem rna_enum_rigidbody_constraint_type_items[]; - -extern const EnumPropertyItem rna_enum_object_axis_items[]; - -extern const EnumPropertyItem rna_enum_controller_type_items[]; - -extern const EnumPropertyItem rna_enum_render_pass_type_items[]; -extern const EnumPropertyItem rna_enum_render_pass_debug_type_items[]; - -extern const EnumPropertyItem rna_enum_bake_pass_type_items[]; -extern const EnumPropertyItem rna_enum_bake_pass_filter_type_items[]; - -extern const EnumPropertyItem rna_enum_keymap_propvalue_items[]; - -extern const EnumPropertyItem rna_enum_operator_context_items[]; - -extern const EnumPropertyItem rna_enum_wm_report_items[]; - -extern const EnumPropertyItem rna_enum_transform_pivot_items_full[]; -extern const EnumPropertyItem rna_enum_transform_orientation_items[]; -extern const EnumPropertyItem rna_enum_transform_mode_types[]; - -extern const EnumPropertyItem rna_enum_property_type_items[]; -extern const EnumPropertyItem rna_enum_property_subtype_items[]; -extern const EnumPropertyItem rna_enum_property_unit_items[]; - -extern const EnumPropertyItem rna_enum_shading_type_items[]; - -extern const EnumPropertyItem rna_enum_navigation_mode_items[]; - -extern const EnumPropertyItem rna_enum_node_socket_in_out_items[]; - -extern const EnumPropertyItem rna_enum_node_math_items[]; -extern const EnumPropertyItem rna_enum_mapping_type_items[]; -extern const EnumPropertyItem rna_enum_node_vec_math_items[]; -extern const EnumPropertyItem rna_enum_node_boolean_math_items[]; -extern const EnumPropertyItem rna_enum_node_float_compare_items[]; -extern const EnumPropertyItem rna_enum_node_filter_items[]; -extern const EnumPropertyItem rna_enum_node_float_to_int_items[]; -extern const EnumPropertyItem rna_enum_node_map_range_items[]; -extern const EnumPropertyItem rna_enum_node_clamp_items[]; - -extern const EnumPropertyItem rna_enum_ramp_blend_items[]; - -extern const EnumPropertyItem rna_enum_prop_dynamicpaint_type_items[]; - -extern const EnumPropertyItem rna_enum_clip_editor_mode_items[]; - -extern const EnumPropertyItem rna_enum_icon_items[]; -extern const EnumPropertyItem rna_enum_uilist_layout_type_items[]; - -extern const EnumPropertyItem rna_enum_linestyle_color_modifier_type_items[]; -extern const EnumPropertyItem rna_enum_linestyle_alpha_modifier_type_items[]; -extern const EnumPropertyItem rna_enum_linestyle_thickness_modifier_type_items[]; -extern const EnumPropertyItem rna_enum_linestyle_geometry_modifier_type_items[]; - -extern const EnumPropertyItem rna_enum_window_cursor_items[]; - -extern const EnumPropertyItem rna_enum_dt_method_vertex_items[]; -extern const EnumPropertyItem rna_enum_dt_method_edge_items[]; -extern const EnumPropertyItem rna_enum_dt_method_loop_items[]; -extern const EnumPropertyItem rna_enum_dt_method_poly_items[]; -extern const EnumPropertyItem rna_enum_dt_mix_mode_items[]; -extern const EnumPropertyItem rna_enum_dt_layers_select_src_items[]; -extern const EnumPropertyItem rna_enum_dt_layers_select_dst_items[]; - -extern const EnumPropertyItem rna_enum_context_mode_items[]; - -extern const EnumPropertyItem rna_enum_curveprofile_preset_items[]; -extern const EnumPropertyItem rna_enum_preference_section_items[]; - -extern const EnumPropertyItem rna_enum_attribute_type_items[]; -extern const EnumPropertyItem rna_enum_attribute_type_with_auto_items[]; -extern const EnumPropertyItem rna_enum_attribute_domain_items[]; -extern const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[]; extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bool *r_free); -extern const EnumPropertyItem rna_enum_collection_color_items[]; - -extern const EnumPropertyItem rna_enum_subdivision_uv_smooth_items[]; -extern const EnumPropertyItem rna_enum_subdivision_boundary_smooth_items[]; /** * For ID filters (#FILTER_ID_AC, #FILTER_ID_AR, ...) an int isn't enough. This version allows 64 * bit integers. So can't use the regular #EnumPropertyItem. Would be nice if RNA supported this diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 719b0f73a9d..36f19907080 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -582,6 +582,23 @@ static int rna_color_quantize(PropertyRNA *prop, PropertyDefRNA *dp) (IS_DNATYPE_FLOAT_COMPAT(dp->dnatype) == 0)); } +/** + * Return the identifier for an enum which is defined in "RNA_enum_items.h". + * + * Prevents expanding duplicate enums bloating the binary size. + */ +static const char *rna_enum_id_from_pointer(const EnumPropertyItem *item) +{ +#define RNA_MAKESRNA +#define DEF_ENUM(id) \ + if (item == id) { \ + return STRINGIFY(id); \ + } +#include "RNA_enum_items.h" +#undef RNA_MAKESRNA + return NULL; +} + static const char *rna_function_string(const void *func) { return (func) ? (const char *)func : "NULL"; @@ -3675,37 +3692,55 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr int i, defaultfound = 0, totflag = 0; if (eprop->item) { - fprintf(f, - "static const EnumPropertyItem rna_%s%s_%s_items[%d] = {\n\t", - srna->identifier, - strnest, - prop->identifier, - eprop->totitem + 1); - - for (i = 0; i < eprop->totitem; i++) { - fprintf(f, "{%d, ", eprop->item[i].value); - rna_print_c_string(f, eprop->item[i].identifier); - fprintf(f, ", "); - fprintf(f, "%d, ", eprop->item[i].icon); - rna_print_c_string(f, eprop->item[i].name); - fprintf(f, ", "); - rna_print_c_string(f, eprop->item[i].description); - fprintf(f, "},\n\t"); - - if (eprop->item[i].identifier[0]) { - if (prop->flag & PROP_ENUM_FLAG) { - totflag |= eprop->item[i].value; + /* Inline the enum if this is not a defined in "RNA_enum_items.h". */ + const char *item_global_id = rna_enum_id_from_pointer(eprop->item); + if (item_global_id == NULL) { + fprintf(f, + "static const EnumPropertyItem rna_%s%s_%s_items[%d] = {\n\t", + srna->identifier, + strnest, + prop->identifier, + eprop->totitem + 1); + + for (i = 0; i < eprop->totitem; i++) { + fprintf(f, "{%d, ", eprop->item[i].value); + rna_print_c_string(f, eprop->item[i].identifier); + fprintf(f, ", "); + fprintf(f, "%d, ", eprop->item[i].icon); + rna_print_c_string(f, eprop->item[i].name); + fprintf(f, ", "); + rna_print_c_string(f, eprop->item[i].description); + fprintf(f, "},\n\t"); + + if (eprop->item[i].identifier[0]) { + if (prop->flag & PROP_ENUM_FLAG) { + totflag |= eprop->item[i].value; + } + else { + if (eprop->defaultvalue == eprop->item[i].value) { + defaultfound = 1; + } + } } - else { - if (eprop->defaultvalue == eprop->item[i].value) { - defaultfound = 1; + } + + fprintf(f, "{0, NULL, 0, NULL, NULL}\n};\n\n"); + } + else { + for (i = 0; i < eprop->totitem; i++) { + if (eprop->item[i].identifier[0]) { + if (prop->flag & PROP_ENUM_FLAG) { + totflag |= eprop->item[i].value; + } + else { + if (eprop->defaultvalue == eprop->item[i].value) { + defaultfound = 1; + } } } } } - fprintf(f, "{0, NULL, 0, NULL, NULL}\n};\n\n"); - if (prop->flag & PROP_ENUM_FLAG) { if (eprop->defaultvalue & ~totflag) { CLOG_ERROR(&LOG, @@ -4047,7 +4082,13 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr rna_function_string(eprop->get_ex), rna_function_string(eprop->set_ex)); if (eprop->item) { - fprintf(f, "rna_%s%s_%s_items, ", srna->identifier, strnest, prop->identifier); + const char *item_global_id = rna_enum_id_from_pointer(eprop->item); + if (item_global_id != NULL) { + fprintf(f, "%s, ", item_global_id); + } + else { + fprintf(f, "rna_%s%s_%s_items, ", srna->identifier, strnest, prop->identifier); + } } else { fprintf(f, "NULL, "); -- cgit v1.2.3 From e98824d6c44d02a9f0223f5406112725899276c3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 18 Aug 2021 00:37:04 +1000 Subject: CMake: add missing headers --- source/blender/editors/util/CMakeLists.txt | 1 + source/blender/makesrna/intern/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 54ec6b22e70..b396e348845 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -64,6 +64,7 @@ set(SRC ../include/ED_info.h ../include/ED_keyframes_draw.h ../include/ED_keyframes_edit.h + ../include/ED_keyframes_keylist.h ../include/ED_keyframing.h ../include/ED_lattice.h ../include/ED_markers.h diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 95b7b7e5406..7e6d0aea2ee 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -175,6 +175,7 @@ set(SRC_RNA_INC ../RNA_access.h ../RNA_define.h ../RNA_documentation.h + ../RNA_enum_items.h ../RNA_enum_types.h ../RNA_types.h ) -- cgit v1.2.3 From 96d0cd57dcca122657eb14fe071c78cdf9580e0d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 17 Aug 2021 18:00:37 +0200 Subject: Fix T90719: Boost sources dowload address needed to be updated. --- build_files/build_environment/install_deps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index ff4aad79bb6..ecaff307885 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -1019,7 +1019,7 @@ PRINT "" PYTHON_SOURCE=( "https://www.python.org/ftp/python/$PYTHON_VERSION/Python-$PYTHON_VERSION.tgz" ) _boost_version_nodots=`echo "$BOOST_VERSION" | sed -r 's/\./_/g'` -BOOST_SOURCE=( "https://dl.bintray.com/boostorg/release/$BOOST_VERSION/source/boost_$_boost_version_nodots.tar.bz2" ) +BOOST_SOURCE=( "https://boostorg.jfrog.io/artifactory/main/release/$BOOST_VERSION/source/boost_$_boost_version_nodots.tar.bz2" ) BOOST_BUILD_MODULES="--with-system --with-filesystem --with-thread --with-regex --with-locale --with-date_time --with-wave --with-iostreams --with-python --with-program_options --with-serialization --with-atomic" TBB_SOURCE=( "https://github.com/oneapi-src/oneTBB/archive/$TBB_VERSION$TBB_VERSION_UPDATE.tar.gz" ) -- cgit v1.2.3 From 88dc274d0533e3b4427a61718958bf84367b5077 Mon Sep 17 00:00:00 2001 From: Henrik Dick Date: Tue, 17 Aug 2021 20:04:27 +0200 Subject: GPencil: Convert from Mesh copying Vertex Groups This patch adds the missing ability to keep the vertex groups when converting to a grease pencil object. This is increadible useful to create rigged grease pencil objects which move together with rigged meshes. Differential Revision: https://developer.blender.org/D12249 --- source/blender/blenkernel/BKE_gpencil_geom.h | 3 +- source/blender/blenkernel/intern/gpencil_geom.cc | 210 +++++++++++++---------- source/blender/editors/gpencil/gpencil_mesh.c | 3 +- source/blender/editors/object/object_add.c | 3 +- 4 files changed, 126 insertions(+), 93 deletions(-) diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index c1ccae7a437..29e3a74b1b2 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -169,7 +169,8 @@ bool BKE_gpencil_convert_mesh(struct Main *bmain, const float matrix[4][4], const int frame_offset, const bool use_seams, - const bool use_faces); + const bool use_faces, + const bool use_vgroups); void BKE_gpencil_stroke_uniform_subdivide(struct bGPdata *gpd, struct bGPDstroke *gps, diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index f8a07939096..f2042d3098c 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -2269,7 +2269,8 @@ static void gpencil_generate_edgeloops(Object *ob, const int thickness, const float offset, const float matrix[4][4], - const bool use_seams) + const bool use_seams, + const bool use_vgroups) { Mesh *me = (Mesh *)ob->data; if (me->totedge == 0) { @@ -2278,9 +2279,9 @@ static void gpencil_generate_edgeloops(Object *ob, /* Arrays for all edge vertices (forward and backward) that form a edge loop. * This is reused for each edge-loop to create gpencil stroke. */ - uint *stroke = (uint *)MEM_callocN(sizeof(uint) * me->totedge * 2, __func__); - uint *stroke_fw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__); - uint *stroke_bw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__); + uint *stroke = (uint *)MEM_mallocN(sizeof(uint) * me->totedge * 2, __func__); + uint *stroke_fw = (uint *)MEM_mallocN(sizeof(uint) * me->totedge, __func__); + uint *stroke_bw = (uint *)MEM_mallocN(sizeof(uint) * me->totedge, __func__); /* Create array with all edges. */ GpEdge *gp_edges = (GpEdge *)MEM_callocN(sizeof(GpEdge) * me->totedge, __func__); @@ -2311,11 +2312,6 @@ static void gpencil_generate_edgeloops(Object *ob, bool pending = true; int e = 0; while (pending) { - /* Clear arrays of stroke. */ - memset(stroke_fw, 0, sizeof(uint) * me->totedge); - memset(stroke_bw, 0, sizeof(uint) * me->totedge); - memset(stroke, 0, sizeof(uint) * me->totedge * 2); - gped = &gp_edges[e]; /* Look first unused edge. */ if (gped->flag != 0) { @@ -2330,7 +2326,7 @@ static void gpencil_generate_edgeloops(Object *ob, stroke_bw[0] = e; gped->flag = 1; - /* Hash used to avoid loop over same vertice. */ + /* Hash used to avoid loop over same vertices. */ GHash *v_table = BLI_ghash_int_new(__func__); /* Look forward edges. */ int totedges = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_fw, e, angle, false); @@ -2354,38 +2350,41 @@ static void gpencil_generate_edgeloops(Object *ob, bGPDstroke *gps_stroke = BKE_gpencil_stroke_add( gpf_stroke, MAX2(stroke_mat_index, 0), array_len + 1, thickness * thickness, false); + /* Create dvert data. */ + MDeformVert *me_dvert = me->dvert; + if (use_vgroups && me_dvert) { + gps_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * (array_len + 1), + "gp_stroke_dverts"); + } + /* Create first segment. */ float fpt[3]; - uint v = stroke[0]; - gped = &gp_edges[v]; - bGPDspoint *pt = &gps_stroke->points[0]; - mul_v3_v3fl(fpt, gped->n1, offset); - add_v3_v3v3(&pt->x, gped->v1_co, fpt); - mul_m4_v3(matrix, &pt->x); - - pt->pressure = 1.0f; - pt->strength = 1.0f; - - pt = &gps_stroke->points[1]; - mul_v3_v3fl(fpt, gped->n2, offset); - add_v3_v3v3(&pt->x, gped->v2_co, fpt); - mul_m4_v3(matrix, &pt->x); - - pt->pressure = 1.0f; - pt->strength = 1.0f; - - /* Add next segments. */ - for (int i = 1; i < array_len; i++) { - v = stroke[i]; - gped = &gp_edges[v]; - - pt = &gps_stroke->points[i + 1]; - mul_v3_v3fl(fpt, gped->n2, offset); - add_v3_v3v3(&pt->x, gped->v2_co, fpt); + for (int i = 0; i < array_len + 1; i++) { + int vertex_index = i == 0 ? gp_edges[stroke[0]].v1 : gp_edges[stroke[i - 1]].v2; + MVert *mv = &me->mvert[vertex_index]; + + /* Add segment. */ + bGPDspoint *pt = &gps_stroke->points[i]; + normal_short_to_float_v3(fpt, mv->no); + mul_v3_v3fl(fpt, fpt, offset); + add_v3_v3v3(&pt->x, mv->co, fpt); mul_m4_v3(matrix, &pt->x); pt->pressure = 1.0f; pt->strength = 1.0f; + + /* Copy vertex groups from mesh. Assuming they already exist in the same order. */ + if (use_vgroups && me_dvert) { + MDeformVert *dv = &gps_stroke->dvert[i]; + MDeformVert *src_dv = &me_dvert[vertex_index]; + dv->totweight = src_dv->totweight; + dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight, + "gp_stroke_dverts_dw"); + for (int j = 0; j < dv->totweight; j++) { + dv->dw[j].weight = src_dv->dw[j].weight; + dv->dw[j].def_nr = src_dv->dw[j].def_nr; + } + } } BKE_gpencil_stroke_geometry_update(gpd, gps_stroke); @@ -2488,7 +2487,8 @@ bool BKE_gpencil_convert_mesh(Main *bmain, const float matrix[4][4], const int frame_offset, const bool use_seams, - const bool use_faces) + const bool use_faces, + const bool use_vgroups) { if (ELEM(nullptr, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == nullptr)) { return false; @@ -2505,83 +2505,105 @@ bool BKE_gpencil_convert_mesh(Main *bmain, char element_name[200]; /* Need at least an edge. */ - if (me_eval->totvert < 2) { + if (me_eval->totedge < 1) { return false; } + /* Create matching vertex groups. */ + BKE_defgroup_copy_list(&gpd->vertex_group_names, &me_eval->vertex_group_names); + gpd->vertex_group_active_index = me_eval->vertex_group_active_index; + const float default_colors[2][4] = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.7f, 0.7f, 0.7f, 1.0f}}; - /* Create stroke material. */ + /* Lookup existing stroke material on gp object. */ make_element_name(ob_mesh->id.name + 2, "Stroke", 64, element_name); int stroke_mat_index = gpencil_material_find_index_by_name(ob_gp, element_name); if (stroke_mat_index == -1) { + /* Create new default stroke material as there is no existing material. */ gpencil_add_material( bmain, ob_gp, element_name, default_colors[0], true, false, &stroke_mat_index); } /* Export faces as filled strokes. */ - if (use_faces) { - + if (use_faces && mpoly_len > 0) { /* Read all polygons and create fill for each. */ - if (mpoly_len > 0) { - make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name); - /* Create Layer and Frame. */ - bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name); - if (gpl_fill == nullptr) { - gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false); - } - bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get( - gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW); - int i; - for (i = 0; i < mpoly_len; i++) { - const MPoly *mp = &mpoly[i]; - - /* Find material. */ - int mat_idx = 0; - Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1); - make_element_name( - ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name); - mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name); - if (mat_idx == -1) { - float color[4]; - if (ma != nullptr) { - copy_v3_v3(color, &ma->r); - color[3] = 1.0f; - } - else { - copy_v4_v4(color, default_colors[1]); - } - gpencil_add_material(bmain, ob_gp, element_name, color, false, true, &mat_idx); + make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name); + /* Create Layer and Frame. */ + bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name); + if (gpl_fill == nullptr) { + gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false); + } + bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get( + gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW); + int i; + for (i = 0; i < mpoly_len; i++) { + const MPoly *mp = &mpoly[i]; + + /* Find material. */ + int mat_idx = 0; + Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1); + make_element_name( + ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name); + mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name); + if (mat_idx == -1) { + float color[4]; + if (ma != nullptr) { + copy_v3_v3(color, &ma->r); + color[3] = 1.0f; } + else { + copy_v4_v4(color, default_colors[1]); + } + gpencil_add_material(bmain, ob_gp, element_name, color, false, true, &mat_idx); + } - bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, mp->totloop, 10, false); - gps_fill->flag |= GP_STROKE_CYCLIC; + bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, mp->totloop, 10, false); + gps_fill->flag |= GP_STROKE_CYCLIC; - /* Add points to strokes. */ - for (int j = 0; j < mp->totloop; j++) { - const MLoop *ml = &mloop[mp->loopstart + j]; - const MVert *mv = &me_eval->mvert[ml->v]; + /* Create dvert data. */ + MDeformVert *me_dvert = me_eval->dvert; + if (use_vgroups && me_dvert) { + gps_fill->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * mp->totloop, + "gp_fill_dverts"); + } - bGPDspoint *pt = &gps_fill->points[j]; - copy_v3_v3(&pt->x, mv->co); - mul_m4_v3(matrix, &pt->x); - pt->pressure = 1.0f; - pt->strength = 1.0f; - } - /* If has only 3 points subdivide. */ - if (mp->totloop == 3) { - BKE_gpencil_stroke_subdivide(gpd, gps_fill, 1, GP_SUBDIV_SIMPLE); + /* Add points to strokes. */ + for (int j = 0; j < mp->totloop; j++) { + const MLoop *ml = &mloop[mp->loopstart + j]; + const MVert *mv = &me_eval->mvert[ml->v]; + + bGPDspoint *pt = &gps_fill->points[j]; + copy_v3_v3(&pt->x, mv->co); + mul_m4_v3(matrix, &pt->x); + pt->pressure = 1.0f; + pt->strength = 1.0f; + + /* Copy vertex groups from mesh. Assuming they already exist in the same order. */ + if (use_vgroups && me_dvert) { + MDeformVert *dv = &gps_fill->dvert[j]; + MDeformVert *src_dv = &me_dvert[ml->v]; + dv->totweight = src_dv->totweight; + dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight, + "gp_fill_dverts_dw"); + for (int k = 0; k < dv->totweight; k++) { + dv->dw[k].weight = src_dv->dw[k].weight; + dv->dw[k].def_nr = src_dv->dw[k].def_nr; + } } - - BKE_gpencil_stroke_geometry_update(gpd, gps_fill); } + /* If has only 3 points subdivide. */ + if (mp->totloop == 3) { + BKE_gpencil_stroke_subdivide(gpd, gps_fill, 1, GP_SUBDIV_SIMPLE); + } + + BKE_gpencil_stroke_geometry_update(gpd, gps_fill); } } /* Create stroke from edges. */ - make_element_name(ob_mesh->id.name + 2, "Lines", 128, element_name); /* Create Layer and Frame. */ + make_element_name(ob_mesh->id.name + 2, "Lines", 128, element_name); bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name); if (gpl_stroke == nullptr) { gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false); @@ -2589,8 +2611,16 @@ bool BKE_gpencil_convert_mesh(Main *bmain, bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get( gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW); - gpencil_generate_edgeloops( - ob_eval, gpd, gpf_stroke, stroke_mat_index, angle, thickness, offset, matrix, use_seams); + gpencil_generate_edgeloops(ob_eval, + gpd, + gpf_stroke, + stroke_mat_index, + angle, + thickness, + offset, + matrix, + use_seams, + use_vgroups); /* Tag for recalculation */ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c index 1882285a230..0939d53736b 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ b/source/blender/editors/gpencil/gpencil_mesh.c @@ -316,7 +316,8 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) ob_eval->obmat, frame_offset, use_seams, - use_faces); + use_faces, + true); /* Reproject all un-tagged created strokes. */ if (project_type != GP_REPROJECT_KEEP) { diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index f98f3242163..12b52907057 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -2844,7 +2844,8 @@ static int object_convert_exec(bContext *C, wmOperator *op) matrix, 0, use_seams, - use_faces); + use_faces, + true); /* Remove unused materials. */ int actcol = ob_gpencil->actcol; -- cgit v1.2.3 From 60d6333b80472ca9eb2e1ab659da0fa2572d0a8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 13 Aug 2021 13:19:05 +0200 Subject: Fix T82336: Cycles standard attributes missing in displacement shaders Standard attributes are not added to the attributes requests when shaders only have displacement. This is because nodes are only considering the case when the surface socket is connected. To support this, added `Shader.has_surface_link()` which checks for both cases (`has_surface` and `has_displacement`) and replaces all checks on `Shader.has_surface`. Reviewed By: brecht Differential Revision: https://developer.blender.org/D12240 --- intern/cycles/render/graph.cpp | 4 ++-- intern/cycles/render/nodes.cpp | 20 ++++++++++---------- intern/cycles/render/shader.h | 8 ++++++++ 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index cdaa878e830..e9da48b624d 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -158,13 +158,13 @@ void ShaderNode::attributes(Shader *shader, AttributeRequestSet *attributes) foreach (ShaderInput *input, inputs) { if (!input->link) { if (input->flags() & SocketType::LINK_TEXTURE_GENERATED) { - if (shader->has_surface) + if (shader->has_surface_link()) attributes->add(ATTR_STD_GENERATED); if (shader->has_volume) attributes->add(ATTR_STD_GENERATED_TRANSFORM); } else if (input->flags() & SocketType::LINK_TEXTURE_UV) { - if (shader->has_surface) + if (shader->has_surface_link()) attributes->add(ATTR_STD_UV); } } diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 42cba342bf8..795166bcf4c 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -350,7 +350,7 @@ void ImageTextureNode::attributes(Shader *shader, AttributeRequestSet *attribute #ifdef WITH_PTEX /* todo: avoid loading other texture coordinates when using ptex, * and hide texture coordinate socket in the UI */ - if (shader->has_surface && string_endswith(filename, ".ptx")) { + if (shader->has_surface_link() && string_endswith(filename, ".ptx")) { /* ptex */ attributes->add(ATTR_STD_PTEX_FACE_ID); attributes->add(ATTR_STD_PTEX_UV); @@ -552,7 +552,7 @@ ImageParams EnvironmentTextureNode::image_params() const void EnvironmentTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes) { #ifdef WITH_PTEX - if (shader->has_surface && string_endswith(filename, ".ptx")) { + if (shader->has_surface_link() && string_endswith(filename, ".ptx")) { /* ptex */ attributes->add(ATTR_STD_PTEX_FACE_ID); attributes->add(ATTR_STD_PTEX_UV); @@ -2319,7 +2319,7 @@ AnisotropicBsdfNode::AnisotropicBsdfNode() : BsdfNode(get_node_type()) void AnisotropicBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes) { - if (shader->has_surface) { + if (shader->has_surface_link()) { ShaderInput *tangent_in = input("Tangent"); if (!tangent_in->link) @@ -2843,7 +2843,7 @@ bool PrincipledBsdfNode::has_surface_bssrdf() void PrincipledBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes) { - if (shader->has_surface) { + if (shader->has_surface_link()) { ShaderInput *tangent_in = input("Tangent"); if (!tangent_in->link) @@ -3684,7 +3684,7 @@ GeometryNode::GeometryNode() : ShaderNode(get_node_type()) void GeometryNode::attributes(Shader *shader, AttributeRequestSet *attributes) { - if (shader->has_surface) { + if (shader->has_surface_link()) { if (!output("Tangent")->links.empty()) { attributes->add(ATTR_STD_GENERATED); } @@ -3830,7 +3830,7 @@ TextureCoordinateNode::TextureCoordinateNode() : ShaderNode(get_node_type()) void TextureCoordinateNode::attributes(Shader *shader, AttributeRequestSet *attributes) { - if (shader->has_surface) { + if (shader->has_surface_link()) { if (!from_dupli) { if (!output("Generated")->links.empty()) attributes->add(ATTR_STD_GENERATED); @@ -4388,7 +4388,7 @@ HairInfoNode::HairInfoNode() : ShaderNode(get_node_type()) void HairInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes) { - if (shader->has_surface) { + if (shader->has_surface_link()) { ShaderOutput *intercept_out = output("Intercept"); if (!intercept_out->links.empty()) @@ -6744,7 +6744,7 @@ NormalMapNode::NormalMapNode() : ShaderNode(get_node_type()) void NormalMapNode::attributes(Shader *shader, AttributeRequestSet *attributes) { - if (shader->has_surface && space == NODE_NORMAL_MAP_TANGENT) { + if (shader->has_surface_link() && space == NODE_NORMAL_MAP_TANGENT) { if (attribute.empty()) { attributes->add(ATTR_STD_UV_TANGENT); attributes->add(ATTR_STD_UV_TANGENT_SIGN); @@ -6838,7 +6838,7 @@ TangentNode::TangentNode() : ShaderNode(get_node_type()) void TangentNode::attributes(Shader *shader, AttributeRequestSet *attributes) { - if (shader->has_surface) { + if (shader->has_surface_link()) { if (direction_type == NODE_TANGENT_UVMAP) { if (attribute.empty()) attributes->add(ATTR_STD_UV_TANGENT); @@ -7021,7 +7021,7 @@ void VectorDisplacementNode::constant_fold(const ConstantFolder &folder) void VectorDisplacementNode::attributes(Shader *shader, AttributeRequestSet *attributes) { - if (shader->has_surface && space == NODE_NORMAL_MAP_TANGENT) { + if (shader->has_surface_link() && space == NODE_NORMAL_MAP_TANGENT) { if (attribute.empty()) { attributes->add(ATTR_STD_UV_TANGENT); attributes->add(ATTR_STD_UV_TANGENT_SIGN); diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h index 50c8bed4669..c65cac351a4 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/render/shader.h @@ -154,6 +154,14 @@ class Shader : public Node { void tag_update(Scene *scene); void tag_used(Scene *scene); + /* Return true when either of the surface or displacement socket of the output node is linked. + * This should be used to ensure that surface attributes are also requested even when only the + * displacement socket is linked. */ + bool has_surface_link() const + { + return has_surface || has_displacement; + } + bool need_update_geometry() const; }; -- cgit v1.2.3 From 23132fcdc136d1238d9980c2a9bf6d058e846cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 13 Aug 2021 13:31:21 +0200 Subject: Fix T77307: Particle Info Node Does Not Consider Time Remapping `frame_current_final()` should be used to access the Scene time after remapping, which also matches how the particles system handles time. Reviewed By: brecht Maniphest Tasks: T77307 Differential Revision: https://developer.blender.org/D12239 --- intern/cycles/blender/blender_particles.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/blender/blender_particles.cpp b/intern/cycles/blender/blender_particles.cpp index ae43dae4dc1..206ee24a093 100644 --- a/intern/cycles/blender/blender_particles.cpp +++ b/intern/cycles/blender/blender_particles.cpp @@ -71,7 +71,7 @@ bool BlenderSync::sync_dupli_particle(BL::Object &b_ob, Particle pa; pa.index = persistent_id[0]; - pa.age = b_scene.frame_current() - b_pa.birth_time(); + pa.age = b_scene.frame_current_final() - b_pa.birth_time(); pa.lifetime = b_pa.lifetime(); pa.location = get_float3(b_pa.location()); pa.rotation = get_float4(b_pa.rotation()); -- cgit v1.2.3 From e3098de2a1fedd98d9f31d3cb562116f0945b7f9 Mon Sep 17 00:00:00 2001 From: Henrik Dick Date: Tue, 17 Aug 2021 22:16:16 +0200 Subject: GPencil: Fix unreported switch direction not flipping weights There was an unreported bug that switch direction would not switch the order of the vertex group weights. This caused join to do it wrong as well. Changed to use `BLI_array_reverse` function here to reverse both the normal points and the weights, therefore simplifying the code. Differential Revision: https://developer.blender.org/D12251 --- source/blender/blenkernel/intern/gpencil_geom.cc | 45 ++++-------------------- 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index f2042d3098c..0f218d6166c 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -31,6 +31,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_array_utils.h" #include "BLI_blenlib.h" #include "BLI_float3.hh" #include "BLI_ghash.h" @@ -2817,46 +2818,12 @@ void BKE_gpencil_stroke_set_random_color(bGPDstroke *gps) /* Flip stroke. */ void BKE_gpencil_stroke_flip(bGPDstroke *gps) { - int end = gps->totpoints - 1; + /* Reverse points. */ + BLI_array_reverse(gps->points, gps->totpoints); - for (int i = 0; i < gps->totpoints / 2; i++) { - bGPDspoint *point, *point2; - bGPDspoint pt; - - /* save first point */ - point = &gps->points[i]; - pt.x = point->x; - pt.y = point->y; - pt.z = point->z; - pt.flag = point->flag; - pt.pressure = point->pressure; - pt.strength = point->strength; - pt.time = point->time; - copy_v4_v4(pt.vert_color, point->vert_color); - - /* replace first point with last point */ - point2 = &gps->points[end]; - point->x = point2->x; - point->y = point2->y; - point->z = point2->z; - point->flag = point2->flag; - point->pressure = point2->pressure; - point->strength = point2->strength; - point->time = point2->time; - copy_v4_v4(point->vert_color, point2->vert_color); - - /* replace last point with first saved before */ - point = &gps->points[end]; - point->x = pt.x; - point->y = pt.y; - point->z = pt.z; - point->flag = pt.flag; - point->pressure = pt.pressure; - point->strength = pt.strength; - point->time = pt.time; - copy_v4_v4(point->vert_color, pt.vert_color); - - end--; + /* Reverse vertex groups if available. */ + if (gps->dvert) { + BLI_array_reverse(gps->dvert, gps->totpoints); } } -- cgit v1.2.3 From 809dce5bdeeb193a160b2961bf069eab76c2ce01 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 17 Aug 2021 16:53:29 -0300 Subject: Cleanup: move 'recalcData' to 'transform_convert.h' The `recalcData` function is defined in `transform_convert.c`, so the header is most expected to be `transform_convert.h`. --- source/blender/editors/transform/transform.h | 1 - source/blender/editors/transform/transform_convert.h | 1 + source/blender/editors/transform/transform_generics.c | 1 + source/blender/editors/transform/transform_mode_align.c | 2 ++ source/blender/editors/transform/transform_mode_baketime.c | 4 +++- source/blender/editors/transform/transform_mode_bbone_resize.c | 4 +++- source/blender/editors/transform/transform_mode_bend.c | 4 +++- source/blender/editors/transform/transform_mode_boneenvelope.c | 4 +++- source/blender/editors/transform/transform_mode_boneroll.c | 4 +++- source/blender/editors/transform/transform_mode_curveshrinkfatten.c | 4 +++- source/blender/editors/transform/transform_mode_edge_bevelweight.c | 4 +++- source/blender/editors/transform/transform_mode_edge_crease.c | 4 +++- source/blender/editors/transform/transform_mode_edge_rotate_normal.c | 3 ++- source/blender/editors/transform/transform_mode_gpopacity.c | 4 +++- source/blender/editors/transform/transform_mode_gpshrinkfatten.c | 4 +++- source/blender/editors/transform/transform_mode_maskshrinkfatten.c | 4 +++- source/blender/editors/transform/transform_mode_mirror.c | 2 ++ source/blender/editors/transform/transform_mode_push_pull.c | 4 +++- source/blender/editors/transform/transform_mode_rotate.c | 4 +++- source/blender/editors/transform/transform_mode_shear.c | 4 +++- source/blender/editors/transform/transform_mode_shrink_fatten.c | 4 +++- source/blender/editors/transform/transform_mode_skin_resize.c | 4 +++- source/blender/editors/transform/transform_mode_tilt.c | 4 +++- source/blender/editors/transform/transform_mode_timescale.c | 1 + source/blender/editors/transform/transform_mode_timeslide.c | 2 ++ source/blender/editors/transform/transform_mode_timetranslate.c | 4 +++- source/blender/editors/transform/transform_mode_tosphere.c | 4 +++- source/blender/editors/transform/transform_mode_trackball.c | 4 +++- 28 files changed, 71 insertions(+), 22 deletions(-) diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 1fa123e8507..549ad770ac6 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -779,7 +779,6 @@ void drawLine(TransInfo *t, const float center[3], const float dir[3], char axis void applyTransObjects(TransInfo *t); void restoreTransObjects(TransInfo *t); -void recalcData(TransInfo *t); void calculateCenter2D(TransInfo *t); void calculateCenterLocal(TransInfo *t, const float center_global[3]); diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 971c23b8c69..55731bfa321 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -43,6 +43,7 @@ void sort_trans_data_dist(TransInfo *t); void createTransData(struct bContext *C, TransInfo *t); bool clipUVTransform(TransInfo *t, float vec[2], const bool resize); void clipUVData(TransInfo *t); +void recalcData(TransInfo *t); /* transform_convert_mesh.c */ void transform_convert_mesh_customdatacorrect_init(TransInfo *t); diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index be8e551a1e8..81fc1496b1a 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -60,6 +60,7 @@ #include "UI_view2d.h" #include "transform.h" +#include "transform_convert.h" #include "transform_mode.h" #include "transform_orientations.h" #include "transform_snap.h" diff --git a/source/blender/editors/transform/transform_mode_align.c b/source/blender/editors/transform/transform_mode_align.c index 5bc2aa68443..1a1d84699f4 100644 --- a/source/blender/editors/transform/transform_mode_align.c +++ b/source/blender/editors/transform/transform_mode_align.c @@ -32,6 +32,8 @@ #include "BLT_translation.h" #include "transform.h" +#include "transform_convert.h" + #include "transform_mode.h" /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_mode_baketime.c b/source/blender/editors/transform/transform_mode_baketime.c index 5efed6920dc..653944b56a7 100644 --- a/source/blender/editors/transform/transform_mode_baketime.c +++ b/source/blender/editors/transform/transform_mode_baketime.c @@ -36,9 +36,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Bake-Time) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_bbone_resize.c b/source/blender/editors/transform/transform_mode_bbone_resize.c index e827e604327..95e2d944b9b 100644 --- a/source/blender/editors/transform/transform_mode_bbone_resize.c +++ b/source/blender/editors/transform/transform_mode_bbone_resize.c @@ -37,9 +37,11 @@ #include "transform.h" #include "transform_constraints.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (EditBone B-Bone width scaling) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_bend.c b/source/blender/editors/transform/transform_mode_bend.c index 850d26571cd..6d84c397fa6 100644 --- a/source/blender/editors/transform/transform_mode_bend.c +++ b/source/blender/editors/transform/transform_mode_bend.c @@ -44,9 +44,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Bend) Custom Data * \{ */ diff --git a/source/blender/editors/transform/transform_mode_boneenvelope.c b/source/blender/editors/transform/transform_mode_boneenvelope.c index ced159a76c9..da7393ab42e 100644 --- a/source/blender/editors/transform/transform_mode_boneenvelope.c +++ b/source/blender/editors/transform/transform_mode_boneenvelope.c @@ -36,9 +36,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Bone Envelope) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_boneroll.c b/source/blender/editors/transform/transform_mode_boneroll.c index da6c0b44c3a..cd04ca2b844 100644 --- a/source/blender/editors/transform/transform_mode_boneroll.c +++ b/source/blender/editors/transform/transform_mode_boneroll.c @@ -36,9 +36,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (EditBone Roll) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c index 68416c780ef..9433502ef55 100644 --- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c @@ -36,9 +36,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Curve Shrink/Fatten) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_edge_bevelweight.c b/source/blender/editors/transform/transform_mode_edge_bevelweight.c index 425bfec241e..5466ba3e91f 100644 --- a/source/blender/editors/transform/transform_mode_edge_bevelweight.c +++ b/source/blender/editors/transform/transform_mode_edge_bevelweight.c @@ -37,9 +37,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Bevel Weight) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_edge_crease.c b/source/blender/editors/transform/transform_mode_edge_crease.c index 91e2507e544..1d3b4dbb4f0 100644 --- a/source/blender/editors/transform/transform_mode_edge_crease.c +++ b/source/blender/editors/transform/transform_mode_edge_crease.c @@ -37,9 +37,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Crease) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c index 6f2bcc148ce..1f57bacf78f 100644 --- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c +++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c @@ -35,9 +35,10 @@ #include "UI_interface.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" /* -------------------------------------------------------------------- */ /** \name Transform (Normal Rotation) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c index 7c496d271ef..748769491f1 100644 --- a/source/blender/editors/transform/transform_mode_gpopacity.c +++ b/source/blender/editors/transform/transform_mode_gpopacity.c @@ -38,9 +38,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (GPencil Strokes Opacity) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c index 608a49f38b1..bc081edd597 100644 --- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c @@ -38,9 +38,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (GPencil Strokes Shrink/Fatten) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c index cfbd6030788..327a639773c 100644 --- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c @@ -36,9 +36,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Mask Shrink/Fatten) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_mirror.c b/source/blender/editors/transform/transform_mode_mirror.c index f225f1a7c06..2ae32f3545a 100644 --- a/source/blender/editors/transform/transform_mode_mirror.c +++ b/source/blender/editors/transform/transform_mode_mirror.c @@ -37,6 +37,8 @@ #include "BLT_translation.h" #include "transform.h" +#include "transform_convert.h" + #include "transform_mode.h" /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_mode_push_pull.c b/source/blender/editors/transform/transform_mode_push_pull.c index 0492ec8df8c..0527d1bc08e 100644 --- a/source/blender/editors/transform/transform_mode_push_pull.c +++ b/source/blender/editors/transform/transform_mode_push_pull.c @@ -38,9 +38,11 @@ #include "transform.h" #include "transform_constraints.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Push/Pull) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c index 44a29cfac45..bfbdaa389f4 100644 --- a/source/blender/editors/transform/transform_mode_rotate.c +++ b/source/blender/editors/transform/transform_mode_rotate.c @@ -34,9 +34,11 @@ #include "UI_interface.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Rotation) Matrix Cache * \{ */ diff --git a/source/blender/editors/transform/transform_mode_shear.c b/source/blender/editors/transform/transform_mode_shear.c index f5672887905..018725ec6dd 100644 --- a/source/blender/editors/transform/transform_mode_shear.c +++ b/source/blender/editors/transform/transform_mode_shear.c @@ -41,9 +41,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Shear) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_shrink_fatten.c b/source/blender/editors/transform/transform_mode_shrink_fatten.c index 4cdaab599b4..b96b8103392 100644 --- a/source/blender/editors/transform/transform_mode_shrink_fatten.c +++ b/source/blender/editors/transform/transform_mode_shrink_fatten.c @@ -40,9 +40,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Shrink-Fatten) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c index 0a7eea8a989..236c9024201 100644 --- a/source/blender/editors/transform/transform_mode_skin_resize.c +++ b/source/blender/editors/transform/transform_mode_skin_resize.c @@ -35,9 +35,11 @@ #include "transform.h" #include "transform_constraints.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Skin) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_tilt.c b/source/blender/editors/transform/transform_mode_tilt.c index d3b72fdf503..b48f474e16e 100644 --- a/source/blender/editors/transform/transform_mode_tilt.c +++ b/source/blender/editors/transform/transform_mode_tilt.c @@ -36,9 +36,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Tilt) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_timescale.c b/source/blender/editors/transform/transform_mode_timescale.c index 7ae97c66660..98ffc0abbd4 100644 --- a/source/blender/editors/transform/transform_mode_timescale.c +++ b/source/blender/editors/transform/transform_mode_timescale.c @@ -39,6 +39,7 @@ #include "BLT_translation.h" #include "transform.h" +#include "transform_convert.h" #include "transform_mode.h" /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_mode_timeslide.c b/source/blender/editors/transform/transform_mode_timeslide.c index 34d3251f9cf..5cc53eb08ce 100644 --- a/source/blender/editors/transform/transform_mode_timeslide.c +++ b/source/blender/editors/transform/transform_mode_timeslide.c @@ -42,6 +42,8 @@ #include "BLT_translation.h" #include "transform.h" +#include "transform_convert.h" + #include "transform_mode.h" /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_mode_timetranslate.c b/source/blender/editors/transform/transform_mode_timetranslate.c index 948242e547f..01b9a08cf27 100644 --- a/source/blender/editors/transform/transform_mode_timetranslate.c +++ b/source/blender/editors/transform/transform_mode_timetranslate.c @@ -39,9 +39,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Animation Translation) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_tosphere.c b/source/blender/editors/transform/transform_mode_tosphere.c index 8587d5ae140..bfc85b2fe44 100644 --- a/source/blender/editors/transform/transform_mode_tosphere.c +++ b/source/blender/editors/transform/transform_mode_tosphere.c @@ -39,9 +39,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name To Sphere Utilities * \{ */ diff --git a/source/blender/editors/transform/transform_mode_trackball.c b/source/blender/editors/transform/transform_mode_trackball.c index 68177c6becf..aa8b0783d0a 100644 --- a/source/blender/editors/transform/transform_mode_trackball.c +++ b/source/blender/editors/transform/transform_mode_trackball.c @@ -37,9 +37,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Rotation - Trackball) Element * \{ */ -- cgit v1.2.3 From 24c16f54571fe948aa5c02f807ca23188b436764 Mon Sep 17 00:00:00 2001 From: Pablo Dobarro Date: Tue, 17 Aug 2021 00:37:46 +0200 Subject: Fix wireframe overlay not blending over paint overlay correctly When using wireframe opacity, the paint overlay needs to be drawn before the wireframes in order to alpha blend correctly. Sculpt overlays were also affected by this, so this commit refactors this part of the code in case other overlays needs to be added in the future. Reviewed By: Mets Differential Revision: https://developer.blender.org/D12235 --- .../blender/draw/engines/overlay/overlay_engine.c | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index 235104245cc..b07e86000fd 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -576,9 +576,20 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_extra_blend_draw(vedata); OVERLAY_volume_draw(vedata); - if (pd->ctx_mode == CTX_MODE_SCULPT) { - /* Sculpt overlays are drawn here to avoid artifacts with wireframe opacity. */ - OVERLAY_sculpt_draw(vedata); + /* These overlays are drawn here to avoid artifacts with wireframe opacity. */ + switch (pd->ctx_mode) { + case CTX_MODE_SCULPT: + OVERLAY_sculpt_draw(vedata); + break; + case CTX_MODE_EDIT_MESH: + case CTX_MODE_POSE: + case CTX_MODE_PAINT_WEIGHT: + case CTX_MODE_PAINT_VERTEX: + case CTX_MODE_PAINT_TEXTURE: + OVERLAY_paint_draw(vedata); + break; + default: + break; } if (DRW_state_is_fbo()) { @@ -601,11 +612,6 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_xray_depth_infront_copy(vedata); - if (pd->ctx_mode == CTX_MODE_PAINT_WEIGHT) { - /* Fix weird case where weightpaint mode needs to draw before xray bones. */ - OVERLAY_paint_draw(vedata); - } - if (DRW_state_is_fbo()) { GPU_framebuffer_bind(fbl->overlay_in_front_fb); } @@ -640,7 +646,6 @@ static void OVERLAY_draw_scene(void *vedata) switch (pd->ctx_mode) { case CTX_MODE_EDIT_MESH: - OVERLAY_paint_draw(vedata); OVERLAY_edit_mesh_draw(vedata); break; case CTX_MODE_EDIT_SURFACE: @@ -654,13 +659,8 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_edit_lattice_draw(vedata); break; case CTX_MODE_POSE: - OVERLAY_paint_draw(vedata); OVERLAY_pose_draw(vedata); break; - case CTX_MODE_PAINT_VERTEX: - case CTX_MODE_PAINT_TEXTURE: - OVERLAY_paint_draw(vedata); - break; case CTX_MODE_PARTICLE: OVERLAY_edit_particle_draw(vedata); break; -- cgit v1.2.3 From f41beca97787d017a437a63cc0da2ce991522dea Mon Sep 17 00:00:00 2001 From: YimingWu Date: Wed, 18 Aug 2021 12:02:32 +0800 Subject: Fix T90695: Lower tile splitting limit for lineart Lowers tile splitting limit so models with extremely dense mesh portions could still have reasonable performance while for more common cases the performance impact should be minimal. Reviewed By: Sebastian Parborg (zeddb), Antonio Vazquez (antoniov) Differential Revision: https://developer.blender.org/D12236 --- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h | 2 +- source/tools | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/release/datafiles/locale b/release/datafiles/locale index 62e82958a76..8a05b618f03 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 62e82958a760dad775d9b3387d7fb535fd6de4c6 +Subproject commit 8a05b618f031582c006c6f62b9e60619ab3eef8b diff --git a/release/scripts/addons b/release/scripts/addons index 4475cbd11a6..67f1fbca148 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 4475cbd11a636382d57571e0f5dfeff1f90bd6b7 +Subproject commit 67f1fbca1482d9d9362a4001332e785c3fd5d230 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 788441f2930..ef6ef414d22 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 788441f2930465bbfba8f0797b12dcef1d46694d +Subproject commit ef6ef414d22c2578fad99327743b925ab640a99c diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 9ac07b9632d..4b71011b99a 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -208,7 +208,7 @@ typedef struct LineartChainRegisterEntry { enum eLineArtTileRecursiveLimit { /* If tile gets this small, it's already much smaller than a pixel. No need to continue * splitting. */ - LRT_TILE_RECURSIVE_PERSPECTIVE = 30, + LRT_TILE_RECURSIVE_PERSPECTIVE = 16, /* This is a tried-and-true safe value for high poly models that also needed ortho rendering. */ LRT_TILE_RECURSIVE_ORTHO = 10, }; diff --git a/source/tools b/source/tools index c8579c5cf43..5cf2fc3e5dc 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit c8579c5cf43229843df505da9644b5b0b7201974 +Subproject commit 5cf2fc3e5dc28025394b57d8743401295528f310 -- cgit v1.2.3 From 400cb25fc77a4131033f69cf328a31cdcf81edb5 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Tue, 17 Aug 2021 21:42:28 -0700 Subject: UDIM: Support tile sets that do not start at 1001 Removes the artificial requirement that UDIM tile sets start at 1001. Blender was already capable of handling sparse tile sets (non-contiguous tiles) so the restriction around starting at 1001 was unnecessary in general. This required fixing a few UDIM-related python bugs around manually updating the `tile_number` field on images as well. See the differential for details. No script changes are necessary but they will now work, correctly, in many more cases. Differential Revision: https://developer.blender.org/D11859 --- intern/cycles/blender/blender_util.h | 6 +- source/blender/blenkernel/BKE_image.h | 2 + source/blender/blenkernel/intern/image.c | 87 ++++++++++++++++++---- source/blender/blenkernel/intern/image_gpu.c | 3 +- source/blender/blenkernel/intern/image_save.c | 18 +++-- source/blender/editors/space_image/image_ops.c | 32 +++++--- .../blender/editors/space_image/image_sequence.c | 34 +++++---- source/blender/makesrna/intern/rna_image.c | 9 +-- 8 files changed, 135 insertions(+), 56 deletions(-) diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 82da3512269..d441575e8af 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -246,7 +246,11 @@ static inline string image_user_file_path(BL::ImageUser &iuser, string filepath_str = string(filepath); if (load_tiled && ima.source() == BL::Image::source_TILED) { - string_replace(filepath_str, "1001", ""); + string udim; + if (ima.tiles.length() > 0) { + udim = to_string(ima.tiles[0].number()); + } + string_replace(filepath_str, udim, ""); } return filepath_str; } diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index ac73bd2b595..3cab1a6b755 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -308,6 +308,8 @@ void BKE_image_get_tile_label(struct Image *ima, struct ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *label); bool BKE_image_remove_tile(struct Image *ima, struct ImageTile *tile); +void BKE_image_reassign_tile(struct Image *ima, struct ImageTile *tile, int new_tile_number); +void BKE_image_sort_tiles(struct Image *ima); bool BKE_image_fill_tile(struct Image *ima, struct ImageTile *tile, diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index ba54858ba84..d2ab54de697 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -670,24 +670,27 @@ bool BKE_image_has_opengl_texture(Image *ima) return false; } +static int image_get_tile_number_from_iuser(Image *ima, const ImageUser *iuser) +{ + BLI_assert(ima != NULL && ima->tiles.first); + ImageTile *tile = ima->tiles.first; + return (iuser && iuser->tile) ? iuser->tile : tile->tile_number; +} + ImageTile *BKE_image_get_tile(Image *ima, int tile_number) { if (ima == NULL) { return NULL; } - /* Verify valid tile range. */ - if ((tile_number != 0) && (tile_number < 1001 || tile_number > IMA_UDIM_MAX)) { - return NULL; - } - - /* Tile number 0 is a special case and refers to the first tile, typically + /* Tiles 0 and 1001 are a special case and refer to the first tile, typically * coming from non-UDIM-aware code. */ if (ELEM(tile_number, 0, 1001)) { return ima->tiles.first; } - if (ima->source != IMA_SRC_TILED) { + /* Must have a tiled image and a valid tile number at this point. */ + if (ima->source != IMA_SRC_TILED || tile_number < 1001 || tile_number > IMA_UDIM_MAX) { return NULL; } @@ -702,7 +705,7 @@ ImageTile *BKE_image_get_tile(Image *ima, int tile_number) ImageTile *BKE_image_get_tile_from_iuser(Image *ima, const ImageUser *iuser) { - return BKE_image_get_tile(ima, (iuser && iuser->tile) ? iuser->tile : 1001); + return BKE_image_get_tile(ima, image_get_tile_number_from_iuser(ima, iuser)); } int BKE_image_get_tile_from_pos(struct Image *ima, @@ -3803,8 +3806,8 @@ bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile) return false; } - if (tile == ima->tiles.first) { - /* Can't remove first tile. */ + if (BLI_listbase_is_single(&ima->tiles)) { + /* Can't remove the last remaining tile. */ return false; } @@ -3815,6 +3818,64 @@ bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile) return true; } +void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_number) +{ + if (ima == NULL || tile == NULL || ima->source != IMA_SRC_TILED) { + return; + } + + if (new_tile_number < 1001 || new_tile_number > IMA_UDIM_MAX) { + return; + } + + const int old_tile_number = tile->tile_number; + tile->tile_number = new_tile_number; + + if (BKE_image_is_multiview(ima)) { + const int totviews = BLI_listbase_count(&ima->views); + for (int i = 0; i < totviews; i++) { + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, old_tile_number); + image_remove_ibuf(ima, i, old_tile_number); + image_assign_ibuf(ima, ibuf, i, new_tile_number); + IMB_freeImBuf(ibuf); + } + } + else { + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, 0, old_tile_number); + image_remove_ibuf(ima, 0, old_tile_number); + image_assign_ibuf(ima, ibuf, 0, new_tile_number); + IMB_freeImBuf(ibuf); + } + + for (int eye = 0; eye < 2; eye++) { + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye] = NULL; + } + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = NULL; + } + } +} + +static int tile_sort_cb(const void *a, const void *b) +{ + const ImageTile *tile_a = a; + const ImageTile *tile_b = b; + return (tile_a->tile_number > tile_b->tile_number) ? 1 : 0; +} + +void BKE_image_sort_tiles(struct Image *ima) +{ + if (ima == NULL || ima->source != IMA_SRC_TILED) { + return; + } + + BLI_listbase_sort(&ima->tiles, tile_sort_cb); +} + bool BKE_image_fill_tile(struct Image *ima, ImageTile *tile, int width, @@ -4890,7 +4951,7 @@ static void image_get_entry_and_index(Image *ima, ImageUser *iuser, int *r_entry } } else if (ima->source == IMA_SRC_TILED) { - frame = (iuser && iuser->tile) ? iuser->tile : 1001; + frame = image_get_tile_number_from_iuser(ima, iuser); } *r_entry = frame; @@ -4955,7 +5016,7 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry, } else if (ima->source == IMA_SRC_TILED) { if (ELEM(ima->type, IMA_TYPE_IMAGE, IMA_TYPE_MULTILAYER)) { - entry = (iuser && iuser->tile) ? iuser->tile : 1001; + entry = image_get_tile_number_from_iuser(ima, iuser); ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); if ((ima->type == IMA_TYPE_IMAGE) && ibuf != NULL) { @@ -5507,7 +5568,7 @@ void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath) index = iuser ? iuser->framenr : ima->lastframe; } else { - index = (iuser && iuser->tile) ? iuser->tile : 1001; + index = image_get_tile_number_from_iuser(ima, iuser); } BLI_path_sequence_decode(filepath, head, tail, &numlen); diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c index bb7495437bb..d179dd40c33 100644 --- a/source/blender/blenkernel/intern/image_gpu.c +++ b/source/blender/blenkernel/intern/image_gpu.c @@ -108,8 +108,9 @@ static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multivi float array_w = GPU_texture_width(tilearray); float array_h = GPU_texture_height(tilearray); + /* Determine maximum tile number. */ + BKE_image_sort_tiles(ima); ImageTile *last_tile = (ImageTile *)ima->tiles.last; - /* Tiles are sorted by number. */ int max_tile = last_tile->tile_number - 1001; /* create image */ diff --git a/source/blender/blenkernel/intern/image_save.c b/source/blender/blenkernel/intern/image_save.c index 360bad3e786..be86da05b57 100644 --- a/source/blender/blenkernel/intern/image_save.c +++ b/source/blender/blenkernel/intern/image_save.c @@ -404,11 +404,12 @@ bool BKE_image_save( if (ima->source == IMA_SRC_TILED) { /* Verify filepath for tiles images. */ - if (BLI_path_sequence_decode(opts->filepath, NULL, NULL, NULL) != 1001) { + ImageTile *first_tile = ima->tiles.first; + if (BLI_path_sequence_decode(opts->filepath, NULL, NULL, NULL) != first_tile->tile_number) { BKE_reportf(reports, RPT_ERROR, - "When saving a tiled image, the path '%s' must contain the UDIM tag 1001", - opts->filepath); + "When saving a tiled image, the path '%s' must contain the UDIM tile number %d", + opts->filepath, first_tile->tile_number); return false; } @@ -430,9 +431,14 @@ bool BKE_image_save( BLI_path_sequence_decode(filepath, head, tail, &numlen); /* Save all other tiles. */ - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - /* Tile 1001 was already saved before the loop. */ - if (tile->tile_number == 1001 || !ok) { + int index; + LISTBASE_FOREACH_INDEX (ImageTile *, tile, &ima->tiles, index) { + /* First tile was already saved before the loop. */ + if (index == 0) { + continue; + } + + if (!ok) { continue; } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 999d2956fef..29c1452b988 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1289,8 +1289,10 @@ static Image *image_open_single(Main *bmain, } if ((range->length > 1) && (ima->source == IMA_SRC_FILE)) { - if (range->udim_tiles.first && range->offset == 1001) { + if (range->udim_tiles.first) { ima->source = IMA_SRC_TILED; + ImageTile *first_tile = ima->tiles.first; + first_tile->tile_number = range->offset; LISTBASE_FOREACH (LinkData *, node, &range->udim_tiles) { BKE_image_add_tile(ima, POINTER_AS_INT(node->data), NULL); } @@ -1806,10 +1808,13 @@ static int image_save_options_init(Main *bmain, } /* append UDIM numbering if not present */ - if (ima->source == IMA_SRC_TILED && - (BLI_path_sequence_decode(ima->filepath, NULL, NULL, NULL) != 1001)) { + if (ima->source == IMA_SRC_TILED) { + char udim[6]; + ImageTile *tile = ima->tiles.first; + BLI_snprintf(udim, sizeof(udim), ".%d", tile->tile_number); + int len = strlen(opts->filepath); - STR_CONCAT(opts->filepath, len, ".1001"); + STR_CONCAT(opts->filepath, len, udim); } } @@ -3868,9 +3873,9 @@ static void tile_fill_init(PointerRNA *ptr, Image *ima, ImageTile *tile) /* Acquire ibuf to get the default values. * If the specified tile has no ibuf, try acquiring the main tile instead - * (unless the specified tile already was the main tile). */ + * (unless the specified tile already was the first tile). */ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); - if (ibuf == NULL && (tile != NULL) && (tile->tile_number != 1001)) { + if (ibuf == NULL && (tile != NULL) && (tile != ima->tiles.first)) { ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); } @@ -3932,27 +3937,30 @@ static int tile_add_exec(bContext *C, wmOperator *op) bool fill_tile = RNA_boolean_get(op->ptr, "fill"); char *label = RNA_string_get_alloc(op->ptr, "label", NULL, 0); - bool created_tile = false; + /* BKE_image_add_tile assumes a pre-sorted list of tiles. */ + BKE_image_sort_tiles(ima); + + ImageTile *last_tile_created = NULL; for (int tile_number = start_tile; tile_number <= end_tile; tile_number++) { ImageTile *tile = BKE_image_add_tile(ima, tile_number, label); if (tile != NULL) { - ima->active_tile_index = BLI_findindex(&ima->tiles, tile); - if (fill_tile) { do_fill_tile(op->ptr, ima, tile); } - created_tile = true; + last_tile_created = tile; } } MEM_freeN(label); - if (!created_tile) { + if (!last_tile_created) { BKE_report(op->reports, RPT_WARNING, "No UDIM tiles were created"); return OPERATOR_CANCELLED; } + ima->active_tile_index = BLI_findindex(&ima->tiles, last_tile_created); + WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); return OPERATOR_FINISHED; } @@ -4041,7 +4049,7 @@ static bool tile_remove_poll(bContext *C) { Image *ima = CTX_data_edit_image(C); - return (ima != NULL && ima->source == IMA_SRC_TILED && ima->active_tile_index != 0); + return (ima != NULL && ima->source == IMA_SRC_TILED && !BLI_listbase_is_single(&ima->tiles)); } static int tile_remove_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c index 02546e3e3b3..288b3d94b1d 100644 --- a/source/blender/editors/space_image/image_sequence.c +++ b/source/blender/editors/space_image/image_sequence.c @@ -124,7 +124,7 @@ static int image_cmp_frame(const void *a, const void *b) * * udim_tiles may get filled even if the result ultimately is false! */ -static int image_get_udim(char *filepath, ListBase *udim_tiles) +static bool image_get_udim(char *filepath, ListBase *udim_tiles, int *udim_start, int *udim_range) { char filename[FILE_MAX], dirname[FILE_MAXDIR]; BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename)); @@ -133,12 +133,12 @@ static int image_get_udim(char *filepath, ListBase *udim_tiles) char base_head[FILE_MAX], base_tail[FILE_MAX]; int id = BLI_path_sequence_decode(filename, base_head, base_tail, &digits); - if (id < 1001 || id >= IMA_UDIM_MAX) { - return 0; + if (id < 1001 || id > IMA_UDIM_MAX) { + return false; } bool is_udim = true; - bool has_primary = false; + int min_udim = IMA_UDIM_MAX + 1; int max_udim = 0; struct direntry *dir; @@ -155,26 +155,27 @@ static int image_get_udim(char *filepath, ListBase *udim_tiles) continue; } - if (id < 1001 || id >= IMA_UDIM_MAX) { + if (id < 1001 || id > IMA_UDIM_MAX) { is_udim = false; break; } - if (id == 1001) { - has_primary = true; - } BLI_addtail(udim_tiles, BLI_genericNodeN(POINTER_FROM_INT(id))); + min_udim = min_ii(min_udim, id); max_udim = max_ii(max_udim, id); } BLI_filelist_free(dir, totfile); - if (is_udim && has_primary) { + if (is_udim && min_udim <= IMA_UDIM_MAX) { char primary_filename[FILE_MAX]; - BLI_path_sequence_encode(primary_filename, base_head, base_tail, digits, 1001); + BLI_path_sequence_encode(primary_filename, base_head, base_tail, digits, min_udim); BLI_join_dirfile(filepath, FILE_MAX, dirname, primary_filename); - return max_udim - 1000; + + *udim_start = min_udim; + *udim_range = max_udim - min_udim + 1; + return true; } - return 0; + return false; } /** @@ -185,11 +186,12 @@ static void image_detect_frame_range(ImageFrameRange *range, const bool detect_u { /* UDIM */ if (detect_udim) { - int len_udim = image_get_udim(range->filepath, &range->udim_tiles); + int udim_start, udim_range; + bool result = image_get_udim(range->filepath, &range->udim_tiles, &udim_start, &udim_range); - if (len_udim > 0) { - range->offset = 1001; - range->length = len_udim; + if (result) { + range->offset = udim_start; + range->length = udim_range; return; } } diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index c058ab6cfcc..e44ddb07d53 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -290,15 +290,10 @@ static void rna_UDIMTile_tile_number_set(PointerRNA *ptr, int value) ImageTile *tile = (ImageTile *)ptr->data; Image *image = (Image *)ptr->owner_id; - /* The index of the first tile can't be changed. */ - if (tile->tile_number == 1001) { - return; - } - /* Check that no other tile already has that number. */ ImageTile *cur_tile = BKE_image_get_tile(image, value); - if (cur_tile == NULL || cur_tile == tile) { - tile->tile_number = value; + if (cur_tile == NULL) { + BKE_image_reassign_tile(image, tile, value); } } -- cgit v1.2.3 From c0f600cad1d2d107d189b15b12e2fcc6bba0985c Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 18 Aug 2021 07:31:43 +0200 Subject: T73434: Improve Weight Paint Overlay Drawing. Master multiplied the weight paint on top of the rendered image. This reduced readability. This patch removes the multiplication for weight painting and adds a hint of the geometry below the overlay. Reviewed By: Mets, pablodp606, campbellbarton Maniphest Tasks: T73434 Differential Revision: https://developer.blender.org/D12170 --- .../blender/draw/engines/overlay/overlay_paint.c | 27 +++++++++++----------- .../blender/draw/engines/overlay/overlay_private.h | 2 +- .../blender/draw/engines/overlay/overlay_shader.c | 13 ++++++----- .../engines/overlay/shaders/paint_weight_frag.glsl | 23 ++++++++++-------- .../engines/overlay/shaders/paint_weight_vert.glsl | 15 ++++++++++++ source/blender/draw/tests/shaders_test.cc | 3 ++- 6 files changed, 52 insertions(+), 31 deletions(-) diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c index d52640ed174..60a90616d29 100644 --- a/source/blender/draw/engines/overlay/overlay_paint.c +++ b/source/blender/draw/engines/overlay/overlay_paint.c @@ -92,18 +92,26 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata) case CTX_MODE_PAINT_WEIGHT: { opacity = is_edit_mode ? 1.0 : pd->overlay.weight_paint_mode_opacity; if (opacity > 0.0f) { - state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; - state |= pd->painting.alpha_blending ? DRW_STATE_BLEND_ALPHA : DRW_STATE_BLEND_MUL; + state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA; DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state); - sh = OVERLAY_shader_paint_weight(); + const bool do_shading = draw_ctx->v3d->shading.type != OB_WIRE; + + sh = OVERLAY_shader_paint_weight(do_shading); pd->paint_surf_grp = grp = DRW_shgroup_create(sh, psl->paint_color_ps); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_bool_copy(grp, "drawContours", draw_contours); - DRW_shgroup_uniform_bool_copy(grp, "useAlphaBlend", pd->painting.alpha_blending); DRW_shgroup_uniform_float_copy(grp, "opacity", opacity); DRW_shgroup_uniform_texture(grp, "colorramp", G_draw.weight_ramp); + /* Arbitrary light to give a hint of the geometry behind the weights. */ + if (do_shading) { + float light_dir[3]; + copy_v3_fl3(light_dir, 0.0f, 0.5f, 0.86602f); + normalize_v3(light_dir); + DRW_shgroup_uniform_vec3_copy(grp, "light_dir", light_dir); + } + if (pd->painting.alpha_blending) { state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; DRW_PASS_CREATE(psl->paint_depth_ps, state | pd->clipping_state); @@ -257,17 +265,10 @@ void OVERLAY_paint_draw(OVERLAY_Data *vedata) OVERLAY_PassList *psl = vedata->psl; OVERLAY_FramebufferList *fbl = vedata->fbl; - DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); if (DRW_state_is_fbo()) { - if (pd->painting.alpha_blending) { - GPU_framebuffer_bind(pd->painting.in_front ? fbl->overlay_in_front_fb : - fbl->overlay_default_fb); - } - else { - /* Paint overlay needs final color because of multiply blend mode. */ - GPU_framebuffer_bind(pd->painting.in_front ? dfbl->in_front_fb : dfbl->default_fb); - } + GPU_framebuffer_bind(pd->painting.in_front ? fbl->overlay_in_front_fb : + fbl->overlay_default_fb); } if (psl->paint_depth_ps) { diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 03bfaf56f24..68f60bee779 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -736,7 +736,7 @@ GPUShader *OVERLAY_shader_paint_face(void); GPUShader *OVERLAY_shader_paint_point(void); GPUShader *OVERLAY_shader_paint_texture(void); GPUShader *OVERLAY_shader_paint_vertcol(void); -GPUShader *OVERLAY_shader_paint_weight(void); +GPUShader *OVERLAY_shader_paint_weight(bool shading); GPUShader *OVERLAY_shader_paint_wire(void); GPUShader *OVERLAY_shader_particle_dot(void); GPUShader *OVERLAY_shader_particle_shape(void); diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c index 7a7ae9a921b..edf9148c8c0 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.c +++ b/source/blender/draw/engines/overlay/overlay_shader.c @@ -211,7 +211,7 @@ typedef struct OVERLAY_Shaders { GPUShader *paint_point; GPUShader *paint_texture; GPUShader *paint_vertcol; - GPUShader *paint_weight; + GPUShader *paint_weight[2]; GPUShader *paint_wire; GPUShader *particle_dot; GPUShader *particle_shape; @@ -1334,13 +1334,14 @@ GPUShader *OVERLAY_shader_paint_vertcol(void) return sh_data->paint_vertcol; } -GPUShader *OVERLAY_shader_paint_weight(void) +GPUShader *OVERLAY_shader_paint_weight(const bool shading) { + int index = shading ? 1 : 0; const DRWContextState *draw_ctx = DRW_context_state_get(); const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg]; OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; - if (!sh_data->paint_weight) { - sh_data->paint_weight = GPU_shader_create_from_arrays({ + if (!sh_data->paint_weight[index]) { + sh_data->paint_weight[index] = GPU_shader_create_from_arrays({ .vert = (const char *[]){sh_cfg->lib, datatoc_common_globals_lib_glsl, datatoc_common_view_lib_glsl, @@ -1349,10 +1350,10 @@ GPUShader *OVERLAY_shader_paint_weight(void) .frag = (const char *[]){datatoc_common_globals_lib_glsl, datatoc_paint_weight_frag_glsl, NULL}, - .defs = (const char *[]){sh_cfg->def, NULL}, + .defs = (const char *[]){sh_cfg->def, shading ? "#define FAKE_SHADING\n" : "", NULL}, }); } - return sh_data->paint_weight; + return sh_data->paint_weight[index]; } GPUShader *OVERLAY_shader_paint_wire(void) diff --git a/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl b/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl index 0020d76ed6a..8009713d655 100644 --- a/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl @@ -1,12 +1,12 @@ in vec2 weight_interp; /* (weight, alert) */ +in float color_fac; out vec4 fragColor; uniform float opacity = 1.0; uniform sampler1D colorramp; -uniform bool useAlphaBlend = false; uniform bool drawContours = false; float contours(float value, float steps, float width_px, float max_rel_width, float gradient) @@ -68,6 +68,13 @@ vec4 contour_grid(float weight, float weight_gradient) return grid * clamp((weight_gradient - flt_eps) / flt_eps, 0.0, 1.0); } +vec4 apply_color_fac(vec4 color_in) +{ + vec4 color = color_in; + color.rgb = max(vec3(0.005), color_in.rgb) * color_fac; + return color; +} + void main() { float alert = weight_interp.y; @@ -75,12 +82,13 @@ void main() /* Missing vertex group alert color. Uniform in practice. */ if (alert > 1.1) { - color = colorVertexMissingData; + color = apply_color_fac(colorVertexMissingData); } /* Weights are available */ else { float weight = weight_interp.x; vec4 weight_color = texture(colorramp, weight, 0); + weight_color = apply_color_fac(weight_color); /* Contour display */ if (drawContours) { @@ -93,14 +101,9 @@ void main() } /* Zero weight alert color. Nonlinear blend to reduce impact. */ - color = mix(weight_color, colorVertexUnreferenced, alert * alert); + vec4 color_unreferenced = apply_color_fac(colorVertexUnreferenced); + color = mix(weight_color, color_unreferenced, alert * alert); } - if (useAlphaBlend) { - fragColor = vec4(color.rgb, opacity); - } - else { - /* mix with 1.0 -> is like opacity when using multiply blend mode */ - fragColor = vec4(mix(vec3(1.0), color.rgb, opacity), 1.0); - } + fragColor = vec4(color.rgb, opacity); } diff --git a/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl b/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl index b3baa8c7b07..31b6dc42cf4 100644 --- a/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl @@ -1,8 +1,13 @@ +#ifdef FAKE_SHADING +uniform vec3 light_dir; +#endif in float weight; in vec3 pos; +in vec3 nor; out vec2 weight_interp; /* (weight, alert) */ +out float color_fac; void main() { @@ -14,6 +19,16 @@ void main() /* Separate actual weight and alerts for independent interpolation */ weight_interp = max(vec2(weight, -weight), 0.0); + /* Saturate the weight to give a hint of the geometry behind the weights. */ +#ifdef FAKE_SHADING + vec3 view_normal = normalize(normal_object_to_view(nor)); + color_fac = abs(dot(view_normal, light_dir)); + color_fac = color_fac * 0.9 + 0.1; + +#else + color_fac = 1.0; +#endif + #ifdef USE_WORLD_CLIP_PLANES world_clip_planes_calc_clip_distance(world_pos); #endif diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc index 0c7cbd4dac8..a9810b4cc77 100644 --- a/source/blender/draw/tests/shaders_test.cc +++ b/source/blender/draw/tests/shaders_test.cc @@ -267,7 +267,8 @@ static void test_overlay_glsl_shaders() EXPECT_NE(OVERLAY_shader_paint_point(), nullptr); EXPECT_NE(OVERLAY_shader_paint_texture(), nullptr); EXPECT_NE(OVERLAY_shader_paint_vertcol(), nullptr); - EXPECT_NE(OVERLAY_shader_paint_weight(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_weight(false), nullptr); + EXPECT_NE(OVERLAY_shader_paint_weight(true), nullptr); EXPECT_NE(OVERLAY_shader_paint_wire(), nullptr); EXPECT_NE(OVERLAY_shader_particle_dot(), nullptr); EXPECT_NE(OVERLAY_shader_particle_shape(), nullptr); -- cgit v1.2.3 From 7bffafab7b2a489c9f0eafd576f9da0cbc1a5081 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 17 Aug 2021 18:25:53 -0300 Subject: Fix T90718: Object selection toggle do not work inside edit mode The object was not deselected as it was expected that it would be activated. But this activation does not happen in edit mode. --- source/blender/editors/space_view3d/view3d_select.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 2ce5684e874..e3f97dd1c63 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -2521,6 +2521,7 @@ static bool ed_object_select_pick(bContext *C, } /* also prevent making it active on mouse selection */ else if (BASE_SELECTABLE(v3d, basact)) { + const bool use_activate_selected_base = (oldbasact != basact) && (is_obedit == false); if (extend) { ED_object_base_select(basact, BA_SELECT); } @@ -2529,7 +2530,8 @@ static bool ed_object_select_pick(bContext *C, } else if (toggle) { if (basact->flag & BASE_SELECTED) { - if (basact == oldbasact) { + /* Keep selected if the base is to be activated. */ + if (use_activate_selected_base == false) { ED_object_base_select(basact, BA_DESELECT); } } @@ -2545,7 +2547,7 @@ static bool ed_object_select_pick(bContext *C, } } - if ((oldbasact != basact) && (is_obedit == false)) { + if (use_activate_selected_base) { ED_object_base_activate(C, basact); /* adds notifier */ if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) { WM_toolsystem_update_from_context_view3d(C); -- cgit v1.2.3 From 787350dde8aec3caf8660c749e1847ff406974c8 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 18 Aug 2021 12:45:37 +0200 Subject: Fix T90737: VSE adding nested strips could have non-unique names Caused by {rBbbb1936411a5}. When adding strips via the new SEQ_add_XXX_strip functions, the `Editing->seqbasep` pointer was passed around. Following in `seq_add_generic_update` this `seqbasep` pointer was used to ensure a unique name. But `seqbasep` is the pointer to the current list of seq's being edited (**which can be limited to the ones within a meta strip**). We need unique names across all strips though (since these are used for RNA paths, FCurves as reported), so now use the scene's `Editing- >seqbase` (**which is the list of the top-most sequences**) instead. Unfortunately this might have screwed files to a borked state, not sure if this could easily be fixed... Maniphest Tasks: T90737 Differential Revision: https://developer.blender.org/D12256 --- source/blender/sequencer/intern/strip_add.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 7b383bcb330..9081c655d2f 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -99,7 +99,7 @@ void SEQ_add_load_data_init(SeqLoadData *load_data, static void seq_add_generic_update(Scene *scene, ListBase *seqbase, Sequence *seq) { - SEQ_sequence_base_unique_name_recursive(scene, seqbase, seq); + SEQ_sequence_base_unique_name_recursive(scene, &scene->ed->seqbase, seq); SEQ_time_update_sequence_bounds(scene, seq); SEQ_sort(seqbase); SEQ_relations_invalidate_cache_composite(scene, seq); -- cgit v1.2.3 From 04376c3bac796c9fb4bc2e33150484e357a65eab Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Wed, 18 Aug 2021 13:16:14 +0100 Subject: Geometry Nodes: Add shader Color Mix node Port color mix shader node to Geometry Nodes. Differential Revision: https://developer.blender.org/D10585 --- release/scripts/startup/nodeitems_builtins.py | 1 + .../nodes/shader/nodes/node_shader_mixRgb.cc | 58 +++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 9c67ab1e57f..6f9b222571c 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -495,6 +495,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeAttributeTransfer"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ + NodeItem("ShaderNodeMixRGB"), NodeItem("ShaderNodeRGBCurve"), NodeItem("ShaderNodeValToRGB"), NodeItem("ShaderNodeSeparateRGB"), diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc index 6baaa17f956..47011caeeb6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc @@ -127,15 +127,71 @@ static int gpu_shader_mix_rgb(GPUMaterial *mat, return 0; } +class MixRGBFunction : public blender::fn::MultiFunction { + private: + bool clamp_; + int type_; + + public: + MixRGBFunction(bool clamp, int type) : clamp_(clamp), type_(type) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"MixRGB"}; + signature.single_input("Fac"); + signature.single_input("Color1"); + signature.single_input("Color2"); + signature.single_output("Color"); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray &fac = params.readonly_single_input(0, "Fac"); + const blender::VArray &col1 = + params.readonly_single_input(1, "Color1"); + const blender::VArray &col2 = + params.readonly_single_input(2, "Color2"); + blender::MutableSpan results = + params.uninitialized_single_output(3, "Color"); + + for (int64_t i : mask) { + results[i] = col1[i]; + ramp_blend(type_, results[i], clamp_f(fac[i], 0.0f, 1.0f), col2[i]); + } + + if (clamp_) { + for (int64_t i : mask) { + clamp_v3(results[i], 0.0f, 1.0f); + } + } + } +}; + +static void sh_node_mix_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +{ + bNode &node = builder.bnode(); + bool clamp = node.custom2 & SHD_MIXRGB_CLAMP; + int mix_type = node.custom1; + builder.construct_and_set_matching_fn(clamp, mix_type); +} + void register_node_type_sh_mix_rgb(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, 0); node_type_socket_templates(&ntype, sh_node_mix_rgb_in, sh_node_mix_rgb_out); node_type_label(&ntype, node_blend_label); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_mix_rgb); node_type_gpu(&ntype, gpu_shader_mix_rgb); + ntype.expand_in_mf_network = sh_node_mix_rgb_expand_in_mf_network; nodeRegisterType(&ntype); } -- cgit v1.2.3 From 9bfd4ae22293caedfb8e150512cc911bfc95d35f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 18 Aug 2021 12:53:15 +0200 Subject: LibOverride: tweak resync detection code at apply phase. This code checks whether an ID pointer property of an override does not match its linked reference when it is expected to do so. This is a goiod indication that a resync is needed. Previous code would falsy detect overrides of IDs referencing themselves as needing a resync, when this is not effectively the case. --- .../makesrna/intern/rna_access_compare_override.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 3912c873fd0..2c552970c82 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -1096,6 +1096,7 @@ static void rna_property_override_check_resync(Main *bmain, PointerRNA *ptr_item_dst, PointerRNA *ptr_item_src) { + ID *id_owner = rna_property_override_property_real_id_owner(bmain, ptr_dst, NULL, NULL); ID *id_src = rna_property_override_property_real_id_owner(bmain, ptr_item_src, NULL, NULL); ID *id_dst = rna_property_override_property_real_id_owner(bmain, ptr_item_dst, NULL, NULL); @@ -1109,9 +1110,18 @@ static void rna_property_override_check_resync(Main *bmain, * remapped to its new local override. In that case overrides and linked data * are always properly matching. */ id_src != id_dst && - /* If one of the pointers is NULL and not the other, or if linked reference ID - * of `id_src` is not `id_dst`, we are in a non-matching case. */ - (ELEM(NULL, id_src, id_dst) || id_src->override_library->reference != id_dst)) { + /* If one of the pointers is NULL and not the other, we are in a non-matching case. */ + (ELEM(NULL, id_src, id_dst) || + /* If `id_dst` is not from same lib as id_src, and linked reference ID of `id_src` is not + * `id_dst`, we are in a non-matching case. */ + (id_dst->lib != id_src->lib && id_src->override_library->reference != id_dst) || + /* If `id_dst` is from same lib as id_src, and is not same as `id_owner`, we are in a + * non-matching case. + * + * NOTE: Here we are testing if `id_owner` is referencing itself, in that case the new + * override copy generated by `BKE_lib_override_library_update` will already have its + * self-references updated to itself, instead of still pointing to its linked source. */ + (id_dst->lib == id_src->lib && id_dst != id_owner))) { ptr_dst->owner_id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; CLOG_INFO(&LOG, 3, "Local override %s detected as needing resync", ptr_dst->owner_id->name); } -- cgit v1.2.3 From ba71eb467e46b33b0066ee6a05062f686d9af354 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 18 Aug 2021 12:56:31 +0200 Subject: LibOverride: Add several missing 'OVERRIDABLE' flags to NodeTrees RNA. This should be a no-change commit for now, but is required to enable initial basic support of nodetrees in library override. NOTE: Proper full support of liboverrides in nodes is yet to be designed (has UX unresolved issues, since we likely do not want to expose/make overridable ALL settings of ALL nodes). --- source/blender/makesrna/intern/rna_nodetree.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 7d5fffcc6c9..d8ab7c7a61b 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -4745,6 +4745,7 @@ static void def_frame(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "Text"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Text", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -5752,6 +5753,7 @@ static void def_sh_tex_pointdensity(StructRNA *srna) NULL, NULL); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "resolution", PROP_INT, PROP_NONE); @@ -6171,6 +6173,7 @@ static void def_sh_tex_ies(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "Text"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "IES Text", "Internal IES file"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -6211,6 +6214,7 @@ static void def_sh_script(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "Text"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Script", "Internal shader script to define the shader"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNodeScript_update"); @@ -7936,6 +7940,7 @@ static void def_cmp_movieclip(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "MovieClip"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Movie Clip", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -7950,6 +7955,7 @@ static void def_cmp_stabilize2d(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "MovieClip"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Movie Clip", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -7980,6 +7986,7 @@ static void def_cmp_moviedistortion(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "MovieClip"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Movie Clip", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -8009,6 +8016,7 @@ static void def_cmp_mask(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "Mask"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Mask", ""); prop = RNA_def_property(srna, "use_feather", PROP_BOOLEAN, PROP_NONE); @@ -8501,6 +8509,7 @@ static void def_cmp_keyingscreen(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "MovieClip"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Movie Clip", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -8638,6 +8647,7 @@ static void def_cmp_trackpos(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "MovieClip"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Movie Clip", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -8702,6 +8712,7 @@ static void def_cmp_planetrackdeform(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "MovieClip"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Movie Clip", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -8832,6 +8843,7 @@ static void def_cmp_cryptomatte(StructRNA *srna) prop, "rna_NodeCryptomatte_scene_get", "rna_NodeCryptomatte_scene_set", NULL, NULL); RNA_def_property_struct_type(prop, "Scene"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Scene", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -8843,6 +8855,7 @@ static void def_cmp_cryptomatte(StructRNA *srna) "rna_NodeCryptomatte_image_poll"); RNA_def_property_struct_type(prop, "Image"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Image", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -8942,6 +8955,7 @@ static void def_tex_image(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "Image"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Image", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -10929,6 +10943,7 @@ static void rna_def_node_socket_object(BlenderRNA *brna, RNA_def_property_update( prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); /* socket interface */ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); @@ -10964,6 +10979,7 @@ static void rna_def_node_socket_image(BlenderRNA *brna, RNA_def_property_update( prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); /* socket interface */ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); @@ -11014,6 +11030,7 @@ static void rna_def_node_socket_collection(BlenderRNA *brna, RNA_def_property_update( prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); /* socket interface */ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); @@ -11049,6 +11066,7 @@ static void rna_def_node_socket_texture(BlenderRNA *brna, RNA_def_property_update( prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); /* socket interface */ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); @@ -11086,6 +11104,7 @@ static void rna_def_node_socket_material(BlenderRNA *brna, RNA_def_property_update( prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); /* socket interface */ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); @@ -11471,12 +11490,14 @@ static void rna_def_node(BlenderRNA *brna) prop = RNA_def_property(srna, "inputs", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "inputs", NULL); RNA_def_property_struct_type(prop, "NodeSocket"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Inputs", ""); rna_def_node_sockets_api(brna, prop, SOCK_IN); prop = RNA_def_property(srna, "outputs", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "outputs", NULL); RNA_def_property_struct_type(prop, "NodeSocket"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Outputs", ""); rna_def_node_sockets_api(brna, prop, SOCK_OUT); @@ -11923,6 +11944,7 @@ static void rna_def_nodetree(BlenderRNA *brna) prop = RNA_def_property(srna, "nodes", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "nodes", NULL); RNA_def_property_struct_type(prop, "Node"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Nodes", ""); rna_def_nodetree_nodes_api(brna, prop); @@ -11940,6 +11962,7 @@ static void rna_def_nodetree(BlenderRNA *brna) RNA_def_property_pointer_funcs( prop, NULL, NULL, NULL, "rna_GPencil_datablocks_annotations_poll"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Grease Pencil Data", "Grease Pencil data-block"); RNA_def_property_update(prop, NC_NODE, NULL); -- cgit v1.2.3 From 1d06d35034e6b9485c61c1f6fea28603ba123ee6 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 18 Aug 2021 16:44:14 +0200 Subject: LibOverride: Do not report embedded IDs as non-overridable in 'foreach_id' code. Embedded IDs (root nodetrees, master collection, etc.) pointer itself is not editable, but their content may be overridden. LibOverride code is supposed to know how to handle those embedded IDs. --- source/blender/blenkernel/intern/lib_query.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 977e53c8474..9400458376d 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -88,8 +88,8 @@ bool BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int /* Update the callback flags with some extra information regarding overrides: all 'loopback', * 'internal', 'embedded' etc. ID pointers are never overridable. */ - if (cb_flag & (IDWALK_CB_INTERNAL | IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | - IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { + if (cb_flag & + (IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE; } -- cgit v1.2.3 From fffe219bdb8d12e1c6a06bb38e68150ab9e40038 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 18 Aug 2021 16:46:12 +0200 Subject: LibOverride: Make nodetree of material overrideable again. This reverts rB6899dbef77cd and makes the pointer explicitely processable by override & diffing code. Previous changes & fixes have fixed the 'driver-workaround' case afaict. Note that this only enables proper generic handling of overrides and their ID pointers, no node property is actually overridable currently. --- source/blender/makesrna/intern/rna_material.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index d91c0bfaf29..ee31b92b2a1 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -850,7 +850,7 @@ void RNA_def_material(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); /* XXX: remove once overrides in material node trees are supported. */ - RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based materials"); prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE); -- cgit v1.2.3 From e5f8db92b696a36e919eb6ac3125190d9e2202d9 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 18 Aug 2021 17:05:49 +0200 Subject: LibOverride: Tag all embedded IDs RNA opinters as overridable. Followup to previous commit, rBfffe219bdb8drBfffe219bdb8d Again this is only for sake of sane ID/overrides managment for now, the nodetrees themselves are not overridable from user PoV yet. --- source/blender/makesrna/intern/rna_material.c | 1 - source/blender/makesrna/intern/rna_scene.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index ee31b92b2a1..144950235c8 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -849,7 +849,6 @@ void RNA_def_material(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); - /* XXX: remove once overrides in material node trees are supported. */ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based materials"); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 7d7eec6f256..f0a1508fb5f 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -7664,6 +7664,7 @@ void RNA_def_scene(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Compositing node tree"); prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE); @@ -7895,6 +7896,7 @@ void RNA_def_scene(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "master_collection"); RNA_def_property_struct_type(prop, "Collection"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Collection", "Scene root collection that owns all the objects and other collections " -- cgit v1.2.3 From b8ecdbcd964a7e78d7e077ef84a914b1374ae076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Tue, 17 Aug 2021 23:24:46 +0200 Subject: Cycles: avoid copying vertex normals attribute twice to the devices Vertex normals are needed for normals maps and therefore are packed and send to the device alongside the other float3 attributes. However, we already pack and send vertex normals through `DeviceScene.tri_vnormal`. This removes the packing of vertex normals from the attributes buffer, and reuses `tri_vnormal` in the kernel for normals lookup for normal maps, which reduces memory usage a bit, and speeds up device updates. This also fixes potential missing normals updates following rB12a06292af86, since the need for vertex normals for normals maps was overlooked. Reviewed By: brecht Differential Revision: https://developer.blender.org/D12237 --- intern/cycles/kernel/geom/geom_triangle.h | 14 ++++++++++++++ intern/cycles/kernel/svm/svm_tex_coord.h | 8 +++----- intern/cycles/render/geometry.cpp | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/intern/cycles/kernel/geom/geom_triangle.h b/intern/cycles/kernel/geom/geom_triangle.h index 208338a934b..1e7fbd9c7fb 100644 --- a/intern/cycles/kernel/geom/geom_triangle.h +++ b/intern/cycles/kernel/geom/geom_triangle.h @@ -107,6 +107,20 @@ triangle_smooth_normal(KernelGlobals *kg, float3 Ng, int prim, float u, float v) return is_zero(N) ? Ng : N; } +ccl_device_inline float3 +triangle_smooth_normal_unnormalized(KernelGlobals *kg, float3 Ng, int prim, float u, float v) +{ + /* load triangle vertices */ + const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); + float3 n0 = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.x)); + float3 n1 = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.y)); + float3 n2 = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.z)); + + float3 N = (1.0f - u - v) * n2 + u * n0 + v * n1; + + return is_zero(N) ? Ng : N; +} + /* Ray differentials on triangle */ ccl_device_inline void triangle_dPdudv(KernelGlobals *kg, diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index fc46bb584be..09ea11ee3ed 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -267,7 +267,7 @@ ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *st if (space == NODE_NORMAL_MAP_TANGENT) { /* tangent space */ - if (sd->object == OBJECT_NONE) { + if (sd->object == OBJECT_NONE || (sd->prim & PRIMITIVE_ALL_TRIANGLE) == 0) { /* Fallback to unperturbed normal. */ stack_store_float3(stack, normal_offset, sd->N); return; @@ -276,10 +276,8 @@ ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *st /* first try to get tangent attribute */ const AttributeDescriptor attr = find_attribute(kg, sd, node.z); const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.w); - const AttributeDescriptor attr_normal = find_attribute(kg, sd, ATTR_STD_VERTEX_NORMAL); - if (attr.offset == ATTR_STD_NOT_FOUND || attr_sign.offset == ATTR_STD_NOT_FOUND || - attr_normal.offset == ATTR_STD_NOT_FOUND) { + if (attr.offset == ATTR_STD_NOT_FOUND || attr_sign.offset == ATTR_STD_NOT_FOUND) { /* Fallback to unperturbed normal. */ stack_store_float3(stack, normal_offset, sd->N); return; @@ -291,7 +289,7 @@ ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *st float3 normal; if (sd->shader & SHADER_SMOOTH_NORMAL) { - normal = primitive_surface_attribute_float3(kg, sd, attr_normal, NULL, NULL); + normal = triangle_smooth_normal_unnormalized(kg, sd->Ng, sd->prim, sd->u, sd->v); } else { normal = sd->Ng; diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 6d084e82576..a8e4db38180 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -788,6 +788,11 @@ void GeometryManager::device_update_attributes(Device *device, foreach (AttributeRequest &req, attributes.requests) { Attribute *attr = geom->attributes.find(req); + /* Vertex normals are stored in DeviceScene.tri_vnormal. */ + if (attr && attr->std == ATTR_STD_VERTEX_NORMAL) { + continue; + } + update_attribute_element_size(geom, attr, ATTR_PRIM_GEOMETRY, @@ -854,6 +859,11 @@ void GeometryManager::device_update_attributes(Device *device, Attribute *attr = geom->attributes.find(req); if (attr) { + /* Vertex normals are stored in DeviceScene.tri_vnormal. */ + if (attr->std == ATTR_STD_VERTEX_NORMAL) { + continue; + } + /* force a copy if we need to reallocate all the data */ attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)]; } @@ -877,6 +887,11 @@ void GeometryManager::device_update_attributes(Device *device, Attribute *subd_attr = mesh->subd_attributes.find(req); if (subd_attr) { + /* Vertex normals are stored in DeviceScene.tri_vnormal. */ + if (subd_attr->std == ATTR_STD_VERTEX_NORMAL) { + continue; + } + /* force a copy if we need to reallocate all the data */ subd_attr->modified |= attributes_need_realloc[Attribute::kernel_type(*subd_attr)]; } -- cgit v1.2.3 From 6b041ad3d025e5b8020d648405c8bc3c63560f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Tue, 17 Aug 2021 23:46:17 +0200 Subject: Cycles: use object coordinates when generated coordinates are missing This modifies the attribute lookup to use object coordinates if no generated coordinates are found on the geometry. This is useful to avoid creating and copying this attribute, thus saving a bit of time and memory. Reviewed By: brecht Differential Revision: https://developer.blender.org/D12238 --- .../kernel/shaders/node_texture_coordinate.osl | 4 +- intern/cycles/kernel/svm/svm_attribute.h | 48 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/intern/cycles/kernel/shaders/node_texture_coordinate.osl b/intern/cycles/kernel/shaders/node_texture_coordinate.osl index ac05e984af2..9cdb925dbfa 100644 --- a/intern/cycles/kernel/shaders/node_texture_coordinate.osl +++ b/intern/cycles/kernel/shaders/node_texture_coordinate.osl @@ -58,7 +58,9 @@ shader node_texture_coordinate( getattribute("geom:uv", UV); } else { - getattribute("geom:generated", Generated); + if (!getattribute("geom:generated", Generated)) { + Generated = transform("object", P); + } getattribute("geom:uv", UV); } diff --git a/intern/cycles/kernel/svm/svm_attribute.h b/intern/cycles/kernel/svm/svm_attribute.h index f598bfb8f8f..62740824ad1 100644 --- a/intern/cycles/kernel/svm/svm_attribute.h +++ b/intern/cycles/kernel/svm/svm_attribute.h @@ -72,6 +72,22 @@ ccl_device void svm_node_attr(KernelGlobals *kg, ShaderData *sd, float *stack, u } #endif + if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) { + /* No generated attribute, fall back to object coordinates. */ + float3 f = sd->P; + object_inverse_position_transform(kg, sd, &f); + if (type == NODE_ATTR_OUTPUT_FLOAT) { + stack_store_float(stack, out_offset, average(f)); + } + else if (type == NODE_ATTR_OUTPUT_FLOAT3) { + stack_store_float3(stack, out_offset, f); + } + else { + stack_store_float(stack, out_offset, 1.0f); + } + return; + } + /* Surface. */ if (desc.type == NODE_ATTR_FLOAT) { float f = primitive_surface_attribute_float(kg, sd, desc, NULL, NULL); @@ -145,6 +161,22 @@ ccl_device void svm_node_attr_bump_dx(KernelGlobals *kg, ShaderData *sd, float * } #endif + if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) { + /* No generated attribute, fall back to object coordinates. */ + float3 f = sd->P + sd->dP.dx; + object_inverse_position_transform(kg, sd, &f); + if (type == NODE_ATTR_OUTPUT_FLOAT) { + stack_store_float(stack, out_offset, average(f)); + } + else if (type == NODE_ATTR_OUTPUT_FLOAT3) { + stack_store_float3(stack, out_offset, f); + } + else { + stack_store_float(stack, out_offset, 1.0f); + } + return; + } + /* Surface */ if (desc.type == NODE_ATTR_FLOAT) { float dx; @@ -222,6 +254,22 @@ ccl_device void svm_node_attr_bump_dy(KernelGlobals *kg, ShaderData *sd, float * } #endif + if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) { + /* No generated attribute, fall back to object coordinates. */ + float3 f = sd->P + sd->dP.dy; + object_inverse_position_transform(kg, sd, &f); + if (type == NODE_ATTR_OUTPUT_FLOAT) { + stack_store_float(stack, out_offset, average(f)); + } + else if (type == NODE_ATTR_OUTPUT_FLOAT3) { + stack_store_float3(stack, out_offset, f); + } + else { + stack_store_float(stack, out_offset, 1.0f); + } + return; + } + /* Surface */ if (desc.type == NODE_ATTR_FLOAT) { float dy; -- cgit v1.2.3 From 55f9014616c9a45cb17eab325617e4912d58c10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Wed, 18 Aug 2021 21:27:52 +0200 Subject: Alembic procedural: remove Generated attribute creation The main reason for this is to speed up updates by avoid unnecessary copies as the Generated coordinates are a copy of the vertices. Creating this attribute may become optional in the future, with UI parameters to select which attribute to use from the Alembic archive as reference. --- intern/cycles/render/alembic.cpp | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp index 6713531c9b0..c1817016955 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/render/alembic.cpp @@ -959,14 +959,6 @@ void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame update_attributes(mesh->attributes, cached_data, frame_time); - /* we don't yet support arbitrary attributes, for now add vertex - * coordinates as generated coordinates if requested */ - if (mesh->need_attribute(scene_, ATTR_STD_GENERATED)) { - Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED); - memcpy( - attr->data_float3(), mesh->get_verts().data(), sizeof(float3) * mesh->get_verts().size()); - } - if (mesh->is_modified()) { bool need_rebuild = mesh->triangles_is_modified(); mesh->tag_update(scene_, need_rebuild); @@ -1053,14 +1045,6 @@ void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame update_attributes(mesh->subd_attributes, cached_data, frame_time); - /* we don't yet support arbitrary attributes, for now add vertex - * coordinates as generated coordinates if requested */ - if (mesh->need_attribute(scene_, ATTR_STD_GENERATED)) { - Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED); - memcpy( - attr->data_float3(), mesh->get_verts().data(), sizeof(float3) * mesh->get_verts().size()); - } - if (mesh->is_modified()) { bool need_rebuild = (mesh->triangles_is_modified()) || (mesh->subd_num_corners_is_modified()) || @@ -1110,17 +1094,6 @@ void AlembicProcedural::read_curves(AlembicObject *abc_object, Abc::chrono_t fra update_attributes(hair->attributes, cached_data, frame_time); - /* we don't yet support arbitrary attributes, for now add first keys as generated coordinates if - * requested */ - if (hair->need_attribute(scene_, ATTR_STD_GENERATED)) { - Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED); - float3 *generated = attr_generated->data_float3(); - - for (size_t i = 0; i < hair->num_curves(); i++) { - generated[i] = hair->get_curve_keys()[hair->get_curve(i).first_key]; - } - } - const bool rebuild = (hair->curve_keys_is_modified() || hair->curve_radius_is_modified()); hair->tag_update(scene_, rebuild); } -- cgit v1.2.3 From a217e043be2d06320800621043d49d002f9081db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20M=C3=BCller?= Date: Wed, 18 Aug 2021 22:22:21 +0200 Subject: Audaspace: porting PulseAudio fixes from upstream. --- .../audaspace/plugins/pulseaudio/PulseAudioDevice.cpp | 19 ++++++++++++++----- .../audaspace/plugins/pulseaudio/PulseAudioDevice.h | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp index cbfb5e96e6c..bf3fad82620 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp @@ -32,17 +32,24 @@ void PulseAudioDevice::PulseAudio_state_callback(pa_context *context, void *data device->m_state = AUD_pa_context_get_state(context); } -void PulseAudioDevice::PulseAudio_request(pa_stream *stream, size_t num_bytes, void *data) +void PulseAudioDevice::PulseAudio_request(pa_stream *stream, size_t total_bytes, void *data) { PulseAudioDevice* device = (PulseAudioDevice*)data; void* buffer; - AUD_pa_stream_begin_write(stream, &buffer, &num_bytes); + while(total_bytes > 0) + { + size_t num_bytes = total_bytes; + + AUD_pa_stream_begin_write(stream, &buffer, &num_bytes); + + device->mix((data_t*)buffer, num_bytes / AUD_DEVICE_SAMPLE_SIZE(device->m_specs)); - device->mix((data_t*)buffer, num_bytes / AUD_DEVICE_SAMPLE_SIZE(device->m_specs)); + AUD_pa_stream_write(stream, buffer, num_bytes, nullptr, 0, PA_SEEK_RELATIVE); - AUD_pa_stream_write(stream, buffer, num_bytes, nullptr, 0, PA_SEEK_RELATIVE); + total_bytes -= num_bytes; + } } void PulseAudioDevice::PulseAudio_underflow(pa_stream *stream, void *data) @@ -96,7 +103,6 @@ void PulseAudioDevice::runMixingThread() PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buffersize) : m_state(PA_CONTEXT_UNCONNECTED), - m_buffersize(buffersize), m_underflows(0) { m_mainloop = AUD_pa_mainloop_new(); @@ -187,6 +193,9 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff AUD_pa_stream_set_write_callback(m_stream, PulseAudio_request, this); AUD_pa_stream_set_underflow_callback(m_stream, PulseAudio_underflow, this); + buffersize *= AUD_DEVICE_SAMPLE_SIZE(m_specs); + m_buffersize = buffersize; + pa_buffer_attr buffer_attr; buffer_attr.fragsize = -1U; diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h index be34cc9032b..45b813a5755 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h @@ -59,7 +59,7 @@ private: * \param num_bytes The length in bytes to be supplied. * \param data The PulseAudio device. */ - AUD_LOCAL static void PulseAudio_request(pa_stream* stream, size_t num_bytes, void* data); + AUD_LOCAL static void PulseAudio_request(pa_stream* stream, size_t total_bytes, void* data); /** * Reports an underflow from the PulseAudio server. -- cgit v1.2.3 From ac09411368a968fd1d90aef2362654d5007b6b48 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 19 Aug 2021 12:15:04 +1000 Subject: Cleanup: unused warning --- source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index e8dd36e528c..49664323e89 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -37,7 +37,7 @@ static bNodeSocketTemplate geo_node_subdivision_surface_out[] = { static void geo_node_subdivision_surface_layout(uiLayout *layout, bContext *UNUSED(C), - PointerRNA *ptr) + PointerRNA *UNUSED(ptr)) { #ifndef WITH_OPENSUBDIV uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR); -- cgit v1.2.3 From feaa61a9680844d0fdc5b9b1a1407819fbb6dc97 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Wed, 18 Aug 2021 19:48:30 -0700 Subject: UI: Remove "Unfitted" Kerning Style Option This patch removes the "Kerning Style" option for UI widget font drawing and uses only the current default of "Fitted", since the other option of "Unfitted" is just the result of truncation errors. see D12231 for much more information. Differential Revision: https://developer.blender.org/D12231 Reviewed by Campbell Barton --- source/blender/blenfont/BLF_api.h | 2 +- source/blender/blenfont/intern/blf_font.c | 4 +- .../blender/editors/interface/interface_handlers.c | 9 ---- source/blender/editors/interface/interface_panel.c | 8 ---- .../editors/interface/interface_region_tooltip.c | 3 -- source/blender/editors/interface/interface_style.c | 45 +----------------- .../blender/editors/interface/interface_widgets.c | 53 ---------------------- source/blender/editors/space_file/filesel.c | 14 +----- source/blender/makesdna/DNA_userdef_types.h | 3 +- source/blender/makesrna/intern/rna_userdef.c | 12 ----- source/blender/python/generic/blf_py_api.c | 1 - 11 files changed, 5 insertions(+), 149 deletions(-) diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 7e92f79a523..eb3f9805240 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -271,7 +271,7 @@ void BLF_state_print(int fontid); #define BLF_ROTATION (1 << 0) #define BLF_CLIPPING (1 << 1) #define BLF_SHADOW (1 << 2) -#define BLF_KERNING_DEFAULT (1 << 3) +// #define BLF_FLAG_UNUSED_3 (1 << 3) /* dirty */ #define BLF_MATRIX (1 << 4) #define BLF_ASPECT (1 << 5) #define BLF_WORD_WRAP (1 << 6) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index d9396bd0f90..36eca3c00e6 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -355,9 +355,7 @@ BLI_INLINE GlyphBLF *blf_utf8_next_fast( #define BLF_KERNING_VARS(_font, _has_kerning, _kern_mode) \ 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; + const FT_UInt _kern_mode = FT_KERNING_DEFAULT; BLI_INLINE void blf_kerning_step_fast(FontBLF *font, const FT_UInt kern_mode, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 6f2232fabe5..b8f324cba83 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3086,11 +3086,6 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con UI_fontstyle_set(&fstyle); - if (fstyle.kerning == 1) { - /* for BLF_width */ - BLF_enable(fstyle.uifont_id, BLF_KERNING_DEFAULT); - } - ui_but_text_password_hide(password_str, but, false); if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { @@ -3141,10 +3136,6 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con but->pos = glyph_data[1] + but->ofs; } - if (fstyle.kerning == 1) { - BLF_disable(fstyle.uifont_id, BLF_KERNING_DEFAULT); - } - ui_but_text_password_hide(password_str, but, true); } diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 97d01ac3763..a64797af24f 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1447,10 +1447,6 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) is_alpha = (region->overlap && (theme_col_back[3] != 255)); - if (fstyle->kerning == 1) { - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - BLF_enable(fontid, BLF_ROTATION); BLF_rotation(fontid, M_PI_2); // UI_fontstyle_set(&style->widget); @@ -1620,10 +1616,6 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) GPU_line_smooth(false); BLF_disable(fontid, BLF_ROTATION); - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } } #undef TABS_PADDING_BETWEEN_FACTOR diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 10bc3760b42..a8f289702f8 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -1175,9 +1175,6 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, data->wrap_width = min_ii(UI_TIP_MAXWIDTH * U.pixelsize / aspect, winx - (UI_TIP_PADDING * 2)); font_flag |= BLF_WORD_WRAP; - if (data->fstyle.kerning == 1) { - font_flag |= BLF_KERNING_DEFAULT; - } BLF_enable(data->fstyle.uifont_id, font_flag); BLF_enable(blf_mono_font, font_flag); BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width); diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 88ab6a377d0..804156ba48c 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -83,7 +83,6 @@ static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id style->paneltitle.uifont_id = uifont_id; style->paneltitle.points = UI_DEFAULT_TITLE_POINTS; - style->paneltitle.kerning = 1; style->paneltitle.shadow = 3; style->paneltitle.shadx = 0; style->paneltitle.shady = -1; @@ -92,7 +91,6 @@ static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id style->grouplabel.uifont_id = uifont_id; style->grouplabel.points = UI_DEFAULT_TITLE_POINTS; - style->grouplabel.kerning = 1; style->grouplabel.shadow = 3; style->grouplabel.shadx = 0; style->grouplabel.shady = -1; @@ -101,7 +99,6 @@ static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id style->widgetlabel.uifont_id = uifont_id; style->widgetlabel.points = UI_DEFAULT_TEXT_POINTS; - style->widgetlabel.kerning = 1; style->widgetlabel.shadow = 3; style->widgetlabel.shadx = 0; style->widgetlabel.shady = -1; @@ -110,7 +107,6 @@ static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id style->widget.uifont_id = uifont_id; style->widget.points = UI_DEFAULT_TEXT_POINTS; - style->widget.kerning = 1; style->widget.shadow = 1; style->widget.shady = -1; style->widget.shadowalpha = 0.5f; @@ -164,9 +160,6 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs, BLF_shadow(fs->uifont_id, fs->shadow, shadow_color); BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady); } - if (fs->kerning == 1) { - font_flag |= BLF_KERNING_DEFAULT; - } if (fs_params->word_wrap == 1) { font_flag |= BLF_WORD_WRAP; } @@ -278,19 +271,12 @@ void UI_fontstyle_draw_rotated(const uiFontStyle *fs, BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady); } - if (fs->kerning == 1) { - BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); - } - BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); BLF_disable(fs->uifont_id, BLF_ROTATION); BLF_disable(fs->uifont_id, BLF_CLIPPING); if (fs->shadow) { BLF_disable(fs->uifont_id, BLF_SHADOW); } - if (fs->kerning == 1) { - BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); - } } /** @@ -302,18 +288,10 @@ void UI_fontstyle_draw_rotated(const uiFontStyle *fs, void UI_fontstyle_draw_simple( const uiFontStyle *fs, float x, float y, const char *str, const uchar col[4]) { - if (fs->kerning == 1) { - BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); - } - UI_fontstyle_set(fs); BLF_position(fs->uifont_id, x, y, 0.0f); BLF_color4ubv(fs->uifont_id, col); BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); - - if (fs->kerning == 1) { - BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); - } } /** @@ -326,10 +304,6 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs, const float col_fg[4], const float col_bg[4]) { - if (fs->kerning == 1) { - BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); - } - UI_fontstyle_set(fs); { @@ -357,10 +331,6 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs, BLF_position(fs->uifont_id, x, y, 0.0f); BLF_color4fv(fs->uifont_id, col_fg); BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); - - if (fs->kerning == 1) { - BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); - } } /* ************** helpers ************************ */ @@ -405,21 +375,8 @@ const uiStyle *UI_style_get_dpi(void) int UI_fontstyle_string_width(const uiFontStyle *fs, const char *str) { - int width; - - if (fs->kerning == 1) { - /* for BLF_width */ - BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); - } - UI_fontstyle_set(fs); - width = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); - - if (fs->kerning == 1) { - BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); - } - - return width; + return (int)BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); } int UI_fontstyle_height_max(const uiFontStyle *fs) diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index d3481c449ac..48f638dac33 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1576,11 +1576,6 @@ float UI_text_clip_middle_ex(const uiFontStyle *fstyle, /* need to set this first */ UI_fontstyle_set(fstyle); - if (fstyle->kerning == 1) { - /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - float strwidth = BLF_width(fstyle->uifont_id, str, max_len); if ((okwidth > 0.0f) && (strwidth > okwidth)) { @@ -1674,10 +1669,6 @@ float UI_text_clip_middle_ex(const uiFontStyle *fstyle, strwidth = BLF_width(fstyle->uifont_id, str, max_len); } - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - BLI_assert(strwidth <= okwidth); return strwidth; @@ -1736,11 +1727,6 @@ static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rct /* need to set this first */ UI_fontstyle_set(fstyle); - if (fstyle->kerning == 1) { - /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - /* define ofs dynamically */ if (but->ofs > but->pos) { but->ofs = but->pos; @@ -1785,10 +1771,6 @@ static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rct } } } - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } } /** @@ -1806,11 +1788,6 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons /* need to set this first */ UI_fontstyle_set(fstyle); - if (fstyle->kerning == 1) { - /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr, sizeof(but->drawstr)); but->ofs = 0; @@ -1870,10 +1847,6 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons but->strwidth = strwidth; but->drawstr[drawstr_len] = 0; } - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } } #ifdef WITH_INPUT_IME @@ -1985,11 +1958,6 @@ static void widget_draw_text(const uiFontStyle *fstyle, align = UI_STYLE_TEXT_CENTER; } - if (fstyle->kerning == 1) { - /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - /* Special case: when we're entering text for multiple buttons, * don't draw the text for any of the multi-editing buttons */ if (UNLIKELY(but->flag & UI_BUT_DRAG_MULTI)) { @@ -2151,10 +2119,6 @@ static void widget_draw_text(const uiFontStyle *fstyle, #endif } - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - #if 0 ui_rasterpos_safe(x, y, but->aspect); transopts = ui_translate_buttons(); @@ -2232,10 +2196,6 @@ static void widget_draw_text(const uiFontStyle *fstyle, } if (ul_index != -1) { - if (fstyle->kerning == 1) { - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - int ul_width = round_fl_to_int(BLF_width(fstyle->uifont_id, "_", 2)); struct UnderlineData ul_data = { @@ -2256,10 +2216,6 @@ static void widget_draw_text(const uiFontStyle *fstyle, BLF_position(fstyle->uifont_id, pos_x, pos_y, 0.0f); BLF_color4ubv(fstyle->uifont_id, wcol->text); BLF_draw(fstyle->uifont_id, "_", 2); - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } } } } @@ -5369,11 +5325,6 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, /* need to set this first */ UI_fontstyle_set(fstyle); - if (fstyle->kerning == 1) { - /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - if (separator_type == UI_MENU_ITEM_SEPARATOR_SHORTCUT) { /* Shrink rect to exclude the shortcut string. */ rect->xmax -= BLF_width(fstyle->uifont_id, cpoin + 1, INT_MAX) + UI_DPI_ICON_SIZE; @@ -5398,10 +5349,6 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, else { BLI_assert_msg(0, "Unknwon menu item separator type"); } - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } } } diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 72e7e0716db..4ab7014cf82 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -863,20 +863,8 @@ FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d, float file_string_width(const char *str) { const uiStyle *style = UI_style_get(); - float width; - UI_fontstyle_set(&style->widget); - if (style->widget.kerning == 1) { /* for BLF_width */ - BLF_enable(style->widget.uifont_id, BLF_KERNING_DEFAULT); - } - - width = BLF_width(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); - - if (style->widget.kerning == 1) { - BLF_disable(style->widget.uifont_id, BLF_KERNING_DEFAULT); - } - - return width; + return BLF_width(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); } float file_font_pointsize(void) diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 28acf5413b8..27376432092 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -71,14 +71,13 @@ typedef struct uiFontStyle { short uifont_id; /** Actual size depends on 'global' dpi. */ short points; - /** Unfitted or default kerning value. */ - short kerning; /** Style hint. */ short italic, bold; /** Value is amount of pixels blur. */ short shadow; /** Shadow offset in pixels. */ short shadx, shady; + char _pad0[2]; /** Total alpha. */ float shadowalpha; /** 1 value, typically white or black anyway. */ diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 483506d5733..73811924c23 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -1118,12 +1118,6 @@ static void rna_def_userdef_theme_ui_font_style(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem font_kerning_style[] = { - {0, "UNFITTED", 0, "Unfitted", "Use scaled but un-grid-fitted kerning distances"}, - {1, "FITTED", 0, "Fitted", "Use scaled and grid-fitted kerning distances"}, - {0, NULL, 0, NULL, NULL}, - }; - srna = RNA_def_struct(brna, "ThemeFontStyle", NULL); RNA_def_struct_sdna(srna, "uiFontStyle"); RNA_def_struct_clear_flag(srna, STRUCT_UNDO); @@ -1134,12 +1128,6 @@ static void rna_def_userdef_theme_ui_font_style(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Points", "Font size in points"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); - prop = RNA_def_property(srna, "font_kerning_style", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "kerning"); - RNA_def_property_enum_items(prop, font_kerning_style); - RNA_def_property_ui_text(prop, "Kerning Style", "Which style to use for font kerning"); - RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); - prop = RNA_def_property(srna, "shadow", PROP_INT, PROP_PIXEL); RNA_def_property_range(prop, 0, 5); RNA_def_property_ui_text(prop, "Shadow Size", "Shadow size (0, 3 and 5 supported)"); diff --git a/source/blender/python/generic/blf_py_api.c b/source/blender/python/generic/blf_py_api.c index 0ec66e22fa9..9e725730d40 100644 --- a/source/blender/python/generic/blf_py_api.c +++ b/source/blender/python/generic/blf_py_api.c @@ -493,7 +493,6 @@ PyObject *BPyInit_blf(void) PyModule_AddIntConstant(submodule, "ROTATION", BLF_ROTATION); PyModule_AddIntConstant(submodule, "CLIPPING", BLF_CLIPPING); PyModule_AddIntConstant(submodule, "SHADOW", BLF_SHADOW); - PyModule_AddIntConstant(submodule, "KERNING_DEFAULT", BLF_KERNING_DEFAULT); PyModule_AddIntConstant(submodule, "WORD_WRAP", BLF_WORD_WRAP); PyModule_AddIntConstant(submodule, "MONOCHROME", BLF_MONOCHROME); -- cgit v1.2.3 From 7a4ef5256a383c2aba4585a2b80ff715b52bf73b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 19 Aug 2021 12:36:23 +1000 Subject: Cleanup: reduce indentation in loops that check region visibility --- source/blender/windowmanager/intern/wm_draw.c | 156 +++++++++++---------- source/blender/windowmanager/intern/wm_operators.c | 7 +- 2 files changed, 88 insertions(+), 75 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index dcb918747f3..328950cf8f9 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -691,46 +691,48 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) /* Then do actual drawing of regions. */ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (region->visible && region->do_draw) { - CTX_wm_region_set(C, region); - bool use_viewport = WM_region_use_viewport(area, region); - - GPU_debug_group_begin(use_viewport ? "Viewport" : "ARegion"); - - if (stereo && wm_draw_region_stereo_set(bmain, area, region, STEREO_LEFT_ID)) { - wm_draw_region_buffer_create(region, true, use_viewport); - - for (int view = 0; view < 2; view++) { - eStereoViews sview; - if (view == 0) { - sview = STEREO_LEFT_ID; - } - else { - sview = STEREO_RIGHT_ID; - wm_draw_region_stereo_set(bmain, area, region, sview); - } - - wm_draw_region_bind(region, view); - ED_region_do_draw(C, region); - wm_draw_region_unbind(region); + if (!region->visible || !region->do_draw) { + continue; + } + + CTX_wm_region_set(C, region); + bool use_viewport = WM_region_use_viewport(area, region); + + GPU_debug_group_begin(use_viewport ? "Viewport" : "ARegion"); + + if (stereo && wm_draw_region_stereo_set(bmain, area, region, STEREO_LEFT_ID)) { + wm_draw_region_buffer_create(region, true, use_viewport); + + for (int view = 0; view < 2; view++) { + eStereoViews sview; + if (view == 0) { + sview = STEREO_LEFT_ID; } - if (use_viewport) { - GPUViewport *viewport = region->draw_buffer->viewport; - GPU_viewport_stereo_composite(viewport, win->stereo3d_format); + else { + sview = STEREO_RIGHT_ID; + wm_draw_region_stereo_set(bmain, area, region, sview); } - } - else { - wm_draw_region_buffer_create(region, false, use_viewport); - wm_draw_region_bind(region, 0); + + wm_draw_region_bind(region, view); ED_region_do_draw(C, region); wm_draw_region_unbind(region); } + if (use_viewport) { + GPUViewport *viewport = region->draw_buffer->viewport; + GPU_viewport_stereo_composite(viewport, win->stereo3d_format); + } + } + else { + wm_draw_region_buffer_create(region, false, use_viewport); + wm_draw_region_bind(region, 0); + ED_region_do_draw(C, region); + wm_draw_region_unbind(region); + } - GPU_debug_group_end(); + GPU_debug_group_end(); - region->do_draw = false; - CTX_wm_region_set(C, NULL); - } + region->do_draw = false; + CTX_wm_region_set(C, NULL); } CTX_wm_area_set(C, NULL); @@ -740,29 +742,30 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) /* Draw menus into their own framebuffer. */ LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { - if (region->visible) { - CTX_wm_menu_set(C, region); + if (!region->visible) { + continue; + } + CTX_wm_menu_set(C, region); - GPU_debug_group_begin("Menu"); + GPU_debug_group_begin("Menu"); - if (region->type && region->type->layout) { - /* UI code reads the OpenGL state, but we have to refresh - * the UI layout beforehand in case the menu size changes. */ - wmViewport(®ion->winrct); - region->type->layout(C, region); - } + if (region->type && region->type->layout) { + /* UI code reads the OpenGL state, but we have to refresh + * the UI layout beforehand in case the menu size changes. */ + wmViewport(®ion->winrct); + region->type->layout(C, region); + } - wm_draw_region_buffer_create(region, false, false); - wm_draw_region_bind(region, 0); - GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); - ED_region_do_draw(C, region); - wm_draw_region_unbind(region); + wm_draw_region_buffer_create(region, false, false); + wm_draw_region_bind(region, 0); + GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); + ED_region_do_draw(C, region); + wm_draw_region_unbind(region); - GPU_debug_group_end(); + GPU_debug_group_end(); - region->do_draw = false; - CTX_wm_menu_set(C, NULL); - } + region->do_draw = false; + CTX_wm_menu_set(C, NULL); } } @@ -786,8 +789,12 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) /* Blit non-overlapping area regions. */ ED_screen_areas_iter (win, screen, area) { LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (region->visible && region->overlap == false) { - /* Blit from offscreen buffer. */ + if (!region->visible) { + continue; + } + + if (region->overlap == false) { + /* Blit from off-screen buffer. */ wm_draw_region_blit(region, view); } } @@ -796,24 +803,25 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) /* Draw overlays and paint cursors. */ ED_screen_areas_iter (win, screen, area) { LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (region->visible) { - const bool do_paint_cursor = (wm->paintcursors.first && region == screen->active_region); - const bool do_draw_overlay = (region->type && region->type->draw_overlay); - if (!(do_paint_cursor || do_draw_overlay)) { - continue; - } + if (!region->visible) { + continue; + } + const bool do_paint_cursor = (wm->paintcursors.first && region == screen->active_region); + const bool do_draw_overlay = (region->type && region->type->draw_overlay); + if (!(do_paint_cursor || do_draw_overlay)) { + continue; + } - CTX_wm_area_set(C, area); - CTX_wm_region_set(C, region); - if (do_draw_overlay) { - wm_region_draw_overlay(C, area, region); - } - if (do_paint_cursor) { - wm_paintcursor_draw(C, area, region); - } - CTX_wm_region_set(C, NULL); - CTX_wm_area_set(C, NULL); + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + if (do_draw_overlay) { + wm_region_draw_overlay(C, area, region); + } + if (do_paint_cursor) { + wm_paintcursor_draw(C, area, region); } + CTX_wm_region_set(C, NULL); + CTX_wm_area_set(C, NULL); } } wmWindowViewport(win); @@ -821,7 +829,10 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) /* Blend in overlapping area regions */ ED_screen_areas_iter (win, screen, area) { LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (region->visible && region->overlap) { + if (!region->visible) { + continue; + } + if (region->overlap) { wm_draw_region_blend(region, 0, true); } } @@ -834,9 +845,10 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) /* Blend in floating regions (menus). */ LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { - if (region->visible) { - wm_draw_region_blend(region, 0, true); + if (!region->visible) { + continue; } + wm_draw_region_blend(region, 0, true); } /* always draw, not only when screen tagged */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 50db8e73844..b5a038757c2 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -3178,10 +3178,11 @@ static void redraw_timer_step(bContext *C, LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) { CTX_wm_area_set(C, area_iter); LISTBASE_FOREACH (ARegion *, region_iter, &area_iter->regionbase) { - if (region_iter->visible) { - CTX_wm_region_set(C, region_iter); - wm_draw_region_test(C, area_iter, region_iter); + if (!region_iter->visible) { + continue; } + CTX_wm_region_set(C, region_iter); + wm_draw_region_test(C, area_iter, region_iter); } } -- cgit v1.2.3 From 594790d8a56777c4436b4da2165113d59d57283a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 19 Aug 2021 12:49:10 +1000 Subject: UI: add function to access the buttons text without it's shortcut --- source/blender/editors/interface/interface_context_menu.c | 8 +------- source/blender/editors/interface/interface_intern.h | 2 ++ source/blender/editors/interface/interface_query.c | 7 +++++++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 8ace057891d..b953d88c896 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -373,13 +373,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um) BLI_assert(ui_but_is_user_menu_compatible(C, but)); char drawstr[sizeof(but->drawstr)]; - STRNCPY(drawstr, but->drawstr); - if (but->flag & UI_BUT_HAS_SEP_CHAR) { - char *sep = strrchr(drawstr, UI_SEP_CHAR); - if (sep) { - *sep = '\0'; - } - } + ui_but_drawstr_without_sep_char(but, drawstr, sizeof(drawstr)); MenuType *mt = NULL; if (but->optype) { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 6b0b8e8df8f..d61104f094e 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1177,6 +1177,8 @@ uiBut *ui_list_find_mouse_over_ex(const struct ARegion *region, bool ui_but_contains_password(const uiBut *but) ATTR_WARN_UNUSED_RESULT; +size_t ui_but_drawstr_without_sep_char(const uiBut *but, char *str, size_t str_maxlen) + ATTR_NONNULL(1, 2); size_t ui_but_drawstr_len_without_sep_char(const uiBut *but); size_t ui_but_tip_len_only_first_line(const uiBut *but); diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 8534c95b6fd..09429bb6df5 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -23,6 +23,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_rect.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "DNA_screen_types.h" @@ -553,6 +554,12 @@ size_t ui_but_drawstr_len_without_sep_char(const uiBut *but) return strlen(but->drawstr); } +size_t ui_but_drawstr_without_sep_char(const uiBut *but, char *str, size_t str_maxlen) +{ + size_t str_len_clip = ui_but_drawstr_len_without_sep_char(but); + return BLI_strncpy_rlen(str, but->drawstr, min_zz(str_len_clip + 1, str_maxlen)); +} + size_t ui_but_tip_len_only_first_line(const uiBut *but) { if (but->tip == NULL) { -- cgit v1.2.3 From 4734de1093a1a60621ec6b10e2e56cd09aa3fc64 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 19 Aug 2021 13:17:37 +1000 Subject: Cleanup: correction to unused warning removal This broke building without opensubdiv --- source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 49664323e89..e524564edab 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -37,9 +37,10 @@ static bNodeSocketTemplate geo_node_subdivision_surface_out[] = { static void geo_node_subdivision_surface_layout(uiLayout *layout, bContext *UNUSED(C), - PointerRNA *UNUSED(ptr)) + PointerRNA *ptr) { #ifndef WITH_OPENSUBDIV + UNUSED_VARS(ptr); uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR); #else uiLayoutSetPropSep(layout, true); -- cgit v1.2.3 From 22ab0159a9754365e2d10a1bc658d4409d084fa6 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Wed, 18 Aug 2021 20:30:58 -0700 Subject: Refactor: BLF Without Kerning Modes Simplification of BLF code after removal of kerning modes. See D12262 for more details. Differential Revision: https://developer.blender.org/D12262 Reviewed by Campbell Barton --- source/blender/blenfont/intern/blf_font.c | 104 ++++++--------------- source/blender/blenfont/intern/blf_glyph.c | 14 +-- .../blender/blenfont/intern/blf_internal_types.h | 7 -- 3 files changed, 30 insertions(+), 95 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 36eca3c00e6..9ddc788d2dc 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -308,19 +308,14 @@ void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) blf_glyph_cache_release(font); } -static void blf_font_ensure_ascii_kerning(FontBLF *font, - GlyphCacheBLF *gc, - const FT_UInt kern_mode) +static void blf_font_ensure_ascii_kerning(FontBLF *font, GlyphCacheBLF *gc) { - KerningCacheBLF *kc = font->kerning_cache; - - font->kerning_mode = kern_mode; - - if (!kc || kc->mode != kern_mode) { - font->kerning_cache = kc = blf_kerning_cache_find(font); - if (!kc) { - font->kerning_cache = kc = blf_kerning_cache_new(font, gc); - } + if (font->kerning_cache || !FT_HAS_KERNING(font->face)) { + return; + } + font->kerning_cache = blf_kerning_cache_find(font); + if (!font->kerning_cache) { + font->kerning_cache = blf_kerning_cache_new(font, gc); } } @@ -353,28 +348,23 @@ BLI_INLINE GlyphBLF *blf_utf8_next_fast( return g; } -#define BLF_KERNING_VARS(_font, _has_kerning, _kern_mode) \ - const bool _has_kerning = FT_HAS_KERNING((_font)->face); \ - const FT_UInt _kern_mode = FT_KERNING_DEFAULT; - BLI_INLINE void blf_kerning_step_fast(FontBLF *font, - const FT_UInt kern_mode, const GlyphBLF *g_prev, const GlyphBLF *g, const uint c_prev, const uint c, int *pen_x_p) { - /* `blf_font_ensure_ascii_kerning(font, gc, kern_mode);` must be called before this function. */ - BLI_assert((font->kerning_mode == kern_mode) && (font->kerning_cache != NULL)); + /* `blf_font_ensure_ascii_kerning(font, gc);` must be called before this function. */ + BLI_assert(font->kerning_cache != NULL); - if (g_prev != NULL) { + if (g_prev != NULL && FT_HAS_KERNING(font->face)) { if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { *pen_x_p += font->kerning_cache->ascii_table[c][c_prev]; } else { FT_Vector delta; - if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, kern_mode, &delta) == 0) { + if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_DEFAULT, &delta) == 0) { *pen_x_p += (int)delta.x >> 6; } } @@ -398,9 +388,7 @@ static void blf_font_draw_ex(FontBLF *font, return; } - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); + blf_font_ensure_ascii_kerning(font, gc); blf_batch_draw_begin(font); @@ -413,9 +401,7 @@ static void blf_font_draw_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, &pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); /* do not return this loop if clipped, we want every character tested */ blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); @@ -449,9 +435,7 @@ static void blf_font_draw_ascii_ex( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); + blf_font_ensure_ascii_kerning(font, gc); blf_batch_draw_begin(font); @@ -465,9 +449,7 @@ static void blf_font_draw_ascii_ex( continue; } } - if (has_kerning) { - blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, &pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); /* do not return this loop if clipped, we want every character tested */ blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); @@ -554,9 +536,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, int chx, chy; int y, x; - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); + blf_font_ensure_ascii_kerning(font, gc); /* another buffer specific call for color conversion */ @@ -569,9 +549,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, &pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); chx = pen_x + ((int)g->pos[0]); chy = pen_y_basis + g->dims[1]; @@ -685,8 +663,6 @@ void blf_font_draw_buffer(FontBLF *font, const char *str, size_t len, struct Res } static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, - const bool has_kerning, - const FT_UInt kern_mode, const uint c_prev, const uint c, GlyphBLF *g_prev, @@ -700,9 +676,7 @@ static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, if (UNLIKELY(g == NULL)) { return false; /* continue the calling loop. */ } - if (has_kerning) { - blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, pen_x); *pen_x += g->advance_i; @@ -720,18 +694,13 @@ size_t blf_font_width_to_strlen( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); const int width_i = (int)width; - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - if (has_kerning) { - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - } + blf_font_ensure_ascii_kerning(font, gc); 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) { g = blf_utf8_next_fast(font, gc, 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)) { + if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) { break; } } @@ -756,11 +725,7 @@ size_t blf_font_width_to_rstrlen( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); const int width_i = (int)width; - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - if (has_kerning) { - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - } + blf_font_ensure_ascii_kerning(font, gc); i = BLI_strnlen(str, len); s = BLI_str_find_prev_char_utf8(str, &str[i]); @@ -781,8 +746,7 @@ size_t blf_font_width_to_rstrlen( BLI_assert(i_tmp == i); } - if (blf_font_width_to_strlen_glyph_process( - font, has_kerning, kern_mode, c_prev, c, g_prev, g, &pen_x, width_i)) { + if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) { break; } } @@ -809,14 +773,12 @@ static void blf_font_boundbox_ex(FontBLF *font, size_t i = 0; rctf gbox; - BLF_KERNING_VARS(font, has_kerning, kern_mode); - box->xmin = 32000.0f; box->xmax = -32000.0f; box->ymin = 32000.0f; box->ymax = -32000.0f; - blf_font_ensure_ascii_kerning(font, gc, kern_mode); + blf_font_ensure_ascii_kerning(font, gc); while ((i < len) && str[i]) { g = blf_utf8_next_fast(font, gc, str, &i, &c); @@ -827,9 +789,7 @@ static void blf_font_boundbox_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, &pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); gbox.xmin = (float)pen_x; gbox.xmax = (float)pen_x + g->advance; @@ -909,9 +869,7 @@ static void blf_font_wrap_apply(FontBLF *font, GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); + blf_font_ensure_ascii_kerning(font, gc); struct WordWrapVars { int wrap_width; @@ -933,9 +891,7 @@ static void blf_font_wrap_apply(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, &pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); /** * Implementation Detail (utf8). @@ -1167,9 +1123,7 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, return; } - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); + blf_font_ensure_ascii_kerning(font, gc); while ((i < len) && str[i]) { i_curr = i; @@ -1181,9 +1135,7 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - blf_kerning_step_fast(font, kern_mode, g_prev, g, c_prev, c, &pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); gbox.xmin = pen_x; gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index c5abc5982e8..5fb69251466 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -57,16 +57,7 @@ KerningCacheBLF *blf_kerning_cache_find(FontBLF *font) { - KerningCacheBLF *p; - - p = (KerningCacheBLF *)font->kerning_caches.first; - while (p) { - if (p->mode == font->kerning_mode) { - return p; - } - p = p->next; - } - return NULL; + return (KerningCacheBLF *)font->kerning_caches.first; } /* Create a new glyph cache for the current kerning mode. */ @@ -75,7 +66,6 @@ KerningCacheBLF *blf_kerning_cache_new(FontBLF *font, GlyphCacheBLF *gc) KerningCacheBLF *kc = MEM_mallocN(sizeof(KerningCacheBLF), __func__); kc->next = NULL; kc->prev = NULL; - kc->mode = font->kerning_mode; GlyphBLF *g_table[KERNING_CACHE_TABLE_SIZE]; for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { @@ -99,7 +89,7 @@ KerningCacheBLF *blf_kerning_cache_new(FontBLF *font, GlyphCacheBLF *gc) continue; } FT_Vector delta; - if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, kc->mode, &delta) == 0) { + if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_DEFAULT, &delta) == 0) { kc->ascii_table[i][j] = (int)delta.x >> 6; } } diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index ece9a5ffae4..caa10b2b125 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -51,10 +51,6 @@ extern BatchBLF g_batch; typedef struct KerningCacheBLF { struct KerningCacheBLF *next, *prev; - - /* kerning mode. */ - FT_UInt mode; - /** * Cache a ascii glyph pairs. Only store the x offset we are interested in, * instead of the full #FT_Vector since it's not used for drawing at the moment. @@ -242,9 +238,6 @@ typedef struct FontBLF { /* freetype2 face. */ FT_Face face; - /* freetype kerning */ - FT_UInt kerning_mode; - /* data for buffer usage (drawing into a texture buffer) */ FontBufInfoBLF buf_info; -- cgit v1.2.3 From cf721942149057684c089c0677c224dbe9d90c52 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 19 Aug 2021 16:57:05 +1000 Subject: Fix T71137: curve minimum twist producing wrong geometry Originally D11886 by @ghaspias with minor edits applied. --- source/blender/blenkernel/intern/curve.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index db0ea71e233..c49788528a4 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -2331,17 +2331,21 @@ static void make_bevel_list_3D_minimum_twist(BevList *bl) bevp1 = bevp2 + (bl->nr - 1); bevp0 = bevp1 - 1; - nr = bl->nr; - while (nr--) { + /* The ordinal of the point being adjusted (bevp2). First point is 1. */ - if (nr + 3 > bl->nr) { /* first time and second time, otherwise first point adjusts last */ - vec_to_quat(bevp1->quat, bevp1->dir, 5, 1); - } - else { - minimum_twist_between_two_points(bevp1, bevp0); - } + /* First point is the reference, don't adjust. + * Skip this point in the following loop. */ + if (bl->nr > 0) { + vec_to_quat(bevp2->quat, bevp2->dir, 5, 1); - bevp0 = bevp1; + bevp0 = bevp1; /* bevp0 is unused */ + bevp1 = bevp2; + bevp2++; + } + for (nr = 1; nr < bl->nr; nr++) { + minimum_twist_between_two_points(bevp2, bevp1); + + bevp0 = bevp1; /* bevp0 is unused */ bevp1 = bevp2; bevp2++; } -- cgit v1.2.3 From 4db4123409de6f42a519cd03275d680b3d821298 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 19 Aug 2021 17:55:40 +1000 Subject: Correct assert from 22ab0159a9754365e2d10a1bc658d4409d084fa6 --- source/blender/blenfont/intern/blf_font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 9ddc788d2dc..00d3cfb09eb 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -356,7 +356,7 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, int *pen_x_p) { /* `blf_font_ensure_ascii_kerning(font, gc);` must be called before this function. */ - BLI_assert(font->kerning_cache != NULL); + BLI_assert(font->kerning_cache != NULL || !FT_HAS_KERNING(font->face)); if (g_prev != NULL && FT_HAS_KERNING(font->face)) { if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { -- cgit v1.2.3 From 5b97c00e9fc4e1e5d6cdfa7130eab5aada0e068e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Thu, 19 Aug 2021 14:13:45 +0200 Subject: Alembic import: option to always add a cache reader The current behavior of the Alembic importer is to only create a `MeshSequenceCache` modifier or a `Transform Cache` constraint to imported objects if they have some animated properties. Since static objects do not have a cache reader, when reloading files those objects are not updated. Currently, the only way to properly reload a file because of this is to reimport it. This adds an option to the importer to always add a cache reader, even if there is no animated data, to ensure that all objects coming from Alembic archive are linked to them and updated properly upon reloads. Reviewed by: brecht, sybren Ref D10197. --- source/blender/editors/io/io_alembic.c | 10 ++++++++++ source/blender/io/alembic/ABC_alembic.h | 1 + source/blender/io/alembic/intern/abc_reader_curves.cc | 2 +- source/blender/io/alembic/intern/abc_reader_mesh.cc | 4 ++-- source/blender/io/alembic/intern/abc_reader_object.cc | 2 +- source/blender/io/alembic/intern/abc_reader_object.h | 2 ++ source/blender/io/alembic/intern/abc_reader_points.cc | 2 +- source/blender/io/alembic/intern/alembic_capi.cc | 2 ++ 8 files changed, 20 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 12890552b1d..bbff37221e8 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -615,6 +615,7 @@ static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "set_frame_range", 0, NULL, ICON_NONE); uiItemR(col, imfptr, "is_sequence", 0, NULL, ICON_NONE); uiItemR(col, imfptr, "validate_meshes", 0, NULL, ICON_NONE); + uiItemR(col, imfptr, "always_add_cache_reader", 0, NULL, ICON_NONE); } static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op) @@ -645,6 +646,7 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op) const bool is_sequence = RNA_boolean_get(op->ptr, "is_sequence"); const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range"); const bool validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes"); + const bool always_add_cache_reader = RNA_boolean_get(op->ptr, "always_add_cache_reader"); const bool as_background_job = RNA_boolean_get(op->ptr, "as_background_job"); int offset = 0; @@ -672,6 +674,7 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op) sequence_len, offset, validate_meshes, + always_add_cache_reader, as_background_job); return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; @@ -721,6 +724,13 @@ void WM_OT_alembic_import(wmOperatorType *ot) "Validate Meshes", "Check imported mesh objects for invalid data (slow)"); + RNA_def_boolean(ot->srna, + "always_add_cache_reader", + false, + "Always Add Cache Reader", + "Add cache modifiers and constraints to imported objects even if they are not " + "animated so that they can be updated when reloading the Alembic archive"); + RNA_def_boolean(ot->srna, "is_sequence", false, diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h index 0dbebb1e4c4..0a3a43bb21f 100644 --- a/source/blender/io/alembic/ABC_alembic.h +++ b/source/blender/io/alembic/ABC_alembic.h @@ -97,6 +97,7 @@ bool ABC_import(struct bContext *C, int sequence_len, int offset, bool validate_meshes, + bool always_add_cache_reader, bool as_background_job); struct CacheArchiveHandle *ABC_create_handle(struct Main *bmain, diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc index 8d6605d6973..27ee35d1b39 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.cc +++ b/source/blender/io/alembic/intern/abc_reader_curves.cc @@ -112,7 +112,7 @@ void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSele read_curve_sample(cu, m_curves_schema, sample_sel); - if (has_animations(m_curves_schema, m_settings)) { + if (m_settings->always_add_cache_reader || has_animations(m_curves_schema, m_settings)) { addCacheModifier(); } } diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index c05df7f1ff5..77edd4908bd 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -578,7 +578,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec readFaceSetsSample(bmain, mesh, sample_sel); - if (has_animations(m_schema, m_settings)) { + if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) { addCacheModifier(); } } @@ -928,7 +928,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec BKE_mesh_validate(mesh, false, false); } - if (has_animations(m_schema, m_settings)) { + if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) { addCacheModifier(); } } diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc index d136d8c3e91..9a5ffd04bd1 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.cc +++ b/source/blender/io/alembic/intern/abc_reader_object.cc @@ -197,7 +197,7 @@ void AbcObjectReader::setupObjectTransform(const float time) BKE_object_apply_mat4(m_object, transform_from_alembic, true, false); BKE_object_to_mat4(m_object, m_object->obmat); - if (!is_constant) { + if (!is_constant || m_settings->always_add_cache_reader) { bConstraint *con = BKE_constraint_add_for_object( m_object, nullptr, CONSTRAINT_TYPE_TRANSFORM_CACHE); bTransformCacheConstraint *data = static_cast(con->data); diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h index dacdcf3f722..89590b26b61 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.h +++ b/source/blender/io/alembic/intern/abc_reader_object.h @@ -51,6 +51,7 @@ struct ImportSettings { int read_flag; bool validate_meshes; + bool always_add_cache_reader; CacheFile *cache_file; @@ -65,6 +66,7 @@ struct ImportSettings { sequence_offset(0), read_flag(0), validate_meshes(false), + always_add_cache_reader(false), cache_file(NULL) { } diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc index f7dcba7a0de..3aeacbd14fe 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.cc +++ b/source/blender/io/alembic/intern/abc_reader_points.cc @@ -95,7 +95,7 @@ void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSel m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); m_object->data = mesh; - if (has_animations(m_schema, m_settings)) { + if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) { addCacheModifier(); } } diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index b94b75b2216..deb945b767c 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -663,6 +663,7 @@ bool ABC_import(bContext *C, int sequence_len, int offset, bool validate_meshes, + bool always_add_cache_reader, bool as_background_job) { /* Using new here since MEM_* functions do not call constructor to properly initialize data. */ @@ -681,6 +682,7 @@ bool ABC_import(bContext *C, job->settings.sequence_len = sequence_len; job->settings.sequence_offset = offset; job->settings.validate_meshes = validate_meshes; + job->settings.always_add_cache_reader = always_add_cache_reader; job->error_code = ABC_NO_ERROR; job->was_cancelled = false; job->archive = nullptr; -- cgit v1.2.3 From 51862c8445c8d5f573d1c86780db9e0972ef14dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Thu, 19 Aug 2021 14:34:01 +0200 Subject: Cycles: experimental integration of Alembic procedural in viewport rendering This patch exposes the Cycles Alembic Procedural through the MeshSequenceCache modifier in order to use and test it from Blender. To enable it, one has to switch the render feature set to experimental and activate the Procedural in the modifier. An Alembic Procedural is then created for each CacheFile from Blender set to use the Procedural, and each Blender object having a MeshSequenceCache modifier is added to list of objects of the right procedural. The procedural's parameters derive from the CacheFile's properties which are already exposed in the UI through the modifier, although more Cycles specific options might be added in the future. As there is currently no cache controls and since we load all the data at the beginning of the render session, the procedural is only available during viewport renders at the moment. When an Alembic procedural is rendered, data from the archive are not read on the Blender side. If a Cycles render is not active and the CacheFile is set to use the Cycles Procedural, bounding boxes are used to display the objects in the scene as a signal that the objects are not processed by Blender anymore. This is standard in other DCCs. However this does not reduce the memory usage from Blender as the Alembic data was already loaded either during an import or during a .blend file read. This is mostly a hack to test the Cycles Alembic procedural until we have a better Blender side mechanism for letting renderers load their own geometry, which will be based on import and export settings on Collections (T68933). Ref T79174, D3089 Reviewed By: brecht, sybren Maniphest Tasks: T79174 Differential Revision: https://developer.blender.org/D10197 --- intern/cycles/blender/CMakeLists.txt | 10 ++ intern/cycles/blender/addon/__init__.py | 1 + intern/cycles/blender/addon/properties.py | 6 ++ intern/cycles/blender/blender_mesh.cpp | 21 +---- intern/cycles/blender/blender_object.cpp | 102 +++++++++++++++++++-- intern/cycles/blender/blender_sync.cpp | 1 + intern/cycles/blender/blender_sync.h | 3 + intern/cycles/blender/blender_util.h | 29 ++++++ source/blender/blenkernel/BKE_cachefile.h | 10 ++ source/blender/blenkernel/BKE_modifier.h | 8 +- source/blender/blenkernel/BKE_object.h | 5 +- source/blender/blenkernel/BKE_scene.h | 4 + source/blender/blenkernel/intern/cachefile.c | 18 ++++ source/blender/blenkernel/intern/constraint.c | 5 + source/blender/blenkernel/intern/modifier.c | 4 +- source/blender/blenkernel/intern/object.c | 7 +- source/blender/blenkernel/intern/scene.c | 18 ++++ .../intern/builder/deg_builder_relations.cc | 3 +- .../editors/interface/interface_templates.c | 25 +++++ source/blender/editors/render/render_update.c | 14 +++ source/blender/makesdna/DNA_cachefile_types.h | 11 ++- source/blender/makesrna/intern/rna_cachefile.c | 17 ++++ source/blender/makesrna/intern/rna_render.c | 6 ++ source/blender/makesrna/intern/rna_scene.c | 10 ++ source/blender/modifiers/intern/MOD_build.c | 4 +- source/blender/modifiers/intern/MOD_cloth.c | 4 +- source/blender/modifiers/intern/MOD_collision.c | 4 +- source/blender/modifiers/intern/MOD_displace.c | 4 +- source/blender/modifiers/intern/MOD_dynamicpaint.c | 4 +- source/blender/modifiers/intern/MOD_explode.c | 4 +- source/blender/modifiers/intern/MOD_fluid.c | 4 +- source/blender/modifiers/intern/MOD_meshcache.c | 4 +- .../modifiers/intern/MOD_meshsequencecache.c | 53 ++++++++++- source/blender/modifiers/intern/MOD_softbody.c | 4 +- source/blender/modifiers/intern/MOD_surface.c | 4 +- .../modifiers/intern/MOD_volume_displace.cc | 2 +- source/blender/modifiers/intern/MOD_warp.c | 4 +- source/blender/modifiers/intern/MOD_wave.c | 4 +- source/blender/modifiers/intern/MOD_weightvgedit.c | 4 +- source/blender/modifiers/intern/MOD_weightvgmix.c | 4 +- .../modifiers/intern/MOD_weightvgproximity.c | 4 +- source/blender/render/RE_engine.h | 7 ++ source/blender/render/intern/engine.c | 13 +++ 43 files changed, 417 insertions(+), 56 deletions(-) diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index 2d48563d8a6..ee5c6157338 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -115,6 +115,16 @@ if(WITH_OPENVDB) ) endif() +if(WITH_ALEMBIC) + add_definitions(-DWITH_ALEMBIC) + list(APPEND INC_SYS + ${ALEMBIC_INCLUDE_DIRS} + ) + list(APPEND LIB + ${ALEMBIC_LIBRARIES} + ) +endif() + if(WITH_OPENIMAGEDENOISE) add_definitions(-DWITH_OPENIMAGEDENOISE) list(APPEND INC_SYS diff --git a/intern/cycles/blender/addon/__init__.py b/intern/cycles/blender/addon/__init__.py index 10b95133912..f728050a3cf 100644 --- a/intern/cycles/blender/addon/__init__.py +++ b/intern/cycles/blender/addon/__init__.py @@ -61,6 +61,7 @@ class CyclesRender(bpy.types.RenderEngine): bl_use_save_buffers = True bl_use_spherical_stereo = True bl_use_custom_freestyle = True + bl_use_alembic_procedural = True def __init__(self): self.session = None diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 124223635d1..ac0aca57028 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -227,6 +227,11 @@ def update_render_passes(self, context): view_layer.update_render_passes() +def update_render_engine(self, context): + scene = context.scene + scene.update_render_engine() + + class CyclesRenderSettings(bpy.types.PropertyGroup): device: EnumProperty( @@ -240,6 +245,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): description="Feature set to use for rendering", items=enum_feature_set, default='SUPPORTED', + update=update_render_engine, ) shading_system: BoolProperty( name="Open Shading Language", diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index ecadc78cbbf..d1042277183 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -1038,23 +1038,6 @@ static void create_subd_mesh(Scene *scene, /* Sync */ -static BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob) -{ - if (b_ob.modifiers.length() > 0) { - BL::Modifier b_mod = b_ob.modifiers[b_ob.modifiers.length() - 1]; - - if (b_mod.type() == BL::Modifier::type_MESH_SEQUENCE_CACHE) { - BL::MeshSequenceCacheModifier mesh_cache = BL::MeshSequenceCacheModifier(b_mod); - - if (MeshSequenceCacheModifier_has_velocity_get(&mesh_cache.ptr)) { - return mesh_cache; - } - } - } - - return BL::MeshSequenceCacheModifier(PointerRNA_NULL); -} - /* Check whether some of "built-in" motion-related attributes are needed to be exported (includes * things like velocity from cache modifier, fluid simulation). * @@ -1095,7 +1078,7 @@ static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *me return; } - BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob); + BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob, true); if (!b_mesh_cache) { return; @@ -1258,7 +1241,7 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph, } /* Cached motion blur already exported. */ - BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob); + BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob, true); if (mesh_cache) { return; } diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 4711e0cbe76..657ecdeeeb7 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "render/alembic.h" #include "render/camera.h" #include "render/graph.h" #include "render/integrator.h" @@ -484,6 +485,64 @@ bool BlenderSync::sync_object_attributes(BL::DepsgraphObjectInstance &b_instance /* Object Loop */ +void BlenderSync::sync_procedural(BL::Object &b_ob, BL::MeshSequenceCacheModifier &b_mesh_cache) +{ +#ifdef WITH_ALEMBIC + BL::CacheFile cache_file = b_mesh_cache.cache_file(); + void *cache_file_key = cache_file.ptr.data; + + AlembicProcedural *procedural = static_cast( + procedural_map.find(cache_file_key)); + + if (procedural == nullptr) { + procedural = scene->create_node(); + procedural_map.add(cache_file_key, procedural); + } + else { + procedural_map.used(procedural); + } + + float current_frame = b_scene.frame_current(); + if (cache_file.override_frame()) { + current_frame = cache_file.frame(); + } + + if (!cache_file.override_frame()) { + procedural->set_start_frame(b_scene.frame_start()); + procedural->set_end_frame(b_scene.frame_end()); + } + + procedural->set_frame(current_frame); + procedural->set_frame_rate(b_scene.render().fps() / b_scene.render().fps_base()); + procedural->set_frame_offset(cache_file.frame_offset()); + + string absolute_path = blender_absolute_path(b_data, b_ob, b_mesh_cache.cache_file().filepath()); + procedural->set_filepath(ustring(absolute_path)); + + procedural->set_scale(cache_file.scale()); + + /* create or update existing AlembicObjects */ + ustring object_path = ustring(b_mesh_cache.object_path()); + + AlembicObject *abc_object = procedural->get_or_create_object(object_path); + + array used_shaders = find_used_shaders(b_ob); + abc_object->set_used_shaders(used_shaders); + + PointerRNA cobj = RNA_pointer_get(&b_ob.ptr, "cycles"); + const float subd_dicing_rate = max(0.1f, RNA_float_get(&cobj, "dicing_rate") * dicing_rate); + abc_object->set_subd_dicing_rate(subd_dicing_rate); + abc_object->set_subd_max_level(max_subdivisions); + + if (abc_object->is_modified() || procedural->is_modified()) { + procedural->tag_update(scene); + } +#else + (void)b_ob; + (void)b_mesh_cache; +#endif +} + void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, float motion_time) @@ -499,6 +558,7 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, light_map.pre_sync(); geometry_map.pre_sync(); object_map.pre_sync(); + procedural_map.pre_sync(); particle_system_map.pre_sync(); motion_times.clear(); } @@ -539,15 +599,38 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, /* Object itself. */ if (b_instance.show_self()) { - sync_object(b_depsgraph, - b_view_layer, - b_instance, - motion_time, - false, - show_lights, - culling, - &use_portal, - sync_hair ? NULL : &geom_task_pool); +#ifdef WITH_ALEMBIC + bool use_procedural = false; + BL::MeshSequenceCacheModifier b_mesh_cache(PointerRNA_NULL); + + /* Experimental as Blender does not have good support for procedurals at the moment, also + * only available in preview renders since currently do not have a good cache policy, the + * data being loaded at once for all the frames. */ + if (experimental && b_v3d) { + b_mesh_cache = object_mesh_cache_find(b_ob, false); + use_procedural = b_mesh_cache && b_mesh_cache.cache_file().use_render_procedural(); + } + + if (use_procedural) { + /* Skip in the motion case, as generating motion blur data will be handled in the + * procedural. */ + if (!motion) { + sync_procedural(b_ob, b_mesh_cache); + } + } + else +#endif + { + sync_object(b_depsgraph, + b_view_layer, + b_instance, + motion_time, + false, + show_lights, + culling, + &use_portal, + sync_hair ? NULL : &geom_task_pool); + } } /* Particle hair as separate object. */ @@ -580,6 +663,7 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, object_map.post_sync(); geometry_map.post_sync(); particle_system_map.post_sync(); + procedural_map.post_sync(); } if (motion) diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 298353203ad..26d64b7bf85 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -59,6 +59,7 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine, b_scene(b_scene), shader_map(scene), object_map(scene), + procedural_map(scene), geometry_map(scene), light_map(scene), particle_system_map(scene), diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 949482b1f9c..0e605fcbf16 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -151,6 +151,8 @@ class BlenderSync { TaskPool *geom_task_pool); void sync_object_motion_init(BL::Object &b_parent, BL::Object &b_ob, Object *object); + void sync_procedural(BL::Object &b_ob, BL::MeshSequenceCacheModifier &b_mesh_cache); + bool sync_object_attributes(BL::DepsgraphObjectInstance &b_instance, Object *object); /* Volume */ @@ -221,6 +223,7 @@ class BlenderSync { id_map shader_map; id_map object_map; + id_map procedural_map; id_map geometry_map; id_map light_map; id_map particle_system_map; diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index d441575e8af..e50b1a4760e 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -572,6 +572,35 @@ static inline BL::FluidDomainSettings object_fluid_gas_domain_find(BL::Object &b return BL::FluidDomainSettings(PointerRNA_NULL); } +static inline BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob, + bool check_velocity) +{ + for (int i = b_ob.modifiers.length() - 1; i >= 0; --i) { + BL::Modifier b_mod = b_ob.modifiers[i]; + + if (b_mod.type() == BL::Modifier::type_MESH_SEQUENCE_CACHE) { + BL::MeshSequenceCacheModifier mesh_cache = BL::MeshSequenceCacheModifier(b_mod); + + if (check_velocity) { + if (!MeshSequenceCacheModifier_has_velocity_get(&mesh_cache.ptr)) { + return BL::MeshSequenceCacheModifier(PointerRNA_NULL); + } + } + + return mesh_cache; + } + + /* Skip possible particles system modifiers as they do not modify the geometry. */ + if (b_mod.type() == BL::Modifier::type_PARTICLE_SYSTEM) { + continue; + } + + break; + } + + return BL::MeshSequenceCacheModifier(PointerRNA_NULL); +} + static inline Mesh::SubdivisionType object_subdivision_type(BL::Object &b_ob, bool preview, bool experimental) diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h index a6b2aa8540a..58d876b184b 100644 --- a/source/blender/blenkernel/BKE_cachefile.h +++ b/source/blender/blenkernel/BKE_cachefile.h @@ -32,6 +32,7 @@ struct CacheReader; struct Depsgraph; struct Main; struct Object; +struct Scene; void BKE_cachefiles_init(void); void BKE_cachefiles_exit(void); @@ -60,6 +61,15 @@ void BKE_cachefile_reader_open(struct CacheFile *cache_file, const char *object_path); void BKE_cachefile_reader_free(struct CacheFile *cache_file, struct CacheReader **reader); +/* Determine whether the CacheFile should use a render engine procedural. If so, data is not read + * from the file and bouding boxes are used to represent the objects in the Scene. Render engines + * will receive the bounding box as a placeholder but can instead load the data directly if they + * support it. + */ +bool BKE_cache_file_uses_render_procedural(const struct CacheFile *cache_file, + struct Scene *scene, + const int dag_eval_mode); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 0b4e1191956..c5f309570cd 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -319,8 +319,10 @@ typedef struct ModifierTypeInfo { * changes. * * This function is optional (assumes false if not present). + * + * The dag_eval_mode should be of type eEvaluationMode. */ - bool (*dependsOnTime)(struct ModifierData *md); + bool (*dependsOnTime)(struct Scene *scene, struct ModifierData *md, const int dag_eval_mode); /** * True when a deform modifier uses normals, the requiredDataMask @@ -425,7 +427,9 @@ void BKE_modifier_copydata(struct ModifierData *md, struct ModifierData *target) void BKE_modifier_copydata_ex(struct ModifierData *md, struct ModifierData *target, const int flag); -bool BKE_modifier_depends_ontime(struct ModifierData *md); +bool BKE_modifier_depends_ontime(struct Scene *scene, + struct ModifierData *md, + int dag_eval_mode); bool BKE_modifier_supports_mapping(struct ModifierData *md); bool BKE_modifier_supports_cage(struct Scene *scene, struct ModifierData *md); bool BKE_modifier_couldbe_cage(struct Scene *scene, struct ModifierData *md); diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 4724e6dfab6..a823602e341 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -401,7 +401,10 @@ void BKE_object_groups_clear(struct Main *bmain, struct Scene *scene, struct Obj struct KDTree_3d *BKE_object_as_kdtree(struct Object *ob, int *r_tot); -bool BKE_object_modifier_use_time(struct Object *ob, struct ModifierData *md); +bool BKE_object_modifier_use_time(struct Scene *scene, + struct Object *ob, + struct ModifierData *md, + int dag_eval_mode); bool BKE_object_modifier_update_subframe(struct Depsgraph *depsgraph, struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 6d58e165ea3..83ce5e72794 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -174,6 +174,10 @@ bool BKE_scene_uses_blender_eevee(const struct Scene *scene); bool BKE_scene_uses_blender_workbench(const struct Scene *scene); bool BKE_scene_uses_cycles(const struct Scene *scene); +/* Return whether the Cycles experimental feature is enabled. It is invalid to call without first + * ensuring that Cycles is the active render engine (e.g. with BKE_scene_uses_cycles). */ +bool BKE_scene_uses_cycles_experimental_features(struct Scene *scene); + void BKE_scene_copy_data_eevee(struct Scene *sce_dst, const struct Scene *sce_src); void BKE_scene_disable_color_management(struct Scene *scene); diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index 75180de94d8..8dd28c889f5 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -49,6 +49,8 @@ #include "DEG_depsgraph_query.h" +#include "RE_engine.h" + #include "BLO_read_write.h" #ifdef WITH_ALEMBIC @@ -409,3 +411,19 @@ float BKE_cachefile_time_offset(const CacheFile *cache_file, const float time, c const float frame = (cache_file->override_frame ? cache_file->frame : time); return cache_file->is_sequence ? frame : frame / fps - time_offset; } + +bool BKE_cache_file_uses_render_procedural(const CacheFile *cache_file, + Scene *scene, + const int dag_eval_mode) +{ + RenderEngineType *render_engine_type = RE_engines_find(scene->r.engine); + + if (cache_file->type != CACHEFILE_TYPE_ALEMBIC || + !RE_engine_supports_alembic_procedural(render_engine_type, scene)) { + return false; + } + + /* The render time procedural is only enabled during viewport rendering. */ + const bool is_final_render = (eEvaluationMode)dag_eval_mode == DAG_EVAL_RENDER; + return cache_file->use_render_procedural && !is_final_render; +} diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 4b26022039e..30aa22387d0 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -5430,6 +5430,11 @@ static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBa return; } + /* Do not process data if using a render time procedural. */ + if (BKE_cache_file_uses_render_procedural(cache_file, scene, DEG_get_mode(cob->depsgraph))) { + return; + } + const float frame = DEG_get_ctime(cob->depsgraph); const float time = BKE_cachefile_time_offset(cache_file, frame, FPS); diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 2088c4268e6..821ca7b98b3 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -249,11 +249,11 @@ bool BKE_modifier_unique_name(ListBase *modifiers, ModifierData *md) return false; } -bool BKE_modifier_depends_ontime(ModifierData *md) +bool BKE_modifier_depends_ontime(Scene *scene, ModifierData *md, const int dag_eval_mode) { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); - return mti->dependsOnTime && mti->dependsOnTime(md); + return mti->dependsOnTime && mti->dependsOnTime(scene, md, dag_eval_mode); } bool BKE_modifier_supports_mapping(ModifierData *md) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 23b9dca8371..86db4b6ace8 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -5411,9 +5411,12 @@ KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot) return tree; } -bool BKE_object_modifier_use_time(Object *ob, ModifierData *md) +bool BKE_object_modifier_use_time(Scene *scene, + Object *ob, + ModifierData *md, + const int dag_eval_mode) { - if (BKE_modifier_depends_ontime(md)) { + if (BKE_modifier_depends_ontime(scene, md, dag_eval_mode)) { return true; } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 5ecd9b7283e..3fe00adc4d5 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -109,6 +109,8 @@ #include "RE_engine.h" +#include "RNA_access.h" + #include "SEQ_edit.h" #include "SEQ_iterator.h" #include "SEQ_modifier.h" @@ -2938,6 +2940,22 @@ bool BKE_scene_uses_cycles(const Scene *scene) return STREQ(scene->r.engine, RE_engine_id_CYCLES); } +/* This enumeration has to match the one defined in the Cycles addon. */ +typedef enum eCyclesFeatureSet { + CYCLES_FEATURES_SUPPORTED = 0, + CYCLES_FEATURES_EXPERIMENTAL = 1, +} eCyclesFeatureSet; + +/* We cannot use const as RNA_id_pointer_create is not using a const ID. */ +bool BKE_scene_uses_cycles_experimental_features(Scene *scene) +{ + BLI_assert(BKE_scene_uses_cycles(scene)); + PointerRNA scene_ptr; + RNA_id_pointer_create(&scene->id, &scene_ptr); + PointerRNA cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles"); + return RNA_enum_get(&cycles_ptr, "feature_set") == CYCLES_FEATURES_EXPERIMENTAL; +} + void BKE_scene_base_flag_to_objects(ViewLayer *view_layer) { Base *base = view_layer->object_bases.first; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 7da3d2e25f0..d88e9bc9c04 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -118,6 +118,7 @@ #include "intern/node/deg_node_operation.h" #include "intern/node/deg_node_time.h" +#include "intern/depsgraph.h" #include "intern/depsgraph_relation.h" #include "intern/depsgraph_type.h" @@ -2095,7 +2096,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); mti->updateDepsgraph(md, &ctx); } - if (BKE_object_modifier_use_time(object, md)) { + if (BKE_object_modifier_use_time(scene_, object, md, graph_->mode)) { TimeSourceKey time_src_key; add_relation(time_src_key, obdata_ubereval_key, "Time Source"); } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 47ff0c9fd3c..f0d50985237 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -84,6 +84,8 @@ #include "ED_screen.h" #include "ED_undo.h" +#include "RE_engine.h" + #include "RNA_access.h" #include "WM_api.h" @@ -6463,6 +6465,29 @@ void uiTemplateCacheFile(uiLayout *layout, row = uiLayoutRow(layout, false); uiItemR(row, &fileptr, "is_sequence", 0, NULL, ICON_NONE); + /* Only enable render procedural option if the active engine supports it. */ + const struct RenderEngineType *engine_type = CTX_data_engine_type(C); + + Scene *scene = CTX_data_scene(C); + const bool engine_supports_procedural = RE_engine_supports_alembic_procedural(engine_type, scene); + + if (!engine_supports_procedural) { + row = uiLayoutRow(layout, false); + /* For Cycles, verify that experimental features are enabled. */ + if (BKE_scene_uses_cycles(scene) && !BKE_scene_uses_cycles_experimental_features(scene)) { + uiItemL(row, + "The Cycles Alembic Procedural is only available with the experimental feature set", + ICON_INFO); + } + else { + uiItemL(row, "The active render engine does not have an Alembic Procedural", ICON_INFO); + } + } + + row = uiLayoutRow(layout, false); + uiLayoutSetActive(row, engine_supports_procedural); + uiItemR(row, &fileptr, "use_render_procedural", 0, NULL, ICON_NONE); + row = uiLayoutRowWithHeading(layout, true, IFACE_("Override Frame")); sub = uiLayoutRow(row, true); uiLayoutSetPropDecorate(sub, false); diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index fb9d11feb63..6db148eb4e1 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -23,6 +23,7 @@ #include #include +#include "DNA_cachefile_types.h" #include "DNA_light_types.h" #include "DNA_material_types.h" #include "DNA_node_types.h" @@ -204,6 +205,19 @@ void ED_render_engine_changed(Main *bmain, const bool update_scene_data) ntreeCompositUpdateRLayers(scene->nodetree); } } + + /* Update CacheFiles to ensure that procedurals are properly taken into account. */ + LISTBASE_FOREACH (CacheFile *, cachefile, &bmain->cachefiles) { + /* Only update cachefiles which are set to use a render procedural. We do not use + * BKE_cachefile_uses_render_procedural here as we need to update regardless of the current + * engine or its settings. */ + if (cachefile->use_render_procedural) { + DEG_id_tag_update(&cachefile->id, ID_RECALC_COPY_ON_WRITE); + /* Rebuild relations so that modifiers are reconnected to or disconnected from the cachefile. + */ + DEG_relations_tag_update(bmain); + } + } } void ED_render_view_layer_changed(Main *bmain, bScreen *screen) diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h index b38c7827ea5..ae4ade49be1 100644 --- a/source/blender/makesdna/DNA_cachefile_types.h +++ b/source/blender/makesdna/DNA_cachefile_types.h @@ -87,14 +87,21 @@ typedef struct CacheFile { /** The frame offset to subtract. */ float frame_offset; + char _pad[4]; + /** Animation flag. */ short flag; - short draw_flag; /* UNUSED */ /* eCacheFileType enum. */ char type; - char _pad[2]; + /** Do not load data from the cache file and display objects in the scene as boxes, Cycles will + * load objects directly from the CacheFile. Other render engines which can load Alembic data + * directly can take care of rendering it themselves. + */ + char use_render_procedural; + + char _pad1[7]; char velocity_unit; /* Name of the velocity property in the archive. */ diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c index b93f494072c..cb0a490417d 100644 --- a/source/blender/makesrna/intern/rna_cachefile.c +++ b/source/blender/makesrna/intern/rna_cachefile.c @@ -37,6 +37,7 @@ # include "BKE_cachefile.h" # include "DEG_depsgraph.h" +# include "DEG_depsgraph_build.h" # include "WM_api.h" # include "WM_types.h" @@ -53,6 +54,12 @@ static void rna_CacheFile_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poin WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL); } +static void rna_CacheFile_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + rna_CacheFile_update(bmain, scene, ptr); + DEG_relations_tag_update(bmain); +} + static void rna_CacheFile_object_paths_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { CacheFile *cache_file = (CacheFile *)ptr->data; @@ -105,6 +112,16 @@ static void rna_def_cachefile(BlenderRNA *brna) prop, "Sequence", "Whether the cache is separated in a series of files"); RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + prop = RNA_def_property(srna, "use_render_procedural", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Use Render Engine Procedural", + "Display boxes in the viewport as placeholders for the objects, Cycles will use a " + "procedural to load the objects during viewport rendering in experimental mode, " + "other render engines will also receive a placeholder and should take care of loading the " + "Alembic data themselves if possible"); + RNA_def_property_update(prop, 0, "rna_CacheFile_dependency_update"); + /* ----------------- For Scene time ------------------- */ prop = RNA_def_property(srna, "override_frame", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 73924c45d52..4400d198b4a 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -896,6 +896,12 @@ static void rna_def_render_engine(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); RNA_def_property_ui_text(prop, "Use Stereo Viewport", "Support rendering stereo 3D viewport"); + prop = RNA_def_property(srna, "bl_use_alembic_procedural", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_ALEMBIC_PROCEDURAL); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_ui_text( + prop, "Use Alembic Procedural", "Support loading Alembic data at render time"); + RNA_define_verify_sdna(1); } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index f0a1508fb5f..9d158761a21 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -616,6 +616,7 @@ const EnumPropertyItem rna_enum_transform_orientation_items[] = { # include "BLI_string_utils.h" # include "DNA_anim_types.h" +# include "DNA_cachefile_types.h" # include "DNA_color_types.h" # include "DNA_mesh_types.h" # include "DNA_node_types.h" @@ -1619,6 +1620,11 @@ static void rna_RenderSettings_engine_update(Main *bmain, ED_render_engine_changed(bmain, true); } +static void rna_Scene_update_render_engine(Main *bmain) +{ + ED_render_engine_changed(bmain, true); +} + static bool rna_RenderSettings_multiple_engines_get(PointerRNA *UNUSED(ptr)) { return (BLI_listbase_count(&R_engines) > 1); @@ -7836,6 +7842,10 @@ void RNA_def_scene(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE, NULL); RNA_def_property_update(prop, NC_SCENE, "rna_Scene_volume_update"); + func = RNA_def_function(srna, "update_render_engine", "rna_Scene_update_render_engine"); + RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_MAIN); + RNA_def_function_ui_description(func, "Trigger a render engine update"); + /* Statistics */ func = RNA_def_function(srna, "statistics", "rna_Scene_statistics_string_get"); RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index 52f21e3d3d0..a344a15b0c1 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -61,7 +61,9 @@ static void initData(ModifierData *md) MEMCPY_STRUCT_AFTER(bmd, DNA_struct_default_get(BuildModifierData), modifier); } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index 4487adcfdda..fa2f70e1a9c 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -220,7 +220,9 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla tclmd->solver_result = NULL; } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index 5dd57469914..e7d5fe056c5 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -95,7 +95,9 @@ static void freeData(ModifierData *md) } } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index a7ac9f618af..07da18f990d 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -95,7 +95,9 @@ static void requiredDataMask(Object *UNUSED(ob), } } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { DisplaceModifierData *dmd = (DisplaceModifierData *)md; diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index 8b1d541d45d..77ae5c4b6f1 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -155,7 +155,9 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index bf197dca7e5..493b59b3a1a 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -86,7 +86,9 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla temd->facepa = NULL; } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index 36d2ab2a11a..a14d582063a 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -132,7 +132,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * #endif /* WITH_FLUID */ } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index e0507320628..6ef64ad8bc9 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -63,7 +63,9 @@ static void initData(ModifierData *md) MEMCPY_STRUCT_AFTER(mcmd, DNA_struct_default_get(MeshCacheModifierData), modifier); } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { MeshCacheModifierData *mcmd = (MeshCacheModifierData *)md; return (mcmd->play_mode == MOD_MESHCACHE_PLAY_CFEA); diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index 3e6081e0a18..2aa76c18c83 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -20,6 +20,7 @@ #include +#include "BLI_math_vector.h" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -39,6 +40,8 @@ #include "BKE_cachefile.h" #include "BKE_context.h" #include "BKE_lib_query.h" +#include "BKE_mesh.h" +#include "BKE_object.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -118,6 +121,44 @@ static bool isDisabled(const struct Scene *UNUSED(scene), return (mcmd->cache_file == NULL) || (mcmd->object_path[0] == '\0'); } +static Mesh *generate_bounding_box_mesh(Object *object, Mesh *org_mesh) +{ + BoundBox *bb = BKE_object_boundbox_get(object); + Mesh *result = BKE_mesh_new_nomain_from_template(org_mesh, 8, 0, 0, 24, 6); + + MVert *mvert = result->mvert; + for (int i = 0; i < 8; ++i) { + copy_v3_v3(mvert[i].co, bb->vec[i]); + } + + /* See DNA_object_types.h for the diagram showing the order of the vertices for a BoundBox. */ + static unsigned int loops_v[6][4] = { + {0, 4, 5, 1}, + {4, 7, 6, 5}, + {7, 3, 2, 6}, + {3, 0, 1, 2}, + {1, 5, 6, 2}, + {3, 7, 4, 0}, + }; + + MLoop *mloop = result->mloop; + for (int i = 0; i < 6; ++i) { + for (int j = 0; j < 4; ++j, ++mloop) { + mloop->v = loops_v[i][j]; + } + } + + MPoly *mpoly = result->mpoly; + for (int i = 0; i < 6; ++i) { + mpoly[i].loopstart = i * 4; + mpoly[i].totloop = 4; + } + + BKE_mesh_calc_edges(result, false, false); + + return result; +} + static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { #if defined(WITH_USD) || defined(WITH_ALEMBIC) @@ -143,6 +184,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } } + /* Do not process data if using a render procedural, return a box instead for displaying in the + * viewport. */ + if (BKE_cache_file_uses_render_procedural(cache_file, scene, DEG_get_mode(ctx->depsgraph))) { + return generate_bounding_box_mesh(ctx->object, org_mesh); + } + /* If this invocation is for the ORCO mesh, and the mesh hasn't changed topology, we * must return the mesh as-is instead of deforming it. */ if (ctx->flag & MOD_APPLY_ORCO) { @@ -228,11 +275,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * #endif } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(Scene *scene, ModifierData *md, const int dag_eval_mode) { #if defined(WITH_USD) || defined(WITH_ALEMBIC) MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; - return (mcmd->cache_file != NULL); + /* Do not evaluate animations if using the render engine procedural. */ + return (mcmd->cache_file != NULL) && + !BKE_cache_file_uses_render_procedural(mcmd->cache_file, scene, dag_eval_mode); #else UNUSED_VARS(md); return false; diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index d7d2f948955..4187f9087a0 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -62,7 +62,9 @@ static void deformVerts(ModifierData *UNUSED(md), ctx->depsgraph, scene, ctx->object, DEG_get_ctime(ctx->depsgraph), vertexCos, numVerts); } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index bfd4cd81803..3f2d0a06db8 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -98,7 +98,9 @@ static void freeData(ModifierData *md) } } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc index af4b31d6bfc..dfd3fdd80f8 100644 --- a/source/blender/modifiers/intern/MOD_volume_displace.cc +++ b/source/blender/modifiers/intern/MOD_volume_displace.cc @@ -95,7 +95,7 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void walk(userData, ob, md, "texture"); } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), ModifierData *md, const int UNUSED(dag_eval_mode)) { VolumeDisplaceModifierData *vdmd = reinterpret_cast(md); if (vdmd->texture) { diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 3bebc52c503..25e33b22bde 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -116,7 +116,9 @@ static void matrix_from_obj_pchan(float mat[4][4], } } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { WarpModifierData *wmd = (WarpModifierData *)md; diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index cf4c195c66d..03f8e3a1dfb 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -70,7 +70,9 @@ static void initData(ModifierData *md) MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WaveModifierData), modifier); } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index 093fa118ee0..a9d01c64ff1 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -112,7 +112,9 @@ static void requiredDataMask(Object *UNUSED(ob), /* No need to ask for CD_PREVIEW_MLOOPCOL... */ } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index 7aae089fa18..b369b82ebb7 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -154,7 +154,9 @@ static void requiredDataMask(Object *UNUSED(ob), /* No need to ask for CD_PREVIEW_MLOOPCOL... */ } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md; diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index 6e78774269a..7ee19e1c537 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -362,7 +362,9 @@ static void requiredDataMask(Object *UNUSED(ob), /* No need to ask for CD_PREVIEW_MLOOPCOL... */ } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; diff --git a/source/blender/render/RE_engine.h b/source/blender/render/RE_engine.h index 6b2861bbefd..dfc0d5d0e9f 100644 --- a/source/blender/render/RE_engine.h +++ b/source/blender/render/RE_engine.h @@ -66,6 +66,7 @@ extern "C" { #define RE_USE_GPU_CONTEXT 512 #define RE_USE_CUSTOM_FREESTYLE 1024 #define RE_USE_NO_IMAGE_SAVE 2048 +#define RE_USE_ALEMBIC_PROCEDURAL 4096 /* RenderEngine.flag */ #define RE_ENGINE_ANIMATION 1 @@ -235,6 +236,12 @@ void RE_engines_register(RenderEngineType *render_type); bool RE_engine_is_opengl(RenderEngineType *render_type); +/** + * Return true if the RenderEngineType has native support for direct loading of Alembic data. For + * Cycles, this also checks that the experimental feature set is enabled. + */ +bool RE_engine_supports_alembic_procedural(const RenderEngineType *render_type, Scene *scene); + RenderEngineType *RE_engines_find(const char *idname); rcti *RE_engine_get_current_tiles(struct Render *re, int *r_total_tiles, bool *r_needs_free); diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 657cd1f606b..75b3f2db249 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -128,6 +128,19 @@ bool RE_engine_is_opengl(RenderEngineType *render_type) return (render_type->draw_engine != NULL) && DRW_engine_render_support(render_type->draw_engine); } +bool RE_engine_supports_alembic_procedural(const RenderEngineType *render_type, Scene *scene) +{ + if ((render_type->flag & RE_USE_ALEMBIC_PROCEDURAL) == 0) { + return false; + } + + if (BKE_scene_uses_cycles(scene) && !BKE_scene_uses_cycles_experimental_features(scene)) { + return false; + } + + return true; +} + /* Create, Free */ RenderEngine *RE_engine_create(RenderEngineType *type) -- cgit v1.2.3 From d5776f48297e79467407164fe9cb4042dbeccb31 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 19 Aug 2021 11:12:37 +0200 Subject: LibOverride: Tag all embedded IDs RNA opinters as overridablei, part II. Not sure how I failed to include those files in rBe5f8db92b696... --- source/blender/makesrna/intern/rna_light.c | 1 + source/blender/makesrna/intern/rna_linestyle.c | 1 + source/blender/makesrna/intern/rna_simulation.c | 1 + source/blender/makesrna/intern/rna_texture.c | 1 + source/blender/makesrna/intern/rna_world.c | 1 + 5 files changed, 5 insertions(+) diff --git a/source/blender/makesrna/intern/rna_light.c b/source/blender/makesrna/intern/rna_light.c index 0593db0dd56..8bec337885e 100644 --- a/source/blender/makesrna/intern/rna_light.c +++ b/source/blender/makesrna/intern/rna_light.c @@ -188,6 +188,7 @@ static void rna_def_light(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based lights"); prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_linestyle.c b/source/blender/makesrna/intern/rna_linestyle.c index ca97f2c9a55..8cd1ac0d963 100644 --- a/source/blender/makesrna/intern/rna_linestyle.c +++ b/source/blender/makesrna/intern/rna_linestyle.c @@ -2189,6 +2189,7 @@ static void rna_def_linestyle(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node-based shaders"); prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_simulation.c b/source/blender/makesrna/intern/rna_simulation.c index cc9a4bec2e7..6f5041c9ed1 100644 --- a/source/blender/makesrna/intern/rna_simulation.c +++ b/source/blender/makesrna/intern/rna_simulation.c @@ -43,6 +43,7 @@ static void rna_def_simulation(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree defining the simulation"); /* common */ diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 128f1cb1e21..5a74cfa9964 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -1642,6 +1642,7 @@ static void rna_def_texture(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node-based textures"); RNA_def_property_update(prop, 0, "rna_Texture_nodes_update"); diff --git a/source/blender/makesrna/intern/rna_world.c b/source/blender/makesrna/intern/rna_world.c index a66e3090548..826e6d21c36 100644 --- a/source/blender/makesrna/intern/rna_world.c +++ b/source/blender/makesrna/intern/rna_world.c @@ -237,6 +237,7 @@ void RNA_def_world(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based worlds"); prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE); -- cgit v1.2.3 From 0f49e4832cf2afad437aa16ebbbe37191de9af71 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 19 Aug 2021 11:13:55 +0200 Subject: Cleanup: Blendwrite: Move code deciding if an ID should be written out of ID callbacks. This was not really useful, and added estra useless steps in case and ID should not actually be written. Further more, it prevented clearing the usercount on write, which can be cause a false positive 'chanhged' detection in undo/redo case. --- source/blender/blenkernel/intern/action.c | 21 ++-- source/blender/blenkernel/intern/armature.c | 31 +++--- source/blender/blenkernel/intern/brush.c | 73 +++++++------ source/blender/blenkernel/intern/cachefile.c | 21 ++-- source/blender/blenkernel/intern/camera.c | 19 ++-- source/blender/blenkernel/intern/collection.c | 23 ++-- source/blender/blenkernel/intern/curve.c | 69 ++++++------ source/blender/blenkernel/intern/font.c | 27 +++-- source/blender/blenkernel/intern/gpencil.c | 71 ++++++------ source/blender/blenkernel/intern/hair.c | 49 +++++---- source/blender/blenkernel/intern/image.c | 57 +++++----- source/blender/blenkernel/intern/key.c | 41 ++++--- source/blender/blenkernel/intern/lattice.c | 33 +++--- source/blender/blenkernel/intern/light.c | 31 +++--- source/blender/blenkernel/intern/lightprobe.c | 15 ++- source/blender/blenkernel/intern/linestyle.c | 35 +++--- source/blender/blenkernel/intern/mask.c | 57 +++++----- source/blender/blenkernel/intern/material.c | 39 ++++--- source/blender/blenkernel/intern/mball.c | 41 ++++--- source/blender/blenkernel/intern/mesh.c | 143 ++++++++++++------------- source/blender/blenkernel/intern/movieclip.c | 45 ++++---- source/blender/blenkernel/intern/node.cc | 21 ++-- source/blender/blenkernel/intern/object.c | 108 +++++++++---------- source/blender/blenkernel/intern/paint.c | 22 ++-- source/blender/blenkernel/intern/particle.c | 85 ++++++++------- source/blender/blenkernel/intern/pointcloud.cc | 41 ++++--- source/blender/blenkernel/intern/screen.c | 18 ++-- source/blender/blenkernel/intern/simulation.cc | 25 +++-- source/blender/blenkernel/intern/sound.c | 29 +++-- source/blender/blenkernel/intern/speaker.c | 15 ++- source/blender/blenkernel/intern/text.c | 3 - source/blender/blenkernel/intern/texture.c | 33 +++--- source/blender/blenkernel/intern/volume.cc | 31 +++--- source/blender/blenkernel/intern/world.c | 29 +++-- source/blender/blenloader/intern/writefile.c | 8 ++ 35 files changed, 687 insertions(+), 722 deletions(-) diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index d55f023d209..981815f400a 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -186,22 +186,21 @@ static void action_foreach_id(ID *id, LibraryForeachIDData *data) static void action_blend_write(BlendWriter *writer, ID *id, const void *id_address) { bAction *act = (bAction *)id; - if (act->id.us > 0 || BLO_write_is_undo(writer)) { - BLO_write_id_struct(writer, bAction, id_address, &act->id); - BKE_id_blend_write(writer, &act->id); - BKE_fcurve_blend_write(writer, &act->curves); + BLO_write_id_struct(writer, bAction, id_address, &act->id); + BKE_id_blend_write(writer, &act->id); - LISTBASE_FOREACH (bActionGroup *, grp, &act->groups) { - BLO_write_struct(writer, bActionGroup, grp); - } + BKE_fcurve_blend_write(writer, &act->curves); - LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { - BLO_write_struct(writer, TimeMarker, marker); - } + LISTBASE_FOREACH (bActionGroup *, grp, &act->groups) { + BLO_write_struct(writer, bActionGroup, grp); + } - BKE_previewimg_blend_write(writer, act->preview); + LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { + BLO_write_struct(writer, TimeMarker, marker); } + + BKE_previewimg_blend_write(writer, act->preview); } static void action_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 1f02b084534..87320c88b1b 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -212,25 +212,24 @@ static void write_bone(BlendWriter *writer, Bone *bone) static void armature_blend_write(BlendWriter *writer, ID *id, const void *id_address) { bArmature *arm = (bArmature *)id; - if (arm->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - arm->bonehash = NULL; - arm->edbo = NULL; - /* Must always be cleared (armatures don't have their own edit-data). */ - arm->needs_flush_to_id = 0; - arm->act_edbone = NULL; - BLO_write_id_struct(writer, bArmature, id_address, &arm->id); - BKE_id_blend_write(writer, &arm->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + arm->bonehash = NULL; + arm->edbo = NULL; + /* Must always be cleared (armatures don't have their own edit-data). */ + arm->needs_flush_to_id = 0; + arm->act_edbone = NULL; - if (arm->adt) { - BKE_animdata_blend_write(writer, arm->adt); - } + BLO_write_id_struct(writer, bArmature, id_address, &arm->id); + BKE_id_blend_write(writer, &arm->id); - /* Direct data */ - LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) { - write_bone(writer, bone); - } + if (arm->adt) { + BKE_animdata_blend_write(writer, arm->adt); + } + + /* Direct data */ + LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) { + write_bone(writer, bone); } } diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 3418e37642c..7b81187be21 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -202,48 +202,47 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data) static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Brush *brush = (Brush *)id; - if (brush->id.us > 0 || BLO_write_is_undo(writer)) { - BLO_write_id_struct(writer, Brush, id_address, &brush->id); - BKE_id_blend_write(writer, &brush->id); - if (brush->curve) { - BKE_curvemapping_blend_write(writer, brush->curve); - } + BLO_write_id_struct(writer, Brush, id_address, &brush->id); + BKE_id_blend_write(writer, &brush->id); - if (brush->gpencil_settings) { - BLO_write_struct(writer, BrushGpencilSettings, brush->gpencil_settings); + if (brush->curve) { + BKE_curvemapping_blend_write(writer, brush->curve); + } - if (brush->gpencil_settings->curve_sensitivity) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_sensitivity); - } - if (brush->gpencil_settings->curve_strength) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_strength); - } - if (brush->gpencil_settings->curve_jitter) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_jitter); - } - if (brush->gpencil_settings->curve_rand_pressure) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_pressure); - } - if (brush->gpencil_settings->curve_rand_strength) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_strength); - } - if (brush->gpencil_settings->curve_rand_uv) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_uv); - } - if (brush->gpencil_settings->curve_rand_hue) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_hue); - } - if (brush->gpencil_settings->curve_rand_saturation) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_saturation); - } - if (brush->gpencil_settings->curve_rand_value) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_value); - } + if (brush->gpencil_settings) { + BLO_write_struct(writer, BrushGpencilSettings, brush->gpencil_settings); + + if (brush->gpencil_settings->curve_sensitivity) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_sensitivity); } - if (brush->gradient) { - BLO_write_struct(writer, ColorBand, brush->gradient); + if (brush->gpencil_settings->curve_strength) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_strength); } + if (brush->gpencil_settings->curve_jitter) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_jitter); + } + if (brush->gpencil_settings->curve_rand_pressure) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_pressure); + } + if (brush->gpencil_settings->curve_rand_strength) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_strength); + } + if (brush->gpencil_settings->curve_rand_uv) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_uv); + } + if (brush->gpencil_settings->curve_rand_hue) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_hue); + } + if (brush->gpencil_settings->curve_rand_saturation) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_saturation); + } + if (brush->gpencil_settings->curve_rand_value) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_value); + } + } + if (brush->gradient) { + BLO_write_struct(writer, ColorBand, brush->gradient); } } diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index 8dd28c889f5..4a60564b4a1 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -97,19 +97,18 @@ static void cache_file_free_data(ID *id) static void cache_file_blend_write(BlendWriter *writer, ID *id, const void *id_address) { CacheFile *cache_file = (CacheFile *)id; - if (cache_file->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - BLI_listbase_clear(&cache_file->object_paths); - cache_file->handle = NULL; - memset(cache_file->handle_filepath, 0, sizeof(cache_file->handle_filepath)); - cache_file->handle_readers = NULL; - BLO_write_id_struct(writer, CacheFile, id_address, &cache_file->id); - BKE_id_blend_write(writer, &cache_file->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + BLI_listbase_clear(&cache_file->object_paths); + cache_file->handle = NULL; + memset(cache_file->handle_filepath, 0, sizeof(cache_file->handle_filepath)); + cache_file->handle_readers = NULL; - if (cache_file->adt) { - BKE_animdata_blend_write(writer, cache_file->adt); - } + BLO_write_id_struct(writer, CacheFile, id_address, &cache_file->id); + BKE_id_blend_write(writer, &cache_file->id); + + if (cache_file->adt) { + BKE_animdata_blend_write(writer, cache_file->adt); } } diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 5172b067eba..46b079fb42e 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -122,18 +122,17 @@ static void camera_foreach_id(ID *id, LibraryForeachIDData *data) static void camera_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Camera *cam = (Camera *)id; - if (cam->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - BLO_write_id_struct(writer, Camera, id_address, &cam->id); - BKE_id_blend_write(writer, &cam->id); - if (cam->adt) { - BKE_animdata_blend_write(writer, cam->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, Camera, id_address, &cam->id); + BKE_id_blend_write(writer, &cam->id); - LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) { - BLO_write_struct(writer, CameraBGImage, bgpic); - } + if (cam->adt) { + BKE_animdata_blend_write(writer, cam->adt); + } + + LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) { + BLO_write_struct(writer, CameraBGImage, bgpic); } } diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index b62e33ff564..d36e9b67d00 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -214,20 +214,19 @@ void BKE_collection_blend_write_nolib(BlendWriter *writer, Collection *collectio static void collection_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Collection *collection = (Collection *)id; - if (collection->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ - collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE; - collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED; - collection->tag = 0; - BLI_listbase_clear(&collection->object_cache); - BLI_listbase_clear(&collection->object_cache_instanced); - BLI_listbase_clear(&collection->parents); - /* write LibData */ - BLO_write_id_struct(writer, Collection, id_address, &collection->id); + /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ + collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE; + collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED; + collection->tag = 0; + BLI_listbase_clear(&collection->object_cache); + BLI_listbase_clear(&collection->object_cache_instanced); + BLI_listbase_clear(&collection->parents); - BKE_collection_blend_write_nolib(writer, collection); - } + /* write LibData */ + BLO_write_id_struct(writer, Collection, id_address, &collection->id); + + BKE_collection_blend_write_nolib(writer, collection); } #ifdef USE_COLLECTION_COMPAT_28 diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index c49788528a4..397838e6904 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -146,51 +146,50 @@ static void curve_foreach_id(ID *id, LibraryForeachIDData *data) static void curve_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Curve *cu = (Curve *)id; - if (cu->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - cu->editnurb = NULL; - cu->editfont = NULL; - cu->batch_cache = NULL; - /* write LibData */ - BLO_write_id_struct(writer, Curve, id_address, &cu->id); - BKE_id_blend_write(writer, &cu->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + cu->editnurb = NULL; + cu->editfont = NULL; + cu->batch_cache = NULL; - /* direct data */ - BLO_write_pointer_array(writer, cu->totcol, cu->mat); - if (cu->adt) { - BKE_animdata_blend_write(writer, cu->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, Curve, id_address, &cu->id); + BKE_id_blend_write(writer, &cu->id); - if (cu->vfont) { - BLO_write_raw(writer, cu->len + 1, cu->str); - BLO_write_struct_array(writer, CharInfo, cu->len_char32 + 1, cu->strinfo); - BLO_write_struct_array(writer, TextBox, cu->totbox, cu->tb); + /* direct data */ + BLO_write_pointer_array(writer, cu->totcol, cu->mat); + if (cu->adt) { + BKE_animdata_blend_write(writer, cu->adt); + } + + if (cu->vfont) { + BLO_write_raw(writer, cu->len + 1, cu->str); + BLO_write_struct_array(writer, CharInfo, cu->len_char32 + 1, cu->strinfo); + BLO_write_struct_array(writer, TextBox, cu->totbox, cu->tb); + } + else { + /* is also the order of reading */ + LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { + BLO_write_struct(writer, Nurb, nu); } - else { - /* is also the order of reading */ - LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { - BLO_write_struct(writer, Nurb, nu); + LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { + if (nu->type == CU_BEZIER) { + BLO_write_struct_array(writer, BezTriple, nu->pntsu, nu->bezt); } - LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { - if (nu->type == CU_BEZIER) { - BLO_write_struct_array(writer, BezTriple, nu->pntsu, nu->bezt); + else { + BLO_write_struct_array(writer, BPoint, nu->pntsu * nu->pntsv, nu->bp); + if (nu->knotsu) { + BLO_write_float_array(writer, KNOTSU(nu), nu->knotsu); } - else { - BLO_write_struct_array(writer, BPoint, nu->pntsu * nu->pntsv, nu->bp); - if (nu->knotsu) { - BLO_write_float_array(writer, KNOTSU(nu), nu->knotsu); - } - if (nu->knotsv) { - BLO_write_float_array(writer, KNOTSV(nu), nu->knotsv); - } + if (nu->knotsv) { + BLO_write_float_array(writer, KNOTSV(nu), nu->knotsv); } } } + } - if (cu->bevel_profile != NULL) { - BKE_curveprofile_blend_write(writer, cu->bevel_profile); - } + if (cu->bevel_profile != NULL) { + BKE_curveprofile_blend_write(writer, cu->bevel_profile); } } diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 72add476bfe..c1765967238 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -126,23 +126,22 @@ static void vfont_blend_write(BlendWriter *writer, ID *id, const void *id_addres { VFont *vf = (VFont *)id; const bool is_undo = BLO_write_is_undo(writer); - if (vf->id.us > 0 || is_undo) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - vf->data = NULL; - vf->temp_pf = NULL; - - /* Do not store packed files in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(vf) && !is_undo) { - vf->packedfile = NULL; - } - /* write LibData */ - BLO_write_id_struct(writer, VFont, id_address, &vf->id); - BKE_id_blend_write(writer, &vf->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + vf->data = NULL; + vf->temp_pf = NULL; - /* direct data */ - BKE_packedfile_blend_write(writer, vf->packedfile); + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(vf) && !is_undo) { + vf->packedfile = NULL; } + + /* write LibData */ + BLO_write_id_struct(writer, VFont, id_address, &vf->id); + BKE_id_blend_write(writer, &vf->id); + + /* direct data */ + BKE_packedfile_blend_write(writer, vf->packedfile); } static void vfont_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index f566e18fb2f..9cdb7395925 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -150,47 +150,46 @@ static void greasepencil_foreach_id(ID *id, LibraryForeachIDData *data) static void greasepencil_blend_write(BlendWriter *writer, ID *id, const void *id_address) { bGPdata *gpd = (bGPdata *)id; - if (gpd->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ - /* XXX not sure why the whole run-time data is not cleared in reading code, - * for now mimicking it here. */ - gpd->runtime.sbuffer = NULL; - gpd->runtime.sbuffer_used = 0; - gpd->runtime.sbuffer_size = 0; - gpd->runtime.tot_cp_points = 0; - /* write gpd data block to file */ - BLO_write_id_struct(writer, bGPdata, id_address, &gpd->id); - BKE_id_blend_write(writer, &gpd->id); + /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ + /* XXX not sure why the whole run-time data is not cleared in reading code, + * for now mimicking it here. */ + gpd->runtime.sbuffer = NULL; + gpd->runtime.sbuffer_used = 0; + gpd->runtime.sbuffer_size = 0; + gpd->runtime.tot_cp_points = 0; - if (gpd->adt) { - BKE_animdata_blend_write(writer, gpd->adt); - } + /* write gpd data block to file */ + BLO_write_id_struct(writer, bGPdata, id_address, &gpd->id); + BKE_id_blend_write(writer, &gpd->id); - BKE_defbase_blend_write(writer, &gpd->vertex_group_names); + if (gpd->adt) { + BKE_animdata_blend_write(writer, gpd->adt); + } - BLO_write_pointer_array(writer, gpd->totcol, gpd->mat); + BKE_defbase_blend_write(writer, &gpd->vertex_group_names); - /* write grease-pencil layers to file */ - BLO_write_struct_list(writer, bGPDlayer, &gpd->layers); - LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - /* Write mask list. */ - BLO_write_struct_list(writer, bGPDlayer_Mask, &gpl->mask_layers); - /* write this layer's frames to file */ - BLO_write_struct_list(writer, bGPDframe, &gpl->frames); - LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { - /* write strokes */ - BLO_write_struct_list(writer, bGPDstroke, &gpf->strokes); - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - BLO_write_struct_array(writer, bGPDspoint, gps->totpoints, gps->points); - BLO_write_struct_array(writer, bGPDtriangle, gps->tot_triangles, gps->triangles); - BKE_defvert_blend_write(writer, gps->totpoints, gps->dvert); - if (gps->editcurve != NULL) { - bGPDcurve *gpc = gps->editcurve; - BLO_write_struct(writer, bGPDcurve, gpc); - BLO_write_struct_array( - writer, bGPDcurve_point, gpc->tot_curve_points, gpc->curve_points); - } + BLO_write_pointer_array(writer, gpd->totcol, gpd->mat); + + /* write grease-pencil layers to file */ + BLO_write_struct_list(writer, bGPDlayer, &gpd->layers); + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* Write mask list. */ + BLO_write_struct_list(writer, bGPDlayer_Mask, &gpl->mask_layers); + /* write this layer's frames to file */ + BLO_write_struct_list(writer, bGPDframe, &gpl->frames); + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + /* write strokes */ + BLO_write_struct_list(writer, bGPDstroke, &gpf->strokes); + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + BLO_write_struct_array(writer, bGPDspoint, gps->totpoints, gps->points); + BLO_write_struct_array(writer, bGPDtriangle, gps->tot_triangles, gps->triangles); + BKE_defvert_blend_write(writer, gps->totpoints, gps->dvert); + if (gps->editcurve != NULL) { + bGPDcurve *gpc = gps->editcurve; + BLO_write_struct(writer, bGPDcurve, gpc); + BLO_write_struct_array( + writer, bGPDcurve_point, gpc->tot_curve_points, gpc->curve_points); } } } diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c index 2894d6daf23..af7cc0acb57 100644 --- a/source/blender/blenkernel/intern/hair.c +++ b/source/blender/blenkernel/intern/hair.c @@ -114,32 +114,31 @@ static void hair_foreach_id(ID *id, LibraryForeachIDData *data) static void hair_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Hair *hair = (Hair *)id; - if (hair->id.us > 0 || BLO_write_is_undo(writer)) { - CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *clayers = NULL, clayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_blend_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - CustomData_blend_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff)); - - /* Write LibData */ - BLO_write_id_struct(writer, Hair, id_address, &hair->id); - BKE_id_blend_write(writer, &hair->id); - - /* Direct data */ - CustomData_blend_write(writer, &hair->pdata, players, hair->totpoint, CD_MASK_ALL, &hair->id); - CustomData_blend_write(writer, &hair->cdata, clayers, hair->totcurve, CD_MASK_ALL, &hair->id); - - BLO_write_pointer_array(writer, hair->totcol, hair->mat); - if (hair->adt) { - BKE_animdata_blend_write(writer, hair->adt); - } - /* Remove temporary data. */ - if (players && players != players_buff) { - MEM_freeN(players); - } - if (clayers && clayers != clayers_buff) { - MEM_freeN(clayers); - } + CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *clayers = NULL, clayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomData_blend_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); + CustomData_blend_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff)); + + /* Write LibData */ + BLO_write_id_struct(writer, Hair, id_address, &hair->id); + BKE_id_blend_write(writer, &hair->id); + + /* Direct data */ + CustomData_blend_write(writer, &hair->pdata, players, hair->totpoint, CD_MASK_ALL, &hair->id); + CustomData_blend_write(writer, &hair->cdata, clayers, hair->totcurve, CD_MASK_ALL, &hair->id); + + BLO_write_pointer_array(writer, hair->totcol, hair->mat); + if (hair->adt) { + BKE_animdata_blend_write(writer, hair->adt); + } + + /* Remove temporary data. */ + if (players && players != players_buff) { + MEM_freeN(players); + } + if (clayers && clayers != clayers_buff) { + MEM_freeN(clayers); } } diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index d2ab54de697..de9f2a5a656 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -229,44 +229,43 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres { Image *ima = (Image *)id; const bool is_undo = BLO_write_is_undo(writer); - if (ima->id.us > 0 || is_undo) { - ImagePackedFile *imapf; - BLI_assert(ima->packedfile == NULL); - /* Do not store packed files in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(ima) && !is_undo) { - BLI_listbase_clear(&ima->packedfiles); - } - else { - /* Some trickery to keep forward compatibility of packed images. */ - if (ima->packedfiles.first != NULL) { - imapf = ima->packedfiles.first; - ima->packedfile = imapf->packedfile; - } + ImagePackedFile *imapf; + + BLI_assert(ima->packedfile == NULL); + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(ima) && !is_undo) { + BLI_listbase_clear(&ima->packedfiles); + } + else { + /* Some trickery to keep forward compatibility of packed images. */ + if (ima->packedfiles.first != NULL) { + imapf = ima->packedfiles.first; + ima->packedfile = imapf->packedfile; } + } - /* write LibData */ - BLO_write_id_struct(writer, Image, id_address, &ima->id); - BKE_id_blend_write(writer, &ima->id); + /* write LibData */ + BLO_write_id_struct(writer, Image, id_address, &ima->id); + BKE_id_blend_write(writer, &ima->id); - for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { - BLO_write_struct(writer, ImagePackedFile, imapf); - BKE_packedfile_blend_write(writer, imapf->packedfile); - } + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + BLO_write_struct(writer, ImagePackedFile, imapf); + BKE_packedfile_blend_write(writer, imapf->packedfile); + } - BKE_previewimg_blend_write(writer, ima->preview); + BKE_previewimg_blend_write(writer, ima->preview); - LISTBASE_FOREACH (ImageView *, iv, &ima->views) { - BLO_write_struct(writer, ImageView, iv); - } - BLO_write_struct(writer, Stereo3dFormat, ima->stereo3d_format); + LISTBASE_FOREACH (ImageView *, iv, &ima->views) { + BLO_write_struct(writer, ImageView, iv); + } + BLO_write_struct(writer, Stereo3dFormat, ima->stereo3d_format); - BLO_write_struct_list(writer, ImageTile, &ima->tiles); + BLO_write_struct_list(writer, ImageTile, &ima->tiles); - ima->packedfile = NULL; + ima->packedfile = NULL; - BLO_write_struct_list(writer, RenderSlot, &ima->renderslots); - } + BLO_write_struct_list(writer, RenderSlot, &ima->renderslots); } static void image_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 724216bee6c..b59f51c36f7 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -114,27 +114,26 @@ static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_add { Key *key = (Key *)id; const bool is_undo = BLO_write_is_undo(writer); - if (key->id.us > 0 || is_undo) { - /* write LibData */ - BLO_write_id_struct(writer, Key, id_address, &key->id); - BKE_id_blend_write(writer, &key->id); - - if (key->adt) { - BKE_animdata_blend_write(writer, key->adt); - } - - /* direct data */ - LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { - KeyBlock tmp_kb = *kb; - /* Do not store actual geometry data in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(key) && !is_undo) { - tmp_kb.totelem = 0; - tmp_kb.data = NULL; - } - BLO_write_struct_at_address(writer, KeyBlock, kb, &tmp_kb); - if (tmp_kb.data != NULL) { - BLO_write_raw(writer, tmp_kb.totelem * key->elemsize, tmp_kb.data); - } + + /* write LibData */ + BLO_write_id_struct(writer, Key, id_address, &key->id); + BKE_id_blend_write(writer, &key->id); + + if (key->adt) { + BKE_animdata_blend_write(writer, key->adt); + } + + /* direct data */ + LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { + KeyBlock tmp_kb = *kb; + /* Do not store actual geometry data in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(key) && !is_undo) { + tmp_kb.totelem = 0; + tmp_kb.data = NULL; + } + BLO_write_struct_at_address(writer, KeyBlock, kb, &tmp_kb); + if (tmp_kb.data != NULL) { + BLO_write_raw(writer, tmp_kb.totelem * key->elemsize, tmp_kb.data); } } } diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 9875d776d33..e804f32e5a6 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -137,26 +137,25 @@ static void lattice_foreach_id(ID *id, LibraryForeachIDData *data) static void lattice_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Lattice *lt = (Lattice *)id; - if (lt->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - lt->editlatt = NULL; - lt->batch_cache = NULL; - - /* write LibData */ - BLO_write_id_struct(writer, Lattice, id_address, <->id); - BKE_id_blend_write(writer, <->id); - - /* write animdata */ - if (lt->adt) { - BKE_animdata_blend_write(writer, lt->adt); - } - /* direct data */ - BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + lt->editlatt = NULL; + lt->batch_cache = NULL; + + /* write LibData */ + BLO_write_id_struct(writer, Lattice, id_address, <->id); + BKE_id_blend_write(writer, <->id); - BKE_defbase_blend_write(writer, <->vertex_group_names); - BKE_defvert_blend_write(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); + /* write animdata */ + if (lt->adt) { + BKE_animdata_blend_write(writer, lt->adt); } + + /* direct data */ + BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def); + + BKE_defbase_blend_write(writer, <->vertex_group_names); + BKE_defvert_blend_write(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); } static void lattice_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index d91d80ac683..c2b71b85973 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -136,27 +136,26 @@ static void light_foreach_id(ID *id, LibraryForeachIDData *data) static void light_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Light *la = (Light *)id; - if (la->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - BLO_write_id_struct(writer, Light, id_address, &la->id); - BKE_id_blend_write(writer, &la->id); - if (la->adt) { - BKE_animdata_blend_write(writer, la->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, Light, id_address, &la->id); + BKE_id_blend_write(writer, &la->id); - if (la->curfalloff) { - BKE_curvemapping_blend_write(writer, la->curfalloff); - } + if (la->adt) { + BKE_animdata_blend_write(writer, la->adt); + } - /* Node-tree is integral part of lights, no libdata. */ - if (la->nodetree) { - BLO_write_struct(writer, bNodeTree, la->nodetree); - ntreeBlendWrite(writer, la->nodetree); - } + if (la->curfalloff) { + BKE_curvemapping_blend_write(writer, la->curfalloff); + } - BKE_previewimg_blend_write(writer, la->preview); + /* Node-tree is integral part of lights, no libdata. */ + if (la->nodetree) { + BLO_write_struct(writer, bNodeTree, la->nodetree); + ntreeBlendWrite(writer, la->nodetree); } + + BKE_previewimg_blend_write(writer, la->preview); } static void light_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index b09aed82921..15733af8ef0 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -60,14 +60,13 @@ static void lightprobe_foreach_id(ID *id, LibraryForeachIDData *data) static void lightprobe_blend_write(BlendWriter *writer, ID *id, const void *id_address) { LightProbe *prb = (LightProbe *)id; - if (prb->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - BLO_write_id_struct(writer, LightProbe, id_address, &prb->id); - BKE_id_blend_write(writer, &prb->id); - - if (prb->adt) { - BKE_animdata_blend_write(writer, prb->adt); - } + + /* write LibData */ + BLO_write_id_struct(writer, LightProbe, id_address, &prb->id); + BKE_id_blend_write(writer, &prb->id); + + if (prb->adt) { + BKE_animdata_blend_write(writer, prb->adt); } } diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index 26d9ab7a8c7..19030fca38b 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -457,28 +457,27 @@ static void write_linestyle_geometry_modifiers(BlendWriter *writer, ListBase *mo static void linestyle_blend_write(BlendWriter *writer, ID *id, const void *id_address) { FreestyleLineStyle *linestyle = (FreestyleLineStyle *)id; - if (linestyle->id.us > 0 || BLO_write_is_undo(writer)) { - BLO_write_id_struct(writer, FreestyleLineStyle, id_address, &linestyle->id); - BKE_id_blend_write(writer, &linestyle->id); - if (linestyle->adt) { - BKE_animdata_blend_write(writer, linestyle->adt); - } + BLO_write_id_struct(writer, FreestyleLineStyle, id_address, &linestyle->id); + BKE_id_blend_write(writer, &linestyle->id); - write_linestyle_color_modifiers(writer, &linestyle->color_modifiers); - write_linestyle_alpha_modifiers(writer, &linestyle->alpha_modifiers); - write_linestyle_thickness_modifiers(writer, &linestyle->thickness_modifiers); - write_linestyle_geometry_modifiers(writer, &linestyle->geometry_modifiers); - for (int a = 0; a < MAX_MTEX; a++) { - if (linestyle->mtex[a]) { - BLO_write_struct(writer, MTex, linestyle->mtex[a]); - } - } - if (linestyle->nodetree) { - BLO_write_struct(writer, bNodeTree, linestyle->nodetree); - ntreeBlendWrite(writer, linestyle->nodetree); + if (linestyle->adt) { + BKE_animdata_blend_write(writer, linestyle->adt); + } + + write_linestyle_color_modifiers(writer, &linestyle->color_modifiers); + write_linestyle_alpha_modifiers(writer, &linestyle->alpha_modifiers); + write_linestyle_thickness_modifiers(writer, &linestyle->thickness_modifiers); + write_linestyle_geometry_modifiers(writer, &linestyle->geometry_modifiers); + for (int a = 0; a < MAX_MTEX; a++) { + if (linestyle->mtex[a]) { + BLO_write_struct(writer, MTex, linestyle->mtex[a]); } } + if (linestyle->nodetree) { + BLO_write_struct(writer, bNodeTree, linestyle->nodetree); + ntreeBlendWrite(writer, linestyle->nodetree); + } } static void direct_link_linestyle_color_modifier(BlendDataReader *reader, diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index f40d1db60ff..a93fcb6e8e0 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -101,48 +101,47 @@ static void mask_foreach_id(ID *id, LibraryForeachIDData *data) static void mask_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Mask *mask = (Mask *)id; - if (mask->id.us > 0 || BLO_write_is_undo(writer)) { - MaskLayer *masklay; - BLO_write_id_struct(writer, Mask, id_address, &mask->id); - BKE_id_blend_write(writer, &mask->id); + MaskLayer *masklay; - if (mask->adt) { - BKE_animdata_blend_write(writer, mask->adt); - } + BLO_write_id_struct(writer, Mask, id_address, &mask->id); + BKE_id_blend_write(writer, &mask->id); - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - MaskLayerShape *masklay_shape; + if (mask->adt) { + BKE_animdata_blend_write(writer, mask->adt); + } - BLO_write_struct(writer, MaskLayer, masklay); + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + MaskLayerShape *masklay_shape; - for (spline = masklay->splines.first; spline; spline = spline->next) { - int i; + BLO_write_struct(writer, MaskLayer, masklay); - void *points_deform = spline->points_deform; - spline->points_deform = NULL; + for (spline = masklay->splines.first; spline; spline = spline->next) { + int i; - BLO_write_struct(writer, MaskSpline, spline); - BLO_write_struct_array(writer, MaskSplinePoint, spline->tot_point, spline->points); + void *points_deform = spline->points_deform; + spline->points_deform = NULL; - spline->points_deform = points_deform; + BLO_write_struct(writer, MaskSpline, spline); + BLO_write_struct_array(writer, MaskSplinePoint, spline->tot_point, spline->points); - for (i = 0; i < spline->tot_point; i++) { - MaskSplinePoint *point = &spline->points[i]; + spline->points_deform = points_deform; - if (point->tot_uw) { - BLO_write_struct_array(writer, MaskSplinePointUW, point->tot_uw, point->uw); - } + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + + if (point->tot_uw) { + BLO_write_struct_array(writer, MaskSplinePointUW, point->tot_uw, point->uw); } } + } - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { - BLO_write_struct(writer, MaskLayerShape, masklay_shape); - BLO_write_float_array( - writer, masklay_shape->tot_vert * MASK_OBJECT_SHAPE_ELEM_SIZE, masklay_shape->data); - } + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; + masklay_shape = masklay_shape->next) { + BLO_write_struct(writer, MaskLayerShape, masklay_shape); + BLO_write_float_array( + writer, masklay_shape->tot_vert * MASK_OBJECT_SHAPE_ELEM_SIZE, masklay_shape->data); } } } diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index ca57038f1c4..13b5bca5638 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -179,31 +179,30 @@ static void material_foreach_id(ID *id, LibraryForeachIDData *data) static void material_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Material *ma = (Material *)id; - if (ma->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - ma->texpaintslot = NULL; - BLI_listbase_clear(&ma->gpumaterial); - /* write LibData */ - BLO_write_id_struct(writer, Material, id_address, &ma->id); - BKE_id_blend_write(writer, &ma->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + ma->texpaintslot = NULL; + BLI_listbase_clear(&ma->gpumaterial); - if (ma->adt) { - BKE_animdata_blend_write(writer, ma->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, Material, id_address, &ma->id); + BKE_id_blend_write(writer, &ma->id); - /* nodetree is integral part of material, no libdata */ - if (ma->nodetree) { - BLO_write_struct(writer, bNodeTree, ma->nodetree); - ntreeBlendWrite(writer, ma->nodetree); - } + if (ma->adt) { + BKE_animdata_blend_write(writer, ma->adt); + } - BKE_previewimg_blend_write(writer, ma->preview); + /* nodetree is integral part of material, no libdata */ + if (ma->nodetree) { + BLO_write_struct(writer, bNodeTree, ma->nodetree); + ntreeBlendWrite(writer, ma->nodetree); + } - /* grease pencil settings */ - if (ma->gp_style) { - BLO_write_struct(writer, MaterialGPencilStyle, ma->gp_style); - } + BKE_previewimg_blend_write(writer, ma->preview); + + /* grease pencil settings */ + if (ma->gp_style) { + BLO_write_struct(writer, MaterialGPencilStyle, ma->gp_style); } } diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index d6b189d484b..45cf0f17840 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -119,28 +119,27 @@ static void metaball_foreach_id(ID *id, LibraryForeachIDData *data) static void metaball_blend_write(BlendWriter *writer, ID *id, const void *id_address) { MetaBall *mb = (MetaBall *)id; - if (mb->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - BLI_listbase_clear(&mb->disp); - mb->editelems = NULL; - /* Must always be cleared (meta's don't have their own edit-data). */ - mb->needs_flush_to_id = 0; - mb->lastelem = NULL; - mb->batch_cache = NULL; - - /* write LibData */ - BLO_write_id_struct(writer, MetaBall, id_address, &mb->id); - BKE_id_blend_write(writer, &mb->id); - - /* direct data */ - BLO_write_pointer_array(writer, mb->totcol, mb->mat); - if (mb->adt) { - BKE_animdata_blend_write(writer, mb->adt); - } - LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { - BLO_write_struct(writer, MetaElem, ml); - } + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + BLI_listbase_clear(&mb->disp); + mb->editelems = NULL; + /* Must always be cleared (meta's don't have their own edit-data). */ + mb->needs_flush_to_id = 0; + mb->lastelem = NULL; + mb->batch_cache = NULL; + + /* write LibData */ + BLO_write_id_struct(writer, MetaBall, id_address, &mb->id); + BKE_id_blend_write(writer, &mb->id); + + /* direct data */ + BLO_write_pointer_array(writer, mb->totcol, mb->mat); + if (mb->adt) { + BKE_animdata_blend_write(writer, mb->adt); + } + + LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { + BLO_write_struct(writer, MetaElem, ml); } } diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index e99670fc488..8257e54c618 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -179,95 +179,90 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address { Mesh *mesh = (Mesh *)id; const bool is_undo = BLO_write_is_undo(writer); - if (mesh->id.us > 0 || is_undo) { - CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *llayers = NULL, llayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; - - /* cache only - don't write */ - mesh->mface = NULL; - mesh->totface = 0; - memset(&mesh->fdata, 0, sizeof(mesh->fdata)); - memset(&mesh->runtime, 0, sizeof(mesh->runtime)); - flayers = flayers_buff; - - /* Do not store actual geometry data in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(mesh) && !is_undo) { - mesh->mvert = NULL; - mesh->totvert = 0; - memset(&mesh->vdata, 0, sizeof(mesh->vdata)); - vlayers = vlayers_buff; - - mesh->medge = NULL; - mesh->totedge = 0; - memset(&mesh->edata, 0, sizeof(mesh->edata)); - elayers = elayers_buff; - - mesh->mloop = NULL; - mesh->totloop = 0; - memset(&mesh->ldata, 0, sizeof(mesh->ldata)); - llayers = llayers_buff; - - mesh->mpoly = NULL; - mesh->totpoly = 0; - memset(&mesh->pdata, 0, sizeof(mesh->pdata)); - players = players_buff; - } - else { - CustomData_blend_write_prepare( - &mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff)); - CustomData_blend_write_prepare( - &mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff)); - CustomData_blend_write_prepare( - &mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff)); - CustomData_blend_write_prepare( - &mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - } - BLO_write_id_struct(writer, Mesh, id_address, &mesh->id); - BKE_id_blend_write(writer, &mesh->id); + CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *llayers = NULL, llayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; - /* direct data */ - if (mesh->adt) { - BKE_animdata_blend_write(writer, mesh->adt); - } + /* cache only - don't write */ + mesh->mface = NULL; + mesh->totface = 0; + memset(&mesh->fdata, 0, sizeof(mesh->fdata)); + memset(&mesh->runtime, 0, sizeof(mesh->runtime)); + flayers = flayers_buff; + + /* Do not store actual geometry data in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(mesh) && !is_undo) { + mesh->mvert = NULL; + mesh->totvert = 0; + memset(&mesh->vdata, 0, sizeof(mesh->vdata)); + vlayers = vlayers_buff; + + mesh->medge = NULL; + mesh->totedge = 0; + memset(&mesh->edata, 0, sizeof(mesh->edata)); + elayers = elayers_buff; + + mesh->mloop = NULL; + mesh->totloop = 0; + memset(&mesh->ldata, 0, sizeof(mesh->ldata)); + llayers = llayers_buff; + + mesh->mpoly = NULL; + mesh->totpoly = 0; + memset(&mesh->pdata, 0, sizeof(mesh->pdata)); + players = players_buff; + } + else { + CustomData_blend_write_prepare(&mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff)); + CustomData_blend_write_prepare(&mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff)); + CustomData_blend_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff)); + CustomData_blend_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); + } + + BLO_write_id_struct(writer, Mesh, id_address, &mesh->id); + BKE_id_blend_write(writer, &mesh->id); - BKE_defbase_blend_write(writer, &mesh->vertex_group_names); + /* direct data */ + if (mesh->adt) { + BKE_animdata_blend_write(writer, mesh->adt); + } + + BKE_defbase_blend_write(writer, &mesh->vertex_group_names); - BLO_write_pointer_array(writer, mesh->totcol, mesh->mat); - BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect); + BLO_write_pointer_array(writer, mesh->totcol, mesh->mat); + BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect); - CustomData_blend_write( - writer, &mesh->vdata, vlayers, mesh->totvert, CD_MASK_MESH.vmask, &mesh->id); - CustomData_blend_write( - writer, &mesh->edata, elayers, mesh->totedge, CD_MASK_MESH.emask, &mesh->id); - /* fdata is really a dummy - written so slots align */ - CustomData_blend_write( - writer, &mesh->fdata, flayers, mesh->totface, CD_MASK_MESH.fmask, &mesh->id); - CustomData_blend_write( - writer, &mesh->ldata, llayers, mesh->totloop, CD_MASK_MESH.lmask, &mesh->id); - CustomData_blend_write( - writer, &mesh->pdata, players, mesh->totpoly, CD_MASK_MESH.pmask, &mesh->id); + CustomData_blend_write( + writer, &mesh->vdata, vlayers, mesh->totvert, CD_MASK_MESH.vmask, &mesh->id); + CustomData_blend_write( + writer, &mesh->edata, elayers, mesh->totedge, CD_MASK_MESH.emask, &mesh->id); + /* fdata is really a dummy - written so slots align */ + CustomData_blend_write( + writer, &mesh->fdata, flayers, mesh->totface, CD_MASK_MESH.fmask, &mesh->id); + CustomData_blend_write( + writer, &mesh->ldata, llayers, mesh->totloop, CD_MASK_MESH.lmask, &mesh->id); + CustomData_blend_write( + writer, &mesh->pdata, players, mesh->totpoly, CD_MASK_MESH.pmask, &mesh->id); - /* Free temporary data */ + /* Free temporary data */ -/* Free custom-data layers, when not assigned a buffer value. */ + /* Free custom-data layers, when not assigned a buffer value. */ #define CD_LAYERS_FREE(id) \ if (id && id != id##_buff) { \ MEM_freeN(id); \ } \ ((void)0) - CD_LAYERS_FREE(vlayers); - CD_LAYERS_FREE(elayers); - // CD_LAYER_FREE(flayers); /* Never allocated. */ - CD_LAYERS_FREE(llayers); - CD_LAYERS_FREE(players); + CD_LAYERS_FREE(vlayers); + CD_LAYERS_FREE(elayers); + // CD_LAYER_FREE(flayers); /* Never allocated. */ + CD_LAYERS_FREE(llayers); + CD_LAYERS_FREE(players); #undef CD_LAYERS_FREE - } } static void mesh_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index f32b0c434c1..e507252307b 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -206,36 +206,35 @@ static void write_movieReconstruction(BlendWriter *writer, static void movieclip_blend_write(BlendWriter *writer, ID *id, const void *id_address) { MovieClip *clip = (MovieClip *)id; - if (clip->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - clip->anim = NULL; - clip->tracking_context = NULL; - clip->tracking.stats = NULL; - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *object; + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + clip->anim = NULL; + clip->tracking_context = NULL; + clip->tracking.stats = NULL; - BLO_write_id_struct(writer, MovieClip, id_address, &clip->id); - BKE_id_blend_write(writer, &clip->id); + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *object; - if (clip->adt) { - BKE_animdata_blend_write(writer, clip->adt); - } + BLO_write_id_struct(writer, MovieClip, id_address, &clip->id); + BKE_id_blend_write(writer, &clip->id); + + if (clip->adt) { + BKE_animdata_blend_write(writer, clip->adt); + } - write_movieTracks(writer, &tracking->tracks); - write_moviePlaneTracks(writer, &tracking->plane_tracks); - write_movieReconstruction(writer, &tracking->reconstruction); + write_movieTracks(writer, &tracking->tracks); + write_moviePlaneTracks(writer, &tracking->plane_tracks); + write_movieReconstruction(writer, &tracking->reconstruction); - object = tracking->objects.first; - while (object) { - BLO_write_struct(writer, MovieTrackingObject, object); + object = tracking->objects.first; + while (object) { + BLO_write_struct(writer, MovieTrackingObject, object); - write_movieTracks(writer, &object->tracks); - write_moviePlaneTracks(writer, &object->plane_tracks); - write_movieReconstruction(writer, &object->reconstruction); + write_movieTracks(writer, &object->tracks); + write_moviePlaneTracks(writer, &object->plane_tracks); + write_movieReconstruction(writer, &object->reconstruction); - object = object->next; - } + object = object->next; } } diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index bd22f049a8b..2a0e05a2616 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -606,19 +606,18 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) static void ntree_blend_write(BlendWriter *writer, ID *id, const void *id_address) { bNodeTree *ntree = (bNodeTree *)id; - if (ntree->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - ntree->init = 0; /* to set callbacks and force setting types */ - ntree->is_updating = false; - ntree->typeinfo = nullptr; - ntree->interface_type = nullptr; - ntree->progress = nullptr; - ntree->execdata = nullptr; - BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + ntree->init = 0; /* to set callbacks and force setting types */ + ntree->is_updating = false; + ntree->typeinfo = nullptr; + ntree->interface_type = nullptr; + ntree->progress = nullptr; + ntree->execdata = nullptr; - ntreeBlendWrite(writer, ntree); - } + BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id); + + ntreeBlendWrite(writer, ntree); } static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 86db4b6ace8..1c08a46adc3 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -523,74 +523,72 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre Object *ob = (Object *)id; const bool is_undo = BLO_write_is_undo(writer); - if (ob->id.us > 0 || is_undo) { - /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ - BKE_object_runtime_reset(ob); - if (is_undo) { - /* For undo we stay in object mode during undo presses, so keep edit-mode disabled on save as - * well, can help reducing false detection of changed data-blocks. */ - ob->mode &= ~OB_MODE_EDIT; - } + /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ + BKE_object_runtime_reset(ob); + + if (is_undo) { + /* For undo we stay in object mode during undo presses, so keep edit-mode disabled on save as + * well, can help reducing false detection of changed data-blocks. */ + ob->mode &= ~OB_MODE_EDIT; + } - /* write LibData */ - BLO_write_id_struct(writer, Object, id_address, &ob->id); - BKE_id_blend_write(writer, &ob->id); + /* write LibData */ + BLO_write_id_struct(writer, Object, id_address, &ob->id); + BKE_id_blend_write(writer, &ob->id); - if (ob->adt) { - BKE_animdata_blend_write(writer, ob->adt); - } + if (ob->adt) { + BKE_animdata_blend_write(writer, ob->adt); + } - /* direct data */ - BLO_write_pointer_array(writer, ob->totcol, ob->mat); - BLO_write_raw(writer, sizeof(char) * ob->totcol, ob->matbits); + /* direct data */ + BLO_write_pointer_array(writer, ob->totcol, ob->mat); + BLO_write_raw(writer, sizeof(char) * ob->totcol, ob->matbits); - bArmature *arm = NULL; - if (ob->type == OB_ARMATURE) { - arm = ob->data; - if (arm && ob->pose && arm->act_bone) { - BLI_strncpy( - ob->pose->proxy_act_bone, arm->act_bone->name, sizeof(ob->pose->proxy_act_bone)); - } + bArmature *arm = NULL; + if (ob->type == OB_ARMATURE) { + arm = ob->data; + if (arm && ob->pose && arm->act_bone) { + BLI_strncpy(ob->pose->proxy_act_bone, arm->act_bone->name, sizeof(ob->pose->proxy_act_bone)); } + } - BKE_pose_blend_write(writer, ob->pose, arm); - write_fmaps(writer, &ob->fmaps); - BKE_constraint_blend_write(writer, &ob->constraints); - animviz_motionpath_blend_write(writer, ob->mpath); + BKE_pose_blend_write(writer, ob->pose, arm); + write_fmaps(writer, &ob->fmaps); + BKE_constraint_blend_write(writer, &ob->constraints); + animviz_motionpath_blend_write(writer, ob->mpath); - BLO_write_struct(writer, PartDeflect, ob->pd); - if (ob->soft) { - /* Set deprecated pointers to prevent crashes of older Blenders */ - ob->soft->pointcache = ob->soft->shared->pointcache; - ob->soft->ptcaches = ob->soft->shared->ptcaches; - BLO_write_struct(writer, SoftBody, ob->soft); - BLO_write_struct(writer, SoftBody_Shared, ob->soft->shared); - BKE_ptcache_blend_write(writer, &(ob->soft->shared->ptcaches)); - BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights); - } + BLO_write_struct(writer, PartDeflect, ob->pd); + if (ob->soft) { + /* Set deprecated pointers to prevent crashes of older Blenders */ + ob->soft->pointcache = ob->soft->shared->pointcache; + ob->soft->ptcaches = ob->soft->shared->ptcaches; + BLO_write_struct(writer, SoftBody, ob->soft); + BLO_write_struct(writer, SoftBody_Shared, ob->soft->shared); + BKE_ptcache_blend_write(writer, &(ob->soft->shared->ptcaches)); + BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights); + } - if (ob->rigidbody_object) { - /* TODO: if any extra data is added to handle duplis, will need separate function then */ - BLO_write_struct(writer, RigidBodyOb, ob->rigidbody_object); - } - if (ob->rigidbody_constraint) { - BLO_write_struct(writer, RigidBodyCon, ob->rigidbody_constraint); - } + if (ob->rigidbody_object) { + /* TODO: if any extra data is added to handle duplis, will need separate function then */ + BLO_write_struct(writer, RigidBodyOb, ob->rigidbody_object); + } + if (ob->rigidbody_constraint) { + BLO_write_struct(writer, RigidBodyCon, ob->rigidbody_constraint); + } - if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) { - BLO_write_struct(writer, ImageUser, ob->iuser); - } + if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) { + BLO_write_struct(writer, ImageUser, ob->iuser); + } - BKE_particle_system_blend_write(writer, &ob->particlesystem); - BKE_modifier_blend_write(writer, &ob->modifiers); - BKE_gpencil_modifier_blend_write(writer, &ob->greasepencil_modifiers); - BKE_shaderfx_blend_write(writer, &ob->shader_fx); + BKE_particle_system_blend_write(writer, &ob->particlesystem); + BKE_modifier_blend_write(writer, &ob->modifiers); + BKE_gpencil_modifier_blend_write(writer, &ob->greasepencil_modifiers); + BKE_shaderfx_blend_write(writer, &ob->shader_fx); - BLO_write_struct_list(writer, LinkData, &ob->pc_ids); + BLO_write_struct_list(writer, LinkData, &ob->pc_ids); - BKE_previewimg_blend_write(writer, ob->preview); - } + BKE_previewimg_blend_write(writer, ob->preview); } /* XXX deprecated - old animation system */ diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index a1fa6aae1ce..d6030941c6d 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -108,14 +108,13 @@ static void palette_free_data(ID *id) static void palette_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Palette *palette = (Palette *)id; - if (palette->id.us > 0 || BLO_write_is_undo(writer)) { - PaletteColor *color; - BLO_write_id_struct(writer, Palette, id_address, &palette->id); - BKE_id_blend_write(writer, &palette->id); - for (color = palette->colors.first; color; color = color->next) { - BLO_write_struct(writer, PaletteColor, color); - } + PaletteColor *color; + BLO_write_id_struct(writer, Palette, id_address, &palette->id); + BKE_id_blend_write(writer, &palette->id); + + for (color = palette->colors.first; color; color = color->next) { + BLO_write_struct(writer, PaletteColor, color); } } @@ -187,12 +186,11 @@ static void paint_curve_free_data(ID *id) static void paint_curve_blend_write(BlendWriter *writer, ID *id, const void *id_address) { PaintCurve *pc = (PaintCurve *)id; - if (pc->id.us > 0 || BLO_write_is_undo(writer)) { - BLO_write_id_struct(writer, PaintCurve, id_address, &pc->id); - BKE_id_blend_write(writer, &pc->id); - BLO_write_struct_array(writer, PaintCurvePoint, pc->tot_points, pc->points); - } + BLO_write_id_struct(writer, PaintCurve, id_address, &pc->id); + BKE_id_blend_write(writer, &pc->id); + + BLO_write_struct_array(writer, PaintCurvePoint, pc->tot_points, pc->points); } static void paint_curve_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index cc8a051eceb..29849c69b6f 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -255,60 +255,59 @@ static void write_boid_state(BlendWriter *writer, BoidState *state) static void particle_settings_blend_write(BlendWriter *writer, ID *id, const void *id_address) { ParticleSettings *part = (ParticleSettings *)id; - if (part->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - BLO_write_id_struct(writer, ParticleSettings, id_address, &part->id); - BKE_id_blend_write(writer, &part->id); - if (part->adt) { - BKE_animdata_blend_write(writer, part->adt); - } - BLO_write_struct(writer, PartDeflect, part->pd); - BLO_write_struct(writer, PartDeflect, part->pd2); - BLO_write_struct(writer, EffectorWeights, part->effector_weights); + /* write LibData */ + BLO_write_id_struct(writer, ParticleSettings, id_address, &part->id); + BKE_id_blend_write(writer, &part->id); - if (part->clumpcurve) { - BKE_curvemapping_blend_write(writer, part->clumpcurve); - } - if (part->roughcurve) { - BKE_curvemapping_blend_write(writer, part->roughcurve); - } - if (part->twistcurve) { - BKE_curvemapping_blend_write(writer, part->twistcurve); - } + if (part->adt) { + BKE_animdata_blend_write(writer, part->adt); + } + BLO_write_struct(writer, PartDeflect, part->pd); + BLO_write_struct(writer, PartDeflect, part->pd2); + BLO_write_struct(writer, EffectorWeights, part->effector_weights); - LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) { - /* update indices, but only if dw->ob is set (can be NULL after loading e.g.) */ - if (dw->ob != NULL) { - dw->index = 0; - if (part->instance_collection) { /* can be NULL if lining fails or set to None */ - FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (part->instance_collection, object) { - if (object == dw->ob) { - break; - } - dw->index++; + if (part->clumpcurve) { + BKE_curvemapping_blend_write(writer, part->clumpcurve); + } + if (part->roughcurve) { + BKE_curvemapping_blend_write(writer, part->roughcurve); + } + if (part->twistcurve) { + BKE_curvemapping_blend_write(writer, part->twistcurve); + } + + LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) { + /* update indices, but only if dw->ob is set (can be NULL after loading e.g.) */ + if (dw->ob != NULL) { + dw->index = 0; + if (part->instance_collection) { /* can be NULL if lining fails or set to None */ + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (part->instance_collection, object) { + if (object == dw->ob) { + break; } - FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + dw->index++; } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } - BLO_write_struct(writer, ParticleDupliWeight, dw); } + BLO_write_struct(writer, ParticleDupliWeight, dw); + } - if (part->boids && part->phystype == PART_PHYS_BOIDS) { - BLO_write_struct(writer, BoidSettings, part->boids); + if (part->boids && part->phystype == PART_PHYS_BOIDS) { + BLO_write_struct(writer, BoidSettings, part->boids); - LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { - write_boid_state(writer, state); - } - } - if (part->fluid && part->phystype == PART_PHYS_FLUID) { - BLO_write_struct(writer, SPHFluidSettings, part->fluid); + LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { + write_boid_state(writer, state); } + } + if (part->fluid && part->phystype == PART_PHYS_FLUID) { + BLO_write_struct(writer, SPHFluidSettings, part->fluid); + } - for (int a = 0; a < MAX_MTEX; a++) { - if (part->mtex[a]) { - BLO_write_struct(writer, MTex, part->mtex[a]); - } + for (int a = 0; a < MAX_MTEX; a++) { + if (part->mtex[a]) { + BLO_write_struct(writer, MTex, part->mtex[a]); } } } diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index d9a7a376e2e..837a772607f 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -112,28 +112,27 @@ static void pointcloud_foreach_id(ID *id, LibraryForeachIDData *data) static void pointcloud_blend_write(BlendWriter *writer, ID *id, const void *id_address) { PointCloud *pointcloud = (PointCloud *)id; - if (pointcloud->id.us > 0 || BLO_write_is_undo(writer)) { - CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_blend_write_prepare( - &pointcloud->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - - /* Write LibData */ - BLO_write_id_struct(writer, PointCloud, id_address, &pointcloud->id); - BKE_id_blend_write(writer, &pointcloud->id); - - /* Direct data */ - CustomData_blend_write( - writer, &pointcloud->pdata, players, pointcloud->totpoint, CD_MASK_ALL, &pointcloud->id); - - BLO_write_pointer_array(writer, pointcloud->totcol, pointcloud->mat); - if (pointcloud->adt) { - BKE_animdata_blend_write(writer, pointcloud->adt); - } - /* Remove temporary data. */ - if (players && players != players_buff) { - MEM_freeN(players); - } + CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE]; + CustomData_blend_write_prepare( + &pointcloud->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); + + /* Write LibData */ + BLO_write_id_struct(writer, PointCloud, id_address, &pointcloud->id); + BKE_id_blend_write(writer, &pointcloud->id); + + /* Direct data */ + CustomData_blend_write( + writer, &pointcloud->pdata, players, pointcloud->totpoint, CD_MASK_ALL, &pointcloud->id); + + BLO_write_pointer_array(writer, pointcloud->totcol, pointcloud->mat); + if (pointcloud->adt) { + BKE_animdata_blend_write(writer, pointcloud->adt); + } + + /* Remove temporary data. */ + if (players && players != players_buff) { + MEM_freeN(players); } } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index e44c5a6b40e..1e725a6afc4 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -255,18 +255,16 @@ static void screen_foreach_id(ID *id, LibraryForeachIDData *data) static void screen_blend_write(BlendWriter *writer, ID *id, const void *id_address) { bScreen *screen = (bScreen *)id; - /* Screens are reference counted, only saved if used by a workspace. */ - if (screen->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ - BLO_write_struct_at_address_with_filecode(writer, ID_SCRN, bScreen, id_address, screen); - BKE_id_blend_write(writer, &screen->id); - BKE_previewimg_blend_write(writer, screen->preview); + /* write LibData */ + /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ + BLO_write_struct_at_address_with_filecode(writer, ID_SCRN, bScreen, id_address, screen); + BKE_id_blend_write(writer, &screen->id); - /* direct data */ - BKE_screen_area_map_blend_write(writer, AREAMAP_FROM_SCREEN(screen)); - } + BKE_previewimg_blend_write(writer, screen->preview); + + /* direct data */ + BKE_screen_area_map_blend_write(writer, AREAMAP_FROM_SCREEN(screen)); } /* Cannot use IDTypeInfo callback yet, because of the return value. */ diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 216563b860d..5aac29c19a7 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -114,19 +114,18 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) static void simulation_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Simulation *simulation = (Simulation *)id; - if (simulation->id.us > 0 || BLO_write_is_undo(writer)) { - BLO_write_id_struct(writer, Simulation, id_address, &simulation->id); - BKE_id_blend_write(writer, &simulation->id); - - if (simulation->adt) { - BKE_animdata_blend_write(writer, simulation->adt); - } - - /* nodetree is integral part of simulation, no libdata */ - if (simulation->nodetree) { - BLO_write_struct(writer, bNodeTree, simulation->nodetree); - ntreeBlendWrite(writer, simulation->nodetree); - } + + BLO_write_id_struct(writer, Simulation, id_address, &simulation->id); + BKE_id_blend_write(writer, &simulation->id); + + if (simulation->adt) { + BKE_animdata_blend_write(writer, simulation->adt); + } + + /* nodetree is integral part of simulation, no libdata */ + if (simulation->nodetree) { + BLO_write_struct(writer, bNodeTree, simulation->nodetree); + ntreeBlendWrite(writer, simulation->nodetree); } } diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 8730d2758e6..c61fa793367 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -137,24 +137,23 @@ static void sound_blend_write(BlendWriter *writer, ID *id, const void *id_addres { bSound *sound = (bSound *)id; const bool is_undo = BLO_write_is_undo(writer); - if (sound->id.us > 0 || is_undo) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - sound->tags = 0; - sound->handle = NULL; - sound->playback_handle = NULL; - sound->spinlock = NULL; - /* Do not store packed files in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(sound) && !is_undo) { - sound->packedfile = NULL; - } - - /* write LibData */ - BLO_write_id_struct(writer, bSound, id_address, &sound->id); - BKE_id_blend_write(writer, &sound->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + sound->tags = 0; + sound->handle = NULL; + sound->playback_handle = NULL; + sound->spinlock = NULL; - BKE_packedfile_blend_write(writer, sound->packedfile); + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(sound) && !is_undo) { + sound->packedfile = NULL; } + + /* write LibData */ + BLO_write_id_struct(writer, bSound, id_address, &sound->id); + BKE_id_blend_write(writer, &sound->id); + + BKE_packedfile_blend_write(writer, sound->packedfile); } static void sound_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index af9b2268879..4b10522c375 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -56,14 +56,13 @@ static void speaker_foreach_id(ID *id, LibraryForeachIDData *data) static void speaker_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Speaker *spk = (Speaker *)id; - if (spk->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - BLO_write_id_struct(writer, Speaker, id_address, &spk->id); - BKE_id_blend_write(writer, &spk->id); - - if (spk->adt) { - BKE_animdata_blend_write(writer, spk->adt); - } + + /* write LibData */ + BLO_write_id_struct(writer, Speaker, id_address, &spk->id); + BKE_id_blend_write(writer, &spk->id); + + if (spk->adt) { + BKE_animdata_blend_write(writer, spk->adt); } } diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 6048e823abb..275cf0d4c38 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -171,9 +171,6 @@ static void text_free_data(ID *id) static void text_blend_write(BlendWriter *writer, ID *id, const void *id_address) { - if (id->us < 1 && !BLO_write_is_undo(writer)) { - return; - } Text *text = (Text *)id; /* NOTE: we are clearing local temp data here, *not* the flag in the actual 'real' ID. */ diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index beb92495d16..228e6fffdf7 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -150,28 +150,27 @@ static void texture_foreach_id(ID *id, LibraryForeachIDData *data) static void texture_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Tex *tex = (Tex *)id; - if (tex->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - BLO_write_id_struct(writer, Tex, id_address, &tex->id); - BKE_id_blend_write(writer, &tex->id); - if (tex->adt) { - BKE_animdata_blend_write(writer, tex->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, Tex, id_address, &tex->id); + BKE_id_blend_write(writer, &tex->id); - /* direct data */ - if (tex->coba) { - BLO_write_struct(writer, ColorBand, tex->coba); - } + if (tex->adt) { + BKE_animdata_blend_write(writer, tex->adt); + } - /* nodetree is integral part of texture, no libdata */ - if (tex->nodetree) { - BLO_write_struct(writer, bNodeTree, tex->nodetree); - ntreeBlendWrite(writer, tex->nodetree); - } + /* direct data */ + if (tex->coba) { + BLO_write_struct(writer, ColorBand, tex->coba); + } - BKE_previewimg_blend_write(writer, tex->preview); + /* nodetree is integral part of texture, no libdata */ + if (tex->nodetree) { + BLO_write_struct(writer, bNodeTree, tex->nodetree); + ntreeBlendWrite(writer, tex->nodetree); } + + BKE_previewimg_blend_write(writer, tex->preview); } static void texture_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index b28d17df814..69452d6896f 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -578,27 +578,26 @@ static void volume_blend_write(BlendWriter *writer, ID *id, const void *id_addre { Volume *volume = (Volume *)id; const bool is_undo = BLO_write_is_undo(writer); - if (volume->id.us > 0 || is_undo) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - volume->runtime.grids = nullptr; - /* Do not store packed files in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(volume) && !is_undo) { - volume->packedfile = nullptr; - } + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + volume->runtime.grids = nullptr; - /* write LibData */ - BLO_write_id_struct(writer, Volume, id_address, &volume->id); - BKE_id_blend_write(writer, &volume->id); + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(volume) && !is_undo) { + volume->packedfile = nullptr; + } - /* direct data */ - BLO_write_pointer_array(writer, volume->totcol, volume->mat); - if (volume->adt) { - BKE_animdata_blend_write(writer, volume->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, Volume, id_address, &volume->id); + BKE_id_blend_write(writer, &volume->id); - BKE_packedfile_blend_write(writer, volume->packedfile); + /* direct data */ + BLO_write_pointer_array(writer, volume->totcol, volume->mat); + if (volume->adt) { + BKE_animdata_blend_write(writer, volume->adt); } + + BKE_packedfile_blend_write(writer, volume->packedfile); } static void volume_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index e889d8af1d5..4abe1ff0f20 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -138,26 +138,25 @@ static void world_foreach_id(ID *id, LibraryForeachIDData *data) static void world_blend_write(BlendWriter *writer, ID *id, const void *id_address) { World *wrld = (World *)id; - if (wrld->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - BLI_listbase_clear(&wrld->gpumaterial); - /* write LibData */ - BLO_write_id_struct(writer, World, id_address, &wrld->id); - BKE_id_blend_write(writer, &wrld->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + BLI_listbase_clear(&wrld->gpumaterial); - if (wrld->adt) { - BKE_animdata_blend_write(writer, wrld->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, World, id_address, &wrld->id); + BKE_id_blend_write(writer, &wrld->id); - /* nodetree is integral part of world, no libdata */ - if (wrld->nodetree) { - BLO_write_struct(writer, bNodeTree, wrld->nodetree); - ntreeBlendWrite(writer, wrld->nodetree); - } + if (wrld->adt) { + BKE_animdata_blend_write(writer, wrld->adt); + } - BKE_previewimg_blend_write(writer, wrld->preview); + /* nodetree is integral part of world, no libdata */ + if (wrld->nodetree) { + BLO_write_struct(writer, bNodeTree, wrld->nodetree); + ntreeBlendWrite(writer, wrld->nodetree); } + + BKE_previewimg_blend_write(writer, wrld->preview); } static void world_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 225548f3832..337279ae84b 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -982,6 +982,14 @@ static bool write_file_handle(Main *mainvar, BLI_assert( (id->tag & (LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT | LIB_TAG_NOT_ALLOCATED)) == 0); + /* We only write unused IDs in undo case. + * NOTE: All Scenes, WindowManagers and WorkSpaces should always be written to disk, so + * their usercount should never be NULL currently. */ + if (id->us == 0 && !wd->use_memfile) { + BLI_assert(!ELEM(GS(id->name), ID_SCE, ID_WM, ID_WS)); + continue; + } + const bool do_override = !ELEM(override_storage, NULL, bmain) && ID_IS_OVERRIDE_LIBRARY_REAL(id); -- cgit v1.2.3 From e648e388874a317f7846efe9ba7242a5b11a5650 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 19 Aug 2021 15:08:13 +0200 Subject: Undo: Clear more ID runtime data on filewrite. This should help reducing false 'changed' status detection when reading back a memfile undo step. Related to T90593 & D12242. --- source/blender/blenloader/intern/writefile.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 337279ae84b..99246603e9a 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1023,12 +1023,23 @@ static bool write_file_handle(Main *mainvar, memcpy(id_buffer, id, idtype_struct_size); + /* Clear runtime data to reduce false detection of changed data in undo/redo context. */ ((ID *)id_buffer)->tag = 0; + ((ID *)id_buffer)->us = 0; + ((ID *)id_buffer)->icon_id = 0; /* Those listbase data change every time we add/remove an ID, and also often when * renaming one (due to re-sorting). This avoids generating a lot of false 'is changed' * detections between undo steps. */ ((ID *)id_buffer)->prev = NULL; ((ID *)id_buffer)->next = NULL; + /* Those runtime pointers should never be set during writing stage, but just in case clear + * them too. */ + ((ID *)id_buffer)->orig_id = NULL; + ((ID *)id_buffer)->newid = NULL; + /* Even though in theory we could be able to preserve this python instance across undo even + * when we need to re-read the ID into its original address, this is currently cleared in + * #direct_link_id_common in `readfile.c` anyway, */ + ((ID *)id_buffer)->py_instance = NULL; const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); if (id_type->blend_write != NULL) { -- cgit v1.2.3 From 46aafbbf6683d20c57f3668d04ce1cf6cd14e0e8 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 19 Aug 2021 09:52:09 -0300 Subject: Cleanup: move animation snap utilities to a separate compilation unit The snap functions of animation editors were scattered in `transform_mode` and `transform_snap`. --- source/blender/editors/transform/CMakeLists.txt | 1 + .../editors/transform/transform_convert_graph.c | 2 + .../editors/transform/transform_convert_nla.c | 2 + source/blender/editors/transform/transform_mode.c | 96 ------------- source/blender/editors/transform/transform_mode.h | 3 - .../editors/transform/transform_mode_timescale.c | 2 + source/blender/editors/transform/transform_snap.c | 40 +----- source/blender/editors/transform/transform_snap.h | 17 ++- .../editors/transform/transform_snap_animation.c | 156 +++++++++++++++++++++ 9 files changed, 175 insertions(+), 144 deletions(-) create mode 100644 source/blender/editors/transform/transform_snap_animation.c diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index ad0a330f0f4..e9efed3cd61 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -101,6 +101,7 @@ set(SRC transform_ops.c transform_orientations.c transform_snap.c + transform_snap_animation.c transform_snap_object.c transform_snap_sequencer.c diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index a6cbbb299ac..c0acdd89b02 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -41,6 +41,8 @@ #include "transform.h" #include "transform_convert.h" +#include "transform_snap.h" + #include "transform_mode.h" typedef struct TransDataGraph { diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index f96f2e93bbc..4d98b7a489f 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -42,6 +42,8 @@ #include "transform.h" #include "transform_convert.h" +#include "transform_snap.h" + #include "transform_mode.h" /** Used for NLA transform (stored in #TransData.extra pointer). */ diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 362ee179e7d..8df95222fa1 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -1067,102 +1067,6 @@ void ElementResize(const TransInfo *t, /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Transform (Frame Utils) - * \{ */ - -/** - * This function returns the snapping 'mode' for Animation Editors only. - * We cannot use the standard snapping due to NLA-strip scaling complexities. - * - * TODO: these modifier checks should be key-mappable. - */ -short getAnimEdit_SnapMode(TransInfo *t) -{ - short autosnap = SACTSNAP_OFF; - - if (t->spacetype == SPACE_ACTION) { - SpaceAction *saction = (SpaceAction *)t->area->spacedata.first; - - if (saction) { - autosnap = saction->autosnap; - } - } - else if (t->spacetype == SPACE_GRAPH) { - SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; - - if (sipo) { - autosnap = sipo->autosnap; - } - } - else if (t->spacetype == SPACE_NLA) { - SpaceNla *snla = (SpaceNla *)t->area->spacedata.first; - - if (snla) { - autosnap = snla->autosnap; - } - } - else { - autosnap = SACTSNAP_OFF; - } - - /* toggle autosnap on/off - * - when toggling on, prefer nearest frame over 1.0 frame increments - */ - if (t->modifiers & MOD_SNAP_INVERT) { - if (autosnap) { - autosnap = SACTSNAP_OFF; - } - else { - autosnap = SACTSNAP_FRAME; - } - } - - return autosnap; -} - -/* This function is used by Animation Editor specific transform functions to do - * the Snap Keyframe to Nearest Frame/Marker - */ -void doAnimEdit_SnapFrame( - TransInfo *t, TransData *td, TransData2D *td2d, AnimData *adt, short autosnap) -{ - if (autosnap != SACTSNAP_OFF) { - float val; - - /* convert frame to nla-action time (if needed) */ - if (adt && (t->spacetype != SPACE_SEQ)) { - val = BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_MAP); - } - else { - val = *(td->val); - } - - snapFrameTransform(t, autosnap, true, val, &val); - - /* convert frame out of nla-action time */ - if (adt && (t->spacetype != SPACE_SEQ)) { - *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); - } - else { - *(td->val) = val; - } - } - - /* If the handles are to be moved too - * (as side-effect of keyframes moving, to keep the general effect) - * offset them by the same amount so that the general angles are maintained - * (i.e. won't change while handles are free-to-roam and keyframes are snap-locked). - */ - if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) { - td2d->h1[0] = td2d->ih1[0] + *td->val - td->ival; - } - if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) { - td2d->h2[0] = td2d->ih2[0] + *td->val - td->ival; - } -} -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Transform Mode Initialization * \{ */ diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h index 027fb6b6982..d8601000ddb 100644 --- a/source/blender/editors/transform/transform_mode.h +++ b/source/blender/editors/transform/transform_mode.h @@ -63,9 +63,6 @@ void ElementResize(const TransInfo *t, const TransDataContainer *tc, TransData *td, const float mat[3][3]); -short getAnimEdit_SnapMode(TransInfo *t); -void doAnimEdit_SnapFrame( - TransInfo *t, TransData *td, TransData2D *td2d, struct AnimData *adt, short autosnap); void transform_mode_init(TransInfo *t, struct wmOperator *op, const int mode); void transform_mode_default_modal_orientation_set(TransInfo *t, int type); diff --git a/source/blender/editors/transform/transform_mode_timescale.c b/source/blender/editors/transform/transform_mode_timescale.c index 98ffc0abbd4..4123663e849 100644 --- a/source/blender/editors/transform/transform_mode_timescale.c +++ b/source/blender/editors/transform/transform_mode_timescale.c @@ -40,6 +40,8 @@ #include "transform.h" #include "transform_convert.h" +#include "transform_snap.h" + #include "transform_mode.h" /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 656a1e5d9c7..ea08c8912e9 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -1461,47 +1461,9 @@ bool snapNodesTransform( /** \} */ /* -------------------------------------------------------------------- */ -/** \name snap Frames +/** \name snap Grid * \{ */ -/* This function is used by Animation Editor specific transform functions to do - * the Snap Keyframe to Nearest Frame/Marker - */ -void snapFrameTransform(TransInfo *t, - const eAnimEdit_AutoSnap autosnap, - const bool is_frame_value, - const float delta, - float *r_val) -{ - double val = delta; - switch (autosnap) { - case SACTSNAP_STEP: - case SACTSNAP_FRAME: - val = floor(val + 0.5); - break; - case SACTSNAP_MARKER: - /* snap to nearest marker */ - /* TODO: need some more careful checks for where data comes from. */ - val = ED_markers_find_nearest_marker_time(&t->scene->markers, (float)val); - break; - case SACTSNAP_SECOND: - case SACTSNAP_TSTEP: { - /* second step */ - const Scene *scene = t->scene; - const double secf = FPS; - val = floor((val / secf) + 0.5); - if (is_frame_value) { - val *= secf; - } - break; - } - case SACTSNAP_OFF: { - break; - } - } - *r_val = (float)val; -} - static void snap_grid_apply( TransInfo *t, const int max_index, const float grid_dist, const float loc[3], float r_out[3]) { diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h index 6dfaeab93e6..20cc590d7cc 100644 --- a/source/blender/editors/transform/transform_snap.h +++ b/source/blender/editors/transform/transform_snap.h @@ -45,12 +45,6 @@ bool snapNodesTransform(struct TransInfo *t, float r_loc[2], float *r_dist_px, char *r_node_border); -void snapFrameTransform(struct TransInfo *t, - const eAnimEdit_AutoSnap autosnap, - const bool is_frame_value, - const float delta, - /* return args */ - float *r_val); bool transformModeUseSnap(const TransInfo *t); @@ -86,3 +80,14 @@ struct TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t) void transform_snap_sequencer_data_free(struct TransSeqSnapData *data); bool transform_snap_sequencer_calc(struct TransInfo *t); void transform_snap_sequencer_apply_translate(TransInfo *t, float *vec); + +/* transform_snap_animation.c */ +short getAnimEdit_SnapMode(TransInfo *t); +void snapFrameTransform(struct TransInfo *t, + const eAnimEdit_AutoSnap autosnap, + const bool is_frame_value, + const float delta, + /* return args */ + float *r_val); +void doAnimEdit_SnapFrame( + TransInfo *t, TransData *td, TransData2D *td2d, struct AnimData *adt, short autosnap); diff --git a/source/blender/editors/transform/transform_snap_animation.c b/source/blender/editors/transform/transform_snap_animation.c new file mode 100644 index 00000000000..ac7c47e408f --- /dev/null +++ b/source/blender/editors/transform/transform_snap_animation.c @@ -0,0 +1,156 @@ +/* + * 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 edtransform + */ + +#include "DNA_anim_types.h" + +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_nla.h" + +#include "ED_markers.h" +#include "ED_screen.h" + +#include "transform.h" +#include "transform_snap.h" + +/* -------------------------------------------------------------------- */ +/** \name Snappint in Anim Editors + * \{ */ + +/** + * This function returns the snapping 'mode' for Animation Editors only. + * We cannot use the standard snapping due to NLA-strip scaling complexities. + * + * TODO: these modifier checks should be key-mappable. + */ +short getAnimEdit_SnapMode(TransInfo *t) +{ + short autosnap = SACTSNAP_OFF; + + if (t->spacetype == SPACE_ACTION) { + SpaceAction *saction = (SpaceAction *)t->area->spacedata.first; + + if (saction) { + autosnap = saction->autosnap; + } + } + else if (t->spacetype == SPACE_GRAPH) { + SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; + + if (sipo) { + autosnap = sipo->autosnap; + } + } + else if (t->spacetype == SPACE_NLA) { + SpaceNla *snla = (SpaceNla *)t->area->spacedata.first; + + if (snla) { + autosnap = snla->autosnap; + } + } + else { + autosnap = SACTSNAP_OFF; + } + + /* toggle autosnap on/off + * - when toggling on, prefer nearest frame over 1.0 frame increments + */ + if (t->modifiers & MOD_SNAP_INVERT) { + if (autosnap) { + autosnap = SACTSNAP_OFF; + } + else { + autosnap = SACTSNAP_FRAME; + } + } + + return autosnap; +} + +void snapFrameTransform(TransInfo *t, + const eAnimEdit_AutoSnap autosnap, + const bool is_frame_value, + const float delta, + /* return args */ + float *r_val) +{ + double val = delta; + switch (autosnap) { + case SACTSNAP_STEP: + case SACTSNAP_FRAME: + val = floor(val + 0.5); + break; + case SACTSNAP_MARKER: + /* snap to nearest marker */ + /* TODO: need some more careful checks for where data comes from. */ + val = ED_markers_find_nearest_marker_time(&t->scene->markers, (float)val); + break; + case SACTSNAP_SECOND: + case SACTSNAP_TSTEP: { + /* second step */ + const Scene *scene = t->scene; + const double secf = FPS; + val = floor((val / secf) + 0.5); + if (is_frame_value) { + val *= secf; + } + break; + } + case SACTSNAP_OFF: { + break; + } + } + *r_val = (float)val; +} + +/* This function is used by Animation Editor specific transform functions to do + * the Snap Keyframe to Nearest Frame/Marker + */ +void doAnimEdit_SnapFrame( + TransInfo *t, TransData *td, TransData2D *td2d, AnimData *adt, short autosnap) +{ + if (autosnap != SACTSNAP_OFF) { + float val; + + /* convert frame to nla-action time (if needed) */ + if (adt && (t->spacetype != SPACE_SEQ)) { + val = BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_MAP); + } + else { + val = *(td->val); + } + + snapFrameTransform(t, autosnap, true, val, &val); + + /* convert frame out of nla-action time */ + if (adt && (t->spacetype != SPACE_SEQ)) { + *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); + } + else { + *(td->val) = val; + } + } +} + +/** \} */ -- cgit v1.2.3 From 85b044b3ef593fb4df5a9b3ca4f5a087587228f4 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 18 Aug 2021 11:02:24 -0300 Subject: Fix incremental snap in animation editors Animation editors have their own snap types and incremental is not supported. --- source/blender/editors/transform/transform_snap.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index ea08c8912e9..05a20a14477 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -594,7 +594,7 @@ static void initSnappingMode(TransInfo *t) else if (t->spacetype == SPACE_SEQ) { t->tsnap.mode = SEQ_tool_settings_snap_mode_get(t->scene); } - else { + else if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) { /* force project off when not supported */ if ((ts->snap_mode & SCE_SNAP_MODE_FACE) == 0) { t->tsnap.project = 0; @@ -608,6 +608,14 @@ static void initSnappingMode(TransInfo *t) t->tsnap.mode |= SCE_SNAP_MODE_GRID; } } + else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) { + /* No incremental snapping. */ + t->tsnap.mode = 0; + } + else { + /* Fallback. */ + t->tsnap.mode = SCE_SNAP_MODE_INCREMENT; + } if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) { /* Only 3D view or UV. */ @@ -654,10 +662,6 @@ static void initSnappingMode(TransInfo *t) setSnappingCallback(t); t->tsnap.modeSelect = SNAP_NOT_SELECTED; } - else { - /* Fallback. */ - t->tsnap.mode = SCE_SNAP_MODE_INCREMENT; - } if (t->spacetype == SPACE_VIEW3D) { if (t->tsnap.object_context == NULL) { -- cgit v1.2.3 From 119d53263f0d0dc5474cb36e888dbdd48a64f9c6 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 19 Aug 2021 10:23:35 -0300 Subject: Transform Convert Action: conventionalize TransData creation `td2d->loc`, `td2d->loc2d`, `td->loc` and `td->iloc` were not being initialized as is done with the other conversion types. This avoids problems with transform modes becoming incompatible. This avoids problems with incompatible transform modes that could result in a crash. --- .../editors/transform/transform_convert_action.c | 46 ++++++++++++---------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index cfa14e21d0d..30cb9fa20e5 100644 --- a/source/blender/editors/transform/transform_convert_action.c +++ b/source/blender/editors/transform/transform_convert_action.c @@ -140,19 +140,37 @@ static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra, boo } /* This function assigns the information to transdata */ -static void TimeToTransData(TransData *td, float *time, AnimData *adt, float ypos) +static void TimeToTransData( + TransData *td, TransData2D *td2d, BezTriple *bezt, AnimData *adt, float ypos) { - /* memory is calloc'ed, so that should zero everything nicely for us */ + float *time = bezt->vec[1]; + + /* Setup #TransData2D. */ + td2d->loc[0] = *time; + td2d->loc2d = time; + td2d->h1 = bezt->vec[0]; + td2d->h2 = bezt->vec[2]; + copy_v2_v2(td2d->ih1, td2d->h1); + copy_v2_v2(td2d->ih2, td2d->h2); + + /* Setup #TransData. */ + td->loc = time; /* Usually #td2d->loc is used here. But this is for when the original location is + not float[3]. */ td->val = time; - td->ival = *(time); - + td->ival = td->iloc[0] = *(time); td->center[0] = td->ival; td->center[1] = ypos; - /* store the AnimData where this keyframe exists as a keyframe of the - * active action as td->extra. - */ + /* Store the AnimData where this keyframe exists as a keyframe of the + * active action as #td->extra. */ td->extra = adt; + + if (bezt->f2 & SELECT) { + td->flag |= TD_SELECTED; + } + + /* Set flags to move handles as necessary. */ + td->flag |= TD_MOVEHANDLE1 | TD_MOVEHANDLE2; } /* This function advances the address to which td points to, so it must return @@ -185,19 +203,7 @@ static TransData *ActionFCurveToTransData(TransData *td, * so can't use BEZT_ISSEL_ANY() macro */ /* only add if on the right 'side' of the current frame */ if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) { - TimeToTransData(td, bezt->vec[1], adt, ypos); - - if (bezt->f2 & SELECT) { - td->flag |= TD_SELECTED; - } - - /* Set flags to move handles as necessary. */ - td->flag |= TD_MOVEHANDLE1 | TD_MOVEHANDLE2; - td2d->h1 = bezt->vec[0]; - td2d->h2 = bezt->vec[2]; - - copy_v2_v2(td2d->ih1, td2d->h1); - copy_v2_v2(td2d->ih2, td2d->h2); + TimeToTransData(td, td2d, bezt, adt, ypos); td++; td2d++; -- cgit v1.2.3 From b0d9e6797fb866e7a58876c7977c98a190070310 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 19 Aug 2021 10:28:43 -0300 Subject: Fix T87173: wrong Auto-Snap in animation editors This was partially broken with {rBde9ea94fc6f}. The `Frame Step` and `Second Step` snapping options were working as if they were `Nearest Frame` and `Nearest Second` respectively in the `Dope Sheet` and `NLA` editors. In the `Graph Editor` the problem was more serious: "Second Step: ... The keyframe itself moves along as though in snapping were active at all, while its handles 'stay behind' until it reaches the next second boundary, at which point the teleport handles to 'catch up'". The snapping code for these modes was spread across the transform mode code and `recalcData` of each data type. Therefore, create a unified snapping code for these options so that all issues are fixed in one place. Differetial Revision: https://developer.blender.org/D12241 --- .../blender/editors/transform/transform_convert.c | 17 +++++ .../blender/editors/transform/transform_convert.h | 3 + .../editors/transform/transform_convert_action.c | 15 +++++ .../editors/transform/transform_convert_graph.c | 59 ++--------------- .../editors/transform/transform_convert_nla.c | 71 ++++++--------------- .../editors/transform/transform_mode_timescale.c | 4 -- .../transform/transform_mode_timetranslate.c | 33 +++++----- source/blender/editors/transform/transform_snap.h | 15 ++--- .../editors/transform/transform_snap_animation.c | 73 +++++++++++----------- 9 files changed, 119 insertions(+), 171 deletions(-) diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 3f730956dd0..094ae080de0 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -1662,6 +1662,23 @@ void animrecord_check_state(TransInfo *t, struct Object *ob) } } +void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const float inv_unit_scale) +{ + /* If the handles are to be moved too + * (as side-effect of keyframes moving, to keep the general effect) + * offset them by the same amount so that the general angles are maintained + * (i.e. won't change while handles are free-to-roam and keyframes are snap-locked). + */ + if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) { + td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0]; + td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale; + } + if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) { + td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0]; + td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale; + } +} + /* called for updating while transform acts, once per redraw */ void recalcData(TransInfo *t) { diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 55731bfa321..fa34e2555d6 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -43,6 +43,9 @@ void sort_trans_data_dist(TransInfo *t); void createTransData(struct bContext *C, TransInfo *t); bool clipUVTransform(TransInfo *t, float vec[2], const bool resize); void clipUVData(TransInfo *t); +void transform_convert_flush_handle2D(TransData *td, + TransData2D *td2d, + const float inv_unit_scale); void recalcData(TransInfo *t); /* transform_convert_mesh.c */ diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index 30cb9fa20e5..8a3b254be5c 100644 --- a/source/blender/editors/transform/transform_convert_action.c +++ b/source/blender/editors/transform/transform_convert_action.c @@ -45,6 +45,8 @@ #include "WM_types.h" #include "transform.h" +#include "transform_snap.h" + #include "transform_convert.h" /* helper struct for gp-frame transforms */ @@ -604,6 +606,19 @@ void recalcData_actedit(TransInfo *t) flushTransIntFrameActionData(t); } + /* Flush 2d vector. */ + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + const short autosnap = getAnimEdit_SnapMode(t); + TransData *td; + TransData2D *td2d; + int i = 0; + for (td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) { + if ((autosnap != SACTSNAP_OFF) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) { + transform_snap_anim_flush_data(t, td, autosnap, td->loc); + } + transform_convert_flush_handle2D(td, td2d, 1.0f); + } + if (ac.datatype != ANIMCONT_MASK) { /* Get animdata blocks visible in editor, * assuming that these will be the ones where things changed. */ diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index c0acdd89b02..d22277f9d91 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -40,6 +40,8 @@ #include "UI_view2d.h" #include "transform.h" +#include "transform_snap.h" + #include "transform_convert.h" #include "transform_snap.h" @@ -662,10 +664,9 @@ static void flushTransGraphData(TransInfo *t) TransData *td; TransData2D *td2d; TransDataGraph *tdg; - Scene *scene = t->scene; - double secf = FPS; int a; + const short autosnap = getAnimEdit_SnapMode(t); TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); /* flush to 2d vector from internally used 3d vector */ @@ -681,22 +682,8 @@ static void flushTransGraphData(TransInfo *t) * - Only apply to keyframes (but never to handles). * - Don't do this when canceling, or else these changes won't go away. */ - if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0) { - const short autosnap = getAnimEdit_SnapMode(t); - switch (autosnap) { - case SACTSNAP_FRAME: /* snap to nearest frame */ - td2d->loc[0] = floor((double)td2d->loc[0] + 0.5); - break; - - case SACTSNAP_SECOND: /* snap to nearest second */ - td2d->loc[0] = floor(((double)td2d->loc[0] / secf) + 0.5) * secf; - break; - - case SACTSNAP_MARKER: /* snap to nearest marker */ - td2d->loc[0] = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, - td2d->loc[0]); - break; - } + if ((autosnap != SACTSNAP_OFF) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) { + transform_snap_anim_flush_data(t, td, autosnap, td->loc); } /* we need to unapply the nla-mapping from the time in some situations */ @@ -707,32 +694,6 @@ static void flushTransGraphData(TransInfo *t) td2d->loc2d[0] = td2d->loc[0]; } - /** Time-stepping auto-snapping modes don't get applied for Graph Editor transforms, - * as these use the generic transform modes which don't account for this sort of thing. - * These ones aren't affected by NLA mapping, so we do this after the conversion... - * - * \note We also have to apply to td->loc, - * as that's what the handle-adjustment step below looks to, - * otherwise we get "swimming handles". - * - * \note We don't do this when canceling transforms, or else these changes don't go away. - */ - if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0) { - const short autosnap = getAnimEdit_SnapMode(t); - switch (autosnap) { - case SACTSNAP_STEP: /* frame step */ - td2d->loc2d[0] = floor((double)td2d->loc[0] + 0.5); - td->loc[0] = floor((double)td->loc[0] + 0.5); - break; - - case SACTSNAP_TSTEP: /* second step */ - /* XXX: the handle behavior in this case is still not quite right... */ - td2d->loc[0] = floor(((double)td2d->loc[0] / secf) + 0.5) * secf; - td->loc[0] = floor(((double)td->loc[0] / secf) + 0.5) * secf; - break; - } - } - /* if int-values only, truncate to integers */ if (td->flag & TD_INTVALUES) { td2d->loc2d[1] = floorf(td2d->loc[1] * inv_unit_scale - tdg->offset + 0.5f); @@ -741,15 +702,7 @@ static void flushTransGraphData(TransInfo *t) td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale - tdg->offset; } - if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) { - td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0]; - td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale; - } - - if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) { - td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0]; - td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale; - } + transform_convert_flush_handle2D(td, td2d, inv_unit_scale); } } diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index 4d98b7a489f..7e5b80c2453 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -41,6 +41,8 @@ #include "RNA_access.h" #include "transform.h" +#include "transform_snap.h" + #include "transform_convert.h" #include "transform_snap.h" @@ -292,21 +294,30 @@ void createTransNlaData(bContext *C, TransInfo *t) void recalcData_nla(TransInfo *t) { SpaceNla *snla = (SpaceNla *)t->area->spacedata.first; - Scene *scene = t->scene; - double secf = FPS; - int i; TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); - TransDataNla *tdn = tc->custom.type.data; + + /* handle auto-snapping + * NOTE: only do this when transform is still running, or we can't restore + */ + if (t->state != TRANS_CANCEL) { + const short autosnap = getAnimEdit_SnapMode(t); + if (autosnap != SACTSNAP_OFF) { + TransData *td = tc->data; + for (int i = 0; i < tc->data_len; i++, td++) { + transform_snap_anim_flush_data(t, td, autosnap, td->loc); + } + } + } /* For each strip we've got, perform some additional validation of the values * that got set before using RNA to set the value (which does some special * operations when setting these values to make sure that everything works ok). */ - for (i = 0; i < tc->data_len; i++, tdn++) { + TransDataNla *tdn = tc->custom.type.data; + for (int i = 0; i < tc->data_len; i++, tdn++) { NlaStrip *strip = tdn->strip; PointerRNA strip_ptr; - short iter; int delta_y1, delta_y2; /* if this tdn has no handles, that means it is just a dummy that should be skipped */ @@ -370,8 +381,7 @@ void recalcData_nla(TransInfo *t) next = next->next; } - for (iter = 0; iter < 5; iter++) { - + for (short iter = 0; iter < 5; iter++) { const bool pExceeded = (prev != NULL) && (tdn->h1[0] < prev->end); const bool nExceeded = (next != NULL) && (tdn->h2[0] > next->start); @@ -410,51 +420,6 @@ void recalcData_nla(TransInfo *t) } } - /* handle auto-snapping - * NOTE: only do this when transform is still running, or we can't restore - */ - if (t->state != TRANS_CANCEL) { - const short autosnap = getAnimEdit_SnapMode(t); - switch (autosnap) { - case SACTSNAP_FRAME: /* snap to nearest frame */ - case SACTSNAP_STEP: /* frame step - this is basically the same, - * since we don't have any remapping going on */ - { - tdn->h1[0] = floorf(tdn->h1[0] + 0.5f); - tdn->h2[0] = floorf(tdn->h2[0] + 0.5f); - break; - } - - case SACTSNAP_SECOND: /* snap to nearest second */ - case SACTSNAP_TSTEP: /* second step - this is basically the same, - * since we don't have any remapping going on */ - { - /* This case behaves differently from the rest, since lengths of strips - * may not be multiples of a second. If we just naively resize adjust - * the handles, things may not work correctly. Instead, we only snap - * the first handle, and move the other to fit. - * - * FIXME: we do run into problems here when user attempts to negatively - * scale the strip, as it then just compresses down and refuses - * to expand out the other end. - */ - float h1_new = (float)(floor(((double)tdn->h1[0] / secf) + 0.5) * secf); - float delta = h1_new - tdn->h1[0]; - - tdn->h1[0] = h1_new; - tdn->h2[0] += delta; - break; - } - - case SACTSNAP_MARKER: /* snap to nearest marker */ - { - tdn->h1[0] = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, tdn->h1[0]); - tdn->h2[0] = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, tdn->h2[0]); - break; - } - } - } - /* Use RNA to write the values to ensure that constraints on these are obeyed * (e.g. for transition strips, the values are taken from the neighbors) * diff --git a/source/blender/editors/transform/transform_mode_timescale.c b/source/blender/editors/transform/transform_mode_timescale.c index 4123663e849..50fd714727b 100644 --- a/source/blender/editors/transform/transform_mode_timescale.c +++ b/source/blender/editors/transform/transform_mode_timescale.c @@ -65,7 +65,6 @@ static void headerTimeScale(TransInfo *t, char str[UI_MAX_DRAW_STR]) static void applyTimeScaleValue(TransInfo *t, float value) { Scene *scene = t->scene; - const short autosnap = getAnimEdit_SnapMode(t); FOREACH_TRANS_DATA_CONTAINER (t, tc) { TransData *td = tc->data; @@ -89,9 +88,6 @@ static void applyTimeScaleValue(TransInfo *t, float value) /* now, calculate the new value */ *(td->val) = ((td->ival - startx) * fac) + startx; - - /* apply nearest snapping */ - doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap); } } } diff --git a/source/blender/editors/transform/transform_mode_timetranslate.c b/source/blender/editors/transform/transform_mode_timetranslate.c index 01b9a08cf27..294040946bd 100644 --- a/source/blender/editors/transform/transform_mode_timetranslate.c +++ b/source/blender/editors/transform/transform_mode_timetranslate.c @@ -59,10 +59,18 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR]) } else { const short autosnap = getAnimEdit_SnapMode(t); - float val = t->values_final[0]; + float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->data->ival; + float val = ival + t->values_final[0]; - float snap_val; - snapFrameTransform(t, autosnap, false, val, &snap_val); + float snap_val = val; + snapFrameTransform(t, autosnap, ival, val, &snap_val); + + if (ELEM(autosnap, SACTSNAP_SECOND, SACTSNAP_TSTEP)) { + /* Convert to seconds. */ + const Scene *scene = t->scene; + const double secf = FPS; + snap_val /= secf; + } if (autosnap == SACTSNAP_FRAME) { BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f (%.4f)", snap_val, val); @@ -88,24 +96,11 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR]) static void applyTimeTranslateValue(TransInfo *t, const float deltax) { - const short autosnap = getAnimEdit_SnapMode(t); - FOREACH_TRANS_DATA_CONTAINER (t, tc) { + /* It doesn't matter whether we apply to t->data. */ TransData *td = tc->data; - TransData2D *td2d = tc->data_2d; - /* It doesn't matter whether we apply to t->data or - * t->data2d, but t->data2d is more convenient. */ - for (int i = 0; i < tc->data_len; i++, td++, td2d++) { - /* It is assumed that td->extra is a pointer to the AnimData, - * whose active action is where this keyframe comes from. - * (this is only valid when not in NLA) - * (also: masks and gpencil don't have animadata) - */ - AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; - - /* apply nearest snapping */ - *(td->val) = td->ival + deltax * td->factor; - doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap); + for (int i = 0; i < tc->data_len; i++, td++) { + *(td->val) = td->loc[0] = td->ival + deltax * td->factor; } } } diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h index 20cc590d7cc..ed7f93304bc 100644 --- a/source/blender/editors/transform/transform_snap.h +++ b/source/blender/editors/transform/transform_snap.h @@ -83,11 +83,12 @@ void transform_snap_sequencer_apply_translate(TransInfo *t, float *vec); /* transform_snap_animation.c */ short getAnimEdit_SnapMode(TransInfo *t); -void snapFrameTransform(struct TransInfo *t, +void snapFrameTransform(TransInfo *t, const eAnimEdit_AutoSnap autosnap, - const bool is_frame_value, - const float delta, - /* return args */ - float *r_val); -void doAnimEdit_SnapFrame( - TransInfo *t, TransData *td, TransData2D *td2d, struct AnimData *adt, short autosnap); + const float val_initial, + const float val_final, + float *r_val_final); +void transform_snap_anim_flush_data(TransInfo *t, + TransData *td, + const eAnimEdit_AutoSnap autosnap, + float *r_val_final); diff --git a/source/blender/editors/transform/transform_snap_animation.c b/source/blender/editors/transform/transform_snap_animation.c index ac7c47e408f..be7fc65a6c2 100644 --- a/source/blender/editors/transform/transform_snap_animation.c +++ b/source/blender/editors/transform/transform_snap_animation.c @@ -90,67 +90,70 @@ short getAnimEdit_SnapMode(TransInfo *t) void snapFrameTransform(TransInfo *t, const eAnimEdit_AutoSnap autosnap, - const bool is_frame_value, - const float delta, - /* return args */ - float *r_val) + const float val_initial, + const float val_final, + float *r_val_final) { - double val = delta; + float deltax = val_final - val_initial; switch (autosnap) { - case SACTSNAP_STEP: case SACTSNAP_FRAME: - val = floor(val + 0.5); + *r_val_final = floorf(val_final + 0.5f); break; case SACTSNAP_MARKER: - /* snap to nearest marker */ + /* Snap to nearest marker. */ /* TODO: need some more careful checks for where data comes from. */ - val = ED_markers_find_nearest_marker_time(&t->scene->markers, (float)val); + *r_val_final = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, val_final); break; case SACTSNAP_SECOND: case SACTSNAP_TSTEP: { - /* second step */ const Scene *scene = t->scene; const double secf = FPS; - val = floor((val / secf) + 0.5); - if (is_frame_value) { - val *= secf; + if (autosnap == SACTSNAP_SECOND) { + *r_val_final = floorf((val_final / secf) + 0.5) * secf; + } + else { + deltax = (float)(floor((deltax / secf) + 0.5) * secf); + *r_val_final = val_initial + deltax; } break; } - case SACTSNAP_OFF: { + case SACTSNAP_STEP: + deltax = floorf(deltax + 0.5f); + *r_val_final = val_initial + deltax; + break; + case SACTSNAP_OFF: break; - } } - *r_val = (float)val; } /* This function is used by Animation Editor specific transform functions to do * the Snap Keyframe to Nearest Frame/Marker */ -void doAnimEdit_SnapFrame( - TransInfo *t, TransData *td, TransData2D *td2d, AnimData *adt, short autosnap) +void transform_snap_anim_flush_data(TransInfo *t, + TransData *td, + const eAnimEdit_AutoSnap autosnap, + float *r_val_final) { - if (autosnap != SACTSNAP_OFF) { - float val; + BLI_assert(autosnap != SACTSNAP_OFF); - /* convert frame to nla-action time (if needed) */ - if (adt && (t->spacetype != SPACE_SEQ)) { - val = BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_MAP); - } - else { - val = *(td->val); - } + float val = td->loc[0]; + float ival = td->iloc[0]; + AnimData *adt = (!ELEM(t->spacetype, SPACE_NLA, SPACE_SEQ)) ? td->extra : NULL; - snapFrameTransform(t, autosnap, true, val, &val); + /* Convert frame to nla-action time (if needed) */ + if (adt) { + val = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_MAP); + ival = BKE_nla_tweakedit_remap(adt, ival, NLATIME_CONVERT_MAP); + } - /* convert frame out of nla-action time */ - if (adt && (t->spacetype != SPACE_SEQ)) { - *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); - } - else { - *(td->val) = val; - } + snapFrameTransform(t, autosnap, ival, val, &val); + + /* Convert frame out of nla-action time. */ + if (adt) { + val = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); } + + *r_val_final = val; } /** \} */ -- cgit v1.2.3 From 7192e57d63a53a96461ba3ea761240f25a4294b7 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 19 Aug 2021 10:31:13 -0300 Subject: Fix the value in the graphical editor header when transforming The header did not display the actual value when transforming with snapping --- .../editors/transform/transform_mode_translate.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 75744f26c15..e44e346d3e4 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -221,22 +221,30 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ } else { float dvec[3]; + copy_v3_v3(dvec, vec); + if (t->spacetype == SPACE_GRAPH) { + /* WORKAROUND: + * Special case where snapping is done in #recalData. + * Update the header based on the first element. */ + const short autosnap = getAnimEdit_SnapMode(t); + float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->data->ival; + float val = ival + dvec[0]; + snapFrameTransform(t, autosnap, ival, val, &dvec[0]); + } + if (t->con.mode & CON_APPLY) { int i = 0; zero_v3(dvec); if (t->con.mode & CON_AXIS0) { - dvec[i++] = vec[0]; + dvec[i++] = dvec[0]; } if (t->con.mode & CON_AXIS1) { - dvec[i++] = vec[1]; + dvec[i++] = dvec[1]; } if (t->con.mode & CON_AXIS2) { - dvec[i++] = vec[2]; + dvec[i++] = dvec[2]; } } - else { - copy_v3_v3(dvec, vec); - } if (t->flag & T_2D_EDIT) { applyAspectRatio(t, dvec); -- cgit v1.2.3 From 71655ff8dfae7eb63a237f03bea414ff4ebc5714 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 19 Aug 2021 16:59:01 +0200 Subject: GPencil: Match row color for Summary in Grease Pencil animation editor The background of the summary row was different in Grease Pencil mode. Reviewed by: Pablo Vazquez, Matias Mendiola --- source/blender/editors/space_action/action_draw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index 903754f4fb1..0a143c35a2a 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -144,12 +144,14 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region uchar col1[4], col2[4]; uchar col1a[4], col2a[4]; uchar col1b[4], col2b[4]; + uchar col_summary[4]; const bool show_group_colors = U.animation_flag & USER_ANIM_SHOW_CHANNEL_GROUP_COLORS; /* get theme colors */ UI_GetThemeColor4ubv(TH_SHADE2, col2); UI_GetThemeColor4ubv(TH_HILITE, col1); + UI_GetThemeColor4ubv(TH_ANIM_ACTIVE, col_summary); UI_GetThemeColor4ubv(TH_GROUP, col2a); UI_GetThemeColor4ubv(TH_GROUP_ACTIVE, col1a); @@ -244,7 +246,11 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region else if (ac->datatype == ANIMCONT_GPENCIL) { uchar *color; uchar gpl_col[4]; - if ((show_group_colors) && (ale->type == ANIMTYPE_GPLAYER)) { + if (ale->type == ANIMTYPE_SUMMARY) { + color = col_summary; + color[3] = col1[3]; + } + else if ((show_group_colors) && (ale->type == ANIMTYPE_GPLAYER)) { bGPDlayer *gpl = (bGPDlayer *)ale->data; rgb_float_to_uchar(gpl_col, gpl->color); gpl_col[3] = col1[3]; -- cgit v1.2.3 From 479cc9a83eeb67cf0076384b378b50ec86cd8d5c Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 19 Aug 2021 17:02:42 +0200 Subject: UI: Match row color for Summary in Mask animation editor The back color of the row was missing. --- source/blender/editors/space_action/action_draw.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index 0a143c35a2a..a15f3507d7e 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -271,7 +271,14 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region else if (ac->datatype == ANIMCONT_MASK) { /* TODO: this is a copy of gpencil. */ /* frames less than one get less saturated background */ - uchar *color = sel ? col1 : col2; + uchar *color; + if (ale->type == ANIMTYPE_SUMMARY) { + color = col_summary; + color[3] = col1[3]; + } + else { + color = sel ? col1 : col2; + } immUniformColor4ubv(color); immRectf(pos, 0.0f, ymin, v2d->cur.xmin, ymax); -- cgit v1.2.3 From 214e4aac976962b7da5ebcbbab119acc60b1a654 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 19 Aug 2021 17:37:49 +0200 Subject: Fix Python error in ./benchmark init after recent changes --- tests/performance/api/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/api/environment.py b/tests/performance/api/environment.py index 70fc15f251c..76c731b6118 100644 --- a/tests/performance/api/environment.py +++ b/tests/performance/api/environment.py @@ -47,7 +47,7 @@ class TestEnvironment: print(f'Init {self.base_dir}') self.base_dir.mkdir(parents=True, exist_ok=True) - if len(self.get_configs_names()) == 0: + if len(self.get_config_names()) == 0: config_dir = self.base_dir / 'default' print(f'Creating default configuration in {config_dir}') TestConfig.write_default_config(self, config_dir) -- cgit v1.2.3 From 0896457c59d722e809188517c042b57d0ea399d3 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 19 Aug 2021 17:04:19 +0200 Subject: Partially fix T90593: Image ID wrongly seen as changed on undos. Several pure runtime data in this ID type were not properly cleared by write/read processes. Note that the initial undo step (the one leading back to initial read file state) is still forcing re-load of image, for some reasons. Common investigation together with Jeroen Bakker (@jbakker), thanks. See also D12242. --- source/blender/blenkernel/intern/image.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index de9f2a5a656..f37073ff135 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -230,6 +230,19 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres Image *ima = (Image *)id; const bool is_undo = BLO_write_is_undo(writer); + /* Clear all data that isn't read to reduce false detection of changed image during memfile undo. + */ + ima->lastused = 0; + ima->cache = NULL; + ima->gpuflag = 0; + BLI_listbase_clear(&ima->anims); + BLI_listbase_clear(&ima->gpu_refresh_areas); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 2; j++) { + ima->gputexture[i][j] = NULL; + } + } + ImagePackedFile *imapf; BLI_assert(ima->packedfile == NULL); @@ -299,6 +312,7 @@ static void image_blend_read_data(BlendDataReader *reader, ID *id) LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { tile->ok = IMA_OK; } + ima->lastused = 0; ima->gpuflag = 0; BLI_listbase_clear(&ima->gpu_refresh_areas); } -- cgit v1.2.3 From 3febcb98ed4cbf65d7e6386fd3bc5ece2ad903bc Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 19 Aug 2021 17:35:04 +0200 Subject: Image blendwrite: Fix handling of packedfiles. Packedfiles need some special attention when writing Image to disk. Source: D12242, Jeroen Bakker (@jbakker), thanks. --- source/blender/blenkernel/intern/image.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index f37073ff135..5701449a9e5 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -246,15 +246,17 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres ImagePackedFile *imapf; BLI_assert(ima->packedfile == NULL); - /* Do not store packed files in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(ima) && !is_undo) { - BLI_listbase_clear(&ima->packedfiles); - } - else { - /* Some trickery to keep forward compatibility of packed images. */ - if (ima->packedfiles.first != NULL) { - imapf = ima->packedfiles.first; - ima->packedfile = imapf->packedfile; + if (!is_undo) { + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(ima)) { + BLI_listbase_clear(&ima->packedfiles); + } + else { + /* Some trickery to keep forward compatibility of packed images. */ + if (ima->packedfiles.first != NULL) { + imapf = ima->packedfiles.first; + ima->packedfile = imapf->packedfile; + } } } -- cgit v1.2.3 From 871f7f4ad8326a944bc96251179c8885986d7ef7 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 19 Aug 2021 19:23:01 +0200 Subject: GPencil: Cleanup old printf debug lines These lines were very old debug code and now it's not required because the code is very tested. --- source/blender/editors/gpencil/annotate_paint.c | 85 ---------------------- source/blender/editors/gpencil/gpencil_convert.c | 25 ------- source/blender/editors/gpencil/gpencil_paint.c | 54 -------------- source/blender/editors/gpencil/gpencil_primitive.c | 5 -- source/blender/editors/gpencil/gpencil_undo.c | 2 - 5 files changed, 171 deletions(-) diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 31ef094afa6..bf47704746b 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -836,10 +836,6 @@ static void annotation_stroke_newfrombuffer(tGPsdata *p) /* exit with error if no valid points from this stroke */ if (totelem == 0) { - if (G.debug & G_DEBUG) { - printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", - gpd->runtime.sbuffer_used); - } return; } @@ -1263,9 +1259,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) /* make sure the active view (at the starting time) is a 3d-view */ if (curarea == NULL) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: No active view for painting\n"); - } return 0; } @@ -1294,11 +1287,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) if (region->regiondata == NULL) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf( - "Error: 3D-View active region doesn't have any region data, so cannot be " - "drawable\n"); - } return 0; } break; @@ -1325,9 +1313,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) /* check that gpencil data is allowed to be drawn */ if (sseq->mainb == SEQ_DRAW_SEQUENCE) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil\n"); - } return 0; } break; @@ -1387,9 +1372,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) /* unsupported views */ default: { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Annotations are not supported in this editor\n"); - } return 0; } } @@ -1398,9 +1380,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) gpd_ptr = ED_annotation_data_get_pointers(C, &p->ownerPtr); if ((gpd_ptr == NULL) || !ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Current context doesn't allow for any Annotation data\n"); - } return 0; } @@ -1507,7 +1486,6 @@ static void annotation_session_cleanup(tGPsdata *p) /* free stroke buffer */ if (gpd->runtime.sbuffer) { - // printf("\t\tGP - free sbuffer\n"); MEM_freeN(gpd->runtime.sbuffer); gpd->runtime.sbuffer = NULL; } @@ -1545,9 +1523,6 @@ static void annotation_paint_initstroke(tGPsdata *p, } if (p->gpl->flag & GP_LAYER_LOCKED) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Cannot paint on locked layer\n"); - } return; } @@ -1573,7 +1548,6 @@ static void annotation_paint_initstroke(tGPsdata *p, if (has_layer_to_erase == false) { p->status = GP_STATUS_CAPTURE; - // if (G.debug & G_DEBUG) printf("Error: Eraser will not be affecting anything (gpencil_paint_init)\n"); return; } @@ -1593,9 +1567,6 @@ static void annotation_paint_initstroke(tGPsdata *p, if (p->gpf == NULL) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: No frame created (gpencil_paint_init)\n"); - } return; } @@ -2063,9 +2034,6 @@ static void annotation_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgr BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke"); p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Grease-Pencil Paint - Add Point Invalid\n"); - } return; } @@ -2221,29 +2189,22 @@ static int annotation_draw_exec(bContext *C, wmOperator *op) tGPsdata *p = NULL; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - // printf("GPencil - Starting Re-Drawing\n"); - /* try to initialize context data needed while drawing */ if (!annotation_draw_init(C, op, NULL)) { if (op->customdata) { MEM_freeN(op->customdata); } - // printf("\tGP - no valid data\n"); return OPERATOR_CANCELLED; } p = op->customdata; - // printf("\tGP - Start redrawing stroke\n"); - /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement), * setting the relevant values in context at each step, then applying */ RNA_BEGIN (op->ptr, itemptr, "stroke") { float mousef[2]; - // printf("\t\tGP - stroke elem\n"); - /* get relevant data for this point from stroke */ RNA_float_get_array(&itemptr, "mouse", mousef); p->mval[0] = (int)mousef[0]; @@ -2277,8 +2238,6 @@ static int annotation_draw_exec(bContext *C, wmOperator *op) } RNA_END; - // printf("\tGP - done\n"); - /* cleanup */ annotation_draw_exit(C, op); @@ -2301,18 +2260,11 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); } - if (G.debug & G_DEBUG) { - printf("GPencil - Starting Drawing\n"); - } - /* try to initialize context data needed while drawing */ if (!annotation_draw_init(C, op, event)) { if (op->customdata) { MEM_freeN(op->customdata); } - if (G.debug & G_DEBUG) { - printf("\tGP - no valid data\n"); - } return OPERATOR_CANCELLED; } @@ -2361,7 +2313,6 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev /* only start drawing immediately if we're allowed to do so... */ if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { /* hotkey invoked - start drawing */ - // printf("\tGP - set first spot\n"); p->status = GP_STATUS_PAINTING; /* handle the initial drawing - i.e. for just doing a simple dot */ @@ -2370,7 +2321,6 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev } else { /* toolbar invoked - don't start drawing yet... */ - // printf("\tGP - hotkey invoked... waiting for click-drag\n"); op->flag |= OP_IS_MODAL_CURSOR_REGION; } @@ -2399,8 +2349,6 @@ static tGPsdata *annotation_stroke_begin(bContext *C, wmOperator *op) p->status = GP_STATUS_ERROR; } - // printf("\t\tGP - start stroke\n"); - /* we may need to set up paint env again if we're resuming */ /* XXX: watch it with the paintmode! in future, * it'd be nice to allow changing paint-mode when in sketching-sessions */ @@ -2537,8 +2485,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve } } - // printf("\tGP - handle modal event...\n"); - /* Exit painting mode (and/or end current stroke) * * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) @@ -2547,7 +2493,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve if (event->val == KM_PRESS && ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY, EVT_EKEY)) { /* exit() ends the current stroke before cleaning up */ - // printf("\t\tGP - end of paint op + end of stroke\n"); p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2571,7 +2516,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve if (sketch) { /* end stroke only, and then wait to resume painting soon */ - // printf("\t\tGP - end stroke only\n"); annotation_stroke_end(op); /* If eraser mode is on, turn it off after the stroke finishes @@ -2602,7 +2546,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } else { - // printf("\t\tGP - end of stroke + op\n"); p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2619,18 +2562,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve ARegion *current_region = BKE_area_find_region_xy( p->area, RGN_TYPE_ANY, event->x, event->y); - if (G.debug & G_DEBUG) { - printf("found alternative region %p (old was %p) - at %d %d (area: %d %d -> %d %d)\n", - current_region, - p->region, - event->x, - event->y, - p->area->totrct.xmin, - p->area->totrct.ymin, - p->area->totrct.xmax, - p->area->totrct.ymax); - } - if (current_region) { /* Assume that since we found the cursor in here, it is in bounds * and that this should be the region that we begin drawing in @@ -2642,10 +2573,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve /* Out of bounds, or invalid in some other way */ p->status = GP_STATUS_ERROR; estate = OPERATOR_CANCELLED; - - if (G.debug & G_DEBUG) { - printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__); - } } } else if (p->region) { @@ -2657,10 +2584,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve /* No region */ p->status = GP_STATUS_ERROR; estate = OPERATOR_CANCELLED; - - if (G.debug & G_DEBUG) { - printf("%s: No active region found in GP Paint session data\n", __func__); - } } if (in_bounds) { @@ -2719,7 +2642,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve } else { /* event handled, so just tag as running modal */ - // printf("\t\t\t\tGP - add point handled!\n"); estate = OPERATOR_RUNNING_MODAL; } } @@ -2729,7 +2651,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve /* just resize the brush (local version) * TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys */ - // printf("\t\tGP - resize eraser\n"); switch (event->type) { case WHEELDOWNMOUSE: /* larger */ case EVT_PADPLUSKEY: @@ -2787,12 +2708,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH: /* event doesn't need to be handled */ -#if 0 - printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n", - event->type, - event->type == MIDDLEMOUSE, - event->type == MOUSEMOVE); -#endif break; } diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index a0a58abc02f..406a7ac77fc 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -389,9 +389,6 @@ static void gpencil_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd, *r_tot_gaps_time = (float)(*nbr_gaps) * gtd->gap_duration; gtd->tot_time += *r_tot_gaps_time; - if (G.debug & G_DEBUG) { - printf("%f, %f, %f, %d\n", gtd->tot_time, delta_time, *r_tot_gaps_time, *nbr_gaps); - } if (gtd->gap_randomness > 0.0f) { BLI_rng_srandom(rng, gtd->seed); } @@ -464,9 +461,6 @@ static void gpencil_stroke_path_animation_add_keyframes(ReportList *reports, INSERTKEY_FAST); last_valid_time = cfra; } - else if (G.debug & G_DEBUG) { - printf("\t Skipping start point %d, too close from end point %d\n", i, end_stroke_idx); - } } else if (i == end_stroke_idx) { /* Always try to insert end point of a curve (should be safe enough, anyway...) */ @@ -546,13 +540,6 @@ static void gpencil_stroke_path_animation(bContext *C, act = ED_id_action_ensure(bmain, (ID *)cu); fcu = ED_action_fcurve_ensure(bmain, act, NULL, &ptr, "eval_time", 0); - if (G.debug & G_DEBUG) { - printf("%s: tot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); - for (int i = 0; i < gtd->num_points; i++) { - printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); - } - } - if (gtd->mode == GP_STROKECONVERT_TIMING_LINEAR) { float cfra; @@ -610,10 +597,6 @@ static void gpencil_stroke_path_animation(bContext *C, time_range = (float)(gtd->end_frame - gtd->start_frame); } - if (G.debug & G_DEBUG) { - printf("GP Stroke Path Conversion: Starting keying!\n"); - } - gpencil_stroke_path_animation_add_keyframes( reports, ptr, prop, depsgraph, fcu, cu, gtd, rng, time_range, nbr_gaps, tot_gaps_time); @@ -623,14 +606,6 @@ static void gpencil_stroke_path_animation(bContext *C, /* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */ calchandles_fcurve(fcu); - if (G.debug & G_DEBUG) { - printf("%s: \ntot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); - for (int i = 0; i < gtd->num_points; i++) { - printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); - } - printf("\n\n"); - } - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); /* send updates */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 9e96c40b2db..d2dbf6ab2a6 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -976,10 +976,6 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) /* exit with error if no valid points from this stroke */ if (totelem == 0) { - if (G.debug & G_DEBUG) { - printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", - gpd->runtime.sbuffer_used); - } return; } @@ -1949,9 +1945,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) /* make sure the active view (at the starting time) is a 3d-view */ if (curarea == NULL) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: No active view for painting\n"); - } return 0; } @@ -1980,11 +1973,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) if (region->regiondata == NULL) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf( - "Error: 3D-View active region doesn't have any region data, so cannot be " - "drawable\n"); - } return 0; } @@ -2010,9 +1998,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) /* unsupported views */ default: { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Active view not appropriate for Grease Pencil drawing\n"); - } return 0; } } @@ -2021,9 +2006,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); if ((gpd_ptr == NULL) || ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Current context doesn't allow for any Grease Pencil data\n"); - } return 0; } @@ -2147,9 +2129,6 @@ static void gpencil_paint_initstroke(tGPsdata *p, if ((paintmode != GP_PAINTMODE_ERASER) && (p->gpl->flag & GP_LAYER_LOCKED)) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Cannot paint on locked layer\n"); - } return; } @@ -2228,9 +2207,6 @@ static void gpencil_paint_initstroke(tGPsdata *p, if (p->gpf == NULL) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: No frame created (gpencil_paint_init)\n"); - } if (!IS_AUTOKEY_ON(scene)) { BKE_report(p->reports, RPT_INFO, "No available frame for creating stroke"); } @@ -2824,9 +2800,6 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke"); p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Grease-Pencil Paint - Add Point Invalid\n"); - } return; } @@ -3198,10 +3171,6 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event Object *ob = CTX_data_active_object(C); bGPdata *gpd = (bGPdata *)ob->data; - if (G.debug & G_DEBUG) { - printf("GPencil - Starting Drawing\n"); - } - /* support for tablets eraser pen */ if (gpencil_is_tablet_eraser_active(event)) { RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); @@ -3239,9 +3208,6 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event if (op->customdata) { MEM_freeN(op->customdata); } - if (G.debug & G_DEBUG) { - printf("\tGP - no valid data\n"); - } return OPERATOR_CANCELLED; } @@ -3730,18 +3696,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) ARegion *current_region = BKE_area_find_region_xy( p->area, RGN_TYPE_ANY, event->x, event->y); - if (G.debug & G_DEBUG) { - printf("found alternative region %p (old was %p) - at %d %d (area: %d %d -> %d %d)\n", - current_region, - p->region, - event->x, - event->y, - p->area->totrct.xmin, - p->area->totrct.ymin, - p->area->totrct.xmax, - p->area->totrct.ymax); - } - if (current_region) { /* Assume that since we found the cursor in here, it is in bounds * and that this should be the region that we begin drawing in @@ -3753,10 +3707,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Out of bounds, or invalid in some other way */ p->status = GP_STATUS_ERROR; estate = OPERATOR_CANCELLED; - - if (G.debug & G_DEBUG) { - printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__); - } } } else if (p->region) { @@ -3768,10 +3718,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* No region */ p->status = GP_STATUS_ERROR; estate = OPERATOR_CANCELLED; - - if (G.debug & G_DEBUG) { - printf("%s: No active region found in GP Paint session data\n", __func__); - } } if (in_bounds) { diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 7e6ff53de14..894fb83d70e 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -1830,11 +1830,6 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_MOVE)) { tgpi->flag = IN_CURVE_EDIT; } - else { - if (G.debug & G_DEBUG) { - printf("GP Add Primitive Modal: LEFTMOUSE %d, Status = %d\n", event->val, tgpi->flag); - } - } break; } case EVT_SPACEKEY: /* confirm */ diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index ede1d3eefaa..96e85cc6135 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -133,8 +133,6 @@ void gpencil_undo_push(bGPdata *gpd) { bGPundonode *undo_node; - // printf("\t\tGP - undo push\n"); - if (cur_node) { /* Remove all undone nodes from stack. */ undo_node = cur_node->next; -- cgit v1.2.3 From dbc4f6fdc900493a9d03c88afe48beae6289fb30 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 19 Aug 2021 20:08:44 +0200 Subject: Fix error rendering Cycles shader nodes from before 2013 After the recent changes to use socket identifiers instead of names. --- intern/cycles/blender/blender_shader.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 24819bacbb5..de7b2761d00 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -1020,13 +1020,21 @@ static ShaderInput *node_find_input_by_name(ShaderNode *node, BL::NodeSocket &b_ input = node->input(name.c_str()); if (!input) { - /* Different internal numbering of two sockets with same name. */ + /* Different internal numbering of two sockets with same name. + * Note that the Blender convention for unique socket names changed + * from . to _ at some point, so we check both to handle old files. */ if (string_endswith(name, "_001")) { string_replace(name, "_001", "2"); } + else if (string_endswith(name, ".001")) { + string_replace(name, ".001", "2"); + } else if (string_endswith(name, "_002")) { string_replace(name, "_002", "3"); } + else if (string_endswith(name, ".002")) { + string_replace(name, ".002", "3"); + } else { name += "1"; } -- cgit v1.2.3 From 72f73c0b71c5378c0d33f0eb83222dc68d93a5ad Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Thu, 19 Aug 2021 20:24:38 +0200 Subject: UI: Use theme's alpha for Summary instead of a hardcoded value --- source/blender/editors/space_action/action_draw.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index a15f3507d7e..a3bdcd2adf5 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -248,7 +248,6 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region uchar gpl_col[4]; if (ale->type == ANIMTYPE_SUMMARY) { color = col_summary; - color[3] = col1[3]; } else if ((show_group_colors) && (ale->type == ANIMTYPE_GPLAYER)) { bGPDlayer *gpl = (bGPDlayer *)ale->data; @@ -274,7 +273,6 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region uchar *color; if (ale->type == ANIMTYPE_SUMMARY) { color = col_summary; - color[3] = col1[3]; } else { color = sel ? col1 : col2; -- cgit v1.2.3 From c0dd6f1164e0247c7e21d57457c88a053e38f7da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Thu, 19 Aug 2021 20:34:54 +0200 Subject: Fix T90776: Cycles normal map node produces artifacts This is caused by a typo in rBb8ecdbcd964a `sd->prim` is the primitive index, but was used to discriminate the primitive type (stored in `sd- >type`). --- intern/cycles/kernel/svm/svm_tex_coord.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index 09ea11ee3ed..fec6a2cc27f 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -267,7 +267,7 @@ ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *st if (space == NODE_NORMAL_MAP_TANGENT) { /* tangent space */ - if (sd->object == OBJECT_NONE || (sd->prim & PRIMITIVE_ALL_TRIANGLE) == 0) { + if (sd->object == OBJECT_NONE || (sd->type & PRIMITIVE_ALL_TRIANGLE) == 0) { /* Fallback to unperturbed normal. */ stack_store_float3(stack, normal_offset, sd->N); return; -- cgit v1.2.3 From 962153dbedb8d6355624516847926df221d9ce63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Thu, 19 Aug 2021 20:35:47 +0200 Subject: Cycles: missing case for ignoring subdivision vertex normals This was missing from rBb8ecdbcd964a. --- intern/cycles/render/geometry.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index a8e4db38180..7ec1d2d9abb 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -805,6 +805,11 @@ void GeometryManager::device_update_attributes(Device *device, Mesh *mesh = static_cast(geom); Attribute *subd_attr = mesh->subd_attributes.find(req); + /* Vertex normals are stored in DeviceScene.tri_vnormal. */ + if (subd_attr && subd_attr->std == ATTR_STD_VERTEX_NORMAL) { + continue; + } + update_attribute_element_size(mesh, subd_attr, ATTR_PRIM_SUBD, -- cgit v1.2.3 From 5b51df0f3301ac829e22e2efcc4c81437668bb58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 20 Aug 2021 02:30:11 +0200 Subject: Cleanup, format --- source/blender/blenkernel/BKE_modifier.h | 4 +--- source/blender/editors/interface/interface_templates.c | 3 ++- source/blender/modifiers/intern/MOD_volume_displace.cc | 4 +++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index c5f309570cd..8be563e4c96 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -427,9 +427,7 @@ void BKE_modifier_copydata(struct ModifierData *md, struct ModifierData *target) void BKE_modifier_copydata_ex(struct ModifierData *md, struct ModifierData *target, const int flag); -bool BKE_modifier_depends_ontime(struct Scene *scene, - struct ModifierData *md, - int dag_eval_mode); +bool BKE_modifier_depends_ontime(struct Scene *scene, struct ModifierData *md, int dag_eval_mode); bool BKE_modifier_supports_mapping(struct ModifierData *md); bool BKE_modifier_supports_cage(struct Scene *scene, struct ModifierData *md); bool BKE_modifier_couldbe_cage(struct Scene *scene, struct ModifierData *md); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index f0d50985237..351b73c320b 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -6469,7 +6469,8 @@ void uiTemplateCacheFile(uiLayout *layout, const struct RenderEngineType *engine_type = CTX_data_engine_type(C); Scene *scene = CTX_data_scene(C); - const bool engine_supports_procedural = RE_engine_supports_alembic_procedural(engine_type, scene); + const bool engine_supports_procedural = RE_engine_supports_alembic_procedural(engine_type, + scene); if (!engine_supports_procedural) { row = uiLayoutRow(layout, false); diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc index dfd3fdd80f8..fcf75040a9a 100644 --- a/source/blender/modifiers/intern/MOD_volume_displace.cc +++ b/source/blender/modifiers/intern/MOD_volume_displace.cc @@ -95,7 +95,9 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void walk(userData, ob, md, "texture"); } -static bool dependsOnTime(struct Scene *UNUSED(scene), ModifierData *md, const int UNUSED(dag_eval_mode)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { VolumeDisplaceModifierData *vdmd = reinterpret_cast(md); if (vdmd->texture) { -- cgit v1.2.3 From f8637cd8af451661a306edd5682cc17029e7e7e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 20 Aug 2021 02:30:50 +0200 Subject: Alembic Procedural: only subdivide if subsurf modifier is present As subdivision objects are first class citizens in Alembic, to differentiate them with non-subdivided polygon meshes, the Alembic Procedural automatically sets up subdivision properties on the generated Cycles Mesh. However, for real-time playback subdivision is far too slow, so this modifies the detection of a MeshSeqCache modifier used to activate the procedural to allow for a Subsurf modifier right after the cache one. If present, the procedural will tag the object for subdivision, if absent, the object will be treated as a regular mesh. This is a temporary measure for until subdivision surface settings are part of the Mesh datablock (see T68891). Reviewed By: brecht Differential Revision: https://developer.blender.org/D11162 --- intern/cycles/blender/blender_mesh.cpp | 4 +-- intern/cycles/blender/blender_object.cpp | 11 ++++-- intern/cycles/blender/blender_sync.h | 4 ++- intern/cycles/blender/blender_util.h | 12 ++++++- intern/cycles/render/alembic.cpp | 57 +++++++++++++++++++++++++++++--- intern/cycles/render/alembic.h | 9 +++++ 6 files changed, 85 insertions(+), 12 deletions(-) diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index d1042277183..ebba6981502 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -1078,7 +1078,7 @@ static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *me return; } - BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob, true); + BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob, true, nullptr); if (!b_mesh_cache) { return; @@ -1241,7 +1241,7 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph, } /* Cached motion blur already exported. */ - BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob, true); + BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob, true, nullptr); if (mesh_cache) { return; } diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 657ecdeeeb7..2dbebac4cc3 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -485,7 +485,9 @@ bool BlenderSync::sync_object_attributes(BL::DepsgraphObjectInstance &b_instance /* Object Loop */ -void BlenderSync::sync_procedural(BL::Object &b_ob, BL::MeshSequenceCacheModifier &b_mesh_cache) +void BlenderSync::sync_procedural(BL::Object &b_ob, + BL::MeshSequenceCacheModifier &b_mesh_cache, + bool has_subdivision_modifier) { #ifdef WITH_ALEMBIC BL::CacheFile cache_file = b_mesh_cache.cache_file(); @@ -534,6 +536,8 @@ void BlenderSync::sync_procedural(BL::Object &b_ob, BL::MeshSequenceCacheModifie abc_object->set_subd_dicing_rate(subd_dicing_rate); abc_object->set_subd_max_level(max_subdivisions); + abc_object->set_ignore_subdivision(!has_subdivision_modifier); + if (abc_object->is_modified() || procedural->is_modified()) { procedural->tag_update(scene); } @@ -601,13 +605,14 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, if (b_instance.show_self()) { #ifdef WITH_ALEMBIC bool use_procedural = false; + bool has_subdivision_modifier = false; BL::MeshSequenceCacheModifier b_mesh_cache(PointerRNA_NULL); /* Experimental as Blender does not have good support for procedurals at the moment, also * only available in preview renders since currently do not have a good cache policy, the * data being loaded at once for all the frames. */ if (experimental && b_v3d) { - b_mesh_cache = object_mesh_cache_find(b_ob, false); + b_mesh_cache = object_mesh_cache_find(b_ob, false, &has_subdivision_modifier); use_procedural = b_mesh_cache && b_mesh_cache.cache_file().use_render_procedural(); } @@ -615,7 +620,7 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, /* Skip in the motion case, as generating motion blur data will be handled in the * procedural. */ if (!motion) { - sync_procedural(b_ob, b_mesh_cache); + sync_procedural(b_ob, b_mesh_cache, has_subdivision_modifier); } } else diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 0e605fcbf16..44322dda6b9 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -151,7 +151,9 @@ class BlenderSync { TaskPool *geom_task_pool); void sync_object_motion_init(BL::Object &b_parent, BL::Object &b_ob, Object *object); - void sync_procedural(BL::Object &b_ob, BL::MeshSequenceCacheModifier &b_mesh_cache); + void sync_procedural(BL::Object &b_ob, + BL::MeshSequenceCacheModifier &b_mesh_cache, + bool has_subdivision); bool sync_object_attributes(BL::DepsgraphObjectInstance &b_instance, Object *object); diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index e50b1a4760e..3cf75b338dc 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -573,7 +573,8 @@ static inline BL::FluidDomainSettings object_fluid_gas_domain_find(BL::Object &b } static inline BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob, - bool check_velocity) + bool check_velocity, + bool *has_subdivision_modifier) { for (int i = b_ob.modifiers.length() - 1; i >= 0; --i) { BL::Modifier b_mod = b_ob.modifiers[i]; @@ -595,6 +596,15 @@ static inline BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b continue; } + /* Only skip the subsurf modifier if we are not checking for the mesh sequence cache modifier + * for motion blur. */ + if (b_mod.type() == BL::Modifier::type_SUBSURF && !check_velocity) { + if (has_subdivision_modifier) { + *has_subdivision_modifier = true; + } + continue; + } + break; } diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp index c1817016955..81f47256739 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/render/alembic.cpp @@ -385,6 +385,8 @@ NODE_DEFINE(AlembicObject) SOCKET_STRING(path, "Alembic Path", ustring()); SOCKET_NODE_ARRAY(used_shaders, "Used Shaders", Shader::get_node_type()); + SOCKET_BOOLEAN(ignore_subdivision, "Ignore Subdivision", true); + SOCKET_INT(subd_max_level, "Max Subdivision Level", 1); SOCKET_FLOAT(subd_dicing_rate, "Subdivision Dicing Rate", 1.0f); @@ -470,6 +472,33 @@ void AlembicObject::load_data_in_cache(CachedData &cached_data, cached_data.clear(); + if (this->get_ignore_subdivision()) { + PolyMeshSchemaData data; + data.topology_variance = schema.getTopologyVariance(); + data.time_sampling = schema.getTimeSampling(); + data.positions = schema.getPositionsProperty(); + data.face_counts = schema.getFaceCountsProperty(); + data.face_indices = schema.getFaceIndicesProperty(); + data.num_samples = schema.getNumSamples(); + data.velocities = schema.getVelocitiesProperty(); + data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders()); + + read_geometry_data(proc, cached_data, data, progress); + + if (progress.get_cancel()) { + return; + } + + /* Use the schema as the base compound property to also be able to look for top level + * properties. */ + read_attributes( + proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress); + + cached_data.invalidate_last_loaded_time(true); + data_loaded = true; + return; + } + SubDSchemaData data; data.time_sampling = schema.getTimeSampling(); data.num_samples = schema.getNumSamples(); @@ -781,6 +810,19 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress) const chrono_t frame_time = (chrono_t)((frame - frame_offset) / frame_rate); + /* Clear the subdivision caches as the data is stored differently. */ + for (Node *node : objects) { + AlembicObject *object = static_cast(node); + + if (object->schema_type != AlembicObject::SUBD) { + continue; + } + + if (object->ignore_subdivision_is_modified()) { + object->clear_cache(); + } + } + build_caches(progress); foreach (Node *node, objects) { @@ -967,13 +1009,13 @@ void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame_time) { - CachedData &cached_data = abc_object->get_cached_data(); - - if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) { - /* need to reset the current data is something changed */ - cached_data.invalidate_last_loaded_time(); + if (abc_object->get_ignore_subdivision()) { + read_mesh(abc_object, frame_time); + return; } + CachedData &cached_data = abc_object->get_cached_data(); + /* Update sockets. */ Object *object = abc_object->get_object(); @@ -988,6 +1030,11 @@ void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame return; } + if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) { + /* need to reset the current data is something changed */ + cached_data.invalidate_last_loaded_time(); + } + Mesh *mesh = static_cast(object->get_geometry()); /* Make sure shader ids are also updated. */ diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h index 61c0e40fe4a..9c58af720f6 100644 --- a/intern/cycles/render/alembic.h +++ b/intern/cycles/render/alembic.h @@ -353,6 +353,10 @@ class AlembicObject : public Node { /* Shaders used for rendering. */ NODE_SOCKET_API_ARRAY(array, used_shaders) + /* Treat this subdivision object as a regular polygon mesh, so no subdivision will be performed. + */ + NODE_SOCKET_API(bool, ignore_subdivision) + /* Maximum number of subdivisions for ISubD objects. */ NODE_SOCKET_API(int, subd_max_level) @@ -416,6 +420,11 @@ class AlembicObject : public Node { return cached_data_.is_constant(); } + void clear_cache() + { + cached_data_.clear(); + } + Object *object = nullptr; bool data_loaded = false; -- cgit v1.2.3 From fca6b2780fa481a6e5673f9cafc94c8f995d2676 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Thu, 19 Aug 2021 19:27:49 -0700 Subject: Cleanup: clang-format --- source/blender/blenkernel/intern/image_save.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/image_save.c b/source/blender/blenkernel/intern/image_save.c index be86da05b57..f93ede517a9 100644 --- a/source/blender/blenkernel/intern/image_save.c +++ b/source/blender/blenkernel/intern/image_save.c @@ -409,7 +409,8 @@ bool BKE_image_save( BKE_reportf(reports, RPT_ERROR, "When saving a tiled image, the path '%s' must contain the UDIM tile number %d", - opts->filepath, first_tile->tile_number); + opts->filepath, + first_tile->tile_number); return false; } -- cgit v1.2.3 From 44b25b0ea5f415ea320cec1301dafa1523fb3ba4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Aug 2021 15:03:22 +1000 Subject: Cleanup: unused warnings --- source/blender/modifiers/intern/MOD_meshsequencecache.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index 2aa76c18c83..259c1cb2417 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -270,7 +270,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return result ? result : mesh; #else - UNUSED_VARS(ctx, md); + UNUSED_VARS(ctx, md, generate_bounding_box_mesh); return mesh; #endif } @@ -283,7 +283,7 @@ static bool dependsOnTime(Scene *scene, ModifierData *md, const int dag_eval_mod return (mcmd->cache_file != NULL) && !BKE_cache_file_uses_render_procedural(mcmd->cache_file, scene, dag_eval_mode); #else - UNUSED_VARS(md); + UNUSED_VARS(scene, md, dag_eval_mode); return false; #endif } -- cgit v1.2.3 From ce3a6d7989387153911296e44ad00da3195f7299 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Aug 2021 15:08:27 +1000 Subject: Cleanup: rename BKE_mesh_free -> BKE_mesh_free_data It wasn't obvious this didn't free the memory of the mesh it's self leading to memory leaks. --- source/blender/blenkernel/BKE_mesh.h | 2 +- source/blender/blenkernel/intern/cloth.c | 4 ++-- source/blender/blenkernel/intern/mesh.c | 4 ++-- source/blender/editors/mesh/editmesh_knife_project.c | 2 +- source/blender/editors/mesh/editmesh_undo.c | 2 +- source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 8000e57e08e..5dab847182f 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -95,7 +95,7 @@ void BKE_mesh_looptri_get_real_edges(const struct Mesh *mesh, const struct MLoopTri *looptri, int r_edges[3]); -void BKE_mesh_free(struct Mesh *me); +void BKE_mesh_free_data(struct Mesh *me); void BKE_mesh_clear_geometry(struct Mesh *me); struct Mesh *BKE_mesh_add(struct Main *bmain, const char *name); void BKE_mesh_copy_parameters_for_eval(struct Mesh *me_dst, const struct Mesh *me_src); diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index f5ff936e18b..9aa2d017c48 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -1574,7 +1574,7 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) BLI_edgeset_free(existing_vert_pairs); free_bvhtree_from_mesh(&treedata); if (tmp_mesh) { - BKE_mesh_free(tmp_mesh); + BKE_mesh_free_data(tmp_mesh); } return false; } @@ -1583,7 +1583,7 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) BLI_edgeset_free(existing_vert_pairs); free_bvhtree_from_mesh(&treedata); if (tmp_mesh) { - BKE_mesh_free(tmp_mesh); + BKE_mesh_free_data(tmp_mesh); } BLI_rng_free(rng); } diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 8257e54c618..c74c31e4c59 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -862,7 +862,7 @@ bool BKE_mesh_has_custom_loop_normals(Mesh *me) } /** Free (or release) any data used by this mesh (does not free the mesh itself). */ -void BKE_mesh_free(Mesh *me) +void BKE_mesh_free_data(Mesh *me) { mesh_free_data(&me->id); } @@ -1078,7 +1078,7 @@ void BKE_mesh_eval_delete(struct Mesh *mesh_eval) { /* Evaluated mesh may point to edit mesh, but never owns it. */ mesh_eval->edit_mesh = NULL; - BKE_mesh_free(mesh_eval); + BKE_mesh_free_data(mesh_eval); BKE_libblock_free_data(&mesh_eval->id, false); MEM_freeN(mesh_eval); } diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 16661897e87..2e63e397628 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -115,7 +115,7 @@ static LinkNode *knifeproject_poly_from_object(const bContext *C, BKE_nurbList_free(&nurbslist); if (me_eval_needs_free) { - BKE_mesh_free((struct Mesh *)me_eval); + BKE_mesh_free_data((struct Mesh *)me_eval); } } diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index fc9e1aa8b1a..acf9e6c2d55 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -759,7 +759,7 @@ static void undomesh_free_data(UndoMesh *um) MEM_freeN(me->key); } - BKE_mesh_free(me); + BKE_mesh_free_data(me); } static Object *editmesh_object_from_context(bContext *C) diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index f02b73e8430..564a3c526f4 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1691,7 +1691,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu } if (obi->free_use_mesh) { - BKE_mesh_free(obi->original_me); + BKE_mesh_free_data(obi->original_me); MEM_freeN(obi->original_me); } -- cgit v1.2.3 From a48df97ada85960de4fd0eeba61b32e260bf378c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Aug 2021 15:19:58 +1000 Subject: Fix T90791: Knife project leaks memory with curve/text cutter --- source/blender/editors/mesh/editmesh_knife_project.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 2e63e397628..669a09b3fd3 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -33,6 +33,7 @@ #include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_layer.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_object.h" @@ -115,7 +116,7 @@ static LinkNode *knifeproject_poly_from_object(const bContext *C, BKE_nurbList_free(&nurbslist); if (me_eval_needs_free) { - BKE_mesh_free_data((struct Mesh *)me_eval); + BKE_id_free(NULL, (ID *)me_eval); } } -- cgit v1.2.3 From 9e2cd6b07784e58f0e9b2ad6800f3ca8b54f9745 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Aug 2021 15:57:50 +1000 Subject: Fix memory leak with building springs in the cloth simulator Error in 2788b0261cb7d33a2f6f2978ff4f55bb4987edae. --- source/blender/blenkernel/intern/cloth.c | 5 +++-- source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 9aa2d017c48..080a7c90c46 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -42,6 +42,7 @@ #include "BKE_cloth.h" #include "BKE_effect.h" #include "BKE_global.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" @@ -1574,7 +1575,7 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) BLI_edgeset_free(existing_vert_pairs); free_bvhtree_from_mesh(&treedata); if (tmp_mesh) { - BKE_mesh_free_data(tmp_mesh); + BKE_id_free(NULL, &tmp_mesh->id); } return false; } @@ -1583,7 +1584,7 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) BLI_edgeset_free(existing_vert_pairs); free_bvhtree_from_mesh(&treedata); if (tmp_mesh) { - BKE_mesh_free_data(tmp_mesh); + BKE_id_free(NULL, &tmp_mesh->id); } BLI_rng_free(rng); } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 564a3c526f4..e6b1012c981 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1649,6 +1649,7 @@ static int lineart_edge_type_duplication_count(char eflag) } static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBuffer *rb) { + printf("========================================================\nTESTING\n"); BMesh *bm; BMVert *v; BMFace *f; -- cgit v1.2.3 From 15a46a8b727a6475e14e503e93e7c135097d4eeb Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Aug 2021 16:02:39 +1000 Subject: Cleanup: accidentally included printf --- source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index e6b1012c981..564a3c526f4 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1649,7 +1649,6 @@ static int lineart_edge_type_duplication_count(char eflag) } static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBuffer *rb) { - printf("========================================================\nTESTING\n"); BMesh *bm; BMVert *v; BMFace *f; -- cgit v1.2.3 From 40f0783d518a62c07f77394de12fb17a53024170 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Aug 2021 16:08:35 +1000 Subject: Cleanup: remove BKE_mesh_free_data use for lineart mesh copies Even though this didn't leak memory, BKE_mesh_free_data doesn't handle freeing data that is part of the ID making it error prone. --- source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 564a3c526f4..99e3d59a57f 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -41,6 +41,7 @@ #include "BKE_gpencil.h" #include "BKE_gpencil_geom.h" #include "BKE_gpencil_modifier.h" +#include "BKE_lib_id.h" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_object.h" @@ -1691,8 +1692,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu } if (obi->free_use_mesh) { - BKE_mesh_free_data(obi->original_me); - MEM_freeN(obi->original_me); + BKE_id_free(NULL, &obi->original_me); } if (rb->remove_doubles) { -- cgit v1.2.3 From e05db0c26bc407d146796a25acb8bf35506fb433 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Aug 2021 16:21:29 +1000 Subject: Cleanup: rename BKE_mesh_free_data -> BKE_mesh_free_data_for_undo This function only makes sense for undo which doesn't initialize the meshes ID. Otherwise BKE_id_free should be used. --- source/blender/blenkernel/BKE_mesh.h | 2 +- source/blender/blenkernel/intern/mesh.c | 9 ++++++--- source/blender/editors/mesh/editmesh_undo.c | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 5dab847182f..ae464a48e9e 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -95,7 +95,7 @@ void BKE_mesh_looptri_get_real_edges(const struct Mesh *mesh, const struct MLoopTri *looptri, int r_edges[3]); -void BKE_mesh_free_data(struct Mesh *me); +void BKE_mesh_free_data_for_undo(struct Mesh *me); void BKE_mesh_clear_geometry(struct Mesh *me); struct Mesh *BKE_mesh_add(struct Main *bmain, const char *name); void BKE_mesh_copy_parameters_for_eval(struct Mesh *me_dst, const struct Mesh *me_src); diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index c74c31e4c59..eb8e6aad736 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -861,8 +861,11 @@ bool BKE_mesh_has_custom_loop_normals(Mesh *me) return CustomData_has_layer(&me->ldata, CD_CUSTOMLOOPNORMAL); } -/** Free (or release) any data used by this mesh (does not free the mesh itself). */ -void BKE_mesh_free_data(Mesh *me) +/** + * Free (or release) any data used by this mesh (does not free the mesh itself). + * Only use for undo, in most cases `BKE_id_free(NULL, me)` should be used. + */ +void BKE_mesh_free_data_for_undo(Mesh *me) { mesh_free_data(&me->id); } @@ -1078,7 +1081,7 @@ void BKE_mesh_eval_delete(struct Mesh *mesh_eval) { /* Evaluated mesh may point to edit mesh, but never owns it. */ mesh_eval->edit_mesh = NULL; - BKE_mesh_free_data(mesh_eval); + mesh_free_data(&mesh_eval->id); BKE_libblock_free_data(&mesh_eval->id, false); MEM_freeN(mesh_eval); } diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index acf9e6c2d55..4d4e0a7d1b0 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -759,7 +759,7 @@ static void undomesh_free_data(UndoMesh *um) MEM_freeN(me->key); } - BKE_mesh_free_data(me); + BKE_mesh_free_data_for_undo(me); } static Object *editmesh_object_from_context(bContext *C) -- cgit v1.2.3 From cea24b4b4a2c76b632e5841ef6e458e11d82baa0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 20 Aug 2021 16:35:35 +1000 Subject: Cleanup: use "free_data" suffix when the argument isn't freed Avoid API misuse that caused leaks in T90791 & 2788b0261cb7d33a2f6f2978ff4f55bb4987edae. --- source/blender/blenkernel/BKE_gpencil.h | 2 +- source/blender/blenkernel/BKE_image.h | 2 +- source/blender/blenkernel/BKE_key.h | 2 +- source/blender/blenkernel/BKE_screen.h | 2 +- source/blender/blenkernel/intern/gpencil.c | 6 +++--- source/blender/blenkernel/intern/image.c | 2 +- source/blender/blenkernel/intern/key.c | 2 +- source/blender/blenkernel/intern/screen.c | 2 +- source/blender/editors/gpencil/gpencil_undo.c | 2 +- source/blender/editors/mesh/editmesh_undo.c | 2 +- source/blender/editors/screen/screen_edit.c | 2 +- source/blender/editors/space_sequencer/space_sequencer.c | 2 +- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index 92e70b41e7b..b58317f4815 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -93,7 +93,7 @@ void BKE_gpencil_free_stroke(struct bGPDstroke *gps); bool BKE_gpencil_free_strokes(struct bGPDframe *gpf); void BKE_gpencil_free_frames(struct bGPDlayer *gpl); void BKE_gpencil_free_layers(struct ListBase *list); -void BKE_gpencil_free(struct bGPdata *gpd, bool free_all); +void BKE_gpencil_free_data(struct bGPdata *gpd, bool free_all); void BKE_gpencil_eval_delete(struct bGPdata *gpd_eval); void BKE_gpencil_free_layer_masks(struct bGPDlayer *gpl); void BKE_gpencil_tag(struct bGPdata *gpd); diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 3cab1a6b755..b62ad3ad24a 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -56,7 +56,7 @@ void BKE_image_free_buffers(struct Image *image); void BKE_image_free_buffers_ex(struct Image *image, bool do_lock); void BKE_image_free_gputextures(struct Image *ima); /* call from library */ -void BKE_image_free(struct Image *image); +void BKE_image_free_data(struct Image *image); typedef void(StampCallback)(void *data, const char *propname, char *propvalue, int len); diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h index 70d65e02246..cb4fc607703 100644 --- a/source/blender/blenkernel/BKE_key.h +++ b/source/blender/blenkernel/BKE_key.h @@ -36,7 +36,7 @@ struct Object; extern "C" { #endif -void BKE_key_free(struct Key *key); +void BKE_key_free_data(struct Key *key); void BKE_key_free_nolib(struct Key *key); struct Key *BKE_key_add(struct Main *bmain, struct ID *id); void BKE_key_sort(struct Key *key); diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 0b08bbfeff5..6f341a12b82 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -473,7 +473,7 @@ void BKE_screen_view3d_shading_init(struct View3DShading *shading); /* screen */ void BKE_screen_foreach_id_screen_area(struct LibraryForeachIDData *data, struct ScrArea *area); -void BKE_screen_free(struct bScreen *screen); +void BKE_screen_free_data(struct bScreen *screen); void BKE_screen_area_map_free(struct ScrAreaMap *area_map) ATTR_NONNULL(); struct ScrEdge *BKE_screen_find_edge(const struct bScreen *screen, diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 9cdb7395925..9062fd2d39c 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -131,7 +131,7 @@ static void greasepencil_free_data(ID *id) { /* Really not ideal, but for now will do... In theory custom behaviors like not freeing cache * should be handled through specific API, and not be part of the generic one. */ - BKE_gpencil_free((bGPdata *)id, true); + BKE_gpencil_free_data((bGPdata *)id, true); } static void greasepencil_foreach_id(ID *id, LibraryForeachIDData *data) @@ -495,7 +495,7 @@ void BKE_gpencil_free_layers(ListBase *list) } /** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */ -void BKE_gpencil_free(bGPdata *gpd, bool free_all) +void BKE_gpencil_free_data(bGPdata *gpd, bool free_all) { /* free layers */ BKE_gpencil_free_layers(&gpd->layers); @@ -518,7 +518,7 @@ void BKE_gpencil_free(bGPdata *gpd, bool free_all) */ void BKE_gpencil_eval_delete(bGPdata *gpd_eval) { - BKE_gpencil_free(gpd_eval, true); + BKE_gpencil_free_data(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/image.c b/source/blender/blenkernel/intern/image.c index 5701449a9e5..d87290e1eb4 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -534,7 +534,7 @@ void BKE_image_free_buffers(Image *ima) } /** Free (or release) any data used by this image (does not free the image itself). */ -void BKE_image_free(Image *ima) +void BKE_image_free_data(Image *ima) { image_free_data(&ima->id); } diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index b59f51c36f7..f79058dcf21 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -245,7 +245,7 @@ typedef struct WeightsArrayCache { } WeightsArrayCache; /** Free (or release) any data used by this shapekey (does not free the key itself). */ -void BKE_key_free(Key *key) +void BKE_key_free_data(Key *key) { shapekey_free_data(&key->id); } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 1e725a6afc4..065240bddbc 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -728,7 +728,7 @@ void BKE_screen_area_map_free(ScrAreaMap *area_map) } /** Free (or release) any data used by this screen (does not free the screen itself). */ -void BKE_screen_free(bScreen *screen) +void BKE_screen_free_data(bScreen *screen) { screen_free_data(&screen->id); } diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index 96e85cc6135..99b8b672327 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -125,7 +125,7 @@ static void gpencil_undo_free_node(bGPundonode *undo_node) */ undo_node->gpd->adt = NULL; - BKE_gpencil_free(undo_node->gpd, false); + BKE_gpencil_free_data(undo_node->gpd, false); MEM_freeN(undo_node->gpd); } diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 4d4e0a7d1b0..f52cd94b8dc 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -755,7 +755,7 @@ static void undomesh_free_data(UndoMesh *um) #endif if (me->key) { - BKE_key_free(me->key); + BKE_key_free_data(me->key); MEM_freeN(me->key); } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 506b5a9859d..1c068fdd6e4 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -230,7 +230,7 @@ bScreen *screen_add(Main *bmain, const char *name, const rcti *rect) void screen_data_copy(bScreen *to, bScreen *from) { /* free contents of 'to', is from blenkernel screen.c */ - BKE_screen_free(to); + BKE_screen_free_data(to); to->flag = from->flag; diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 00f3bf6ac72..2a6e49edfb6 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -194,7 +194,7 @@ static void sequencer_free(SpaceLink *sl) SpaceSeq *sseq = (SpaceSeq *)sl; SequencerScopes *scopes = &sseq->scopes; - /* XXX if (sseq->gpd) BKE_gpencil_free(sseq->gpd); */ + /* XXX if (sseq->gpd) BKE_gpencil_free_data(sseq->gpd); */ if (scopes->zebra_ibuf) { IMB_freeImBuf(scopes->zebra_ibuf); -- cgit v1.2.3 From e95b197e987d3ed5d9efb6b228a7bdb56e1bd7bc Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 20 Aug 2021 11:27:18 +0200 Subject: Cleanup: Add CLOG to wm_files_link.c --- .../blender/windowmanager/intern/wm_files_link.c | 51 ++++++++++------------ 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index cdcb6c5163f..606c9252ff9 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -30,6 +30,8 @@ #include #include +#include "CLG_log.h" + #include "MEM_guardedalloc.h" #include "DNA_ID.h" @@ -76,6 +78,8 @@ #include "wm_files.h" +static CLG_LogRef LOG = {"wm.files_link"}; + /* -------------------------------------------------------------------- */ /** \name Link/Append Operator * \{ */ @@ -315,7 +319,7 @@ static bool wm_link_append_item_poll(ReportList *reports, short idcode; if (!group || !name) { - printf("skipping %s\n", path); + CLOG_WARN(&LOG, "Skipping %s", path); return false; } @@ -759,12 +763,12 @@ static void lib_relocate_do_remap(Main *bmain, BLI_assert(new_id); } if (new_id) { -#ifdef PRINT_DEBUG - printf("before remap of %s, old_id users: %d, new_id users: %d\n", - old_id->name, - old_id->us, - new_id->us); -#endif + CLOG_INFO(&LOG, + 4, + "Before remap of %s, old_id users: %d, new_id users: %d", + old_id->name, + old_id->us, + new_id->us); BKE_libblock_remap_locked(bmain, old_id, new_id, remap_flags); if (old_id->flag & LIB_FAKEUSER) { @@ -772,12 +776,12 @@ static void lib_relocate_do_remap(Main *bmain, id_fake_user_set(new_id); } -#ifdef PRINT_DEBUG - printf("after remap of %s, old_id users: %d, new_id users: %d\n", - old_id->name, - old_id->us, - new_id->us); -#endif + CLOG_INFO(&LOG, + 4, + "After remap of %s, old_id users: %d, new_id users: %d", + old_id->name, + old_id->us, + new_id->us); /* In some cases, new_id might become direct link, remove parent of library in this case. */ if (new_id->lib->parent && (new_id->tag & LIB_TAG_INDIRECT) == 0) { @@ -875,9 +879,7 @@ static void lib_relocate_do(bContext *C, item = wm_link_append_data_item_add(lapp_data, id->name + 2, idcode, id); BLI_bitmap_set_all(item->libraries, true, lapp_data->num_libraries); -#ifdef PRINT_DEBUG - printf("\tdatablock to seek for: %s\n", id->name); -#endif + CLOG_INFO(&LOG, 4, "Datablock to seek for: %s", id->name); } } } @@ -1117,9 +1119,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) } if (BLI_path_cmp(lib->filepath_abs, path) == 0) { -#ifdef PRINT_DEBUG - printf("We are supposed to reload '%s' lib (%d)...\n", lib->filepath, lib->id.us); -#endif + CLOG_INFO(&LOG, 4, "We are supposed to reload '%s' lib (%d)", lib->filepath, lib->id.us); do_reload = true; @@ -1129,9 +1129,8 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) else { int totfiles = 0; -#ifdef PRINT_DEBUG - printf("We are supposed to relocate '%s' lib to new '%s' one...\n", lib->filepath, libname); -#endif + CLOG_INFO( + &LOG, 4, "We are supposed to relocate '%s' lib to new '%s' one", lib->filepath, libname); /* Check if something is indicated for relocate. */ prop = RNA_struct_find_property(op->ptr, "files"); @@ -1157,17 +1156,13 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) continue; } -#ifdef PRINT_DEBUG - printf("\t candidate new lib to reload datablocks from: %s\n", path); -#endif + CLOG_INFO(&LOG, 4, "\tCandidate new lib to reload datablocks from: %s", path); wm_link_append_data_library_add(lapp_data, path); } RNA_END; } else { -#ifdef PRINT_DEBUG - printf("\t candidate new lib to reload datablocks from: %s\n", path); -#endif + CLOG_INFO(&LOG, 4, "\tCandidate new lib to reload datablocks from: %s", path); wm_link_append_data_library_add(lapp_data, path); } } -- cgit v1.2.3 From 2b6f0cc8367c04df8147cf231b11de4b4b8b41d5 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 20 Aug 2021 11:41:24 +0200 Subject: BLI: add utility methods to IndexMask --- source/blender/blenlib/BLI_index_mask.hh | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh index f04c0e9c80a..7a3169520ca 100644 --- a/source/blender/blenlib/BLI_index_mask.hh +++ b/source/blender/blenlib/BLI_index_mask.hh @@ -58,11 +58,7 @@ class IndexMask { */ IndexMask(Span indices) : indices_(indices) { -#ifdef DEBUG - for (int64_t i = 1; i < indices.size(); i++) { - BLI_assert(indices[i - 1] < indices[i]); - } -#endif + BLI_assert(IndexMask::indices_are_valid_index_mask(indices)); } /** @@ -94,6 +90,22 @@ class IndexMask { { } + /** Checks that the indices are non-negative and in ascending order. */ + static bool indices_are_valid_index_mask(Span indices) + { + if (!indices.is_empty()) { + if (indices.first() < 0) { + return false; + } + } + for (int64_t i = 1; i < indices.size(); i++) { + if (indices[i - 1] >= indices[i]) { + return false; + } + } + return true; + } + operator Span() const { return indices_; @@ -204,6 +216,11 @@ class IndexMask { { return indices_.size(); } + + bool is_empty() const + { + return indices_.is_empty(); + } }; } // namespace blender -- cgit v1.2.3 From fd51b05a02abcbdc9b551a9f2bf72a4ea6bc1f1c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 20 Aug 2021 11:42:31 +0200 Subject: Functions: add clear method to vector array --- source/blender/functions/FN_generic_vector_array.hh | 2 ++ source/blender/functions/intern/generic_vector_array.cc | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh index eeba0c9dba2..179e85671f8 100644 --- a/source/blender/functions/FN_generic_vector_array.hh +++ b/source/blender/functions/FN_generic_vector_array.hh @@ -82,6 +82,8 @@ class GVectorArray : NonCopyable, NonMovable { void extend(IndexMask mask, const GVVectorArray &values); void extend(IndexMask mask, const GVectorArray &values); + void clear(IndexMask mask); + GMutableSpan operator[](int64_t index); GSpan operator[](int64_t index) const; diff --git a/source/blender/functions/intern/generic_vector_array.cc b/source/blender/functions/intern/generic_vector_array.cc index 9556d24218e..ec95a283919 100644 --- a/source/blender/functions/intern/generic_vector_array.cc +++ b/source/blender/functions/intern/generic_vector_array.cc @@ -78,6 +78,15 @@ void GVectorArray::extend(IndexMask mask, const GVectorArray &values) this->extend(mask, virtual_values); } +void GVectorArray::clear(IndexMask mask) +{ + for (const int64_t i : mask) { + Item &item = items_[i]; + type_.destruct_n(item.start, item.length); + item.length = 0; + } +} + GMutableSpan GVectorArray::operator[](const int64_t index) { Item &item = items_[index]; -- cgit v1.2.3 From d217b3421481161d33f27a30f6aec24e31c4ac61 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 20 Aug 2021 11:43:54 +0200 Subject: Functions: add utility methods to parameter builder --- source/blender/functions/FN_multi_function_params.hh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index e292d11def7..a480287d578 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -54,6 +54,11 @@ class MFParamsBuilder { MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size); + template void add_readonly_single_input_value(T value, StringRef expected_name = "") + { + T *value_ptr = &scope_.add_value(std::move(value), __func__); + this->add_readonly_single_input(value_ptr, expected_name); + } template void add_readonly_single_input(const T *value, StringRef expected_name = "") { this->add_readonly_single_input(scope_.construct( @@ -83,6 +88,12 @@ class MFParamsBuilder { this->add_readonly_vector_input( scope_.construct(__func__, vector_array), expected_name); } + void add_readonly_vector_input(const GSpan single_vector, StringRef expected_name = "") + { + this->add_readonly_vector_input( + scope_.construct(__func__, single_vector, min_array_size_), + expected_name); + } void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "") { this->assert_current_param_type(MFParamType::ForVectorInput(ref.type()), expected_name); -- cgit v1.2.3 From c1227fd4089f22edda3e137a7a1185fbb3daa968 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 20 Aug 2021 12:05:03 +0200 Subject: Cleanup: remove duplicate line --- source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index e524564edab..4f70252ae75 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -148,7 +148,5 @@ void register_node_type_geo_subdivision_surface() "NodeGeometrySubdivisionSurface", node_free_standard_storage, node_copy_standard_storage); - node_type_socket_templates( - &ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out); nodeRegisterType(&ntype); } -- cgit v1.2.3 From 7d8c71e8003ecb4d3a7fe2483a328d3f2d184faa Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 20 Aug 2021 12:19:24 +0200 Subject: Geometry Nodes: add missing versioning for subdivision surface node This was missing from rBfecec1644ce54ea386eaeed5ca6748d4a7b2737b. --- source/blender/blenloader/intern/versioning_300.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index cac607ed152..eb01bfbfb9c 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -787,5 +787,23 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Add node storage for subdivision surface node. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_GEOMETRY) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_SUBDIVISION_SURFACE) { + if (node->storage == NULL) { + NodeGeometrySubdivisionSurface *data = MEM_callocN( + sizeof(NodeGeometrySubdivisionSurface), __func__); + data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; + data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; + node->storage = data; + } + } + } + } + } + FOREACH_NODETREE_END; } } -- cgit v1.2.3 From 00812008129bdab48d328775b09332af12118867 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 20 Aug 2021 13:14:39 +0200 Subject: Functions: remove multi-function network The multi-function network system was able to compose multiple multi-functions into a new one and to evaluate that efficiently. This functionality was heavily used by the particle nodes prototype a year ago. However, since then we only used multi-functions without the need to compose them in geometry nodes. The upcoming "fields" in geometry nodes will need a way to compose multi-functions again. Unfortunately, the code removed in this commit was not ideal for this different kind of function composition. I've been working on an alternative that will be added separately when it becomes needed. I've had to update all the function nodes, because their interface depended on the multi-function network data structure a bit. The actual multi-function implementations are still the same though. --- source/blender/blenkernel/BKE_node.h | 15 +- source/blender/blenkernel/intern/simulation.cc | 4 - source/blender/functions/CMakeLists.txt | 7 - .../blender/functions/FN_generic_virtual_array.hh | 2 +- .../blender/functions/FN_multi_function_network.hh | 536 ---------- .../FN_multi_function_network_evaluation.hh | 62 -- .../FN_multi_function_network_optimization.hh | 29 - .../functions/FN_multi_function_signature.hh | 15 + .../functions/intern/multi_function_network.cc | 330 ------ .../intern/multi_function_network_evaluation.cc | 1083 -------------------- .../intern/multi_function_network_optimization.cc | 501 --------- .../tests/FN_multi_function_network_test.cc | 280 ----- .../functions/tests/FN_multi_function_test.cc | 92 +- .../tests/FN_multi_function_test_common.hh | 174 ++++ source/blender/modifiers/intern/MOD_nodes.cc | 5 +- .../modifiers/intern/MOD_nodes_evaluator.cc | 2 +- .../modifiers/intern/MOD_nodes_evaluator.hh | 6 +- source/blender/nodes/CMakeLists.txt | 6 +- source/blender/nodes/NOD_multi_function.hh | 130 +++ .../blender/nodes/NOD_node_tree_multi_function.hh | 390 ------- source/blender/nodes/NOD_type_callbacks.hh | 34 - .../blender/nodes/function/node_function_util.hh | 2 +- .../nodes/function/nodes/node_fn_boolean_math.cc | 19 +- .../nodes/function/nodes/node_fn_float_compare.cc | 25 +- .../nodes/function/nodes/node_fn_float_to_int.cc | 19 +- .../nodes/function/nodes/node_fn_input_string.cc | 12 +- .../nodes/function/nodes/node_fn_input_vector.cc | 10 +- .../nodes/function/nodes/node_fn_random_float.cc | 9 +- source/blender/nodes/intern/node_geometry_exec.cc | 1 - source/blender/nodes/intern/node_multi_function.cc | 40 + source/blender/nodes/intern/node_socket.cc | 1 - .../nodes/intern/node_tree_multi_function.cc | 409 -------- source/blender/nodes/intern/type_callbacks.cc | 60 -- source/blender/nodes/shader/node_shader_util.h | 2 +- .../nodes/shader/nodes/node_shader_clamp.cc | 6 +- .../nodes/shader/nodes/node_shader_curves.cc | 14 +- .../nodes/shader/nodes/node_shader_map_range.cc | 8 +- .../blender/nodes/shader/nodes/node_shader_math.cc | 67 +- .../nodes/shader/nodes/node_shader_mixRgb.cc | 6 +- .../nodes/shader/nodes/node_shader_sepcombRGB.cc | 8 +- .../nodes/shader/nodes/node_shader_sepcombXYZ.cc | 8 +- .../nodes/shader/nodes/node_shader_valToRgb.cc | 7 +- .../nodes/shader/nodes/node_shader_value.cc | 6 +- .../nodes/shader/nodes/node_shader_vector_math.cc | 28 +- .../shader/nodes/node_shader_vector_rotate.cc | 37 +- 45 files changed, 534 insertions(+), 3973 deletions(-) delete mode 100644 source/blender/functions/FN_multi_function_network.hh delete mode 100644 source/blender/functions/FN_multi_function_network_evaluation.hh delete mode 100644 source/blender/functions/FN_multi_function_network_optimization.hh delete mode 100644 source/blender/functions/intern/multi_function_network.cc delete mode 100644 source/blender/functions/intern/multi_function_network_evaluation.cc delete mode 100644 source/blender/functions/intern/multi_function_network_optimization.cc delete mode 100644 source/blender/functions/tests/FN_multi_function_network_test.cc create mode 100644 source/blender/functions/tests/FN_multi_function_test_common.hh create mode 100644 source/blender/nodes/NOD_multi_function.hh delete mode 100644 source/blender/nodes/NOD_node_tree_multi_function.hh delete mode 100644 source/blender/nodes/NOD_type_callbacks.hh create mode 100644 source/blender/nodes/intern/node_multi_function.cc delete mode 100644 source/blender/nodes/intern/node_tree_multi_function.cc delete mode 100644 source/blender/nodes/intern/type_callbacks.cc diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index caa7ab6de0a..c4393246926 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -111,8 +111,7 @@ typedef struct bNodeSocketTemplate { #ifdef __cplusplus namespace blender { namespace nodes { -class SocketMFNetworkBuilder; -class NodeMFNetworkBuilder; +class NodeMultiFunctionBuilder; class GeoNodeExecParams; } // namespace nodes namespace fn { @@ -121,18 +120,16 @@ class MFDataType; } // namespace fn } // namespace blender -using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder); +using NodeMultiFunctionBuildFunction = void (*)(blender::nodes::NodeMultiFunctionBuilder &builder); using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params); using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)(); using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); using SocketGetGeometryNodesCPPTypeFunction = const blender::fn::CPPType *(*)(); using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); -using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetworkBuilder &builder); #else -typedef void *NodeExpandInMFNetworkFunction; -typedef void *SocketExpandInMFNetworkFunction; +typedef void *NodeMultiFunctionBuildFunction; typedef void *NodeGeometryExecFunction; typedef void *SocketGetCPPTypeFunction; typedef void *SocketGetGeometryNodesCPPTypeFunction; @@ -196,8 +193,6 @@ typedef struct bNodeSocketType { /* Callback to free the socket type. */ void (*free_self)(struct bNodeSocketType *stype); - /* Expands the socket into a multi-function node that outputs the socket value. */ - SocketExpandInMFNetworkFunction expand_in_mf_network; /* Return the CPPType of this socket. */ SocketGetCPPTypeFunction get_base_cpp_type; /* Get the value of this socket in a generic way. */ @@ -332,8 +327,8 @@ typedef struct bNodeType { /* gpu */ NodeGPUExecFunction gpu_fn; - /* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */ - NodeExpandInMFNetworkFunction expand_in_mf_network; + /* Build a multi-function for this node. */ + NodeMultiFunctionBuildFunction build_multi_function; /* Execute a geometry node. */ NodeGeometryExecFunction geometry_node_execute; diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 5aac29c19a7..4c97ccdf8b1 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -49,14 +49,10 @@ #include "BKE_simulation.h" #include "NOD_geometry.h" -#include "NOD_node_tree_multi_function.hh" #include "BLI_map.hh" #include "BLT_translation.h" -#include "FN_multi_function_network_evaluation.hh" -#include "FN_multi_function_network_optimization.hh" - #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 809294ad274..f8d2acc74a8 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -33,9 +33,6 @@ set(SRC intern/generic_virtual_vector_array.cc intern/multi_function.cc intern/multi_function_builder.cc - intern/multi_function_network.cc - intern/multi_function_network_evaluation.cc - intern/multi_function_network_optimization.cc FN_cpp_type.hh FN_cpp_type_make.hh @@ -49,9 +46,6 @@ set(SRC FN_multi_function_builder.hh FN_multi_function_context.hh FN_multi_function_data_type.hh - FN_multi_function_network.hh - FN_multi_function_network_evaluation.hh - FN_multi_function_network_optimization.hh FN_multi_function_param_type.hh FN_multi_function_params.hh FN_multi_function_signature.hh @@ -68,7 +62,6 @@ if(WITH_GTESTS) tests/FN_cpp_type_test.cc tests/FN_generic_span_test.cc tests/FN_generic_vector_array_test.cc - tests/FN_multi_function_network_test.cc tests/FN_multi_function_test.cc ) set(TEST_LIB diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index c9398ceb547..f429243e2de 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -129,7 +129,7 @@ class GVArray { } /* Same as `get_internal_single`, but `r_value` points to initialized memory. */ - void get_single_to_uninitialized(void *r_value) const + void get_internal_single_to_uninitialized(void *r_value) const { type_->default_construct(r_value); this->get_internal_single(r_value); diff --git a/source/blender/functions/FN_multi_function_network.hh b/source/blender/functions/FN_multi_function_network.hh deleted file mode 100644 index b303589106a..00000000000 --- a/source/blender/functions/FN_multi_function_network.hh +++ /dev/null @@ -1,536 +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. - */ - -#pragma once - -/** \file - * \ingroup fn - * - * A multi-function network (`MFNetwork`) allows you to connect multiple multi-functions. The - * `MFNetworkEvaluator` is a multi-function that wraps an entire network into a new multi-function - * (which can be used in another network and so on). - * - * A MFNetwork is a graph data structure with two kinds of nodes: - * - MFFunctionNode: Represents a multi-function. Its input and output sockets correspond to - * parameters of the referenced multi-function. - * - MFDummyNode: Does not reference a multi-function. Instead it just has sockets that can be - * used to represent node group inputs and outputs. - * - * Links represent data flow. Unlinked input sockets have no value. In order to execute a function - * node, all its inputs have to be connected to something. - * - * Links are only allowed between sockets with the exact same MFDataType. There are no implicit - * conversions. - * - * Every input and output parameter of a multi-function corresponds to exactly one input or output - * socket respectively. A multiple parameter belongs to exactly one input AND one output socket. - * - * There is an .to_dot() method that generates a graph in dot format for debugging purposes. - */ - -#include "FN_multi_function.hh" - -#include "BLI_vector_set.hh" - -namespace blender::fn { - -class MFNode; -class MFFunctionNode; -class MFDummyNode; -class MFSocket; -class MFInputSocket; -class MFOutputSocket; -class MFNetwork; - -class MFNode : NonCopyable, NonMovable { - protected: - MFNetwork *network_; - Span inputs_; - Span outputs_; - bool is_dummy_; - int id_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - int id() const; - - MFNetwork &network(); - const MFNetwork &network() const; - - bool is_dummy() const; - bool is_function() const; - - MFDummyNode &as_dummy(); - const MFDummyNode &as_dummy() const; - - MFFunctionNode &as_function(); - const MFFunctionNode &as_function() const; - - MFInputSocket &input(int index); - const MFInputSocket &input(int index) const; - - MFOutputSocket &output(int index); - const MFOutputSocket &output(int index) const; - - Span inputs(); - Span inputs() const; - - Span outputs(); - Span outputs() const; - - bool has_unlinked_inputs() const; - - private: - void destruct_sockets(); -}; - -class MFFunctionNode : public MFNode { - private: - const MultiFunction *function_; - Span input_param_indices_; - Span output_param_indices_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - const MultiFunction &function() const; - - const MFInputSocket &input_for_param(int param_index) const; - const MFOutputSocket &output_for_param(int param_index) const; -}; - -class MFDummyNode : public MFNode { - protected: - StringRefNull name_; - MutableSpan input_names_; - MutableSpan output_names_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - Span input_names() const; - Span output_names() const; -}; - -class MFSocket : NonCopyable, NonMovable { - protected: - MFNode *node_; - bool is_output_; - int index_; - MFDataType data_type_; - int id_; - StringRefNull name_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - int id() const; - int index() const; - - const MFDataType &data_type() const; - - MFNode &node(); - const MFNode &node() const; - - bool is_input() const; - bool is_output() const; - - MFInputSocket &as_input(); - const MFInputSocket &as_input() const; - - MFOutputSocket &as_output(); - const MFOutputSocket &as_output() const; -}; - -class MFInputSocket : public MFSocket { - private: - MFOutputSocket *origin_; - - friend MFNetwork; - - public: - MFOutputSocket *origin(); - const MFOutputSocket *origin() const; -}; - -class MFOutputSocket : public MFSocket { - private: - Vector targets_; - - friend MFNetwork; - - public: - Span targets(); - Span targets() const; -}; - -class MFNetwork : NonCopyable, NonMovable { - private: - LinearAllocator<> allocator_; - - VectorSet function_nodes_; - VectorSet dummy_nodes_; - - Vector node_or_null_by_id_; - Vector socket_or_null_by_id_; - - public: - MFNetwork() = default; - ~MFNetwork(); - - MFFunctionNode &add_function(const MultiFunction &function); - MFDummyNode &add_dummy(StringRef name, - Span input_types, - Span output_types, - Span input_names, - Span output_names); - void add_link(MFOutputSocket &from, MFInputSocket &to); - - MFOutputSocket &add_input(StringRef name, MFDataType data_type); - MFInputSocket &add_output(StringRef name, MFDataType data_type); - - void relink(MFOutputSocket &old_output, MFOutputSocket &new_output); - - void remove(MFNode &node); - void remove(Span nodes); - - int socket_id_amount() const; - int node_id_amount() const; - - Span dummy_nodes(); - Span function_nodes(); - - MFNode *node_or_null_by_id(int id); - const MFNode *node_or_null_by_id(int id) const; - - MFSocket *socket_or_null_by_id(int id); - const MFSocket *socket_or_null_by_id(int id) const; - - void find_dependencies(Span sockets, - VectorSet &r_dummy_sockets, - VectorSet &r_unlinked_inputs) const; - - bool have_dummy_or_unlinked_dependencies(Span sockets) const; - - std::string to_dot(Span marked_nodes = {}) const; -}; - -/* -------------------------------------------------------------------- - * MFNode inline methods. - */ - -inline StringRefNull MFNode::name() const -{ - if (is_dummy_) { - return this->as_dummy().name(); - } - else { - return this->as_function().name(); - } -} - -inline int MFNode::id() const -{ - return id_; -} - -inline MFNetwork &MFNode::network() -{ - return *network_; -} - -inline const MFNetwork &MFNode::network() const -{ - return *network_; -} - -inline bool MFNode::is_dummy() const -{ - return is_dummy_; -} - -inline bool MFNode::is_function() const -{ - return !is_dummy_; -} - -inline MFDummyNode &MFNode::as_dummy() -{ - BLI_assert(is_dummy_); - return static_cast(*this); -} - -inline const MFDummyNode &MFNode::as_dummy() const -{ - BLI_assert(is_dummy_); - return static_cast(*this); -} - -inline MFFunctionNode &MFNode::as_function() -{ - BLI_assert(!is_dummy_); - return static_cast(*this); -} - -inline const MFFunctionNode &MFNode::as_function() const -{ - BLI_assert(!is_dummy_); - return static_cast(*this); -} - -inline MFInputSocket &MFNode::input(int index) -{ - return *inputs_[index]; -} - -inline const MFInputSocket &MFNode::input(int index) const -{ - return *inputs_[index]; -} - -inline MFOutputSocket &MFNode::output(int index) -{ - return *outputs_[index]; -} - -inline const MFOutputSocket &MFNode::output(int index) const -{ - return *outputs_[index]; -} - -inline Span MFNode::inputs() -{ - return inputs_; -} - -inline Span MFNode::inputs() const -{ - return inputs_; -} - -inline Span MFNode::outputs() -{ - return outputs_; -} - -inline Span MFNode::outputs() const -{ - return outputs_; -} - -inline bool MFNode::has_unlinked_inputs() const -{ - for (const MFInputSocket *socket : inputs_) { - if (socket->origin() == nullptr) { - return true; - } - } - return false; -} - -/* -------------------------------------------------------------------- - * MFFunctionNode inline methods. - */ - -inline StringRefNull MFFunctionNode::name() const -{ - return function_->name(); -} - -inline const MultiFunction &MFFunctionNode::function() const -{ - return *function_; -} - -inline const MFInputSocket &MFFunctionNode::input_for_param(int param_index) const -{ - return this->input(input_param_indices_.first_index(param_index)); -} - -inline const MFOutputSocket &MFFunctionNode::output_for_param(int param_index) const -{ - return this->output(output_param_indices_.first_index(param_index)); -} - -/* -------------------------------------------------------------------- - * MFDummyNode inline methods. - */ - -inline StringRefNull MFDummyNode::name() const -{ - return name_; -} - -inline Span MFDummyNode::input_names() const -{ - return input_names_; -} - -inline Span MFDummyNode::output_names() const -{ - return output_names_; -} - -/* -------------------------------------------------------------------- - * MFSocket inline methods. - */ - -inline StringRefNull MFSocket::name() const -{ - return name_; -} - -inline int MFSocket::id() const -{ - return id_; -} - -inline int MFSocket::index() const -{ - return index_; -} - -inline const MFDataType &MFSocket::data_type() const -{ - return data_type_; -} - -inline MFNode &MFSocket::node() -{ - return *node_; -} - -inline const MFNode &MFSocket::node() const -{ - return *node_; -} - -inline bool MFSocket::is_input() const -{ - return !is_output_; -} - -inline bool MFSocket::is_output() const -{ - return is_output_; -} - -inline MFInputSocket &MFSocket::as_input() -{ - BLI_assert(this->is_input()); - return static_cast(*this); -} - -inline const MFInputSocket &MFSocket::as_input() const -{ - BLI_assert(this->is_input()); - return static_cast(*this); -} - -inline MFOutputSocket &MFSocket::as_output() -{ - BLI_assert(this->is_output()); - return static_cast(*this); -} - -inline const MFOutputSocket &MFSocket::as_output() const -{ - BLI_assert(this->is_output()); - return static_cast(*this); -} - -/* -------------------------------------------------------------------- - * MFInputSocket inline methods. - */ - -inline MFOutputSocket *MFInputSocket::origin() -{ - return origin_; -} - -inline const MFOutputSocket *MFInputSocket::origin() const -{ - return origin_; -} - -/* -------------------------------------------------------------------- - * MFOutputSocket inline methods. - */ - -inline Span MFOutputSocket::targets() -{ - return targets_; -} - -inline Span MFOutputSocket::targets() const -{ - return targets_; -} - -/* -------------------------------------------------------------------- - * MFNetwork inline methods. - */ - -inline Span MFNetwork::dummy_nodes() -{ - return dummy_nodes_; -} - -inline Span MFNetwork::function_nodes() -{ - return function_nodes_; -} - -inline MFNode *MFNetwork::node_or_null_by_id(int id) -{ - return node_or_null_by_id_[id]; -} - -inline const MFNode *MFNetwork::node_or_null_by_id(int id) const -{ - return node_or_null_by_id_[id]; -} - -inline MFSocket *MFNetwork::socket_or_null_by_id(int id) -{ - return socket_or_null_by_id_[id]; -} - -inline const MFSocket *MFNetwork::socket_or_null_by_id(int id) const -{ - return socket_or_null_by_id_[id]; -} - -inline int MFNetwork::socket_id_amount() const -{ - return socket_or_null_by_id_.size(); -} - -inline int MFNetwork::node_id_amount() const -{ - return node_or_null_by_id_.size(); -} - -} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_network_evaluation.hh b/source/blender/functions/FN_multi_function_network_evaluation.hh deleted file mode 100644 index 17cffa406f7..00000000000 --- a/source/blender/functions/FN_multi_function_network_evaluation.hh +++ /dev/null @@ -1,62 +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. - */ - -#pragma once - -/** \file - * \ingroup fn - */ - -#include "FN_multi_function_network.hh" - -namespace blender::fn { - -class MFNetworkEvaluationStorage; - -class MFNetworkEvaluator : public MultiFunction { - private: - MFSignature signature_; - Vector inputs_; - Vector outputs_; - - public: - MFNetworkEvaluator(Vector inputs, Vector outputs); - - void call(IndexMask mask, MFParams params, MFContext context) const override; - - private: - using Storage = MFNetworkEvaluationStorage; - - void copy_inputs_to_storage(MFParams params, Storage &storage) const; - void copy_outputs_to_storage( - MFParams params, - Storage &storage, - Vector &outputs_to_initialize_in_the_end) const; - - void evaluate_network_to_compute_outputs(MFContext &global_context, Storage &storage) const; - - void evaluate_function(MFContext &global_context, - const MFFunctionNode &function_node, - Storage &storage) const; - - bool can_do_single_value_evaluation(const MFFunctionNode &function_node, Storage &storage) const; - - void initialize_remaining_outputs(MFParams params, - Storage &storage, - Span remaining_outputs) const; -}; - -} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_network_optimization.hh b/source/blender/functions/FN_multi_function_network_optimization.hh deleted file mode 100644 index 96664fa368e..00000000000 --- a/source/blender/functions/FN_multi_function_network_optimization.hh +++ /dev/null @@ -1,29 +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. - */ - -#pragma once - -#include "FN_multi_function_network.hh" - -#include "BLI_resource_scope.hh" - -namespace blender::fn::mf_network_optimization { - -void dead_node_removal(MFNetwork &network); -void constant_folding(MFNetwork &network, ResourceScope &scope); -void common_subnetwork_elimination(MFNetwork &network); - -} // namespace blender::fn::mf_network_optimization diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh index 23309c9a5e6..d05948cc645 100644 --- a/source/blender/functions/FN_multi_function_signature.hh +++ b/source/blender/functions/FN_multi_function_signature.hh @@ -160,6 +160,21 @@ class MFSignatureBuilder { } } + void add(StringRef name, const MFParamType ¶m_type) + { + switch (param_type.interface_type()) { + case MFParamType::Input: + this->input(name, param_type.data_type()); + break; + case MFParamType::Mutable: + this->mutable_(name, param_type.data_type()); + break; + case MFParamType::Output: + this->output(name, param_type.data_type()); + break; + } + } + /* Context */ /** This indicates that the function accesses the context. This disables optimizations that diff --git a/source/blender/functions/intern/multi_function_network.cc b/source/blender/functions/intern/multi_function_network.cc deleted file mode 100644 index b5c2c09a35a..00000000000 --- a/source/blender/functions/intern/multi_function_network.cc +++ /dev/null @@ -1,330 +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. - */ - -#include "BLI_dot_export.hh" -#include "BLI_stack.hh" - -#include "FN_multi_function_network.hh" - -namespace blender::fn { - -MFNetwork::~MFNetwork() -{ - for (MFFunctionNode *node : function_nodes_) { - node->destruct_sockets(); - node->~MFFunctionNode(); - } - for (MFDummyNode *node : dummy_nodes_) { - node->destruct_sockets(); - node->~MFDummyNode(); - } -} - -void MFNode::destruct_sockets() -{ - for (MFInputSocket *socket : inputs_) { - socket->~MFInputSocket(); - } - for (MFOutputSocket *socket : outputs_) { - socket->~MFOutputSocket(); - } -} - -/** - * Add a new function node to the network. The caller keeps the ownership of the function. The - * function should not be freed before the network. A reference to the new node is returned. The - * node is owned by the network. - */ -MFFunctionNode &MFNetwork::add_function(const MultiFunction &function) -{ - Vector input_param_indices, output_param_indices; - - for (int param_index : function.param_indices()) { - switch (function.param_type(param_index).interface_type()) { - case MFParamType::Input: { - input_param_indices.append(param_index); - break; - } - case MFParamType::Output: { - output_param_indices.append(param_index); - break; - } - case MFParamType::Mutable: { - input_param_indices.append(param_index); - output_param_indices.append(param_index); - break; - } - } - } - - MFFunctionNode &node = *allocator_.construct().release(); - function_nodes_.add_new(&node); - - node.network_ = this; - node.is_dummy_ = false; - node.id_ = node_or_null_by_id_.append_and_get_index(&node); - node.function_ = &function; - node.input_param_indices_ = allocator_.construct_array_copy(input_param_indices); - node.output_param_indices_ = allocator_.construct_array_copy(output_param_indices); - - node.inputs_ = allocator_.construct_elements_and_pointer_array( - input_param_indices.size()); - node.outputs_ = allocator_.construct_elements_and_pointer_array( - output_param_indices.size()); - - for (int i : input_param_indices.index_range()) { - int param_index = input_param_indices[i]; - MFParamType param = function.param_type(param_index); - BLI_assert(param.is_input_or_mutable()); - - MFInputSocket &socket = *node.inputs_[i]; - socket.data_type_ = param.data_type(); - socket.node_ = &node; - socket.index_ = i; - socket.is_output_ = false; - socket.name_ = function.param_name(param_index); - socket.origin_ = nullptr; - socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket); - } - - for (int i : output_param_indices.index_range()) { - int param_index = output_param_indices[i]; - MFParamType param = function.param_type(param_index); - BLI_assert(param.is_output_or_mutable()); - - MFOutputSocket &socket = *node.outputs_[i]; - socket.data_type_ = param.data_type(); - socket.node_ = &node; - socket.index_ = i; - socket.is_output_ = true; - socket.name_ = function.param_name(param_index); - socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket); - } - - return node; -} - -/** - * Add a dummy node with the given input and output sockets. - */ -MFDummyNode &MFNetwork::add_dummy(StringRef name, - Span input_types, - Span output_types, - Span input_names, - Span output_names) -{ - assert_same_size(input_types, input_names); - assert_same_size(output_types, output_names); - - MFDummyNode &node = *allocator_.construct().release(); - dummy_nodes_.add_new(&node); - - node.network_ = this; - node.is_dummy_ = true; - node.name_ = allocator_.copy_string(name); - node.id_ = node_or_null_by_id_.append_and_get_index(&node); - - node.inputs_ = allocator_.construct_elements_and_pointer_array( - input_types.size()); - node.outputs_ = allocator_.construct_elements_and_pointer_array( - output_types.size()); - - node.input_names_ = allocator_.allocate_array(input_types.size()); - node.output_names_ = allocator_.allocate_array(output_types.size()); - - for (int i : input_types.index_range()) { - MFInputSocket &socket = *node.inputs_[i]; - socket.data_type_ = input_types[i]; - socket.node_ = &node; - socket.index_ = i; - socket.is_output_ = false; - socket.name_ = allocator_.copy_string(input_names[i]); - socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket); - node.input_names_[i] = socket.name_; - } - - for (int i : output_types.index_range()) { - MFOutputSocket &socket = *node.outputs_[i]; - socket.data_type_ = output_types[i]; - socket.node_ = &node; - socket.index_ = i; - socket.is_output_ = true; - socket.name_ = allocator_.copy_string(output_names[i]); - socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket); - node.output_names_[i] = socket.name_; - } - - return node; -} - -/** - * Connect two sockets. This invokes undefined behavior if the sockets belong to different - * networks, the sockets have a different data type, or the `to` socket is connected to something - * else already. - */ -void MFNetwork::add_link(MFOutputSocket &from, MFInputSocket &to) -{ - BLI_assert(to.origin_ == nullptr); - BLI_assert(from.node_->network_ == to.node_->network_); - BLI_assert(from.data_type_ == to.data_type_); - from.targets_.append(&to); - to.origin_ = &from; -} - -MFOutputSocket &MFNetwork::add_input(StringRef name, MFDataType data_type) -{ - return this->add_dummy(name, {}, {data_type}, {}, {"Value"}).output(0); -} - -MFInputSocket &MFNetwork::add_output(StringRef name, MFDataType data_type) -{ - return this->add_dummy(name, {data_type}, {}, {"Value"}, {}).input(0); -} - -void MFNetwork::relink(MFOutputSocket &old_output, MFOutputSocket &new_output) -{ - BLI_assert(&old_output != &new_output); - BLI_assert(old_output.data_type_ == new_output.data_type_); - for (MFInputSocket *input : old_output.targets()) { - input->origin_ = &new_output; - } - new_output.targets_.extend(old_output.targets_); - old_output.targets_.clear(); -} - -void MFNetwork::remove(MFNode &node) -{ - for (MFInputSocket *socket : node.inputs_) { - if (socket->origin_ != nullptr) { - socket->origin_->targets_.remove_first_occurrence_and_reorder(socket); - } - socket_or_null_by_id_[socket->id_] = nullptr; - } - for (MFOutputSocket *socket : node.outputs_) { - for (MFInputSocket *other : socket->targets_) { - other->origin_ = nullptr; - } - socket_or_null_by_id_[socket->id_] = nullptr; - } - node.destruct_sockets(); - if (node.is_dummy()) { - MFDummyNode &dummy_node = node.as_dummy(); - dummy_node.~MFDummyNode(); - dummy_nodes_.remove_contained(&dummy_node); - } - else { - MFFunctionNode &function_node = node.as_function(); - function_node.~MFFunctionNode(); - function_nodes_.remove_contained(&function_node); - } - node_or_null_by_id_[node.id_] = nullptr; -} - -void MFNetwork::remove(Span nodes) -{ - for (MFNode *node : nodes) { - this->remove(*node); - } -} - -void MFNetwork::find_dependencies(Span sockets, - VectorSet &r_dummy_sockets, - VectorSet &r_unlinked_inputs) const -{ - Set visited_nodes; - Stack sockets_to_check; - sockets_to_check.push_multiple(sockets); - - while (!sockets_to_check.is_empty()) { - const MFInputSocket &socket = *sockets_to_check.pop(); - const MFOutputSocket *origin_socket = socket.origin(); - if (origin_socket == nullptr) { - r_unlinked_inputs.add(&socket); - continue; - } - - const MFNode &origin_node = origin_socket->node(); - - if (origin_node.is_dummy()) { - r_dummy_sockets.add(origin_socket); - continue; - } - - if (visited_nodes.add(&origin_node)) { - sockets_to_check.push_multiple(origin_node.inputs()); - } - } -} - -bool MFNetwork::have_dummy_or_unlinked_dependencies(Span sockets) const -{ - VectorSet dummy_sockets; - VectorSet unlinked_inputs; - this->find_dependencies(sockets, dummy_sockets, unlinked_inputs); - return dummy_sockets.size() + unlinked_inputs.size() > 0; -} - -std::string MFNetwork::to_dot(Span marked_nodes) const -{ - dot::DirectedGraph digraph; - digraph.set_rankdir(dot::Attr_rankdir::LeftToRight); - - Map dot_nodes; - - Vector all_nodes; - all_nodes.extend(function_nodes_.as_span().cast()); - all_nodes.extend(dummy_nodes_.as_span().cast()); - - for (const MFNode *node : all_nodes) { - dot::Node &dot_node = digraph.new_node(""); - - Vector input_names, output_names; - for (const MFInputSocket *socket : node->inputs_) { - input_names.append(socket->name() + "(" + socket->data_type().to_string() + ")"); - } - for (const MFOutputSocket *socket : node->outputs_) { - output_names.append(socket->name() + " (" + socket->data_type().to_string() + ")"); - } - - dot::NodeWithSocketsRef dot_node_ref{dot_node, node->name(), input_names, output_names}; - dot_nodes.add_new(node, dot_node_ref); - } - - for (const MFDummyNode *node : dummy_nodes_) { - dot_nodes.lookup(node).node().set_background_color("#77EE77"); - } - for (const MFNode *node : marked_nodes) { - dot_nodes.lookup(node).node().set_background_color("#7777EE"); - } - - for (const MFNode *to_node : all_nodes) { - dot::NodeWithSocketsRef to_dot_node = dot_nodes.lookup(to_node); - - for (const MFInputSocket *to_socket : to_node->inputs_) { - const MFOutputSocket *from_socket = to_socket->origin_; - if (from_socket != nullptr) { - const MFNode *from_node = from_socket->node_; - dot::NodeWithSocketsRef from_dot_node = dot_nodes.lookup(from_node); - digraph.new_edge(from_dot_node.output(from_socket->index_), - to_dot_node.input(to_socket->index_)); - } - } - } - - return digraph.to_dot_string(); -} - -} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc deleted file mode 100644 index 9a0cb0c35ce..00000000000 --- a/source/blender/functions/intern/multi_function_network_evaluation.cc +++ /dev/null @@ -1,1083 +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. - */ - -/** \file - * \ingroup fn - * - * The `MFNetworkEvaluator` class is a multi-function that consists of potentially many smaller - * multi-functions. When called, it traverses the underlying MFNetwork and executes the required - * function nodes. - * - * There are many possible approaches to evaluate a function network. The approach implemented - * below has the following features: - * - It does not use recursion. Those could become problematic with long node chains. - * - It can handle all existing parameter types (including mutable parameters). - * - Avoids data copies in many cases. - * - Every node is executed at most once. - * - Can compute sub-functions on a single element, when the result is the same for all elements. - * - * Possible improvements: - * - Cache and reuse buffers. - * - Use "deepest depth first" heuristic to decide which order the inputs of a node should be - * computed. This reduces the number of required temporary buffers when they are reused. - */ - -#include "FN_multi_function_network_evaluation.hh" - -#include "BLI_resource_scope.hh" -#include "BLI_stack.hh" - -namespace blender::fn { - -struct Value; - -/** - * This keeps track of all the values that flow through the multi-function network. Therefore it - * maintains a mapping between output sockets and their corresponding values. Every `value` - * references some memory, that is owned either by the caller or this storage. - * - * A value can be owned by different sockets over time to avoid unnecessary copies. - */ -class MFNetworkEvaluationStorage { - private: - LinearAllocator<> allocator_; - IndexMask mask_; - Array value_per_output_id_; - int64_t min_array_size_; - - public: - MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount); - ~MFNetworkEvaluationStorage(); - - /* Add the values that have been provided by the caller of the multi-function network. */ - void add_single_input_from_caller(const MFOutputSocket &socket, const GVArray &virtual_array); - void add_vector_input_from_caller(const MFOutputSocket &socket, - const GVVectorArray &virtual_vector_array); - void add_single_output_from_caller(const MFOutputSocket &socket, GMutableSpan span); - void add_vector_output_from_caller(const MFOutputSocket &socket, GVectorArray &vector_array); - - /* Get input buffers for function node evaluations. */ - const GVArray &get_single_input__full(const MFInputSocket &socket, ResourceScope &scope); - const GVArray &get_single_input__single(const MFInputSocket &socket, ResourceScope &scope); - const GVVectorArray &get_vector_input__full(const MFInputSocket &socket, ResourceScope &scope); - const GVVectorArray &get_vector_input__single(const MFInputSocket &socket, ResourceScope &scope); - - /* Get output buffers for function node evaluations. */ - GMutableSpan get_single_output__full(const MFOutputSocket &socket); - GMutableSpan get_single_output__single(const MFOutputSocket &socket); - GVectorArray &get_vector_output__full(const MFOutputSocket &socket); - GVectorArray &get_vector_output__single(const MFOutputSocket &socket); - - /* Get mutable buffers for function node evaluations. */ - GMutableSpan get_mutable_single__full(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope); - GMutableSpan get_mutable_single__single(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope); - GVectorArray &get_mutable_vector__full(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope); - GVectorArray &get_mutable_vector__single(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope); - - /* Mark a node as being done with evaluation. This might free temporary buffers that are no - * longer needed. */ - void finish_node(const MFFunctionNode &node); - void finish_output_socket(const MFOutputSocket &socket); - void finish_input_socket(const MFInputSocket &socket); - - IndexMask mask() const; - bool socket_is_computed(const MFOutputSocket &socket); - bool is_same_value_for_every_index(const MFOutputSocket &socket); - bool socket_has_buffer_for_output(const MFOutputSocket &socket); -}; - -MFNetworkEvaluator::MFNetworkEvaluator(Vector inputs, - Vector outputs) - : inputs_(std::move(inputs)), outputs_(std::move(outputs)) -{ - BLI_assert(outputs_.size() > 0); - MFSignatureBuilder signature{"Function Tree"}; - - for (const MFOutputSocket *socket : inputs_) { - BLI_assert(socket->node().is_dummy()); - - MFDataType type = socket->data_type(); - switch (type.category()) { - case MFDataType::Single: - signature.single_input(socket->name(), type.single_type()); - break; - case MFDataType::Vector: - signature.vector_input(socket->name(), type.vector_base_type()); - break; - } - } - - for (const MFInputSocket *socket : outputs_) { - BLI_assert(socket->node().is_dummy()); - - MFDataType type = socket->data_type(); - switch (type.category()) { - case MFDataType::Single: - signature.single_output(socket->name(), type.single_type()); - break; - case MFDataType::Vector: - signature.vector_output(socket->name(), type.vector_base_type()); - break; - } - } - - signature_ = signature.build(); - this->set_signature(&signature_); -} - -void MFNetworkEvaluator::call(IndexMask mask, MFParams params, MFContext context) const -{ - if (mask.size() == 0) { - return; - } - - const MFNetwork &network = outputs_[0]->node().network(); - Storage storage(mask, network.socket_id_amount()); - - Vector outputs_to_initialize_in_the_end; - - this->copy_inputs_to_storage(params, storage); - this->copy_outputs_to_storage(params, storage, outputs_to_initialize_in_the_end); - this->evaluate_network_to_compute_outputs(context, storage); - this->initialize_remaining_outputs(params, storage, outputs_to_initialize_in_the_end); -} - -BLI_NOINLINE void MFNetworkEvaluator::copy_inputs_to_storage(MFParams params, - Storage &storage) const -{ - for (int input_index : inputs_.index_range()) { - int param_index = input_index + 0; - const MFOutputSocket &socket = *inputs_[input_index]; - switch (socket.data_type().category()) { - case MFDataType::Single: { - const GVArray &input_list = params.readonly_single_input(param_index); - storage.add_single_input_from_caller(socket, input_list); - break; - } - case MFDataType::Vector: { - const GVVectorArray &input_list_list = params.readonly_vector_input(param_index); - storage.add_vector_input_from_caller(socket, input_list_list); - break; - } - } - } -} - -BLI_NOINLINE void MFNetworkEvaluator::copy_outputs_to_storage( - MFParams params, - Storage &storage, - Vector &outputs_to_initialize_in_the_end) const -{ - for (int output_index : outputs_.index_range()) { - int param_index = output_index + inputs_.size(); - const MFInputSocket &socket = *outputs_[output_index]; - const MFOutputSocket &origin = *socket.origin(); - - if (origin.node().is_dummy()) { - BLI_assert(inputs_.contains(&origin)); - /* Don't overwrite input buffers. */ - outputs_to_initialize_in_the_end.append(&socket); - continue; - } - - if (storage.socket_has_buffer_for_output(origin)) { - /* When two outputs will be initialized to the same values. */ - outputs_to_initialize_in_the_end.append(&socket); - continue; - } - - switch (socket.data_type().category()) { - case MFDataType::Single: { - GMutableSpan span = params.uninitialized_single_output(param_index); - storage.add_single_output_from_caller(origin, span); - break; - } - case MFDataType::Vector: { - GVectorArray &vector_array = params.vector_output(param_index); - storage.add_vector_output_from_caller(origin, vector_array); - break; - } - } - } -} - -BLI_NOINLINE void MFNetworkEvaluator::evaluate_network_to_compute_outputs( - MFContext &global_context, Storage &storage) const -{ - Stack sockets_to_compute; - for (const MFInputSocket *socket : outputs_) { - sockets_to_compute.push(socket->origin()); - } - - /* This is the main loop that traverses the MFNetwork. */ - while (!sockets_to_compute.is_empty()) { - const MFOutputSocket &socket = *sockets_to_compute.peek(); - const MFNode &node = socket.node(); - - if (storage.socket_is_computed(socket)) { - sockets_to_compute.pop(); - continue; - } - - BLI_assert(node.is_function()); - BLI_assert(!node.has_unlinked_inputs()); - const MFFunctionNode &function_node = node.as_function(); - - bool all_origins_are_computed = true; - for (const MFInputSocket *input_socket : function_node.inputs()) { - const MFOutputSocket *origin = input_socket->origin(); - if (origin != nullptr) { - if (!storage.socket_is_computed(*origin)) { - sockets_to_compute.push(origin); - all_origins_are_computed = false; - } - } - } - - if (all_origins_are_computed) { - this->evaluate_function(global_context, function_node, storage); - sockets_to_compute.pop(); - } - } -} - -BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_context, - const MFFunctionNode &function_node, - Storage &storage) const -{ - - const MultiFunction &function = function_node.function(); - // std::cout << "Function: " << function.name() << "\n"; - - if (this->can_do_single_value_evaluation(function_node, storage)) { - /* The function output would be the same for all elements. Therefore, it is enough to call the - * function only on a single element. This can avoid many duplicate computations. */ - MFParamsBuilder params{function, 1}; - ResourceScope &scope = params.resource_scope(); - - for (int param_index : function.param_indices()) { - MFParamType param_type = function.param_type(param_index); - switch (param_type.category()) { - case MFParamType::SingleInput: { - const MFInputSocket &socket = function_node.input_for_param(param_index); - const GVArray &values = storage.get_single_input__single(socket, scope); - params.add_readonly_single_input(values); - break; - } - case MFParamType::VectorInput: { - const MFInputSocket &socket = function_node.input_for_param(param_index); - const GVVectorArray &values = storage.get_vector_input__single(socket, scope); - params.add_readonly_vector_input(values); - break; - } - case MFParamType::SingleOutput: { - const MFOutputSocket &socket = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_single_output__single(socket); - params.add_uninitialized_single_output(values); - break; - } - case MFParamType::VectorOutput: { - const MFOutputSocket &socket = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_vector_output__single(socket); - params.add_vector_output(values); - break; - } - case MFParamType::SingleMutable: { - const MFInputSocket &input = function_node.input_for_param(param_index); - const MFOutputSocket &output = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_mutable_single__single(input, output, scope); - params.add_single_mutable(values); - break; - } - case MFParamType::VectorMutable: { - const MFInputSocket &input = function_node.input_for_param(param_index); - const MFOutputSocket &output = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_mutable_vector__single(input, output, scope); - params.add_vector_mutable(values); - break; - } - } - } - - function.call(IndexRange(1), params, global_context); - } - else { - MFParamsBuilder params{function, storage.mask().min_array_size()}; - ResourceScope &scope = params.resource_scope(); - - for (int param_index : function.param_indices()) { - MFParamType param_type = function.param_type(param_index); - switch (param_type.category()) { - case MFParamType::SingleInput: { - const MFInputSocket &socket = function_node.input_for_param(param_index); - const GVArray &values = storage.get_single_input__full(socket, scope); - params.add_readonly_single_input(values); - break; - } - case MFParamType::VectorInput: { - const MFInputSocket &socket = function_node.input_for_param(param_index); - const GVVectorArray &values = storage.get_vector_input__full(socket, scope); - params.add_readonly_vector_input(values); - break; - } - case MFParamType::SingleOutput: { - const MFOutputSocket &socket = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_single_output__full(socket); - params.add_uninitialized_single_output(values); - break; - } - case MFParamType::VectorOutput: { - const MFOutputSocket &socket = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_vector_output__full(socket); - params.add_vector_output(values); - break; - } - case MFParamType::SingleMutable: { - const MFInputSocket &input = function_node.input_for_param(param_index); - const MFOutputSocket &output = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_mutable_single__full(input, output, scope); - params.add_single_mutable(values); - break; - } - case MFParamType::VectorMutable: { - const MFInputSocket &input = function_node.input_for_param(param_index); - const MFOutputSocket &output = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_mutable_vector__full(input, output, scope); - params.add_vector_mutable(values); - break; - } - } - } - - function.call(storage.mask(), params, global_context); - } - - storage.finish_node(function_node); -} - -bool MFNetworkEvaluator::can_do_single_value_evaluation(const MFFunctionNode &function_node, - Storage &storage) const -{ - for (const MFInputSocket *socket : function_node.inputs()) { - if (!storage.is_same_value_for_every_index(*socket->origin())) { - return false; - } - } - if (storage.mask().min_array_size() >= 1) { - for (const MFOutputSocket *socket : function_node.outputs()) { - if (storage.socket_has_buffer_for_output(*socket)) { - return false; - } - } - } - return true; -} - -BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs( - MFParams params, Storage &storage, Span remaining_outputs) const -{ - ResourceScope scope; - for (const MFInputSocket *socket : remaining_outputs) { - int param_index = inputs_.size() + outputs_.first_index_of(socket); - - switch (socket->data_type().category()) { - case MFDataType::Single: { - const GVArray &values = storage.get_single_input__full(*socket, scope); - GMutableSpan output_values = params.uninitialized_single_output(param_index); - values.materialize_to_uninitialized(storage.mask(), output_values.data()); - break; - } - case MFDataType::Vector: { - const GVVectorArray &values = storage.get_vector_input__full(*socket, scope); - GVectorArray &output_values = params.vector_output(param_index); - output_values.extend(storage.mask(), values); - break; - } - } - } -} - -/* -------------------------------------------------------------------- */ -/** \name Value Types - * \{ */ - -enum class ValueType { - InputSingle, - InputVector, - OutputSingle, - OutputVector, - OwnSingle, - OwnVector, -}; - -struct Value { - ValueType type; - - Value(ValueType type) : type(type) - { - } -}; - -struct InputSingleValue : public Value { - /** This virtual array has been provided by the code that called the multi-function network. */ - const GVArray &virtual_array; - - InputSingleValue(const GVArray &virtual_array) - : Value(ValueType::InputSingle), virtual_array(virtual_array) - { - } -}; - -struct InputVectorValue : public Value { - /** This virtual vector has been provided by the code that called the multi-function network. */ - const GVVectorArray &virtual_vector_array; - - InputVectorValue(const GVVectorArray &virtual_vector_array) - : Value(ValueType::InputVector), virtual_vector_array(virtual_vector_array) - { - } -}; - -struct OutputValue : public Value { - bool is_computed = false; - - OutputValue(ValueType type) : Value(type) - { - } -}; - -struct OutputSingleValue : public OutputValue { - /** This span has been provided by the code that called the multi-function network. */ - GMutableSpan span; - - OutputSingleValue(GMutableSpan span) : OutputValue(ValueType::OutputSingle), span(span) - { - } -}; - -struct OutputVectorValue : public OutputValue { - /** This vector array has been provided by the code that called the multi-function network. */ - GVectorArray *vector_array; - - OutputVectorValue(GVectorArray &vector_array) - : OutputValue(ValueType::OutputVector), vector_array(&vector_array) - { - } -}; - -struct OwnSingleValue : public Value { - /** This span has been allocated during the evaluation of the multi-function network and contains - * intermediate data. It has to be freed once the network evaluation is finished. */ - GMutableSpan span; - int max_remaining_users; - bool is_single_allocated; - - OwnSingleValue(GMutableSpan span, int max_remaining_users, bool is_single_allocated) - : Value(ValueType::OwnSingle), - span(span), - max_remaining_users(max_remaining_users), - is_single_allocated(is_single_allocated) - { - } -}; - -struct OwnVectorValue : public Value { - /** This vector array has been allocated during the evaluation of the multi-function network and - * contains intermediate data. It has to be freed once the network evaluation is finished. */ - GVectorArray *vector_array; - int max_remaining_users; - - OwnVectorValue(GVectorArray &vector_array, int max_remaining_users) - : Value(ValueType::OwnVector), - vector_array(&vector_array), - max_remaining_users(max_remaining_users) - { - } -}; - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Storage methods - * \{ */ - -MFNetworkEvaluationStorage::MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount) - : mask_(mask), - value_per_output_id_(socket_id_amount, nullptr), - min_array_size_(mask.min_array_size()) -{ -} - -MFNetworkEvaluationStorage::~MFNetworkEvaluationStorage() -{ - for (Value *any_value : value_per_output_id_) { - if (any_value == nullptr) { - continue; - } - if (any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast(any_value); - GMutableSpan span = value->span; - const CPPType &type = span.type(); - if (value->is_single_allocated) { - type.destruct(span.data()); - } - else { - type.destruct_indices(span.data(), mask_); - MEM_freeN(span.data()); - } - } - else if (any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast(any_value); - delete value->vector_array; - } - } -} - -IndexMask MFNetworkEvaluationStorage::mask() const -{ - return mask_; -} - -bool MFNetworkEvaluationStorage::socket_is_computed(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - return false; - } - if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) { - return static_cast(any_value)->is_computed; - } - return true; -} - -bool MFNetworkEvaluationStorage::is_same_value_for_every_index(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - switch (any_value->type) { - case ValueType::OwnSingle: - return static_cast(any_value)->span.size() == 1; - case ValueType::OwnVector: - return static_cast(any_value)->vector_array->size() == 1; - case ValueType::InputSingle: - return static_cast(any_value)->virtual_array.is_single(); - case ValueType::InputVector: - return static_cast(any_value)->virtual_vector_array.is_single_vector(); - case ValueType::OutputSingle: - return static_cast(any_value)->span.size() == 1; - case ValueType::OutputVector: - return static_cast(any_value)->vector_array->size() == 1; - } - BLI_assert(false); - return false; -} - -bool MFNetworkEvaluationStorage::socket_has_buffer_for_output(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - return false; - } - - BLI_assert(ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)); - return true; -} - -void MFNetworkEvaluationStorage::finish_node(const MFFunctionNode &node) -{ - for (const MFInputSocket *socket : node.inputs()) { - this->finish_input_socket(*socket); - } - for (const MFOutputSocket *socket : node.outputs()) { - this->finish_output_socket(*socket); - } -} - -void MFNetworkEvaluationStorage::finish_output_socket(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - return; - } - - if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) { - static_cast(any_value)->is_computed = true; - } -} - -void MFNetworkEvaluationStorage::finish_input_socket(const MFInputSocket &socket) -{ - const MFOutputSocket &origin = *socket.origin(); - - Value *any_value = value_per_output_id_[origin.id()]; - if (any_value == nullptr) { - /* Can happen when a value has been forward to the next node. */ - return; - } - - switch (any_value->type) { - case ValueType::InputSingle: - case ValueType::OutputSingle: - case ValueType::InputVector: - case ValueType::OutputVector: { - break; - } - case ValueType::OwnSingle: { - OwnSingleValue *value = static_cast(any_value); - BLI_assert(value->max_remaining_users >= 1); - value->max_remaining_users--; - if (value->max_remaining_users == 0) { - GMutableSpan span = value->span; - const CPPType &type = span.type(); - if (value->is_single_allocated) { - type.destruct(span.data()); - } - else { - type.destruct_indices(span.data(), mask_); - MEM_freeN(span.data()); - } - value_per_output_id_[origin.id()] = nullptr; - } - break; - } - case ValueType::OwnVector: { - OwnVectorValue *value = static_cast(any_value); - BLI_assert(value->max_remaining_users >= 1); - value->max_remaining_users--; - if (value->max_remaining_users == 0) { - delete value->vector_array; - value_per_output_id_[origin.id()] = nullptr; - } - break; - } - } -} - -void MFNetworkEvaluationStorage::add_single_input_from_caller(const MFOutputSocket &socket, - const GVArray &virtual_array) -{ - BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(virtual_array.size() >= min_array_size_); - - auto *value = allocator_.construct(virtual_array).release(); - value_per_output_id_[socket.id()] = value; -} - -void MFNetworkEvaluationStorage::add_vector_input_from_caller( - const MFOutputSocket &socket, const GVVectorArray &virtual_vector_array) -{ - BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(virtual_vector_array.size() >= min_array_size_); - - auto *value = allocator_.construct(virtual_vector_array).release(); - value_per_output_id_[socket.id()] = value; -} - -void MFNetworkEvaluationStorage::add_single_output_from_caller(const MFOutputSocket &socket, - GMutableSpan span) -{ - BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(span.size() >= min_array_size_); - - auto *value = allocator_.construct(span).release(); - value_per_output_id_[socket.id()] = value; -} - -void MFNetworkEvaluationStorage::add_vector_output_from_caller(const MFOutputSocket &socket, - GVectorArray &vector_array) -{ - BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(vector_array.size() >= min_array_size_); - - auto *value = allocator_.construct(vector_array).release(); - value_per_output_id_[socket.id()] = value; -} - -GMutableSpan MFNetworkEvaluationStorage::get_single_output__full(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - const CPPType &type = socket.data_type().single_type(); - void *buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT); - GMutableSpan span(type, buffer, min_array_size_); - - auto *value = - allocator_.construct(span, socket.targets().size(), false).release(); - value_per_output_id_[socket.id()] = value; - - return span; - } - - BLI_assert(any_value->type == ValueType::OutputSingle); - return static_cast(any_value)->span; -} - -GMutableSpan MFNetworkEvaluationStorage::get_single_output__single(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - const CPPType &type = socket.data_type().single_type(); - void *buffer = allocator_.allocate(type.size(), type.alignment()); - GMutableSpan span(type, buffer, 1); - - auto *value = - allocator_.construct(span, socket.targets().size(), true).release(); - value_per_output_id_[socket.id()] = value; - - return value->span; - } - - BLI_assert(any_value->type == ValueType::OutputSingle); - GMutableSpan span = static_cast(any_value)->span; - BLI_assert(span.size() == 1); - return span; -} - -GVectorArray &MFNetworkEvaluationStorage::get_vector_output__full(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - const CPPType &type = socket.data_type().vector_base_type(); - GVectorArray *vector_array = new GVectorArray(type, min_array_size_); - - auto *value = - allocator_.construct(*vector_array, socket.targets().size()).release(); - value_per_output_id_[socket.id()] = value; - - return *value->vector_array; - } - - BLI_assert(any_value->type == ValueType::OutputVector); - return *static_cast(any_value)->vector_array; -} - -GVectorArray &MFNetworkEvaluationStorage::get_vector_output__single(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - const CPPType &type = socket.data_type().vector_base_type(); - GVectorArray *vector_array = new GVectorArray(type, 1); - - auto *value = - allocator_.construct(*vector_array, socket.targets().size()).release(); - value_per_output_id_[socket.id()] = value; - - return *value->vector_array; - } - - BLI_assert(any_value->type == ValueType::OutputVector); - GVectorArray &vector_array = *static_cast(any_value)->vector_array; - BLI_assert(vector_array.size() == 1); - return vector_array; -} - -GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope) -{ - const MFOutputSocket &from = *input.origin(); - const MFOutputSocket &to = output; - const CPPType &type = from.data_type().single_type(); - - Value *from_any_value = value_per_output_id_[from.id()]; - Value *to_any_value = value_per_output_id_[to.id()]; - BLI_assert(from_any_value != nullptr); - BLI_assert(type == to.data_type().single_type()); - - if (to_any_value != nullptr) { - BLI_assert(to_any_value->type == ValueType::OutputSingle); - GMutableSpan span = static_cast(to_any_value)->span; - const GVArray &virtual_array = this->get_single_input__full(input, scope); - virtual_array.materialize_to_uninitialized(mask_, span.data()); - return span; - } - - if (from_any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast(from_any_value); - if (value->max_remaining_users == 1 && !value->is_single_allocated) { - value_per_output_id_[to.id()] = value; - value_per_output_id_[from.id()] = nullptr; - value->max_remaining_users = to.targets().size(); - return value->span; - } - } - - const GVArray &virtual_array = this->get_single_input__full(input, scope); - void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT); - GMutableSpan new_array_ref(type, new_buffer, min_array_size_); - virtual_array.materialize_to_uninitialized(mask_, new_array_ref.data()); - - OwnSingleValue *new_value = - allocator_.construct(new_array_ref, to.targets().size(), false).release(); - value_per_output_id_[to.id()] = new_value; - return new_array_ref; -} - -GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope) -{ - const MFOutputSocket &from = *input.origin(); - const MFOutputSocket &to = output; - const CPPType &type = from.data_type().single_type(); - - Value *from_any_value = value_per_output_id_[from.id()]; - Value *to_any_value = value_per_output_id_[to.id()]; - BLI_assert(from_any_value != nullptr); - BLI_assert(type == to.data_type().single_type()); - - if (to_any_value != nullptr) { - BLI_assert(to_any_value->type == ValueType::OutputSingle); - GMutableSpan span = static_cast(to_any_value)->span; - BLI_assert(span.size() == 1); - const GVArray &virtual_array = this->get_single_input__single(input, scope); - virtual_array.get_single_to_uninitialized(span[0]); - return span; - } - - if (from_any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast(from_any_value); - if (value->max_remaining_users == 1) { - value_per_output_id_[to.id()] = value; - value_per_output_id_[from.id()] = nullptr; - value->max_remaining_users = to.targets().size(); - BLI_assert(value->span.size() == 1); - return value->span; - } - } - - const GVArray &virtual_array = this->get_single_input__single(input, scope); - - void *new_buffer = allocator_.allocate(type.size(), type.alignment()); - virtual_array.get_single_to_uninitialized(new_buffer); - GMutableSpan new_array_ref(type, new_buffer, 1); - - OwnSingleValue *new_value = - allocator_.construct(new_array_ref, to.targets().size(), true).release(); - value_per_output_id_[to.id()] = new_value; - return new_array_ref; -} - -GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope) -{ - const MFOutputSocket &from = *input.origin(); - const MFOutputSocket &to = output; - const CPPType &base_type = from.data_type().vector_base_type(); - - Value *from_any_value = value_per_output_id_[from.id()]; - Value *to_any_value = value_per_output_id_[to.id()]; - BLI_assert(from_any_value != nullptr); - BLI_assert(base_type == to.data_type().vector_base_type()); - - if (to_any_value != nullptr) { - BLI_assert(to_any_value->type == ValueType::OutputVector); - GVectorArray &vector_array = *static_cast(to_any_value)->vector_array; - const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope); - vector_array.extend(mask_, virtual_vector_array); - return vector_array; - } - - if (from_any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast(from_any_value); - if (value->max_remaining_users == 1) { - value_per_output_id_[to.id()] = value; - value_per_output_id_[from.id()] = nullptr; - value->max_remaining_users = to.targets().size(); - return *value->vector_array; - } - } - - const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope); - - GVectorArray *new_vector_array = new GVectorArray(base_type, min_array_size_); - new_vector_array->extend(mask_, virtual_vector_array); - - OwnVectorValue *new_value = - allocator_.construct(*new_vector_array, to.targets().size()).release(); - value_per_output_id_[to.id()] = new_value; - - return *new_vector_array; -} - -GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope) -{ - const MFOutputSocket &from = *input.origin(); - const MFOutputSocket &to = output; - const CPPType &base_type = from.data_type().vector_base_type(); - - Value *from_any_value = value_per_output_id_[from.id()]; - Value *to_any_value = value_per_output_id_[to.id()]; - BLI_assert(from_any_value != nullptr); - BLI_assert(base_type == to.data_type().vector_base_type()); - - if (to_any_value != nullptr) { - BLI_assert(to_any_value->type == ValueType::OutputVector); - GVectorArray &vector_array = *static_cast(to_any_value)->vector_array; - BLI_assert(vector_array.size() == 1); - const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope); - vector_array.extend({0}, virtual_vector_array); - return vector_array; - } - - if (from_any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast(from_any_value); - if (value->max_remaining_users == 1) { - value_per_output_id_[to.id()] = value; - value_per_output_id_[from.id()] = nullptr; - value->max_remaining_users = to.targets().size(); - return *value->vector_array; - } - } - - const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope); - - GVectorArray *new_vector_array = new GVectorArray(base_type, 1); - new_vector_array->extend({0}, virtual_vector_array); - - OwnVectorValue *new_value = - allocator_.construct(*new_vector_array, to.targets().size()).release(); - value_per_output_id_[to.id()] = new_value; - return *new_vector_array; -} - -const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &socket, - ResourceScope &scope) -{ - const MFOutputSocket &origin = *socket.origin(); - Value *any_value = value_per_output_id_[origin.id()]; - BLI_assert(any_value != nullptr); - - if (any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast(any_value); - if (value->is_single_allocated) { - return scope.construct( - __func__, value->span.type(), min_array_size_, value->span.data()); - } - - return scope.construct(__func__, value->span); - } - if (any_value->type == ValueType::InputSingle) { - InputSingleValue *value = static_cast(any_value); - return value->virtual_array; - } - if (any_value->type == ValueType::OutputSingle) { - OutputSingleValue *value = static_cast(any_value); - BLI_assert(value->is_computed); - return scope.construct(__func__, value->span); - } - - BLI_assert(false); - return scope.construct(__func__, CPPType::get()); -} - -const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket, - ResourceScope &scope) -{ - const MFOutputSocket &origin = *socket.origin(); - Value *any_value = value_per_output_id_[origin.id()]; - BLI_assert(any_value != nullptr); - - if (any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast(any_value); - BLI_assert(value->span.size() == 1); - return scope.construct(__func__, value->span); - } - if (any_value->type == ValueType::InputSingle) { - InputSingleValue *value = static_cast(any_value); - BLI_assert(value->virtual_array.is_single()); - return value->virtual_array; - } - if (any_value->type == ValueType::OutputSingle) { - OutputSingleValue *value = static_cast(any_value); - BLI_assert(value->is_computed); - BLI_assert(value->span.size() == 1); - return scope.construct(__func__, value->span); - } - - BLI_assert(false); - return scope.construct(__func__, CPPType::get()); -} - -const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( - const MFInputSocket &socket, ResourceScope &scope) -{ - const MFOutputSocket &origin = *socket.origin(); - Value *any_value = value_per_output_id_[origin.id()]; - BLI_assert(any_value != nullptr); - - if (any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast(any_value); - if (value->vector_array->size() == 1) { - GSpan span = (*value->vector_array)[0]; - return scope.construct(__func__, span, min_array_size_); - } - - return scope.construct(__func__, *value->vector_array); - } - if (any_value->type == ValueType::InputVector) { - InputVectorValue *value = static_cast(any_value); - return value->virtual_vector_array; - } - if (any_value->type == ValueType::OutputVector) { - OutputVectorValue *value = static_cast(any_value); - return scope.construct(__func__, *value->vector_array); - } - - BLI_assert(false); - return scope.construct(__func__, GSpan(CPPType::get()), 0); -} - -const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( - const MFInputSocket &socket, ResourceScope &scope) -{ - const MFOutputSocket &origin = *socket.origin(); - Value *any_value = value_per_output_id_[origin.id()]; - BLI_assert(any_value != nullptr); - - if (any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast(any_value); - BLI_assert(value->vector_array->size() == 1); - return scope.construct(__func__, *value->vector_array); - } - if (any_value->type == ValueType::InputVector) { - InputVectorValue *value = static_cast(any_value); - BLI_assert(value->virtual_vector_array.is_single_vector()); - return value->virtual_vector_array; - } - if (any_value->type == ValueType::OutputVector) { - OutputVectorValue *value = static_cast(any_value); - BLI_assert(value->vector_array->size() == 1); - return scope.construct(__func__, *value->vector_array); - } - - BLI_assert(false); - return scope.construct(__func__, GSpan(CPPType::get()), 0); -} - -/** \} */ - -} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc deleted file mode 100644 index 75c3583c5e5..00000000000 --- a/source/blender/functions/intern/multi_function_network_optimization.cc +++ /dev/null @@ -1,501 +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. - */ - -/** \file - * \ingroup fn - */ - -/* Used to check if two multi-functions have the exact same type. */ -#include - -#include "FN_multi_function_builder.hh" -#include "FN_multi_function_network_evaluation.hh" -#include "FN_multi_function_network_optimization.hh" - -#include "BLI_disjoint_set.hh" -#include "BLI_ghash.h" -#include "BLI_map.hh" -#include "BLI_multi_value_map.hh" -#include "BLI_rand.h" -#include "BLI_stack.hh" - -namespace blender::fn::mf_network_optimization { - -/* -------------------------------------------------------------------- */ -/** \name Utility functions to find nodes in a network. - * \{ */ - -static bool set_tag_and_check_if_modified(bool &tag, bool new_value) -{ - if (tag != new_value) { - tag = new_value; - return true; - } - - return false; -} - -static Array mask_nodes_to_the_left(MFNetwork &network, Span nodes) -{ - Array is_to_the_left(network.node_id_amount(), false); - Stack nodes_to_check; - - for (MFNode *node : nodes) { - is_to_the_left[node->id()] = true; - nodes_to_check.push(node); - } - - while (!nodes_to_check.is_empty()) { - MFNode &node = *nodes_to_check.pop(); - - for (MFInputSocket *input_socket : node.inputs()) { - MFOutputSocket *origin = input_socket->origin(); - if (origin != nullptr) { - MFNode &origin_node = origin->node(); - if (set_tag_and_check_if_modified(is_to_the_left[origin_node.id()], true)) { - nodes_to_check.push(&origin_node); - } - } - } - } - - return is_to_the_left; -} - -static Array mask_nodes_to_the_right(MFNetwork &network, Span nodes) -{ - Array is_to_the_right(network.node_id_amount(), false); - Stack nodes_to_check; - - for (MFNode *node : nodes) { - is_to_the_right[node->id()] = true; - nodes_to_check.push(node); - } - - while (!nodes_to_check.is_empty()) { - MFNode &node = *nodes_to_check.pop(); - - for (MFOutputSocket *output_socket : node.outputs()) { - for (MFInputSocket *target_socket : output_socket->targets()) { - MFNode &target_node = target_socket->node(); - if (set_tag_and_check_if_modified(is_to_the_right[target_node.id()], true)) { - nodes_to_check.push(&target_node); - } - } - } - } - - return is_to_the_right; -} - -static Vector find_nodes_based_on_mask(MFNetwork &network, - Span id_mask, - bool mask_value) -{ - Vector nodes; - for (int id : id_mask.index_range()) { - if (id_mask[id] == mask_value) { - MFNode *node = network.node_or_null_by_id(id); - if (node != nullptr) { - nodes.append(node); - } - } - } - return nodes; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Dead Node Removal - * \{ */ - -/** - * Unused nodes are all those nodes that no dummy node depends upon. - */ -void dead_node_removal(MFNetwork &network) -{ - Array node_is_used_mask = mask_nodes_to_the_left(network, - network.dummy_nodes().cast()); - Vector nodes_to_remove = find_nodes_based_on_mask(network, node_is_used_mask, false); - network.remove(nodes_to_remove); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Constant Folding - * \{ */ - -static bool function_node_can_be_constant(MFFunctionNode *node) -{ - if (node->has_unlinked_inputs()) { - return false; - } - if (node->function().depends_on_context()) { - return false; - } - return true; -} - -static Vector find_non_constant_nodes(MFNetwork &network) -{ - Vector non_constant_nodes; - non_constant_nodes.extend(network.dummy_nodes().cast()); - - for (MFFunctionNode *node : network.function_nodes()) { - if (!function_node_can_be_constant(node)) { - non_constant_nodes.append(node); - } - } - return non_constant_nodes; -} - -static bool output_has_non_constant_target_node(MFOutputSocket *output_socket, - Span is_not_constant_mask) -{ - for (MFInputSocket *target_socket : output_socket->targets()) { - MFNode &target_node = target_socket->node(); - bool target_is_not_constant = is_not_constant_mask[target_node.id()]; - if (target_is_not_constant) { - return true; - } - } - return false; -} - -static MFInputSocket *try_find_dummy_target_socket(MFOutputSocket *output_socket) -{ - for (MFInputSocket *target_socket : output_socket->targets()) { - if (target_socket->node().is_dummy()) { - return target_socket; - } - } - return nullptr; -} - -static Vector find_constant_inputs_to_fold( - MFNetwork &network, Vector &r_temporary_nodes) -{ - Vector non_constant_nodes = find_non_constant_nodes(network); - Array is_not_constant_mask = mask_nodes_to_the_right(network, non_constant_nodes); - Vector constant_nodes = find_nodes_based_on_mask(network, is_not_constant_mask, false); - - Vector sockets_to_compute; - for (MFNode *node : constant_nodes) { - if (node->inputs().size() == 0) { - continue; - } - - for (MFOutputSocket *output_socket : node->outputs()) { - MFDataType data_type = output_socket->data_type(); - if (output_has_non_constant_target_node(output_socket, is_not_constant_mask)) { - MFInputSocket *dummy_target = try_find_dummy_target_socket(output_socket); - if (dummy_target == nullptr) { - dummy_target = &network.add_output("Dummy", data_type); - network.add_link(*output_socket, *dummy_target); - r_temporary_nodes.append(&dummy_target->node().as_dummy()); - } - - sockets_to_compute.append(dummy_target); - } - } - } - return sockets_to_compute; -} - -static void prepare_params_for_constant_folding(const MultiFunction &network_fn, - MFParamsBuilder ¶ms, - ResourceScope &scope) -{ - for (int param_index : network_fn.param_indices()) { - MFParamType param_type = network_fn.param_type(param_index); - MFDataType data_type = param_type.data_type(); - - switch (data_type.category()) { - case MFDataType::Single: { - /* Allocates memory for a single constant folded value. */ - const CPPType &cpp_type = data_type.single_type(); - void *buffer = scope.linear_allocator().allocate(cpp_type.size(), cpp_type.alignment()); - GMutableSpan array{cpp_type, buffer, 1}; - params.add_uninitialized_single_output(array); - break; - } - case MFDataType::Vector: { - /* Allocates memory for a constant folded vector. */ - const CPPType &cpp_type = data_type.vector_base_type(); - GVectorArray &vector_array = scope.construct(AT, cpp_type, 1); - params.add_vector_output(vector_array); - break; - } - } - } -} - -static Array add_constant_folded_sockets(const MultiFunction &network_fn, - MFParamsBuilder ¶ms, - ResourceScope &scope, - MFNetwork &network) -{ - Array folded_sockets{network_fn.param_indices().size(), nullptr}; - - for (int param_index : network_fn.param_indices()) { - MFParamType param_type = network_fn.param_type(param_index); - MFDataType data_type = param_type.data_type(); - - const MultiFunction *constant_fn = nullptr; - - switch (data_type.category()) { - case MFDataType::Single: { - const CPPType &cpp_type = data_type.single_type(); - GMutableSpan array = params.computed_array(param_index); - void *buffer = array.data(); - scope.add(buffer, array.type().destruct_fn(), AT); - - constant_fn = &scope.construct(AT, cpp_type, buffer); - break; - } - case MFDataType::Vector: { - GVectorArray &vector_array = params.computed_vector_array(param_index); - GSpan array = vector_array[0]; - constant_fn = &scope.construct(AT, array); - break; - } - } - - MFFunctionNode &folded_node = network.add_function(*constant_fn); - folded_sockets[param_index] = &folded_node.output(0); - } - return folded_sockets; -} - -static Array compute_constant_sockets_and_add_folded_nodes( - MFNetwork &network, Span sockets_to_compute, ResourceScope &scope) -{ - MFNetworkEvaluator network_fn{{}, sockets_to_compute}; - - MFContextBuilder context; - MFParamsBuilder params{network_fn, 1}; - prepare_params_for_constant_folding(network_fn, params, scope); - network_fn.call({0}, params, context); - return add_constant_folded_sockets(network_fn, params, scope, network); -} - -class MyClass { - MFDummyNode node; -}; - -/** - * Find function nodes that always output the same value and replace those with constant nodes. - */ -void constant_folding(MFNetwork &network, ResourceScope &scope) -{ - Vector temporary_nodes; - Vector inputs_to_fold = find_constant_inputs_to_fold(network, temporary_nodes); - if (inputs_to_fold.size() == 0) { - return; - } - - Array folded_sockets = compute_constant_sockets_and_add_folded_nodes( - network, inputs_to_fold, scope); - - for (int i : inputs_to_fold.index_range()) { - MFOutputSocket &original_socket = *inputs_to_fold[i]->origin(); - network.relink(original_socket, *folded_sockets[i]); - } - - network.remove(temporary_nodes.as_span().cast()); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Common Sub-network Elimination - * \{ */ - -static uint64_t compute_node_hash(MFFunctionNode &node, RNG *rng, Span node_hashes) -{ - if (node.function().depends_on_context()) { - return BLI_rng_get_uint(rng); - } - if (node.has_unlinked_inputs()) { - return BLI_rng_get_uint(rng); - } - - uint64_t combined_inputs_hash = 394659347u; - for (MFInputSocket *input_socket : node.inputs()) { - MFOutputSocket *origin_socket = input_socket->origin(); - uint64_t input_hash = BLI_ghashutil_combine_hash(node_hashes[origin_socket->node().id()], - origin_socket->index()); - combined_inputs_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, input_hash); - } - - uint64_t function_hash = node.function().hash(); - uint64_t node_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, function_hash); - return node_hash; -} - -/** - * Produces a hash for every node. Two nodes with the same hash should have a high probability of - * outputting the same values. - */ -static Array compute_node_hashes(MFNetwork &network) -{ - RNG *rng = BLI_rng_new(0); - Array node_hashes(network.node_id_amount()); - Array node_is_hashed(network.node_id_amount(), false); - - /* No dummy nodes are not assumed to output the same values. */ - for (MFDummyNode *node : network.dummy_nodes()) { - uint64_t node_hash = BLI_rng_get_uint(rng); - node_hashes[node->id()] = node_hash; - node_is_hashed[node->id()] = true; - } - - Stack nodes_to_check; - nodes_to_check.push_multiple(network.function_nodes()); - - while (!nodes_to_check.is_empty()) { - MFFunctionNode &node = *nodes_to_check.peek(); - if (node_is_hashed[node.id()]) { - nodes_to_check.pop(); - continue; - } - - /* Make sure that origin nodes are hashed first. */ - bool all_dependencies_ready = true; - for (MFInputSocket *input_socket : node.inputs()) { - MFOutputSocket *origin_socket = input_socket->origin(); - if (origin_socket != nullptr) { - MFNode &origin_node = origin_socket->node(); - if (!node_is_hashed[origin_node.id()]) { - all_dependencies_ready = false; - nodes_to_check.push(&origin_node.as_function()); - } - } - } - if (!all_dependencies_ready) { - continue; - } - - uint64_t node_hash = compute_node_hash(node, rng, node_hashes); - node_hashes[node.id()] = node_hash; - node_is_hashed[node.id()] = true; - nodes_to_check.pop(); - } - - BLI_rng_free(rng); - return node_hashes; -} - -static MultiValueMap group_nodes_by_hash(MFNetwork &network, - Span node_hashes) -{ - MultiValueMap nodes_by_hash; - for (int id : IndexRange(network.node_id_amount())) { - MFNode *node = network.node_or_null_by_id(id); - if (node != nullptr) { - uint64_t node_hash = node_hashes[id]; - nodes_by_hash.add(node_hash, node); - } - } - return nodes_by_hash; -} - -static bool functions_are_equal(const MultiFunction &a, const MultiFunction &b) -{ - if (&a == &b) { - return true; - } - if (typeid(a) == typeid(b)) { - return a.equals(b); - } - return false; -} - -static bool nodes_output_same_values(DisjointSet &cache, const MFNode &a, const MFNode &b) -{ - if (cache.in_same_set(a.id(), b.id())) { - return true; - } - - if (a.is_dummy() || b.is_dummy()) { - return false; - } - if (!functions_are_equal(a.as_function().function(), b.as_function().function())) { - return false; - } - for (int i : a.inputs().index_range()) { - const MFOutputSocket *origin_a = a.input(i).origin(); - const MFOutputSocket *origin_b = b.input(i).origin(); - if (origin_a == nullptr || origin_b == nullptr) { - return false; - } - if (!nodes_output_same_values(cache, origin_a->node(), origin_b->node())) { - return false; - } - } - - cache.join(a.id(), b.id()); - return true; -} - -static void relink_duplicate_nodes(MFNetwork &network, - MultiValueMap &nodes_by_hash) -{ - DisjointSet same_node_cache{network.node_id_amount()}; - - for (Span nodes_with_same_hash : nodes_by_hash.values()) { - if (nodes_with_same_hash.size() <= 1) { - continue; - } - - Vector nodes_to_check = nodes_with_same_hash; - while (nodes_to_check.size() >= 2) { - Vector remaining_nodes; - - MFNode &deduplicated_node = *nodes_to_check[0]; - for (MFNode *node : nodes_to_check.as_span().drop_front(1)) { - /* This is true with fairly high probability, but hash collisions can happen. So we have to - * check if the node actually output the same values. */ - if (nodes_output_same_values(same_node_cache, deduplicated_node, *node)) { - for (int i : deduplicated_node.outputs().index_range()) { - network.relink(node->output(i), deduplicated_node.output(i)); - } - } - else { - remaining_nodes.append(node); - } - } - nodes_to_check = std::move(remaining_nodes); - } - } -} - -/** - * Tries to detect duplicate sub-networks and eliminates them. This can help quite a lot when node - * groups were used to create the network. - */ -void common_subnetwork_elimination(MFNetwork &network) -{ - Array node_hashes = compute_node_hashes(network); - MultiValueMap nodes_by_hash = group_nodes_by_hash(network, node_hashes); - relink_duplicate_nodes(network, nodes_by_hash); -} - -/** \} */ - -} // namespace blender::fn::mf_network_optimization diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc deleted file mode 100644 index 7b9738e5ca4..00000000000 --- a/source/blender/functions/tests/FN_multi_function_network_test.cc +++ /dev/null @@ -1,280 +0,0 @@ -/* Apache License, Version 2.0 */ - -#include "testing/testing.h" - -#include "FN_multi_function_builder.hh" -#include "FN_multi_function_network.hh" -#include "FN_multi_function_network_evaluation.hh" - -namespace blender::fn::tests { -namespace { - -TEST(multi_function_network, Test1) -{ - CustomMF_SI_SO add_10_fn("add 10", [](int value) { return value + 10; }); - CustomMF_SI_SI_SO multiply_fn("multiply", [](int a, int b) { return a * b; }); - - MFNetwork network; - - MFNode &node1 = network.add_function(add_10_fn); - MFNode &node2 = network.add_function(multiply_fn); - MFOutputSocket &input_socket = network.add_input("Input", MFDataType::ForSingle()); - MFInputSocket &output_socket = network.add_output("Output", MFDataType::ForSingle()); - network.add_link(node1.output(0), node2.input(0)); - network.add_link(node1.output(0), node2.input(1)); - network.add_link(node2.output(0), output_socket); - network.add_link(input_socket, node1.input(0)); - - MFNetworkEvaluator network_fn{{&input_socket}, {&output_socket}}; - - { - Array values = {4, 6, 1, 2, 0}; - Array results(values.size(), 0); - - MFParamsBuilder params(network_fn, values.size()); - params.add_readonly_single_input(values.as_span()); - params.add_uninitialized_single_output(results.as_mutable_span()); - - MFContextBuilder context; - - network_fn.call({0, 2, 3, 4}, params, context); - - EXPECT_EQ(results[0], 14 * 14); - EXPECT_EQ(results[1], 0); - EXPECT_EQ(results[2], 11 * 11); - EXPECT_EQ(results[3], 12 * 12); - EXPECT_EQ(results[4], 10 * 10); - } - { - int value = 3; - Array results(5, 0); - - MFParamsBuilder params(network_fn, results.size()); - params.add_readonly_single_input(&value); - params.add_uninitialized_single_output(results.as_mutable_span()); - - MFContextBuilder context; - - network_fn.call({1, 2, 4}, params, context); - - EXPECT_EQ(results[0], 0); - EXPECT_EQ(results[1], 13 * 13); - EXPECT_EQ(results[2], 13 * 13); - EXPECT_EQ(results[3], 0); - EXPECT_EQ(results[4], 13 * 13); - } -} - -class ConcatVectorsFunction : public MultiFunction { - public: - ConcatVectorsFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Concat Vectors"}; - signature.vector_mutable("A"); - signature.vector_input("B"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - GVectorArray &a = params.vector_mutable(0); - const GVVectorArray &b = params.readonly_vector_input(1); - a.extend(mask, b); - } -}; - -class AppendFunction : public MultiFunction { - public: - AppendFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Append"}; - signature.vector_mutable("Vector"); - signature.single_input("Value"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - GVectorArray_TypedMutableRef vectors = params.vector_mutable(0); - const VArray &values = params.readonly_single_input(1); - - for (int64_t i : mask) { - vectors.append(i, values[i]); - } - } -}; - -class SumVectorFunction : public MultiFunction { - public: - SumVectorFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Sum Vectors"}; - signature.vector_input("Vector"); - signature.single_output("Sum"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VVectorArray &vectors = params.readonly_vector_input(0); - MutableSpan sums = params.uninitialized_single_output(1); - - for (int64_t i : mask) { - int sum = 0; - for (int j : IndexRange(vectors.get_vector_size(i))) { - sum += vectors.get_vector_element(i, j); - } - sums[i] = sum; - } - } -}; - -class CreateRangeFunction : public MultiFunction { - public: - CreateRangeFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Create Range"}; - signature.single_input("Size"); - signature.vector_output("Range"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray &sizes = params.readonly_single_input(0, "Size"); - GVectorArray_TypedMutableRef ranges = params.vector_output(1, "Range"); - - for (int64_t i : mask) { - int size = sizes[i]; - for (int j : IndexRange(size)) { - ranges.append(i, j); - } - } - } -}; - -TEST(multi_function_network, Test2) -{ - CustomMF_SI_SO add_3_fn("add 3", [](int value) { return value + 3; }); - - ConcatVectorsFunction concat_vectors_fn; - AppendFunction append_fn; - SumVectorFunction sum_fn; - CreateRangeFunction create_range_fn; - - MFNetwork network; - - MFOutputSocket &input1 = network.add_input("Input 1", MFDataType::ForVector()); - MFOutputSocket &input2 = network.add_input("Input 2", MFDataType::ForSingle()); - MFInputSocket &output1 = network.add_output("Output 1", MFDataType::ForVector()); - MFInputSocket &output2 = network.add_output("Output 2", MFDataType::ForSingle()); - - MFNode &node1 = network.add_function(add_3_fn); - MFNode &node2 = network.add_function(create_range_fn); - MFNode &node3 = network.add_function(concat_vectors_fn); - MFNode &node4 = network.add_function(sum_fn); - MFNode &node5 = network.add_function(append_fn); - MFNode &node6 = network.add_function(sum_fn); - - network.add_link(input2, node1.input(0)); - network.add_link(node1.output(0), node2.input(0)); - network.add_link(node2.output(0), node3.input(1)); - network.add_link(input1, node3.input(0)); - network.add_link(input1, node4.input(0)); - network.add_link(node4.output(0), node5.input(1)); - network.add_link(node3.output(0), node5.input(0)); - network.add_link(node5.output(0), node6.input(0)); - network.add_link(node3.output(0), output1); - network.add_link(node6.output(0), output2); - - // std::cout << network.to_dot() << "\n\n"; - - MFNetworkEvaluator network_fn{{&input1, &input2}, {&output1, &output2}}; - - { - Array input_value_1 = {3, 6}; - int input_value_2 = 4; - - GVectorArray output_value_1(CPPType::get(), 5); - Array output_value_2(5, -1); - - MFParamsBuilder params(network_fn, 5); - GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5}; - params.add_readonly_vector_input(inputs_1); - params.add_readonly_single_input(&input_value_2); - params.add_vector_output(output_value_1); - params.add_uninitialized_single_output(output_value_2.as_mutable_span()); - - MFContextBuilder context; - - network_fn.call({1, 2, 4}, params, context); - - EXPECT_EQ(output_value_1[0].size(), 0); - EXPECT_EQ(output_value_1[1].size(), 9); - EXPECT_EQ(output_value_1[2].size(), 9); - EXPECT_EQ(output_value_1[3].size(), 0); - EXPECT_EQ(output_value_1[4].size(), 9); - - EXPECT_EQ(output_value_2[0], -1); - EXPECT_EQ(output_value_2[1], 39); - EXPECT_EQ(output_value_2[2], 39); - EXPECT_EQ(output_value_2[3], -1); - EXPECT_EQ(output_value_2[4], 39); - } - { - GVectorArray input_value_1(CPPType::get(), 3); - GVectorArray_TypedMutableRef input_value_1_ref{input_value_1}; - input_value_1_ref.extend(0, {3, 4, 5}); - input_value_1_ref.extend(1, {1, 2}); - - Array input_value_2 = {4, 2, 3}; - - GVectorArray output_value_1(CPPType::get(), 3); - Array output_value_2(3, -1); - - MFParamsBuilder params(network_fn, 3); - params.add_readonly_vector_input(input_value_1); - params.add_readonly_single_input(input_value_2.as_span()); - params.add_vector_output(output_value_1); - params.add_uninitialized_single_output(output_value_2.as_mutable_span()); - - MFContextBuilder context; - - network_fn.call({0, 1, 2}, params, context); - - EXPECT_EQ(output_value_1[0].size(), 10); - EXPECT_EQ(output_value_1[1].size(), 7); - EXPECT_EQ(output_value_1[2].size(), 6); - - EXPECT_EQ(output_value_2[0], 45); - EXPECT_EQ(output_value_2[1], 16); - EXPECT_EQ(output_value_2[2], 15); - } -} - -} // namespace -} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc index 3d73e020eb2..91c72a51dd6 100644 --- a/source/blender/functions/tests/FN_multi_function_test.cc +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -4,6 +4,7 @@ #include "FN_multi_function.hh" #include "FN_multi_function_builder.hh" +#include "FN_multi_function_test_common.hh" namespace blender::fn::tests { namespace { @@ -59,33 +60,6 @@ TEST(multi_function, AddFunction) EXPECT_EQ(output[2], 36); } -class AddPrefixFunction : public MultiFunction { - public: - AddPrefixFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Add Prefix"}; - signature.single_input("Prefix"); - signature.single_mutable("Strings"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray &prefixes = params.readonly_single_input(0, "Prefix"); - MutableSpan strings = params.single_mutable(1, "Strings"); - - for (int64_t i : mask) { - strings[i] = prefixes[i] + strings[i]; - } - } -}; - TEST(multi_function, AddPrefixFunction) { AddPrefixFunction fn; @@ -113,43 +87,13 @@ TEST(multi_function, AddPrefixFunction) EXPECT_EQ(strings[3], "ABAnother much longer string to trigger an allocation"); } -class CreateRangeFunction : public MultiFunction { - public: - CreateRangeFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Create Range"}; - signature.single_input("Size"); - signature.vector_output("Range"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray &sizes = params.readonly_single_input(0, "Size"); - GVectorArray &ranges = params.vector_output(1, "Range"); - - for (int64_t i : mask) { - uint size = sizes[i]; - for (uint j : IndexRange(size)) { - ranges.append(i, &j); - } - } - } -}; - TEST(multi_function, CreateRangeFunction) { CreateRangeFunction fn; - GVectorArray ranges(CPPType::get(), 5); - GVectorArray_TypedMutableRef ranges_ref{ranges}; - Array sizes = {3, 0, 6, 1, 4}; + GVectorArray ranges(CPPType::get(), 5); + GVectorArray_TypedMutableRef ranges_ref{ranges}; + Array sizes = {3, 0, 6, 1, 4}; MFParamsBuilder params(fn, ranges.size()); params.add_readonly_single_input(sizes.as_span()); @@ -172,34 +116,6 @@ TEST(multi_function, CreateRangeFunction) EXPECT_EQ(ranges_ref[2][1], 1); } -class GenericAppendFunction : public MultiFunction { - private: - MFSignature signature_; - - public: - GenericAppendFunction(const CPPType &type) - { - MFSignatureBuilder signature{"Append"}; - signature.vector_mutable("Vector", type); - signature.single_input("Value", type); - signature_ = signature.build(); - this->set_signature(&signature_); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - GVectorArray &vectors = params.vector_mutable(0, "Vector"); - const GVArray &values = params.readonly_single_input(1, "Value"); - - for (int64_t i : mask) { - BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer); - values.get(i, buffer); - vectors.append(i, buffer); - values.type().destruct(buffer); - } - } -}; - TEST(multi_function, GenericAppendFunction) { GenericAppendFunction fn(CPPType::get()); diff --git a/source/blender/functions/tests/FN_multi_function_test_common.hh b/source/blender/functions/tests/FN_multi_function_test_common.hh new file mode 100644 index 00000000000..51c8fac8a96 --- /dev/null +++ b/source/blender/functions/tests/FN_multi_function_test_common.hh @@ -0,0 +1,174 @@ +/* Apache License, Version 2.0 */ + +#include "FN_multi_function.hh" + +namespace blender::fn::tests { + +class AddPrefixFunction : public MultiFunction { + public: + AddPrefixFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Add Prefix"}; + signature.single_input("Prefix"); + signature.single_mutable("Strings"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VArray &prefixes = params.readonly_single_input(0, "Prefix"); + MutableSpan strings = params.single_mutable(1, "Strings"); + + for (int64_t i : mask) { + strings[i] = prefixes[i] + strings[i]; + } + } +}; + +class CreateRangeFunction : public MultiFunction { + public: + CreateRangeFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Create Range"}; + signature.single_input("Size"); + signature.vector_output("Range"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VArray &sizes = params.readonly_single_input(0, "Size"); + GVectorArray &ranges = params.vector_output(1, "Range"); + + for (int64_t i : mask) { + int size = sizes[i]; + for (int j : IndexRange(size)) { + ranges.append(i, &j); + } + } + } +}; + +class GenericAppendFunction : public MultiFunction { + private: + MFSignature signature_; + + public: + GenericAppendFunction(const CPPType &type) + { + MFSignatureBuilder signature{"Append"}; + signature.vector_mutable("Vector", type); + signature.single_input("Value", type); + signature_ = signature.build(); + this->set_signature(&signature_); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArray &vectors = params.vector_mutable(0, "Vector"); + const GVArray &values = params.readonly_single_input(1, "Value"); + + for (int64_t i : mask) { + BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer); + values.get(i, buffer); + vectors.append(i, buffer); + values.type().destruct(buffer); + } + } +}; + +class ConcatVectorsFunction : public MultiFunction { + public: + ConcatVectorsFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Concat Vectors"}; + signature.vector_mutable("A"); + signature.vector_input("B"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArray &a = params.vector_mutable(0); + const GVVectorArray &b = params.readonly_vector_input(1); + a.extend(mask, b); + } +}; + +class AppendFunction : public MultiFunction { + public: + AppendFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Append"}; + signature.vector_mutable("Vector"); + signature.single_input("Value"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArray_TypedMutableRef vectors = params.vector_mutable(0); + const VArray &values = params.readonly_single_input(1); + + for (int64_t i : mask) { + vectors.append(i, values[i]); + } + } +}; + +class SumVectorFunction : public MultiFunction { + public: + SumVectorFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Sum Vectors"}; + signature.vector_input("Vector"); + signature.single_output("Sum"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VVectorArray &vectors = params.readonly_vector_input(0); + MutableSpan sums = params.uninitialized_single_output(1); + + for (int64_t i : mask) { + int sum = 0; + for (int j : IndexRange(vectors.get_vector_size(i))) { + sum += vectors.get_vector_element(i, j); + } + sums[i] = sum; + } + } +}; + +} // namespace blender::fn::tests diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 3853b345c14..620c7ef438a 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -84,7 +84,8 @@ #include "NOD_derived_node_tree.hh" #include "NOD_geometry.h" #include "NOD_geometry_nodes_eval_log.hh" -#include "NOD_node_tree_multi_function.hh" + +#include "FN_multi_function.hh" using blender::destruct_ptr; using blender::float3; @@ -858,7 +859,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, { blender::ResourceScope scope; blender::LinearAllocator<> &allocator = scope.linear_allocator(); - blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, scope); + blender::nodes::NodeMultiFunctions mf_by_node{tree, scope}; Map group_inputs; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 47dfd9bc8f6..5646e37707c 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -836,7 +836,7 @@ class GeometryNodesEvaluator { } /* Use the multi-function implementation if it exists. */ - const MultiFunction *multi_function = params_.mf_by_node->lookup_default(node, nullptr); + const MultiFunction *multi_function = params_.mf_by_node->try_get(node); if (multi_function != nullptr) { this->execute_multi_function_node(node, *multi_function, node_state); return; diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh index d8c60d31986..5151be07aa2 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh @@ -20,12 +20,14 @@ #include "NOD_derived_node_tree.hh" #include "NOD_geometry_nodes_eval_log.hh" -#include "NOD_node_tree_multi_function.hh" +#include "NOD_multi_function.hh" #include "FN_generic_pointer.hh" #include "DNA_modifier_types.h" +#include "FN_multi_function.hh" + namespace geo_log = blender::nodes::geometry_nodes_eval_log; namespace blender::modifiers::geometry_nodes { @@ -45,7 +47,7 @@ struct GeometryNodesEvaluationParams { * necessary in all cases. Sometimes `log_socket_value_fn` might just want to look at the value * and then it can be freed. */ Vector force_compute_sockets; - nodes::MultiFunctionByNode *mf_by_node; + nodes::NodeMultiFunctions *mf_by_node; const NodesModifierData *modifier_; Depsgraph *depsgraph; Object *self_object; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 46fb9f54bfe..8680fcee49a 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -345,11 +345,10 @@ set(SRC intern/node_common.c intern/node_exec.cc intern/node_geometry_exec.cc + intern/node_multi_function.cc intern/node_socket.cc - intern/node_tree_multi_function.cc intern/node_tree_ref.cc intern/node_util.c - intern/type_callbacks.cc intern/type_conversions.cc composite/node_composite_util.h @@ -366,13 +365,12 @@ set(SRC NOD_geometry_exec.hh NOD_geometry_nodes_eval_log.hh NOD_math_functions.hh - NOD_node_tree_multi_function.hh + NOD_multi_function.hh NOD_node_tree_ref.hh NOD_shader.h NOD_socket.h NOD_static_types.h NOD_texture.h - NOD_type_callbacks.hh NOD_type_conversions.hh intern/node_common.h intern/node_exec.h diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh new file mode 100644 index 00000000000..2f4b104fb4c --- /dev/null +++ b/source/blender/nodes/NOD_multi_function.hh @@ -0,0 +1,130 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "FN_multi_function.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +namespace blender::nodes { + +using namespace fn::multi_function_types; + +class NodeMultiFunctions; + +/** + * Utility class to help nodes build a multi-function for themselves. + */ +class NodeMultiFunctionBuilder : NonCopyable, NonMovable { + private: + ResourceScope &resource_scope_; + bNode &node_; + bNodeTree &tree_; + const MultiFunction *built_fn_ = nullptr; + + friend NodeMultiFunctions; + + public: + NodeMultiFunctionBuilder(ResourceScope &resource_scope, bNode &node, bNodeTree &tree); + + /** + * Assign a multi-function for the current node. The input and output parameters of the function + * have to match the available sockets in the node. + */ + void set_matching_fn(const MultiFunction *fn); + void set_matching_fn(const MultiFunction &fn); + + /** + * Utility method for creating and assigning a multi-function when it can't have a static + * lifetime. + */ + template void construct_and_set_matching_fn(Args &&...args); + + bNode &node(); + bNodeTree &tree(); + + ResourceScope &resource_scope(); +}; + +/** + * Gives access to multi-functions for all nodes in a node tree that support them. + */ +class NodeMultiFunctions { + private: + Map map_; + + public: + NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope); + + const MultiFunction *try_get(const DNode &node) const; +}; + +/* -------------------------------------------------------------------- + * NodeMultiFunctionBuilder inline methods. + */ + +inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(ResourceScope &resource_scope, + bNode &node, + bNodeTree &tree) + : resource_scope_(resource_scope), node_(node), tree_(tree) +{ +} + +inline bNode &NodeMultiFunctionBuilder::node() +{ + return node_; +} + +inline bNodeTree &NodeMultiFunctionBuilder::tree() +{ + return tree_; +} + +inline ResourceScope &NodeMultiFunctionBuilder::resource_scope() +{ + return resource_scope_; +} + +inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn) +{ + built_fn_ = fn; +} + +inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn) +{ + this->set_matching_fn(&fn); +} + +template +inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args) +{ + const T &fn = resource_scope_.construct(__func__, std::forward(args)...); + this->set_matching_fn(&fn); +} + +/* -------------------------------------------------------------------- + * NodeMultiFunctions inline methods. + */ + +inline const MultiFunction *NodeMultiFunctions::try_get(const DNode &node) const +{ + return map_.lookup_default(node->bnode(), nullptr); +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh deleted file mode 100644 index 7eeeaef0b98..00000000000 --- a/source/blender/nodes/NOD_node_tree_multi_function.hh +++ /dev/null @@ -1,390 +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. - */ - -#pragma once - -/** \file - * \ingroup nodes - * - * This file allows you to generate a multi-function network from a user-generated node tree. - */ - -#include "FN_multi_function_builder.hh" -#include "FN_multi_function_network.hh" - -#include "NOD_derived_node_tree.hh" -#include "NOD_type_callbacks.hh" - -#include "BLI_multi_value_map.hh" -#include "BLI_resource_scope.hh" - -namespace blender::nodes { - -/** - * A MFNetworkTreeMap maps various components of a node tree to components of a fn::MFNetwork. This - * is necessary for further processing of a multi-function network that has been generated from a - * node tree. - */ -class MFNetworkTreeMap { - private: - /** - * Store by id instead of using a hash table to avoid unnecessary hash table lookups. - * - * Input sockets in a node tree can have multiple corresponding sockets in the generated - * MFNetwork. This is because nodes are allowed to expand into multiple multi-function nodes. - */ - const DerivedNodeTree &tree_; - fn::MFNetwork &network_; - MultiValueMap sockets_by_dsocket_; - - public: - MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network) - : tree_(tree), network_(network) - { - } - - const DerivedNodeTree &tree() const - { - return tree_; - } - - const fn::MFNetwork &network() const - { - return network_; - } - - fn::MFNetwork &network() - { - return network_; - } - - void add(const DSocket &dsocket, fn::MFSocket &socket) - { - BLI_assert(dsocket->is_input() == socket.is_input()); - BLI_assert(dsocket->is_input() || sockets_by_dsocket_.lookup(dsocket).is_empty()); - sockets_by_dsocket_.add(dsocket, &socket); - } - - void add(const DInputSocket &dsocket, fn::MFInputSocket &socket) - { - sockets_by_dsocket_.add(dsocket, &socket); - } - - void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket) - { - /* There can be at most one matching output socket. */ - BLI_assert(sockets_by_dsocket_.lookup(dsocket).is_empty()); - sockets_by_dsocket_.add(dsocket, &socket); - } - - void add(const DTreeContext &context, - Span dsockets, - Span sockets) - { - assert_same_size(dsockets, sockets); - for (int i : dsockets.index_range()) { - this->add(DInputSocket(&context, dsockets[i]), *sockets[i]); - } - } - - void add(const DTreeContext &context, - Span dsockets, - Span sockets) - { - assert_same_size(dsockets, sockets); - for (int i : dsockets.index_range()) { - this->add(DOutputSocket(&context, dsockets[i]), *sockets[i]); - } - } - - void add_try_match(const DNode &dnode, fn::MFNode &node) - { - this->add_try_match(*dnode.context(), - dnode->inputs().cast(), - node.inputs().cast()); - this->add_try_match(*dnode.context(), - dnode->outputs().cast(), - node.outputs().cast()); - } - - void add_try_match(const DTreeContext &context, - Span dsockets, - Span sockets) - { - this->add_try_match( - context, dsockets.cast(), sockets.cast()); - } - - void add_try_match(const DTreeContext &context, - Span dsockets, - Span sockets) - { - this->add_try_match( - context, dsockets.cast(), sockets.cast()); - } - - void add_try_match(const DTreeContext &context, - Span dsockets, - Span sockets) - { - int used_sockets = 0; - for (const SocketRef *dsocket : dsockets) { - if (!dsocket->is_available()) { - continue; - } - if (!socket_is_mf_data_socket(*dsocket->typeinfo())) { - continue; - } - fn::MFSocket *socket = sockets[used_sockets]; - this->add(DSocket(&context, dsocket), *socket); - used_sockets++; - } - } - - fn::MFOutputSocket &lookup(const DOutputSocket &dsocket) - { - return sockets_by_dsocket_.lookup(dsocket)[0]->as_output(); - } - - Span lookup(const DInputSocket &dsocket) - { - return sockets_by_dsocket_.lookup(dsocket).cast(); - } - - fn::MFInputSocket &lookup_dummy(const DInputSocket &dsocket) - { - Span sockets = this->lookup(dsocket); - BLI_assert(sockets.size() == 1); - fn::MFInputSocket &socket = *sockets[0]; - BLI_assert(socket.node().is_dummy()); - return socket; - } - - fn::MFOutputSocket &lookup_dummy(const DOutputSocket &dsocket) - { - fn::MFOutputSocket &socket = this->lookup(dsocket); - BLI_assert(socket.node().is_dummy()); - return socket; - } - - bool is_mapped(const DSocket &dsocket) const - { - return !sockets_by_dsocket_.lookup(dsocket).is_empty(); - } -}; - -/** - * This data is necessary throughout the generation of a MFNetwork from a node tree. - */ -struct CommonMFNetworkBuilderData { - ResourceScope &scope; - fn::MFNetwork &network; - MFNetworkTreeMap &network_map; - const DerivedNodeTree &tree; -}; - -class MFNetworkBuilderBase { - protected: - CommonMFNetworkBuilderData &common_; - - public: - MFNetworkBuilderBase(CommonMFNetworkBuilderData &common) : common_(common) - { - } - - /** - * Returns the network that is currently being built. - */ - fn::MFNetwork &network() - { - return common_.network; - } - - /** - * Returns the map between the node tree and the multi-function network that is being built. - */ - MFNetworkTreeMap &network_map() - { - return common_.network_map; - } - - /** - * Returns a resource collector that will only be destructed after the multi-function network is - * destructed. - */ - ResourceScope &resource_scope() - { - return common_.scope; - } - - /** - * Constructs a new function that will live at least as long as the MFNetwork. - */ - template T &construct_fn(Args &&...args) - { - BLI_STATIC_ASSERT((std::is_base_of_v), ""); - void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T)); - T *fn = new (buffer) T(std::forward(args)...); - common_.scope.add(destruct_ptr(fn), fn->name().c_str()); - return *fn; - } -}; - -/** - * This class is used by socket implementations to define how an unlinked input socket is handled - * in a multi-function network. - */ -class SocketMFNetworkBuilder : public MFNetworkBuilderBase { - private: - bNodeSocket *bsocket_; - fn::MFOutputSocket *built_socket_ = nullptr; - - public: - SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket) - : MFNetworkBuilderBase(common), bsocket_(dsocket->bsocket()) - { - } - - /** - * Returns the socket that is currently being built. - */ - bNodeSocket &bsocket() - { - return *bsocket_; - } - - /** - * Utility method that returns bsocket->default_value for the current socket. - */ - template T *socket_default_value() - { - return static_cast(bsocket_->default_value); - } - - /** - * Builds a function node for that socket that outputs the given constant value. - */ - template void set_constant_value(T value) - { - this->construct_generator_fn>(std::move(value)); - } - void set_constant_value(const CPPType &type, const void *value) - { - /* The value has live as long as the generated mf network. */ - this->construct_generator_fn(type, value); - } - - template void construct_generator_fn(Args &&...args) - { - const fn::MultiFunction &fn = this->construct_fn(std::forward(args)...); - this->set_generator_fn(fn); - } - - /** - * Uses the first output of the given multi-function as value of the socket. - */ - void set_generator_fn(const fn::MultiFunction &fn) - { - fn::MFFunctionNode &node = common_.network.add_function(fn); - this->set_socket(node.output(0)); - } - - /** - * Define a multi-function socket that outputs the value of the bsocket. - */ - void set_socket(fn::MFOutputSocket &socket) - { - built_socket_ = &socket; - } - - fn::MFOutputSocket *built_socket() - { - return built_socket_; - } -}; - -/** - * This class is used by node implementations to define how a user-level node expands into - * multi-function nodes internally. - */ -class NodeMFNetworkBuilder : public MFNetworkBuilderBase { - private: - DNode dnode_; - - public: - NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, DNode dnode) - : MFNetworkBuilderBase(common), dnode_(dnode) - { - } - - /** - * Tells the builder to build a function that corresponds to the node that is being built. It - * will try to match up sockets. - */ - template T &construct_and_set_matching_fn(Args &&...args) - { - T &function = this->construct_fn(std::forward(args)...); - this->set_matching_fn(function); - return function; - } - - const fn::MultiFunction &get_not_implemented_fn() - { - return this->get_default_fn("Not Implemented (" + dnode_->name() + ")"); - } - - const fn::MultiFunction &get_default_fn(StringRef name); - - const void set_not_implemented() - { - this->set_matching_fn(this->get_not_implemented_fn()); - } - - /** - * Tells the builder that the given function corresponds to the node that is being built. It will - * try to match up sockets. For that it skips unavailable and non-data sockets. - */ - void set_matching_fn(const fn::MultiFunction &function) - { - fn::MFFunctionNode &node = common_.network.add_function(function); - common_.network_map.add_try_match(dnode_, node); - } - - /** - * Returns the node that is currently being built. - */ - bNode &bnode() - { - return *dnode_->bnode(); - } - - /** - * Returns the node that is currently being built. - */ - const DNode &dnode() const - { - return dnode_; - } -}; - -MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network, - const DerivedNodeTree &tree, - ResourceScope &scope); - -using MultiFunctionByNode = Map; -MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope); - -} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_type_callbacks.hh b/source/blender/nodes/NOD_type_callbacks.hh deleted file mode 100644 index 2be78f929db..00000000000 --- a/source/blender/nodes/NOD_type_callbacks.hh +++ /dev/null @@ -1,34 +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. - */ - -#pragma once - -#include - -#include "BKE_node.h" - -#include "FN_multi_function_data_type.hh" - -namespace blender::nodes { - -using fn::CPPType; -using fn::MFDataType; - -std::optional socket_mf_type_get(const bNodeSocketType &stype); -bool socket_is_mf_data_socket(const bNodeSocketType &stype); -void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder); - -} // namespace blender::nodes diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh index 9fbd6712827..96a8f29c3e9 100644 --- a/source/blender/nodes/function/node_function_util.hh +++ b/source/blender/nodes/function/node_function_util.hh @@ -30,7 +30,7 @@ #include "BLT_translation.h" #include "NOD_function.h" -#include "NOD_node_tree_multi_function.hh" +#include "NOD_multi_function.hh" #include "node_util.h" diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index 7a83ff8e016..0ba9080918c 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -58,7 +58,7 @@ static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) +static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) { static blender::fn::CustomMF_SI_SI_SO and_fn{ "And", [](bool a, bool b) { return a && b; }}; @@ -68,20 +68,21 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) switch (bnode.custom1) { case NODE_BOOLEAN_MATH_AND: - return and_fn; + return &and_fn; case NODE_BOOLEAN_MATH_OR: - return or_fn; + return &or_fn; case NODE_BOOLEAN_MATH_NOT: - return not_fn; + return ¬_fn; } - BLI_assert(false); - return blender::fn::dummy_multi_function; + BLI_assert_unreachable(); + return nullptr; } -static void node_boolean_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_boolean_math_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -93,7 +94,7 @@ void register_node_type_fn_boolean_math() node_type_socket_templates(&ntype, fn_node_boolean_math_in, fn_node_boolean_math_out); node_type_label(&ntype, node_boolean_math_label); node_type_update(&ntype, node_boolean_math_update); - ntype.expand_in_mf_network = node_boolean_expand_in_mf_network; + ntype.build_multi_function = fn_node_boolean_math_build_multi_function; ntype.draw_buttons = fn_node_boolean_math_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index 6c8df8f2ea0..16ffb761a15 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -64,7 +64,7 @@ static void node_float_compare_label(bNodeTree *UNUSED(ntree), BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction &get_multi_function(bNode &node) +static const blender::fn::MultiFunction *get_multi_function(bNode &node) { static blender::fn::CustomMF_SI_SI_SO less_than_fn{ "Less Than", [](float a, float b) { return a < b; }}; @@ -81,26 +81,27 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &node) switch (node.custom1) { case NODE_FLOAT_COMPARE_LESS_THAN: - return less_than_fn; + return &less_than_fn; case NODE_FLOAT_COMPARE_LESS_EQUAL: - return less_equal_fn; + return &less_equal_fn; case NODE_FLOAT_COMPARE_GREATER_THAN: - return greater_than_fn; + return &greater_than_fn; case NODE_FLOAT_COMPARE_GREATER_EQUAL: - return greater_equal_fn; + return &greater_equal_fn; case NODE_FLOAT_COMPARE_EQUAL: - return equal_fn; + return &equal_fn; case NODE_FLOAT_COMPARE_NOT_EQUAL: - return not_equal_fn; + return ¬_equal_fn; } - BLI_assert(false); - return blender::fn::dummy_multi_function; + BLI_assert_unreachable(); + return nullptr; } -static void node_float_compare_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_float_compare_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -112,7 +113,7 @@ void register_node_type_fn_float_compare() node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out); node_type_label(&ntype, node_float_compare_label); node_type_update(&ntype, node_float_compare_update); - ntype.expand_in_mf_network = node_float_compare_expand_in_mf_network; + ntype.build_multi_function = fn_node_float_compare_build_multi_function; ntype.draw_buttons = geo_node_float_compare_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index 26cde576400..52acfefe615 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -50,7 +50,7 @@ static void node_float_to_int_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) +static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) { static blender::fn::CustomMF_SI_SO round_fn{"Round", [](float a) { return (int)round(a); }}; @@ -63,22 +63,23 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) switch (static_cast(bnode.custom1)) { case FN_NODE_FLOAT_TO_INT_ROUND: - return round_fn; + return &round_fn; case FN_NODE_FLOAT_TO_INT_FLOOR: - return floor_fn; + return &floor_fn; case FN_NODE_FLOAT_TO_INT_CEIL: - return ceil_fn; + return &ceil_fn; case FN_NODE_FLOAT_TO_INT_TRUNCATE: - return trunc_fn; + return &trunc_fn; } BLI_assert_unreachable(); - return blender::fn::dummy_multi_function; + return nullptr; } -static void node_float_to_int_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_float_to_int_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -89,7 +90,7 @@ void register_node_type_fn_float_to_int() fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, fn_node_float_to_int_in, fn_node_float_to_int_out); node_type_label(&ntype, node_float_to_int_label); - ntype.expand_in_mf_network = node_float_to_int_expand_in_mf_network; + ntype.build_multi_function = fn_node_float_to_int_build_multi_function; ntype.draw_buttons = fn_node_float_to_int_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc index f16bdef2f38..560ace57aba 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -29,14 +29,14 @@ static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), P uiItemR(layout, ptr, "string", 0, "", ICON_NONE); } -static void fn_node_input_string_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_input_string_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); NodeInputString *node_storage = static_cast(bnode.storage); std::string string = std::string((node_storage->string) ? node_storage->string : ""); - - builder.construct_and_set_matching_fn>(string); + builder.construct_and_set_matching_fn>( + std::move(string)); } static void fn_node_input_string_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -78,7 +78,7 @@ void register_node_type_fn_input_string() node_type_socket_templates(&ntype, nullptr, fn_node_input_string_out); node_type_init(&ntype, fn_node_input_string_init); node_type_storage(&ntype, "NodeInputString", fn_node_input_string_free, fn_node_string_copy); - ntype.expand_in_mf_network = fn_node_input_string_expand_in_mf_network; + ntype.build_multi_function = fn_node_input_string_build_multi_function; ntype.draw_buttons = fn_node_input_string_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index 2cd4eb1d9df..244c045de9a 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -32,16 +32,14 @@ static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), P uiItemR(col, ptr, "vector", UI_ITEM_R_EXPAND, "", ICON_NONE); } -static void fn_node_vector_input_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_vector_input_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); NodeInputVector *node_storage = static_cast(bnode.storage); blender::float3 vector(node_storage->vector); - builder.construct_and_set_matching_fn>(vector); } - static void fn_node_input_vector_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeInputVector *data = (NodeInputVector *)MEM_callocN(sizeof(NodeInputVector), @@ -58,7 +56,7 @@ void register_node_type_fn_input_vector() node_type_init(&ntype, fn_node_input_vector_init); node_type_storage( &ntype, "NodeInputVector", node_free_standard_storage, node_copy_standard_storage); - ntype.expand_in_mf_network = fn_node_vector_input_expand_in_mf_network; + ntype.build_multi_function = fn_node_vector_input_build_multi_function; ntype.draw_buttons = fn_node_input_vector_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc index a3c9f44b6a1..47ec9adf6bd 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc @@ -67,10 +67,11 @@ class RandomFloatFunction : public blender::fn::MultiFunction { } }; -static void fn_node_random_float_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_random_float_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - builder.construct_and_set_matching_fn(); + static RandomFloatFunction fn; + builder.set_matching_fn(fn); } void register_node_type_fn_random_float() @@ -79,6 +80,6 @@ void register_node_type_fn_random_float() fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0); node_type_socket_templates(&ntype, fn_node_random_float_in, fn_node_random_float_out); - ntype.expand_in_mf_network = fn_node_random_float_expand_in_mf_network; + ntype.build_multi_function = fn_node_random_float_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index ffa20579acc..a3bbca90731 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -19,7 +19,6 @@ #include "DEG_depsgraph_query.h" #include "NOD_geometry_exec.hh" -#include "NOD_type_callbacks.hh" #include "NOD_type_conversions.hh" #include "node_geometry_util.hh" diff --git a/source/blender/nodes/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc new file mode 100644 index 00000000000..c91899ed8c2 --- /dev/null +++ b/source/blender/nodes/intern/node_multi_function.cc @@ -0,0 +1,40 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "NOD_multi_function.hh" + +namespace blender::nodes { + +NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope) +{ + for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) { + bNodeTree *btree = tree_ref->btree(); + for (const NodeRef *node : tree_ref->nodes()) { + bNode *bnode = node->bnode(); + if (bnode->typeinfo->build_multi_function == nullptr) { + continue; + } + NodeMultiFunctionBuilder builder{resource_scope, *bnode, *btree}; + bnode->typeinfo->build_multi_function(builder); + const MultiFunction *fn = builder.built_fn_; + if (fn != nullptr) { + map_.add_new(bnode, fn); + } + } + } +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 4be3fd2468b..528616eb23a 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -44,7 +44,6 @@ #include "MEM_guardedalloc.h" -#include "NOD_node_tree_multi_function.hh" #include "NOD_socket.h" #include "FN_cpp_type_make.hh" diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc deleted file mode 100644 index 7ab6495f733..00000000000 --- a/source/blender/nodes/intern/node_tree_multi_function.cc +++ /dev/null @@ -1,409 +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. - */ - -#include "NOD_node_tree_multi_function.hh" -#include "NOD_type_conversions.hh" - -#include "FN_multi_function_network_evaluation.hh" - -#include "BLI_color.hh" -#include "BLI_float2.hh" -#include "BLI_float3.hh" - -namespace blender::nodes { - -const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name) -{ - Vector input_types; - Vector output_types; - - for (const InputSocketRef *dsocket : dnode_->inputs()) { - if (dsocket->is_available()) { - std::optional data_type = socket_mf_type_get(*dsocket->typeinfo()); - if (data_type.has_value()) { - input_types.append(*data_type); - } - } - } - for (const OutputSocketRef *dsocket : dnode_->outputs()) { - if (dsocket->is_available()) { - std::optional data_type = socket_mf_type_get(*dsocket->typeinfo()); - if (data_type.has_value()) { - output_types.append(*data_type); - } - } - } - - const fn::MultiFunction &fn = this->construct_fn( - name, input_types, output_types); - return fn; -} - -static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &dnode) -{ - constexpr int stack_capacity = 10; - - Vector input_types; - Vector input_names; - Vector input_dsockets; - - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - std::optional data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo); - if (data_type.has_value()) { - input_types.append(*data_type); - input_names.append(dsocket->name()); - input_dsockets.append(dsocket); - } - } - } - - Vector output_types; - Vector output_names; - Vector output_dsockets; - - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - std::optional data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo); - if (data_type.has_value()) { - output_types.append(*data_type); - output_names.append(dsocket->name()); - output_dsockets.append(dsocket); - } - } - } - - fn::MFDummyNode &dummy_node = common.network.add_dummy( - dnode->name(), input_types, output_types, input_names, output_names); - - common.network_map.add(*dnode.context(), input_dsockets, dummy_node.inputs()); - common.network_map.add(*dnode.context(), output_dsockets, dummy_node.outputs()); -} - -static bool has_data_sockets(const DNode &dnode) -{ - for (const InputSocketRef *socket : dnode->inputs()) { - if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) { - return true; - } - } - for (const OutputSocketRef *socket : dnode->outputs()) { - if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) { - return true; - } - } - return false; -} - -static void foreach_node_to_insert(CommonMFNetworkBuilderData &common, - FunctionRef callback) -{ - common.tree.foreach_node([&](const DNode dnode) { - if (dnode->is_group_node()) { - return; - } - /* Don't insert non-root group input/output nodes, because they will be inlined. */ - if (!dnode.context()->is_root()) { - if (dnode->is_group_input_node() || dnode->is_group_output_node()) { - return; - } - } - callback(dnode); - }); -} - -/** - * Expands all function nodes in the multi-function network. Nodes that don't have an expand - * function, but do have data sockets, will get corresponding dummy nodes. - */ -static void insert_nodes(CommonMFNetworkBuilderData &common) -{ - foreach_node_to_insert(common, [&](const DNode dnode) { - const bNodeType *node_type = dnode->typeinfo(); - if (node_type->expand_in_mf_network != nullptr) { - NodeMFNetworkBuilder builder{common, dnode}; - node_type->expand_in_mf_network(builder); - } - else if (has_data_sockets(dnode)) { - insert_dummy_node(common, dnode); - } - }); -} - -static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common, - fn::MFDataType type) -{ - const fn::MultiFunction *default_fn; - if (type.is_single()) { - default_fn = &common.scope.construct( - AT, type.single_type(), type.single_type().default_value()); - } - else { - default_fn = &common.scope.construct( - AT, fn::GSpan(type.vector_base_type())); - } - - fn::MFNode &node = common.network.add_function(*default_fn); - return node.output(0); -} - -static fn::MFOutputSocket *insert_unlinked_input(CommonMFNetworkBuilderData &common, - const DInputSocket &dsocket) -{ - BLI_assert(socket_is_mf_data_socket(*dsocket->typeinfo())); - - SocketMFNetworkBuilder builder{common, dsocket}; - socket_expand_in_mf_network(builder); - - fn::MFOutputSocket *built_socket = builder.built_socket(); - BLI_assert(built_socket != nullptr); - return built_socket; -} - -static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common) -{ - foreach_node_to_insert(common, [&](const DNode dnode) { - for (const InputSocketRef *socket_ref : dnode->inputs()) { - const DInputSocket to_dsocket{dnode.context(), socket_ref}; - if (!to_dsocket->is_available()) { - continue; - } - if (!socket_is_mf_data_socket(*to_dsocket->typeinfo())) { - continue; - } - - Span to_sockets = common.network_map.lookup(to_dsocket); - BLI_assert(to_sockets.size() >= 1); - const fn::MFDataType to_type = to_sockets[0]->data_type(); - - Vector from_dsockets; - to_dsocket.foreach_origin_socket([&](DSocket socket) { from_dsockets.append(socket); }); - if (from_dsockets.size() > 1) { - fn::MFOutputSocket &from_socket = insert_default_value_for_type(common, to_type); - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(from_socket, *to_socket); - } - continue; - } - if (from_dsockets.is_empty()) { - /* The socket is not linked. Need to use the value of the socket itself. */ - fn::MFOutputSocket *built_socket = insert_unlinked_input(common, to_dsocket); - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(*built_socket, *to_socket); - } - continue; - } - if (from_dsockets[0]->is_input()) { - DInputSocket from_dsocket{from_dsockets[0]}; - fn::MFOutputSocket *built_socket = insert_unlinked_input(common, from_dsocket); - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(*built_socket, *to_socket); - } - continue; - } - DOutputSocket from_dsocket{from_dsockets[0]}; - fn::MFOutputSocket *from_socket = &common.network_map.lookup(from_dsocket); - const fn::MFDataType from_type = from_socket->data_type(); - - if (from_type != to_type) { - const fn::MultiFunction *conversion_fn = - get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type); - if (conversion_fn != nullptr) { - fn::MFNode &node = common.network.add_function(*conversion_fn); - common.network.add_link(*from_socket, node.input(0)); - from_socket = &node.output(0); - } - else { - from_socket = &insert_default_value_for_type(common, to_type); - } - } - - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(*from_socket, *to_socket); - } - } - }); -} - -/** - * Expands all function nodes contained in the given node tree within the given multi-function - * network. - * - * Returns a mapping between the original node tree and the generated nodes/sockets for further - * processing. - */ -MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network, - const DerivedNodeTree &tree, - ResourceScope &scope) -{ - MFNetworkTreeMap network_map{tree, network}; - - CommonMFNetworkBuilderData common{scope, network, network_map, tree}; - - insert_nodes(common); - insert_links_and_unlinked_inputs(common); - - return network_map; -} - -/** - * A single node is allowed to expand into multiple nodes before evaluation. Depending on what - * nodes it expands to, it belongs a different type of the ones below. - */ -enum class NodeExpandType { - SingleFunctionNode, - MultipleFunctionNodes, - HasDummyNodes, -}; - -/** - * Checks how the given node expanded in the multi-function network. If it is only a single - * function node, the corresponding function is returned as well. - */ -static NodeExpandType get_node_expand_type(MFNetworkTreeMap &network_map, - const DNode &dnode, - const fn::MultiFunction **r_single_function) -{ - const fn::MFFunctionNode *single_function_node = nullptr; - bool has_multiple_nodes = false; - bool has_dummy_nodes = false; - - auto check_mf_node = [&](fn::MFNode &mf_node) { - if (mf_node.is_function()) { - if (single_function_node == nullptr) { - single_function_node = &mf_node.as_function(); - } - if (&mf_node != single_function_node) { - has_multiple_nodes = true; - } - } - else { - BLI_assert(mf_node.is_dummy()); - has_dummy_nodes = true; - } - }; - - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - for (fn::MFInputSocket *mf_input : - network_map.lookup(DInputSocket(dnode.context(), dsocket))) { - check_mf_node(mf_input->node()); - } - } - } - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket)); - check_mf_node(mf_output.node()); - } - } - - if (has_dummy_nodes) { - return NodeExpandType::HasDummyNodes; - } - if (has_multiple_nodes) { - return NodeExpandType::MultipleFunctionNodes; - } - *r_single_function = &single_function_node->function(); - return NodeExpandType::SingleFunctionNode; -} - -static const fn::MultiFunction &create_function_for_node_that_expands_into_multiple( - const DNode &dnode, - fn::MFNetwork &network, - MFNetworkTreeMap &network_map, - ResourceScope &scope) -{ - Vector dummy_fn_inputs; - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - MFDataType data_type = *socket_mf_type_get(*dsocket->typeinfo()); - fn::MFOutputSocket &fn_input = network.add_input(data_type.to_string(), data_type); - for (fn::MFInputSocket *mf_input : - network_map.lookup(DInputSocket(dnode.context(), dsocket))) { - network.add_link(fn_input, *mf_input); - dummy_fn_inputs.append(&fn_input); - } - } - } - Vector dummy_fn_outputs; - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket)); - MFDataType data_type = mf_output.data_type(); - fn::MFInputSocket &fn_output = network.add_output(data_type.to_string(), data_type); - network.add_link(mf_output, fn_output); - dummy_fn_outputs.append(&fn_output); - } - } - - fn::MFNetworkEvaluator &fn_evaluator = scope.construct( - __func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs)); - return fn_evaluator; -} - -/** - * Returns a single multi-function for every node that supports it. This makes it easier to reuse - * the multi-function implementation of nodes in different contexts. - */ -MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope) -{ - /* Build a network that nodes can insert themselves into. However, the individual nodes are not - * connected. */ - fn::MFNetwork &network = scope.construct(__func__); - MFNetworkTreeMap network_map{tree, network}; - MultiFunctionByNode functions_by_node; - - CommonMFNetworkBuilderData common{scope, network, network_map, tree}; - - tree.foreach_node([&](DNode dnode) { - const bNodeType *node_type = dnode->typeinfo(); - if (node_type->expand_in_mf_network == nullptr) { - /* This node does not have a multi-function implementation. */ - return; - } - - NodeMFNetworkBuilder builder{common, dnode}; - node_type->expand_in_mf_network(builder); - - const fn::MultiFunction *single_function = nullptr; - const NodeExpandType expand_type = get_node_expand_type(network_map, dnode, &single_function); - - switch (expand_type) { - case NodeExpandType::HasDummyNodes: { - /* Dummy nodes cannot be executed, so skip them. */ - break; - } - case NodeExpandType::SingleFunctionNode: { - /* This is the common case. Most nodes just expand to a single function. */ - functions_by_node.add_new(dnode, single_function); - break; - } - case NodeExpandType::MultipleFunctionNodes: { - /* If a node expanded into multiple functions, a new function has to be created that - * combines those. */ - const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple( - dnode, network, network_map, scope); - functions_by_node.add_new(dnode, &fn); - break; - } - } - }); - - return functions_by_node; -} - -} // namespace blender::nodes diff --git a/source/blender/nodes/intern/type_callbacks.cc b/source/blender/nodes/intern/type_callbacks.cc deleted file mode 100644 index 881a02c92e9..00000000000 --- a/source/blender/nodes/intern/type_callbacks.cc +++ /dev/null @@ -1,60 +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. - */ - -#include "NOD_node_tree_multi_function.hh" -#include "NOD_type_callbacks.hh" - -namespace blender::nodes { - -std::optional socket_mf_type_get(const bNodeSocketType &stype) -{ - const CPPType *cpp_type = stype.get_base_cpp_type ? stype.get_base_cpp_type() : nullptr; - if (cpp_type != nullptr) { - return MFDataType::ForSingle(*cpp_type); - } - return {}; -} - -bool socket_is_mf_data_socket(const bNodeSocketType &stype) -{ - if (!socket_mf_type_get(stype).has_value()) { - return false; - } - if (stype.expand_in_mf_network == nullptr && stype.get_base_cpp_value == nullptr) { - return false; - } - return true; -} - -void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder) -{ - bNodeSocket &socket = builder.bsocket(); - if (socket.typeinfo->expand_in_mf_network != nullptr) { - socket.typeinfo->expand_in_mf_network(builder); - } - else if (socket.typeinfo->get_base_cpp_value != nullptr) { - const CPPType &type = *socket.typeinfo->get_base_cpp_type(); - void *buffer = builder.resource_scope().linear_allocator().allocate(type.size(), - type.alignment()); - socket.typeinfo->get_base_cpp_value(socket, buffer); - builder.set_constant_value(type, buffer); - } - else { - BLI_assert_unreachable(); - } -} - -} // namespace blender::nodes diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h index dc44f0fa98f..a75354d3381 100644 --- a/source/blender/nodes/shader/node_shader_util.h +++ b/source/blender/nodes/shader/node_shader_util.h @@ -72,7 +72,7 @@ #ifdef __cplusplus # include "FN_multi_function_builder.hh" -# include "NOD_node_tree_multi_function.hh" +# include "NOD_multi_function.hh" # include "BLI_color.hh" # include "BLI_float3.hh" diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index 4f77421cfe0..f105f8bcaf9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -51,7 +51,7 @@ static int gpu_shader_clamp(GPUMaterial *mat, GPU_stack_link(mat, node, "clamp_range", in, out); } -static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_clamp_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static blender::fn::CustomMF_SI_SI_SI_SO minmax_fn{ "Clamp (Min Max)", @@ -65,7 +65,7 @@ static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuil return clamp_f(value, b, a); }}; - int clamp_type = builder.bnode().custom1; + int clamp_type = builder.node().custom1; if (clamp_type == NODE_CLAMP_MINMAX) { builder.set_matching_fn(minmax_fn); } @@ -82,7 +82,7 @@ void register_node_type_sh_clamp(void) node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out); node_type_init(&ntype, node_shader_init_clamp); node_type_gpu(&ntype, gpu_shader_clamp); - ntype.expand_in_mf_network = sh_node_clamp_expand_in_mf_network; + ntype.build_multi_function = sh_node_clamp_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index f1d5040a292..df075d6e973 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -143,9 +143,10 @@ class CurveVecFunction : public blender::fn::MultiFunction { } }; -static void sh_node_curve_vec_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_curve_vec_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; BKE_curvemapping_init(cumap); builder.construct_and_set_matching_fn(*cumap); @@ -162,7 +163,7 @@ void register_node_type_sh_curve_vec(void) node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec); node_type_gpu(&ntype, gpu_shader_curve_vec); - ntype.expand_in_mf_network = sh_node_curve_vec_expand_in_mf_network; + ntype.build_multi_function = sh_node_curve_vec_build_multi_function; nodeRegisterType(&ntype); } @@ -317,9 +318,10 @@ class CurveRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_curve_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_curve_rgb_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; BKE_curvemapping_init(cumap); builder.construct_and_set_matching_fn(*cumap); @@ -336,7 +338,7 @@ void register_node_type_sh_curve_rgb(void) node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb); node_type_gpu(&ntype, gpu_shader_curve_rgb); - ntype.expand_in_mf_network = sh_node_curve_rgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_curve_rgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index ad7abd9d491..e4739e2864d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -261,9 +261,10 @@ class MapRangeSmootherstepFunction : public blender::fn::MultiFunction { } }; -static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_map_range_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); bool clamp = bnode.custom1 != 0; int interpolation_type = bnode.custom2; @@ -301,7 +302,6 @@ static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetwork break; } default: - builder.set_not_implemented(); break; } } @@ -315,7 +315,7 @@ void register_node_type_sh_map_range(void) node_type_init(&ntype, node_shader_init_map_range); node_type_update(&ntype, node_shader_update_map_range); node_type_gpu(&ntype, gpu_shader_map_range); - ntype.expand_in_mf_network = sh_node_map_range_expand_in_mf_network; + ntype.build_multi_function = sh_node_map_range_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 7a846031456..c30f2948ab1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -69,11 +69,9 @@ static int gpu_shader_math(GPUMaterial *mat, return 0; } -static const blender::fn::MultiFunction &get_base_multi_function( - blender::nodes::NodeMFNetworkBuilder &builder) +static const blender::fn::MultiFunction *get_base_multi_function(bNode &node) { - const int mode = builder.bnode().custom1; - + const int mode = node.custom1; const blender::fn::MultiFunction *base_fn = nullptr; blender::nodes::try_dispatch_float_math_fl_to_fl( @@ -82,7 +80,7 @@ static const blender::fn::MultiFunction &get_base_multi_function( base_fn = &fn; }); if (base_fn != nullptr) { - return *base_fn; + return base_fn; } blender::nodes::try_dispatch_float_math_fl_fl_to_fl( @@ -92,7 +90,7 @@ static const blender::fn::MultiFunction &get_base_multi_function( base_fn = &fn; }); if (base_fn != nullptr) { - return *base_fn; + return base_fn; } blender::nodes::try_dispatch_float_math_fl_fl_fl_to_fl( @@ -102,36 +100,51 @@ static const blender::fn::MultiFunction &get_base_multi_function( base_fn = &fn; }); if (base_fn != nullptr) { - return *base_fn; + return base_fn; } - return builder.get_not_implemented_fn(); + return nullptr; } -static void sh_node_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) -{ - const blender::fn::MultiFunction &base_function = get_base_multi_function(builder); +class ClampWrapperFunction : public blender::fn::MultiFunction { + private: + const blender::fn::MultiFunction &fn_; - const blender::nodes::DNode &dnode = builder.dnode(); - blender::fn::MFNetwork &network = builder.network(); - blender::fn::MFFunctionNode &base_node = network.add_function(base_function); + public: + ClampWrapperFunction(const blender::fn::MultiFunction &fn) : fn_(fn) + { + this->set_signature(&fn.signature()); + } - builder.network_map().add_try_match(*dnode.context(), dnode->inputs(), base_node.inputs()); + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext context) const override + { + fn_.call(mask, params, context); + + /* Assumes the output parameter is the last one. */ + const int output_param_index = this->param_amount() - 1; + /* This has actually been initialized in the call above. */ + blender::MutableSpan results = params.uninitialized_single_output( + output_param_index); + + for (const int i : mask) { + float &value = results[i]; + CLAMP(value, 0.0f, 1.0f); + } + } +}; + +static void sh_node_math_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + const blender::fn::MultiFunction *base_function = get_base_multi_function(builder.node()); - const bool clamp_output = builder.bnode().custom2 != 0; + const bool clamp_output = builder.node().custom2 != 0; if (clamp_output) { - static blender::fn::CustomMF_SI_SO clamp_fn{"Clamp", [](float value) { - CLAMP(value, 0.0f, 1.0f); - return value; - }}; - blender::fn::MFFunctionNode &clamp_node = network.add_function(clamp_fn); - network.add_link(base_node.output(0), clamp_node.input(0)); - builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)), - clamp_node.output(0)); + builder.construct_and_set_matching_fn(*base_function); } else { - builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)), - base_node.output(0)); + builder.set_matching_fn(base_function); } } @@ -144,7 +157,7 @@ void register_node_type_sh_math(void) node_type_label(&ntype, node_math_label); node_type_gpu(&ntype, gpu_shader_math); node_type_update(&ntype, node_math_update); - ntype.expand_in_mf_network = sh_node_math_expand_in_mf_network; + ntype.build_multi_function = sh_node_math_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc index 47011caeeb6..ade35a40366 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc @@ -174,9 +174,9 @@ class MixRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_mix_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_mix_rgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &node = builder.bnode(); + bNode &node = builder.node(); bool clamp = node.custom2 & SHD_MIXRGB_CLAMP; int mix_type = node.custom1; builder.construct_and_set_matching_fn(clamp, mix_type); @@ -191,7 +191,7 @@ void register_node_type_sh_mix_rgb(void) node_type_label(&ntype, node_blend_label); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_mix_rgb); node_type_gpu(&ntype, gpu_shader_mix_rgb); - ntype.expand_in_mf_network = sh_node_mix_rgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_mix_rgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index a7239154633..2779fc6bf68 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -96,7 +96,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_seprgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_seprgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static SeparateRGBFunction fn; builder.set_matching_fn(fn); @@ -110,7 +110,7 @@ void register_node_type_sh_seprgb(void) node_type_socket_templates(&ntype, sh_node_seprgb_in, sh_node_seprgb_out); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_seprgb); node_type_gpu(&ntype, gpu_shader_seprgb); - ntype.expand_in_mf_network = sh_node_seprgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_seprgb_build_multi_function; nodeRegisterType(&ntype); } @@ -153,7 +153,7 @@ static int gpu_shader_combrgb(GPUMaterial *mat, return GPU_stack_link(mat, node, "combine_rgb", in, out); } -static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_combrgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static blender::fn::CustomMF_SI_SI_SI_SO fn{ "Combine RGB", @@ -169,7 +169,7 @@ void register_node_type_sh_combrgb(void) node_type_socket_templates(&ntype, sh_node_combrgb_in, sh_node_combrgb_out); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_combrgb); node_type_gpu(&ntype, gpu_shader_combrgb); - ntype.expand_in_mf_network = sh_node_combrgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_combrgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index efa9581c414..1fd794cdd0a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -81,7 +81,7 @@ class MF_SeparateXYZ : public blender::fn::MultiFunction { } }; -static void sh_node_sepxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_sepxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static MF_SeparateXYZ separate_fn; builder.set_matching_fn(separate_fn); @@ -94,7 +94,7 @@ void register_node_type_sh_sepxyz(void) sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, sh_node_sepxyz_in, sh_node_sepxyz_out); node_type_gpu(&ntype, gpu_shader_sepxyz); - ntype.expand_in_mf_network = sh_node_sepxyz_expand_in_mf_network; + ntype.build_multi_function = sh_node_sepxyz_build_multi_function; nodeRegisterType(&ntype); } @@ -120,7 +120,7 @@ static int gpu_shader_combxyz(GPUMaterial *mat, return GPU_stack_link(mat, node, "combine_xyz", in, out); } -static void sh_node_combxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_combxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static blender::fn::CustomMF_SI_SI_SI_SO fn{ "Combine Vector", [](float x, float y, float z) { return blender::float3(x, y, z); }}; @@ -134,7 +134,7 @@ void register_node_type_sh_combxyz(void) sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, sh_node_combxyz_in, sh_node_combxyz_out); node_type_gpu(&ntype, gpu_shader_combxyz); - ntype.expand_in_mf_network = sh_node_combxyz_expand_in_mf_network; + ntype.build_multi_function = sh_node_combxyz_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 5b2eb300aac..1bc42ab0cc6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -163,9 +163,10 @@ class ColorBandFunction : public blender::fn::MultiFunction { } }; -static void sh_node_valtorgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_valtorgb_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); const ColorBand *color_band = (const ColorBand *)bnode.storage; builder.construct_and_set_matching_fn(*color_band); } @@ -181,7 +182,7 @@ void register_node_type_sh_valtorgb(void) node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_valtorgb); node_type_gpu(&ntype, gpu_shader_valtorgb); - ntype.expand_in_mf_network = sh_node_valtorgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_valtorgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 495c8d12824..602d5a1cf56 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -39,9 +39,9 @@ static int gpu_shader_value(GPUMaterial *mat, return GPU_stack_link(mat, node, "set_value", in, out, link); } -static void sh_node_value_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_value_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { - const bNodeSocket *bsocket = builder.dnode()->output(0).bsocket(); + const bNodeSocket *bsocket = (bNodeSocket *)builder.node().outputs.first; const bNodeSocketValueFloat *value = (const bNodeSocketValueFloat *)bsocket->default_value; builder.construct_and_set_matching_fn>(value->value); } @@ -53,7 +53,7 @@ void register_node_type_sh_value(void) sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); node_type_socket_templates(&ntype, nullptr, sh_node_value_out); node_type_gpu(&ntype, gpu_shader_value); - ntype.expand_in_mf_network = sh_node_value_expand_in_mf_network; + ntype.build_multi_function = sh_node_value_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 419a11201aa..4424db6aed1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -183,12 +183,11 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node } } -static const blender::fn::MultiFunction &get_multi_function( - blender::nodes::NodeMFNetworkBuilder &builder) +static const blender::fn::MultiFunction *get_multi_function(bNode &node) { using blender::float3; - NodeVectorMathOperation operation = NodeVectorMathOperation(builder.bnode().custom1); + NodeVectorMathOperation operation = NodeVectorMathOperation(node.custom1); const blender::fn::MultiFunction *multi_fn = nullptr; @@ -199,7 +198,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl3_fl3_to_fl3( @@ -209,7 +208,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl3_fl_to_fl3( @@ -219,7 +218,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl( @@ -229,7 +228,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl_to_fl3( @@ -239,7 +238,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_to_fl3( @@ -248,7 +247,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_to_fl( @@ -257,15 +256,16 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } - return builder.get_not_implemented_fn(); + return nullptr; } -static void sh_node_vector_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_vector_math_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -278,7 +278,7 @@ void register_node_type_sh_vect_math(void) node_type_label(&ntype, node_vector_math_label); node_type_gpu(&ntype, gpu_shader_vector_math); node_type_update(&ntype, node_shader_update_vector_math); - ntype.expand_in_mf_network = sh_node_vector_math_expand_in_mf_network; + ntype.build_multi_function = sh_node_vector_math_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index 3b2c2fa5a03..bc51b7e29ea 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -100,11 +100,10 @@ static float3 sh_node_vector_rotate_euler(const float3 vector, return result + center; } -static const blender::fn::MultiFunction &get_multi_function( - blender::nodes::NodeMFNetworkBuilder &builder) +static const blender::fn::MultiFunction *get_multi_function(bNode &node) { - bool invert = builder.bnode().custom2; - const int mode = builder.bnode().custom1; + bool invert = node.custom2; + const int mode = node.custom1; switch (mode) { case NODE_VECTOR_ROTATE_TYPE_AXIS: { @@ -113,13 +112,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SI_SO fn{ "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_AXIS_X: { float3 axis = float3(1.0f, 0.0f, 0.0f); @@ -128,13 +127,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate X-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO fn{ "Rotate X-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_AXIS_Y: { float3 axis = float3(0.0f, 1.0f, 0.0f); @@ -143,13 +142,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Y-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO fn{ "Rotate Y-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_AXIS_Z: { float3 axis = float3(0.0f, 0.0f, 1.0f); @@ -158,13 +157,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Z-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO fn{ "Rotate Z-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: { if (invert) { @@ -172,24 +171,24 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Euler", [](float3 in, float3 center, float3 rotation) { return sh_node_vector_rotate_euler(in, center, rotation, true); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO fn{ "Rotate Euler", [](float3 in, float3 center, float3 rotation) { return sh_node_vector_rotate_euler(in, center, rotation, false); }}; - return fn; + return &fn; } default: BLI_assert_unreachable(); - return builder.get_not_implemented_fn(); + return nullptr; } } -static void sh_node_vector_rotate_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_vector_rotate_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -211,7 +210,7 @@ void register_node_type_sh_vector_rotate(void) node_type_socket_templates(&ntype, sh_node_vector_rotate_in, sh_node_vector_rotate_out); node_type_gpu(&ntype, gpu_shader_vector_rotate); node_type_update(&ntype, node_shader_update_vector_rotate); - ntype.expand_in_mf_network = sh_node_vector_rotate_expand_in_mf_network; + ntype.build_multi_function = sh_node_vector_rotate_build_multi_function; nodeRegisterType(&ntype); } -- cgit v1.2.3 From ef502127ddc0f5a06e76cb2f634655b8e9c8f740 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 20 Aug 2021 08:40:13 -0300 Subject: Fix T90795: Moving keys in Grease Pencil Dopesheet crashes Blender `td->loc` is referenced but not initialized. --- source/blender/editors/transform/transform_convert_action.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index 8a3b254be5c..a5565b5fb88 100644 --- a/source/blender/editors/transform/transform_convert_action.c +++ b/source/blender/editors/transform/transform_convert_action.c @@ -241,16 +241,15 @@ static int GPLayerToTransData(TransData *td, for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { if (is_prop_edit || (gpf->flag & GP_FRAME_SELECT)) { if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) { - /* memory is calloc'ed, so that should zero everything nicely for us */ - td->val = &tfd->val; - td->ival = (float)gpf->framenum; + tfd->val = (float)gpf->framenum; + tfd->sdata = &gpf->framenum; + + td->val = td->loc = &tfd->val; /* XXX: It's not a 3d array. */ + td->ival = td->iloc[0] = (float)gpf->framenum; td->center[0] = td->ival; td->center[1] = ypos; - tfd->val = (float)gpf->framenum; - tfd->sdata = &gpf->framenum; - /* Advance `td` now. */ td++; tfd++; -- cgit v1.2.3 From accf3045be40433045c197cfcdbcbc32a7724403 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 20 Aug 2021 09:05:52 -0300 Subject: Fix memory leak while processing mouse event Assignment missed. --- intern/ghost/intern/GHOST_SystemWin32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 347067eae50..f44107ee000 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1741,7 +1741,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, case WM_MOUSELEAVE: { window->m_mousePresent = false; if (window->getTabletData().Active == GHOST_kTabletModeNone) { - processCursorEvent(window); + event = processCursorEvent(window); } GHOST_Wintab *wt = window->getWintab(); if (wt) { -- cgit v1.2.3 From 9bfc47c9334bc4fbecbe7871fff9af4cc46c8832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 20 Aug 2021 14:29:05 +0200 Subject: Alembic Procedural: basic cache control settings This adds a setting to enable data caching, and another one to set the maximum cache size in megabytes. When caching is enabled we load the data for the entire animation in memory, as we already do, however, if the data exceeds the memory limit, render is aborted. When caching is disabled, we simply load the data for the current frame in memory. Ref D10197 Reviewed By: brecht Differential Revision: https://developer.blender.org/D11163 --- intern/cycles/blender/blender_object.cpp | 3 + intern/cycles/render/alembic.cpp | 70 ++++++++++++++++++++++ intern/cycles/render/alembic.h | 30 ++++++++++ intern/cycles/render/alembic_read.cpp | 16 ++++- .../editors/interface/interface_templates.c | 15 +++++ source/blender/makesdna/DNA_cachefile_defaults.h | 2 + source/blender/makesdna/DNA_cachefile_types.h | 10 +++- source/blender/makesrna/intern/rna_cachefile.c | 17 ++++++ 8 files changed, 159 insertions(+), 4 deletions(-) diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 2dbebac4cc3..a7eae421b55 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -523,6 +523,9 @@ void BlenderSync::sync_procedural(BL::Object &b_ob, procedural->set_scale(cache_file.scale()); + procedural->set_use_prefetch(cache_file.use_prefetch()); + procedural->set_prefetch_cache_size(cache_file.prefetch_cache_size()); + /* create or update existing AlembicObjects */ ustring object_path = ustring(b_mesh_cache.object_path()); diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp index 81f47256739..69bc0712674 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/render/alembic.cpp @@ -25,6 +25,7 @@ #include "render/shader.h" #include "util/util_foreach.h" +#include "util/util_logging.h" #include "util/util_progress.h" #include "util/util_transform.h" #include "util/util_vector.h" @@ -211,6 +212,35 @@ void CachedData::set_time_sampling(TimeSampling time_sampling) } } +size_t CachedData::memory_used() const +{ + size_t mem_used = 0; + + mem_used += curve_first_key.memory_used(); + mem_used += curve_keys.memory_used(); + mem_used += curve_radius.memory_used(); + mem_used += curve_shader.memory_used(); + mem_used += num_ngons.memory_used(); + mem_used += shader.memory_used(); + mem_used += subd_creases_edge.memory_used(); + mem_used += subd_creases_weight.memory_used(); + mem_used += subd_face_corners.memory_used(); + mem_used += subd_num_corners.memory_used(); + mem_used += subd_ptex_offset.memory_used(); + mem_used += subd_smooth.memory_used(); + mem_used += subd_start_corner.memory_used(); + mem_used += transforms.memory_used(); + mem_used += triangles.memory_used(); + mem_used += uv_loops.memory_used(); + mem_used += vertices.memory_used(); + + for (const CachedAttribute &attr : attributes) { + mem_used += attr.data.memory_used(); + } + + return mem_used; +} + static M44d convert_yup_zup(const M44d &mtx, float scale_mult) { V3d scale, shear, rotation, translation; @@ -706,6 +736,9 @@ NODE_DEFINE(AlembicProcedural) SOCKET_NODE_ARRAY(objects, "Objects", AlembicObject::get_node_type()); + SOCKET_BOOLEAN(use_prefetch, "Use Prefetch", true); + SOCKET_INT(prefetch_cache_size, "Prefetch Cache Size", 4096); + return type; } @@ -823,6 +856,30 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress) } } + if (use_prefetch_is_modified()) { + if (!use_prefetch) { + for (Node *node : objects) { + AlembicObject *object = static_cast(node); + object->clear_cache(); + } + } + } + + if (prefetch_cache_size_is_modified()) { + /* Check whether the current memory usage fits in the new requested size, + * abort the render if it is any higher. */ + size_t memory_used = 0ul; + for (Node *node : objects) { + AlembicObject *object = static_cast(node); + memory_used += object->get_cached_data().memory_used(); + } + + if (memory_used > get_prefetch_cache_size_in_bytes()) { + progress.set_error("Error: Alembic Procedural memory limit reached"); + return; + } + } + build_caches(progress); foreach (Node *node, objects) { @@ -1300,6 +1357,8 @@ void AlembicProcedural::walk_hierarchy( void AlembicProcedural::build_caches(Progress &progress) { + size_t memory_used = 0; + for (Node *node : objects) { AlembicObject *object = static_cast(node); @@ -1353,7 +1412,18 @@ void AlembicProcedural::build_caches(Progress &progress) if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) { object->setup_transform_cache(object->get_cached_data(), scale); } + + memory_used += object->get_cached_data().memory_used(); + + if (use_prefetch) { + if (memory_used > get_prefetch_cache_size_in_bytes()) { + progress.set_error("Error: Alembic Procedural memory limit reached"); + return; + } + } } + + VLOG(1) << "AlembicProcedural memory usage : " << string_human_readable_size(memory_used); } CCL_NAMESPACE_END diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h index 9c58af720f6..8e166a5ab04 100644 --- a/intern/cycles/render/alembic.h +++ b/intern/cycles/render/alembic.h @@ -272,6 +272,21 @@ template class DataStore { node->set(*socket, value); } + size_t memory_used() const + { + if constexpr (is_array::value) { + size_t mem_used = 0; + + for (const T &array : data) { + mem_used += array.size() * sizeof(array[0]); + } + + return mem_used; + } + + return data.size() * sizeof(T); + } + private: const TimeIndexPair &get_index_for_time(double time) const { @@ -332,6 +347,8 @@ struct CachedData { void invalidate_last_loaded_time(bool attributes_only = false); void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling); + + size_t memory_used() const; }; /* Representation of an Alembic object for the AlembicProcedural. @@ -482,6 +499,13 @@ class AlembicProcedural : public Procedural { * software. */ NODE_SOCKET_API(float, scale) + /* Cache controls */ + NODE_SOCKET_API(bool, use_prefetch) + + /* Memory limit for the cache, if the data does not fit within this limit, rendering is aborted. + */ + NODE_SOCKET_API(int, prefetch_cache_size) + AlembicProcedural(); ~AlembicProcedural(); @@ -531,6 +555,12 @@ class AlembicProcedural : public Procedural { void read_subd(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time); void build_caches(Progress &progress); + + size_t get_prefetch_cache_size_in_bytes() const + { + /* prefetch_cache_size is in megabytes, so convert to bytes. */ + return static_cast(prefetch_cache_size) * 1024 * 1024; + } }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp index c53ec668938..b105af63b44 100644 --- a/intern/cycles/render/alembic_read.cpp +++ b/intern/cycles/render/alembic_read.cpp @@ -44,9 +44,19 @@ static set get_relevant_sample_times(AlembicProcedural *proc, return result; } - // load the data for the entire animation - const double start_frame = static_cast(proc->get_start_frame()); - const double end_frame = static_cast(proc->get_end_frame()); + double start_frame; + double end_frame; + + if (proc->get_use_prefetch()) { + // load the data for the entire animation + start_frame = static_cast(proc->get_start_frame()); + end_frame = static_cast(proc->get_end_frame()); + } + else { + // load the data for the current frame + start_frame = static_cast(proc->get_frame()); + end_frame = start_frame; + } const double frame_rate = static_cast(proc->get_frame_rate()); const double start_time = start_frame / frame_rate; diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 351b73c320b..cdb35d19855 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -6489,6 +6489,17 @@ void uiTemplateCacheFile(uiLayout *layout, uiLayoutSetActive(row, engine_supports_procedural); uiItemR(row, &fileptr, "use_render_procedural", 0, NULL, ICON_NONE); + const bool use_render_procedural = RNA_boolean_get(&fileptr, "use_render_procedural"); + const bool use_prefetch = RNA_boolean_get(&fileptr, "use_prefetch"); + + row = uiLayoutRow(layout, false); + uiLayoutSetEnabled(row, use_render_procedural); + uiItemR(row, &fileptr, "use_prefetch", 0, NULL, ICON_NONE); + + sub = uiLayoutRow(layout, false); + uiLayoutSetEnabled(sub, use_prefetch && use_render_procedural); + uiItemR(sub, &fileptr, "prefetch_cache_size", 0, NULL, ICON_NONE); + row = uiLayoutRowWithHeading(layout, true, IFACE_("Override Frame")); sub = uiLayoutRow(row, true); uiLayoutSetPropDecorate(sub, false); @@ -6510,6 +6521,10 @@ void uiTemplateCacheFile(uiLayout *layout, uiItemR(layout, &fileptr, "velocity_name", 0, NULL, ICON_NONE); uiItemR(layout, &fileptr, "velocity_unit", 0, NULL, ICON_NONE); + row = uiLayoutRow(layout, false); + uiLayoutSetActive(row, engine_supports_procedural && use_render_procedural); + uiItemR(row, &fileptr, "default_radius", 0, NULL, ICON_NONE); + /* TODO: unused for now, so no need to expose. */ #if 0 row = uiLayoutRow(layout, false); diff --git a/source/blender/makesdna/DNA_cachefile_defaults.h b/source/blender/makesdna/DNA_cachefile_defaults.h index 521b72567d4..74fbe5012ab 100644 --- a/source/blender/makesdna/DNA_cachefile_defaults.h +++ b/source/blender/makesdna/DNA_cachefile_defaults.h @@ -40,6 +40,8 @@ .handle = NULL, \ .handle_filepath[0] = '\0', \ .handle_readers = NULL, \ + .use_prefetch = 1, \ + .prefetch_cache_size = 4096, \ } /** \} */ diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h index ae4ade49be1..0f4c53a6e7e 100644 --- a/source/blender/makesdna/DNA_cachefile_types.h +++ b/source/blender/makesdna/DNA_cachefile_types.h @@ -101,7 +101,15 @@ typedef struct CacheFile { */ char use_render_procedural; - char _pad1[7]; + char _pad1[3]; + + /** Enable data prefetching when using the Cycles Procedural. */ + char use_prefetch; + + /** Size in megabytes for the prefetch cache used by the Cycles Procedural. */ + int prefetch_cache_size; + + char _pad2[7]; char velocity_unit; /* Name of the velocity property in the archive. */ diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c index cb0a490417d..74d924b8937 100644 --- a/source/blender/makesrna/intern/rna_cachefile.c +++ b/source/blender/makesrna/intern/rna_cachefile.c @@ -150,6 +150,23 @@ static void rna_def_cachefile(BlenderRNA *brna) "determine which file to use in a file sequence"); RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + /* ----------------- Cache controls ----------------- */ + + prop = RNA_def_property(srna, "use_prefetch", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Use Prefetch", + "When enabled, the Cycles Procedural will preload animation data for faster updates"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + prop = RNA_def_property(srna, "prefetch_cache_size", PROP_INT, PROP_UNSIGNED); + RNA_def_property_ui_text( + prop, + "Prefetch Cache Size", + "Memory usage limit in megabytes for the Cycles Procedural cache, if the data does not " + "fit within the limit, rendering is aborted"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + /* ----------------- Axis Conversion ----------------- */ prop = RNA_def_property(srna, "forward_axis", PROP_ENUM, PROP_NONE); -- cgit v1.2.3 From 6a404bc633586a96d635e2e3bf2e207f87ab3785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 20 Aug 2021 14:48:47 +0200 Subject: Cleanup, remove extra code from previous commit This got accidentally introduced while revising dependencies between patches for this feature, did not notice until it was too late. --- source/blender/editors/interface/interface_templates.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index cdb35d19855..0e5a6a79137 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -6521,10 +6521,6 @@ void uiTemplateCacheFile(uiLayout *layout, uiItemR(layout, &fileptr, "velocity_name", 0, NULL, ICON_NONE); uiItemR(layout, &fileptr, "velocity_unit", 0, NULL, ICON_NONE); - row = uiLayoutRow(layout, false); - uiLayoutSetActive(row, engine_supports_procedural && use_render_procedural); - uiItemR(row, &fileptr, "default_radius", 0, NULL, ICON_NONE); - /* TODO: unused for now, so no need to expose. */ #if 0 row = uiLayoutRow(layout, false); -- cgit v1.2.3 From 1b5f17b86771faa87d36ff12183aa6dbb5c1e9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 20 Aug 2021 15:00:58 +0200 Subject: Cleanup, use BKE_scene_uses_cycles_experimental_features --- source/blender/modifiers/intern/MOD_subsurf.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index ce427281db3..db0b769684e 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -358,13 +358,8 @@ static bool get_show_adaptive_options(const bContext *C, Panel *panel) /* Don't show adaptive options if the cycles experimental feature set is disabled. */ Scene *scene = CTX_data_scene(C); - PointerRNA scene_ptr; - RNA_id_pointer_create(&scene->id, &scene_ptr); - if (BKE_scene_uses_cycles(scene)) { - PointerRNA cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles"); - if (RNA_enum_get(&cycles_ptr, "feature_set") != 1) { /* EXPERIMENTAL */ - return false; - } + if (!BKE_scene_uses_cycles_experimental_features(scene)) { + return false; } return true; -- cgit v1.2.3 From cd8d9383e74a798e75ebced95cdd6d8de9f02369 Mon Sep 17 00:00:00 2001 From: Alaska Date: Fri, 20 Aug 2021 17:43:15 +0200 Subject: Fix T90804: small grammatical error in noise threshold description Differential Revision: https://developer.blender.org/D12277 --- intern/cycles/blender/addon/properties.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index ac0aca57028..0c3af3fabeb 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -408,7 +408,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): adaptive_threshold: FloatProperty( name="Adaptive Sampling Threshold", - description="Noise level step to stop sampling at, lower values reduce noise the cost of render time. Zero for automatic setting based on number of AA samples", + description="Noise level step to stop sampling at, lower values reduce noise at the cost of render time. Zero for automatic setting based on number of AA samples", min=0.0, max=1.0, default=0.0, precision=4, -- cgit v1.2.3 From 86622467c573e0703eea2554ebb0da5c67cbfb8c Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Fri, 20 Aug 2021 13:46:38 -0400 Subject: DocPy: Update Dependancies Updates sphinx and the theme to the latest version along with any of their dependencies. Note that we will be sticking to sphinx 4.1.1 until sphinx 4.2 for the same reasons listed in: https://developer.blender.org/rBM8334 --- doc/python_api/requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/python_api/requirements.txt b/doc/python_api/requirements.txt index a854d2a267b..b5a9d15bf7b 100644 --- a/doc/python_api/requirements.txt +++ b/doc/python_api/requirements.txt @@ -1,13 +1,13 @@ -sphinx==3.5.4 +sphinx==4.1.1 # Sphinx dependencies that are important -Jinja2==2.11.3 -Pygments==2.9.0 -docutils==0.16 +Jinja2==3.0.1 +Pygments==2.10.0 +docutils==0.17.1 snowballstemmer==2.1.0 babel==2.9.1 -requests==2.25.1 +requests==2.26.0 # Only needed to match the theme used for the official documentation. # Without this theme, the default theme will be used. -sphinx_rtd_theme==0.5.2 +sphinx_rtd_theme==1.0.0rc1 -- cgit v1.2.3 From b6a1bf757d9f0675f0fb5f21a72ff4ee31f8d6d9 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Fri, 20 Aug 2021 14:54:46 -0400 Subject: DocPy: Cleanup missing newline resulting in wrong html generation --- source/blender/python/intern/bpy_app_icons.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/python/intern/bpy_app_icons.c b/source/blender/python/intern/bpy_app_icons.c index 7cca3ae4700..acd809fb8d5 100644 --- a/source/blender/python/intern/bpy_app_icons.c +++ b/source/blender/python/intern/bpy_app_icons.c @@ -35,7 +35,7 @@ /* We may want to load direct from file. */ PyDoc_STRVAR( bpy_app_icons_new_triangles_doc, - ".. function:: new_triangles(range, coords, colors)" + ".. function:: new_triangles(range, coords, colors)\n" "\n" " Create a new icon from triangle geometry.\n" "\n" @@ -91,7 +91,7 @@ static PyObject *bpy_app_icons_new_triangles(PyObject *UNUSED(self), PyObject *a } PyDoc_STRVAR(bpy_app_icons_new_triangles_from_file_doc, - ".. function:: new_triangles_from_file(filename)" + ".. function:: new_triangles_from_file(filename)\n" "\n" " Create a new icon from triangle geometry.\n" "\n" @@ -122,7 +122,7 @@ static PyObject *bpy_app_icons_new_triangles_from_file(PyObject *UNUSED(self), } PyDoc_STRVAR(bpy_app_icons_release_doc, - ".. function:: release(icon_id)" + ".. function:: release(icon_id)\n" "\n" " Release the icon.\n"); static PyObject *bpy_app_icons_release(PyObject *UNUSED(self), PyObject *args, PyObject *kw) -- cgit v1.2.3 From 0d7aab2375e6bb06e89dad851550b283a1ff805c Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Fri, 20 Aug 2021 17:48:42 -0700 Subject: Refactor: BLF Kerning Cache After Use Optimization of font kerning by only caching kerning values after a pair is encountered. Also saves unscaled values so they don't have to be rebuilt between font size changes. See D12274 for more details and speed comparison. Differential Revision: https://developer.blender.org/D12274 Reviewed by Campbell Barton --- source/blender/blenfont/intern/blf.c | 1 - source/blender/blenfont/intern/blf_font.c | 96 +++++++++++++--------- source/blender/blenfont/intern/blf_glyph.c | 50 ----------- source/blender/blenfont/intern/blf_internal.h | 4 - .../blender/blenfont/intern/blf_internal_types.h | 9 +- 5 files changed, 62 insertions(+), 98 deletions(-) diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 9168e7aa19c..2f9eb0753ac 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -108,7 +108,6 @@ void BLF_cache_clear(void) FontBLF *font = global_font[i]; if (font) { blf_glyph_cache_clear(font); - blf_kerning_cache_clear(font); } } } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 00d3cfb09eb..02847b74df1 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -308,17 +308,6 @@ void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) blf_glyph_cache_release(font); } -static void blf_font_ensure_ascii_kerning(FontBLF *font, GlyphCacheBLF *gc) -{ - if (font->kerning_cache || !FT_HAS_KERNING(font->face)) { - return; - } - font->kerning_cache = blf_kerning_cache_find(font); - if (!font->kerning_cache) { - font->kerning_cache = blf_kerning_cache_new(font, gc); - } -} - /* Fast path for runs of ASCII characters. Given that common UTF-8 * input will consist of an overwhelming majority of ASCII * characters. @@ -348,6 +337,31 @@ BLI_INLINE GlyphBLF *blf_utf8_next_fast( return g; } +/* Convert a FreeType 26.6 value representing an unscaled design size to pixels. + * This is an exact copy of the scaling done inside FT_Get_Kerning when called + * with FT_KERNING_DEFAULT, including arbitrary resizing for small fonts. + */ +static int blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) +{ + /* Scale value by font size using integer-optimized multiplication. */ + FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale); + + /* FreeType states that this '25' has been determined heuristically. */ + if (font->face->size->metrics.x_ppem < 25) { + scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25); + } + + /* Copies of internal FreeType macros needed here. */ +#define FT_PIX_FLOOR(x) ((x) & ~FT_TYPEOF(x) 63) +#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32) + + /* Round to even 64ths, then divide by 64. */ + return FT_PIX_ROUND(scaled) >> 6; + +#undef FT_PIX_FLOOR +#undef FT_PIX_ROUND +} + BLI_INLINE void blf_kerning_step_fast(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g, @@ -355,18 +369,28 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, const uint c, int *pen_x_p) { - /* `blf_font_ensure_ascii_kerning(font, gc);` must be called before this function. */ - BLI_assert(font->kerning_cache != NULL || !FT_HAS_KERNING(font->face)); + if (FT_HAS_KERNING(font->face) && g_prev != NULL) { + FT_Vector delta = {KERNING_ENTRY_UNSET}; - if (g_prev != NULL && FT_HAS_KERNING(font->face)) { + /* Get unscaled kerning value from our cache if ASCII. */ if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - *pen_x_p += font->kerning_cache->ascii_table[c][c_prev]; + delta.x = font->kerning_cache->ascii_table[c][c_prev]; } - else { - FT_Vector delta; - if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_DEFAULT, &delta) == 0) { - *pen_x_p += (int)delta.x >> 6; - } + + /* If not ASCII or not found in cache, ask FreeType for kerning. */ + if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { + /* Note that this function sets delta values to zero on any error. */ + FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); + } + + /* If ASCII we save this value to our cache for quicker access next time. */ + if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { + font->kerning_cache->ascii_table[c][c_prev] = delta.x; + } + + if (delta.x != 0) { + /* Convert unscaled design units to pixels and move pen. */ + *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x); } } } @@ -388,8 +412,6 @@ static void blf_font_draw_ex(FontBLF *font, return; } - blf_font_ensure_ascii_kerning(font, gc); - blf_batch_draw_begin(font); while ((i < len) && str[i]) { @@ -435,8 +457,6 @@ static void blf_font_draw_ascii_ex( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_kerning(font, gc); - blf_batch_draw_begin(font); while ((c = *(str++)) && len--) { @@ -536,8 +556,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font, int chx, chy; int y, x; - blf_font_ensure_ascii_kerning(font, gc); - /* another buffer specific call for color conversion */ while ((i < len) && str[i]) { @@ -694,8 +712,6 @@ size_t blf_font_width_to_strlen( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); const int width_i = (int)width; - blf_font_ensure_ascii_kerning(font, gc); - 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) { g = blf_utf8_next_fast(font, gc, str, &i, &c); @@ -725,8 +741,6 @@ size_t blf_font_width_to_rstrlen( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); const int width_i = (int)width; - blf_font_ensure_ascii_kerning(font, gc); - i = BLI_strnlen(str, len); s = BLI_str_find_prev_char_utf8(str, &str[i]); i = (size_t)((s != NULL) ? s - str : 0); @@ -778,8 +792,6 @@ static void blf_font_boundbox_ex(FontBLF *font, box->ymin = 32000.0f; box->ymax = -32000.0f; - blf_font_ensure_ascii_kerning(font, gc); - while ((i < len) && str[i]) { g = blf_utf8_next_fast(font, gc, str, &i, &c); @@ -869,8 +881,6 @@ static void blf_font_wrap_apply(FontBLF *font, GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_kerning(font, gc); - struct WordWrapVars { int wrap_width; size_t start, last[2]; @@ -1123,8 +1133,6 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, return; } - blf_font_ensure_ascii_kerning(font, gc); - while ((i < len) && str[i]) { i_curr = i; g = blf_utf8_next_fast(font, gc, str, &i, &c); @@ -1205,7 +1213,9 @@ void blf_font_free(FontBLF *font) blf_glyph_cache_free(gc); } - blf_kerning_cache_clear(font); + if (font->kerning_cache) { + MEM_freeN(font->kerning_cache); + } FT_Done_Face(font->face); if (font->filename) { @@ -1246,7 +1256,6 @@ static void blf_font_fill(FontBLF *font) font->dpi = 0; font->size = 0; BLI_listbase_clear(&font->cache); - BLI_listbase_clear(&font->kerning_caches); font->kerning_cache = NULL; #if BLF_BLUR_ENABLE font->blur = 0; @@ -1307,6 +1316,17 @@ FontBLF *blf_font_new(const char *name, const char *filename) font->name = BLI_strdup(name); font->filename = BLI_strdup(filename); blf_font_fill(font); + + if (FT_HAS_KERNING(font->face)) { + /* Create kerning cache table and fill with value indicating "unset". */ + font->kerning_cache = MEM_mallocN(sizeof(KerningCacheBLF), __func__); + for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { + for (uint j = 0; j < KERNING_CACHE_TABLE_SIZE; j++) { + font->kerning_cache->ascii_table[i][j] = KERNING_ENTRY_UNSET; + } + } + } + return font; } diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 5fb69251466..6cdf5fc5996 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -55,56 +55,6 @@ #include "BLI_math_vector.h" #include "BLI_strict_flags.h" -KerningCacheBLF *blf_kerning_cache_find(FontBLF *font) -{ - return (KerningCacheBLF *)font->kerning_caches.first; -} - -/* Create a new glyph cache for the current kerning mode. */ -KerningCacheBLF *blf_kerning_cache_new(FontBLF *font, GlyphCacheBLF *gc) -{ - KerningCacheBLF *kc = MEM_mallocN(sizeof(KerningCacheBLF), __func__); - kc->next = NULL; - kc->prev = NULL; - - GlyphBLF *g_table[KERNING_CACHE_TABLE_SIZE]; - for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { - GlyphBLF *g = blf_glyph_search(gc, i); - if (UNLIKELY(g == NULL)) { - FT_UInt glyph_index = FT_Get_Char_Index(font->face, i); - g = blf_glyph_add(font, gc, glyph_index, i); - } - g_table[i] = g; - } - - memset(kc->ascii_table, 0, sizeof(kc->ascii_table)); - for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { - GlyphBLF *g = g_table[i]; - if (g == NULL) { - continue; - } - for (uint j = 0; j < KERNING_CACHE_TABLE_SIZE; j++) { - GlyphBLF *g_prev = g_table[j]; - if (g_prev == NULL) { - continue; - } - FT_Vector delta; - if (FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_DEFAULT, &delta) == 0) { - kc->ascii_table[i][j] = (int)delta.x >> 6; - } - } - } - - BLI_addhead(&font->kerning_caches, kc); - return kc; -} - -void blf_kerning_cache_clear(FontBLF *font) -{ - font->kerning_cache = NULL; - BLI_freelistN(&font->kerning_caches); -} - GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, unsigned int size, unsigned int dpi) { GlyphCacheBLF *p; diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 35a6d019eac..ab2a26b1e06 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -121,10 +121,6 @@ int blf_font_count_missing_chars(struct FontBLF *font, void blf_font_free(struct FontBLF *font); -struct KerningCacheBLF *blf_kerning_cache_find(struct FontBLF *font); -struct KerningCacheBLF *blf_kerning_cache_new(struct FontBLF *font, struct GlyphCacheBLF *gc); -void blf_kerning_cache_clear(struct FontBLF *font); - struct GlyphCacheBLF *blf_glyph_cache_find(struct FontBLF *font, unsigned int size, unsigned int dpi); diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index caa10b2b125..38d7d7b6e21 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -34,6 +34,9 @@ /* Number of characters in KerningCacheBLF.table. */ #define KERNING_CACHE_TABLE_SIZE 128 +/* A value in the kerning cache that indicates it is not yet set. */ +#define KERNING_ENTRY_UNSET INT_MAX + typedef struct BatchBLF { struct FontBLF *font; /* can only batch glyph from the same font */ struct GPUBatch *batch; @@ -50,7 +53,6 @@ typedef struct BatchBLF { extern BatchBLF g_batch; typedef struct KerningCacheBLF { - struct KerningCacheBLF *next, *prev; /** * Cache a ascii glyph pairs. Only store the x offset we are interested in, * instead of the full #FT_Vector since it's not used for drawing at the moment. @@ -223,10 +225,7 @@ typedef struct FontBLF { */ ListBase cache; - /* list of kerning cache for this font. */ - ListBase kerning_caches; - - /* current kerning cache for this font and kerning mode. */ + /* Cache of unscaled kerning values. Will be NULL if font does not have kerning. */ KerningCacheBLF *kerning_cache; /* freetype2 lib handle. */ -- cgit v1.2.3 From aed5a27755531aeef1e6f7a19dbde066cd9f2aba Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Aug 2021 13:22:47 +1000 Subject: Correct build error from 0d7aab2375e6bb06e89dad851550b283a1ff805c --- source/blender/blenfont/intern/blf_font.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 02847b74df1..436c4712e25 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -352,11 +352,11 @@ static int blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) } /* Copies of internal FreeType macros needed here. */ -#define FT_PIX_FLOOR(x) ((x) & ~FT_TYPEOF(x) 63) +#define FT_PIX_FLOOR(x) ((x) & ~63) #define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32) /* Round to even 64ths, then divide by 64. */ - return FT_PIX_ROUND(scaled) >> 6; + return (int)FT_PIX_ROUND(scaled) >> 6; #undef FT_PIX_FLOOR #undef FT_PIX_ROUND @@ -385,7 +385,7 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, /* If ASCII we save this value to our cache for quicker access next time. */ if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - font->kerning_cache->ascii_table[c][c_prev] = delta.x; + font->kerning_cache->ascii_table[c][c_prev] = (int)delta.x; } if (delta.x != 0) { -- cgit v1.2.3 From c671bfe14e8d8c72a146b74e4dcc1c8ee7ab5bb4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Aug 2021 13:23:39 +1000 Subject: Cleanup: spelling in comments & minor cleanup Also hyphenate 'mouse-move' use doxy sections in render_update.c & move function comment from the header to the source. --- intern/ghost/GHOST_IEvent.h | 6 ++-- source/blender/blenkernel/BKE_cachefile.h | 5 ---- source/blender/blenkernel/intern/cachefile.c | 8 +++++- source/blender/draw/engines/eevee/eevee_lookdev.c | 2 +- .../blender/editors/gpencil/gpencil_interpolate.c | 4 +-- source/blender/editors/gpencil/gpencil_primitive.c | 4 +-- .../blender/editors/interface/interface_handlers.c | 8 +++--- source/blender/editors/mesh/editmesh_tools.c | 2 +- source/blender/editors/physics/particle_edit.c | 2 +- source/blender/editors/render/render_update.c | 32 ++++++++++++++-------- .../editors/space_view3d/view3d_gizmo_ruler.c | 4 ++- .../editors/transform/transform_convert_armature.c | 4 +-- .../editors/transform/transform_convert_object.c | 2 +- .../editors/transform/transform_snap_animation.c | 4 +-- source/blender/io/collada/collada_internal.cpp | 2 +- .../blender/windowmanager/intern/wm_event_system.c | 4 +-- 16 files changed, 53 insertions(+), 40 deletions(-) diff --git a/intern/ghost/GHOST_IEvent.h b/intern/ghost/GHOST_IEvent.h index bcccd536ebd..239eea21088 100644 --- a/intern/ghost/GHOST_IEvent.h +++ b/intern/ghost/GHOST_IEvent.h @@ -32,10 +32,10 @@ class GHOST_IWindow; /** * Interface class for events received from GHOST. * You should not need to inherit this class. The system will pass these events - * to the GHOST_IEventConsumer::processEvent() method of event consumers.
- * Use the getType() method to retrieve the type of event and the getData() + * to the #GHOST_IEventConsumer::processEvent() method of event consumers.
+ * Use the #getType() method to retrieve the type of event and the #getData() * method to get the event data out. Using the event type you can cast the - * event data to the correct event dat structure. + * event data to the correct event data structure. * \see GHOST_IEventConsumer#processEvent * \see GHOST_TEventType */ diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h index 58d876b184b..836597f95da 100644 --- a/source/blender/blenkernel/BKE_cachefile.h +++ b/source/blender/blenkernel/BKE_cachefile.h @@ -61,11 +61,6 @@ void BKE_cachefile_reader_open(struct CacheFile *cache_file, const char *object_path); void BKE_cachefile_reader_free(struct CacheFile *cache_file, struct CacheReader **reader); -/* Determine whether the CacheFile should use a render engine procedural. If so, data is not read - * from the file and bouding boxes are used to represent the objects in the Scene. Render engines - * will receive the bounding box as a placeholder but can instead load the data directly if they - * support it. - */ bool BKE_cache_file_uses_render_procedural(const struct CacheFile *cache_file, struct Scene *scene, const int dag_eval_mode); diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index 4a60564b4a1..87b1584d422 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -368,7 +368,7 @@ void BKE_cachefile_eval(Main *bmain, Depsgraph *depsgraph, CacheFile *cache_file #endif if (DEG_is_active(depsgraph)) { - /* Flush object paths back to original datablock for UI. */ + /* Flush object paths back to original data-block for UI. */ CacheFile *cache_file_orig = (CacheFile *)DEG_get_original_id(&cache_file->id); BLI_freelistN(&cache_file_orig->object_paths); BLI_duplicatelist(&cache_file_orig->object_paths, &cache_file->object_paths); @@ -411,6 +411,12 @@ float BKE_cachefile_time_offset(const CacheFile *cache_file, const float time, c return cache_file->is_sequence ? frame : frame / fps - time_offset; } +/** + * Determine whether the #CacheFile should use a render engine procedural. If so, data is not read + * from the file and bounding boxes are used to represent the objects in the Scene. + * Render engines will receive the bounding box as a placeholder but can instead + * load the data directly if they support it. + */ bool BKE_cache_file_uses_render_procedural(const CacheFile *cache_file, Scene *scene, const int dag_eval_mode) diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index f4dc553e982..879a7b08eba 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -246,7 +246,7 @@ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, DRW_shgroup_uniform_float_copy(grp, "studioLightIntensity", shading->studiolight_intensity); BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); DRW_shgroup_uniform_texture_ex(grp, "studioLight", sl->equirect_radiance_gputexture, state); - /* Do not fadeout when doing probe rendering, only when drawing the background */ + /* Do not fade-out when doing probe rendering, only when drawing the background. */ DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); } else { diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 8640ffa67cf..a8bd3b11bb1 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -890,9 +890,9 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent } case MOUSEMOVE: /* calculate new position */ { - /* only handle mousemove if not doing numinput */ + /* Only handle mouse-move if not doing numeric-input. */ if (has_numinput == false) { - /* update shift based on position of mouse */ + /* Update shift based on position of mouse. */ gpencil_mouse_update_shift(tgpi, op, event); /* update screen */ diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 894fb83d70e..5ecb6d9a212 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -1944,9 +1944,9 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e if (ELEM(tgpi->flag, IN_CURVE_EDIT)) { break; } - /* only handle mousemove if not doing numinput */ + /* Only handle mouse-move if not doing numeric-input. */ if (has_numinput == false) { - /* update position of mouse */ + /* Update position of mouse. */ copy_v2_v2(tgpi->end, tgpi->mval); copy_v2_v2(tgpi->start, tgpi->origin); if (tgpi->flag == IDLE) { diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index b8f324cba83..76f6640c714 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -6025,7 +6025,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co * the slot menu fails to switch a second time. * * The active state of the button could be maintained some other way - * and remove this mousemove event. + * and remove this mouse-move event. */ WM_event_add_mousemove(data->window); @@ -8364,7 +8364,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s } } - /* wait for mousemove to enable drag */ + /* Wait for mouse-move to enable drag. */ if (state == BUTTON_STATE_WAIT_DRAG) { but->flag &= ~UI_SELECT; } @@ -8631,9 +8631,9 @@ static void button_activate_exit( ui_but_update(but); } - /* adds empty mousemove in queue for re-init handler, in case mouse is + /* Adds empty mouse-move in queue for re-initialize handler, in case mouse is * still over a button. We cannot just check for this ourselves because - * at this point the mouse may be over a button in another region */ + * at this point the mouse may be over a button in another region. */ if (mousemove) { WM_event_add_mousemove(CTX_wm_window(C)); } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index b8bbf2d3e70..956658bd2b7 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -8627,7 +8627,7 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent * RNA_enum_set(op->ptr, "mode", mode); } - /* Only handle mousemove event in case we are in mouse mode. */ + /* Only handle mouse-move event in case we are in mouse mode. */ if (event->type == MOUSEMOVE || force_mousemove) { if (mode == EDBM_CLNOR_POINTTO_MODE_MOUSE) { ARegion *region = CTX_wm_region(C); diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 85883a2d29a..8afc5c583e0 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -4667,7 +4667,7 @@ typedef struct BrushEdit { int lastmouse[2]; float zfac; - /* optional cached view settings to avoid setting on every mousemove */ + /** Optional cached view settings to avoid setting on every mouse-move. */ PEData data; } BrushEdit; diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index 6db148eb4e1..8bc2281db73 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -64,7 +64,9 @@ #include -/***************************** Render Engines ********************************/ +/* -------------------------------------------------------------------- */ +/** \name Render Engines + * \{ */ /* Update 3D viewport render or draw engine on changes to the scene or view settings. */ void ED_render_view3d_update(Depsgraph *depsgraph, @@ -206,15 +208,15 @@ void ED_render_engine_changed(Main *bmain, const bool update_scene_data) } } - /* Update CacheFiles to ensure that procedurals are properly taken into account. */ + /* Update #CacheFiles to ensure that procedurals are properly taken into account. */ LISTBASE_FOREACH (CacheFile *, cachefile, &bmain->cachefiles) { - /* Only update cachefiles which are set to use a render procedural. We do not use - * BKE_cachefile_uses_render_procedural here as we need to update regardless of the current - * engine or its settings. */ + /* Only update cache-files which are set to use a render procedural. + * We do not use #BKE_cachefile_uses_render_procedural here as we need to update regardless of + * the current engine or its settings. */ if (cachefile->use_render_procedural) { DEG_id_tag_update(&cachefile->id, ID_RECALC_COPY_ON_WRITE); - /* Rebuild relations so that modifiers are reconnected to or disconnected from the cachefile. - */ + /* Rebuild relations so that modifiers are reconnected to or disconnected from the + * cache-file. */ DEG_relations_tag_update(bmain); } } @@ -227,10 +229,16 @@ void ED_render_view_layer_changed(Main *bmain, bScreen *screen) } } -/***************************** Updates *********************************** - * ED_render_id_flush_update gets called from DEG_id_tag_update, to do * - * editor level updates when the ID changes. when these ID blocks are in * - * the dependency graph, we can get rid of the manual dependency checks. */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Updates + * + * #ED_render_id_flush_update gets called from #DEG_id_tag_update, + * to do editor level updates when the ID changes. + * When these ID blocks are in the dependency graph, + * we can get rid of the manual dependency checks. + * \{ */ static void material_changed(Main *UNUSED(bmain), Material *ma) { @@ -336,3 +344,5 @@ void ED_render_id_flush_update(const DEGEditorUpdateContext *update_ctx, ID *id) break; } } + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 49299d73337..edc34d0d883 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -299,7 +299,9 @@ static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3], cons ED_view3d_win_to_3d_int(ruler_info->area->spacedata.first, ruler_info->region, r_co, xy, r_co); } -/* use for mousemove events */ +/** + * Use for mouse-move events. + */ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, RulerInfo *ruler_info, RulerItem *ruler_item, diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index f56d60b7376..5627a910ab4 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -131,12 +131,12 @@ static void autokeyframe_pose( ListBase dsources = {NULL, NULL}; - /* add datasource override for the camera object */ + /* Add data-source override for the camera object. */ ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan); /* only insert into active keyingset? */ if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) { - /* run the active Keying Set on the current datasource */ + /* Run the active Keying Set on the current data-source. */ ANIM_apply_keyingset( C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time); } diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index ee6cb391fdc..bcbac009948 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -749,7 +749,7 @@ static void autokeyframe_object( /* Get flags used for inserting keyframes. */ flag = ANIM_get_keyframing_flags(scene, true); - /* add datasource override for the object */ + /* Add data-source override for the object. */ ANIM_relative_keyingset_add_source(&dsources, id, NULL, NULL); if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) { diff --git a/source/blender/editors/transform/transform_snap_animation.c b/source/blender/editors/transform/transform_snap_animation.c index be7fc65a6c2..08335924ddf 100644 --- a/source/blender/editors/transform/transform_snap_animation.c +++ b/source/blender/editors/transform/transform_snap_animation.c @@ -35,14 +35,14 @@ #include "transform_snap.h" /* -------------------------------------------------------------------- */ -/** \name Snappint in Anim Editors +/** \name Snapping in Anim Editors * \{ */ /** * This function returns the snapping 'mode' for Animation Editors only. * We cannot use the standard snapping due to NLA-strip scaling complexities. * - * TODO: these modifier checks should be key-mappable. + * TODO: these modifier checks should be accessible from the key-map. */ short getAnimEdit_SnapMode(TransInfo *t) { diff --git a/source/blender/io/collada/collada_internal.cpp b/source/blender/io/collada/collada_internal.cpp index 355aa5c22f0..bd6f496c8ec 100644 --- a/source/blender/io/collada/collada_internal.cpp +++ b/source/blender/io/collada/collada_internal.cpp @@ -162,7 +162,7 @@ void UnitConverter::calculate_scale(Scene &sce) * Translation map. * Used to translate every COLLADA id to a valid id, no matter what "wrong" letters may be * included. Look at the IDREF XSD declaration for more. - * Follows strictly the COLLADA XSD declaration which explicitly allows non-english chars, + * Follows strictly the COLLADA XSD declaration which explicitly allows non-English chars, * like special chars (e.g. micro sign), umlauts and so on. * The COLLADA spec also allows additional chars for member access ('.'), these * must obviously be removed too, otherwise they would be heavily misinterpreted. diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 5cc361fc1bd..b9a3dd0c3fb 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3471,8 +3471,8 @@ void wm_event_do_handlers(bContext *C) } CTX_wm_area_set(C, NULL); - /* NOTE: do not escape on WM_HANDLER_BREAK, - * mousemove needs handled for previous area. */ + /* NOTE: do not escape on #WM_HANDLER_BREAK, + * mouse-move needs handled for previous area. */ } } -- cgit v1.2.3 From 47e68537f83075dd78ed66aac3216643389b0e3a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Aug 2021 17:41:40 +1000 Subject: Cleanup: organize blf_font.c functions using doxy-sections Functions in this file were scattered and not well organized. --- source/blender/blenfont/intern/blf_font.c | 743 ++++++++++++++++-------------- 1 file changed, 403 insertions(+), 340 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 436c4712e25..47fa3544474 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -71,6 +71,38 @@ static FT_Library ft_lib; static SpinLock ft_lib_mutex; static SpinLock blf_glyph_cache_mutex; +/* -------------------------------------------------------------------- */ +/** \name FreeType Utilities (Internal) + * \{ */ + +/** + * Convert a FreeType 26.6 value representing an unscaled design size to pixels. + * This is an exact copy of the scaling done inside FT_Get_Kerning when called + * with #FT_KERNING_DEFAULT, including arbitrary resizing for small fonts. + */ +static int blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) +{ + /* Scale value by font size using integer-optimized multiplication. */ + FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale); + + /* FreeType states that this '25' has been determined heuristically. */ + if (font->face->size->metrics.x_ppem < 25) { + scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25); + } + + /* Copies of internal FreeType macros needed here. */ +#define FT_PIX_FLOOR(x) ((x) & ~63) +#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32) + + /* Round to even 64ths, then divide by 64. */ + return (int)FT_PIX_ROUND(scaled) >> 6; + +#undef FT_PIX_FLOOR +#undef FT_PIX_ROUND +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Glyph Batching * \{ */ @@ -257,56 +289,8 @@ static void blf_batch_draw_end(void) /** \} */ /* -------------------------------------------------------------------- */ - -int blf_font_init(void) -{ - memset(&g_batch, 0, sizeof(g_batch)); - BLI_spin_init(&ft_lib_mutex); - BLI_spin_init(&blf_glyph_cache_mutex); - return FT_Init_FreeType(&ft_lib); -} - -void blf_font_exit(void) -{ - FT_Done_FreeType(ft_lib); - BLI_spin_end(&ft_lib_mutex); - BLI_spin_end(&blf_glyph_cache_mutex); - blf_batch_draw_exit(); -} - -void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) -{ - GlyphCacheBLF *gc; - FT_Error err; - - blf_glyph_cache_acquire(font); - - gc = blf_glyph_cache_find(font, size, dpi); - if (gc) { - /* Optimization: do not call FT_Set_Char_Size if size did not change. */ - if (font->size == size && font->dpi == dpi) { - blf_glyph_cache_release(font); - return; - } - } - - err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi); - if (err) { - /* FIXME: here we can go through the fixed size and choice a close one */ - printf("The current font don't support the size, %u and dpi, %u\n", size, dpi); - - blf_glyph_cache_release(font); - return; - } - - font->size = size; - font->dpi = dpi; - - if (!gc) { - blf_glyph_cache_new(font); - } - blf_glyph_cache_release(font); -} +/** \name Glyph Stepping Utilities (Internal) + * \{ */ /* Fast path for runs of ASCII characters. Given that common UTF-8 * input will consist of an overwhelming majority of ASCII @@ -337,31 +321,6 @@ BLI_INLINE GlyphBLF *blf_utf8_next_fast( return g; } -/* Convert a FreeType 26.6 value representing an unscaled design size to pixels. - * This is an exact copy of the scaling done inside FT_Get_Kerning when called - * with FT_KERNING_DEFAULT, including arbitrary resizing for small fonts. - */ -static int blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) -{ - /* Scale value by font size using integer-optimized multiplication. */ - FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale); - - /* FreeType states that this '25' has been determined heuristically. */ - if (font->face->size->metrics.x_ppem < 25) { - scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25); - } - - /* Copies of internal FreeType macros needed here. */ -#define FT_PIX_FLOOR(x) ((x) & ~63) -#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32) - - /* Round to even 64ths, then divide by 64. */ - return (int)FT_PIX_ROUND(scaled) >> 6; - -#undef FT_PIX_FLOOR -#undef FT_PIX_ROUND -} - BLI_INLINE void blf_kerning_step_fast(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g, @@ -395,6 +354,12 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Drawing: GPU + * \{ */ + static void blf_font_draw_ex(FontBLF *font, GlyphCacheBLF *gc, const char *str, @@ -535,6 +500,12 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) return columns; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Drawgin: Buffer + * \{ */ + /* Sanity checks are done by BLF_draw_buffer() */ static void blf_font_draw_buffer_ex(FontBLF *font, GlyphCacheBLF *gc, @@ -680,6 +651,16 @@ void blf_font_draw_buffer(FontBLF *font, const char *str, size_t len, struct Res blf_glyph_cache_release(font); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Width to Sting Length + * + * Use to implement exported functions: + * - #BLF_width_to_strlen + * - #BLF_width_to_rstrlen + * \{ */ + static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, const uint c_prev, const uint c, @@ -773,6 +754,12 @@ size_t blf_font_width_to_rstrlen( return i; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Glyph Bound Box with Callback + * \{ */ + static void blf_font_boundbox_ex(FontBLF *font, GlyphCacheBLF *gc, const char *str, @@ -847,73 +834,230 @@ void blf_font_boundbox( blf_glyph_cache_release(font); } -/* -------------------------------------------------------------------- */ -/** \name Word-Wrap Support - * \{ */ +void blf_font_width_and_height(FontBLF *font, + const char *str, + size_t len, + float *r_width, + float *r_height, + struct ResultBLF *r_info) +{ + float xa, ya; + rctf box; -/** - * Generic function to add word-wrap support for other existing functions. - * - * Wraps on spaces and respects newlines. - * Intentionally ignores non-unix newlines, tabs and more advanced text formatting. - * - * \note If we want rich text - we better have a higher level API to handle that - * (color, bold, switching fonts... etc). - */ -static void blf_font_wrap_apply(FontBLF *font, - const char *str, - size_t len, - struct ResultBLF *r_info, - void (*callback)(FontBLF *font, - GlyphCacheBLF *gc, - const char *str, - size_t len, - int pen_y, - void *userdata), - void *userdata) + if (font->flags & BLF_ASPECT) { + xa = font->aspect[0]; + ya = font->aspect[1]; + } + else { + xa = 1.0f; + ya = 1.0f; + } + + if (font->flags & BLF_WORD_WRAP) { + blf_font_boundbox__wrap(font, str, len, &box, r_info); + } + else { + blf_font_boundbox(font, str, len, &box, r_info); + } + *r_width = (BLI_rctf_size_x(&box) * xa); + *r_height = (BLI_rctf_size_y(&box) * ya); +} + +float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) { - unsigned int c, c_prev = BLI_UTF8_ERR; - GlyphBLF *g, *g_prev = NULL; - int pen_x = 0, pen_y = 0; - size_t i = 0; - int lines = 0; - int pen_x_next = 0; + float xa; + rctf box; - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + if (font->flags & BLF_ASPECT) { + xa = font->aspect[0]; + } + else { + xa = 1.0f; + } - struct WordWrapVars { - int wrap_width; - size_t start, last[2]; - } wrap = {font->wrap_width != -1 ? font->wrap_width : INT_MAX, 0, {0, 0}}; + if (font->flags & BLF_WORD_WRAP) { + blf_font_boundbox__wrap(font, str, len, &box, r_info); + } + else { + blf_font_boundbox(font, str, len, &box, r_info); + } + return BLI_rctf_size_x(&box) * xa; +} - // printf("%s wrapping (%d, %d) `%s`:\n", __func__, len, strlen(str), str); - while ((i < len) && str[i]) { +float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +{ + float ya; + rctf box; - /* wrap vars */ - size_t i_curr = i; - bool do_draw = false; + if (font->flags & BLF_ASPECT) { + ya = font->aspect[1]; + } + else { + ya = 1.0f; + } - g = blf_utf8_next_fast(font, gc, str, &i, &c); + if (font->flags & BLF_WORD_WRAP) { + blf_font_boundbox__wrap(font, str, len, &box, r_info); + } + else { + blf_font_boundbox(font, str, len, &box, r_info); + } + return BLI_rctf_size_y(&box) * ya; +} - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } - if (UNLIKELY(g == NULL)) { - continue; - } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); +float blf_font_fixed_width(FontBLF *font) +{ + const unsigned int c = ' '; - /** - * Implementation Detail (utf8). - * - * Take care with single byte offsets here, - * since this is utf8 we can't be sure a single byte is a single character. - * - * This is _only_ done when we know for sure the character is ascii (newline or a space). - */ - pen_x_next = pen_x + g->advance_i; - if (UNLIKELY((pen_x_next >= wrap.wrap_width) && (wrap.start != wrap.last[0]))) { - do_draw = true; + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + GlyphBLF *g = blf_glyph_search(gc, c); + if (!g) { + g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c); + + /* if we don't find the glyph. */ + if (!g) { + blf_glyph_cache_release(font); + return 0.0f; + } + } + + blf_glyph_cache_release(font); + return g->advance; +} + +static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, + GlyphCacheBLF *gc, + const char *str, + size_t len, + BLF_GlyphBoundsFn user_fn, + void *user_data, + struct ResultBLF *r_info, + int pen_y) +{ + unsigned int c, c_prev = BLI_UTF8_ERR; + GlyphBLF *g, *g_prev = NULL; + int pen_x = 0; + size_t i = 0, i_curr; + rcti gbox; + + if (len == 0) { + /* early output. */ + return; + } + + while ((i < len) && str[i]) { + i_curr = i; + g = blf_utf8_next_fast(font, gc, str, &i, &c); + + if (UNLIKELY(c == BLI_UTF8_ERR)) { + break; + } + if (UNLIKELY(g == NULL)) { + continue; + } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + + gbox.xmin = pen_x; + gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); + gbox.ymin = pen_y; + gbox.ymax = gbox.ymin - g->dims[1]; + + pen_x += g->advance_i; + + if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) { + break; + } + + g_prev = g; + c_prev = c; + } + + if (r_info) { + r_info->lines = 1; + r_info->width = pen_x; + } +} +void blf_font_boundbox_foreach_glyph(FontBLF *font, + const char *str, + size_t len, + BLF_GlyphBoundsFn user_fn, + void *user_data, + struct ResultBLF *r_info) +{ + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + blf_font_boundbox_foreach_glyph_ex(font, gc, str, len, user_fn, user_data, r_info, 0); + blf_glyph_cache_release(font); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Word-Wrap with Callback + * \{ */ + +/** + * Generic function to add word-wrap support for other existing functions. + * + * Wraps on spaces and respects newlines. + * Intentionally ignores non-unix newlines, tabs and more advanced text formatting. + * + * \note If we want rich text - we better have a higher level API to handle that + * (color, bold, switching fonts... etc). + */ +static void blf_font_wrap_apply(FontBLF *font, + const char *str, + size_t len, + struct ResultBLF *r_info, + void (*callback)(FontBLF *font, + GlyphCacheBLF *gc, + const char *str, + size_t len, + int pen_y, + void *userdata), + void *userdata) +{ + unsigned int c, c_prev = BLI_UTF8_ERR; + GlyphBLF *g, *g_prev = NULL; + int pen_x = 0, pen_y = 0; + size_t i = 0; + int lines = 0; + int pen_x_next = 0; + + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + + struct WordWrapVars { + int wrap_width; + size_t start, last[2]; + } wrap = {font->wrap_width != -1 ? font->wrap_width : INT_MAX, 0, {0, 0}}; + + // printf("%s wrapping (%d, %d) `%s`:\n", __func__, len, strlen(str), str); + while ((i < len) && str[i]) { + + /* wrap vars */ + size_t i_curr = i; + bool do_draw = false; + + g = blf_utf8_next_fast(font, gc, str, &i, &c); + + if (UNLIKELY(c == BLI_UTF8_ERR)) { + break; + } + if (UNLIKELY(g == NULL)) { + continue; + } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + + /** + * Implementation Detail (utf8). + * + * Take care with single byte offsets here, + * since this is utf8 we can't be sure a single byte is a single character. + * + * This is _only_ done when we know for sure the character is ascii (newline or a space). + */ + pen_x_next = pen_x + g->advance_i; + if (UNLIKELY((pen_x_next >= wrap.wrap_width) && (wrap.start != wrap.last[0]))) { + do_draw = true; } else if (UNLIKELY(((i < len) && str[i]) == 0)) { /* need check here for trailing newline, else we draw it */ @@ -1018,216 +1162,120 @@ void blf_font_draw_buffer__wrap(FontBLF *font, /** \} */ -void blf_font_width_and_height(FontBLF *font, - const char *str, - size_t len, - float *r_width, - float *r_height, - struct ResultBLF *r_info) +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Count Missing Characters + * \{ */ + +int blf_font_count_missing_chars(FontBLF *font, + const char *str, + const size_t len, + int *r_tot_chars) { - float xa, ya; - rctf box; + int missing = 0; + size_t i = 0; - if (font->flags & BLF_ASPECT) { - xa = font->aspect[0]; - ya = font->aspect[1]; - } - else { - xa = 1.0f; - ya = 1.0f; - } + *r_tot_chars = 0; + while (i < len) { + unsigned int c; - if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); - } - else { - blf_font_boundbox(font, str, len, &box, r_info); + if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) { + i++; + } + else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) { + if (FT_Get_Char_Index((font)->face, c) == 0) { + missing++; + } + } + (*r_tot_chars)++; } - *r_width = (BLI_rctf_size_x(&box) * xa); - *r_height = (BLI_rctf_size_y(&box) * ya); + return missing; } -float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) -{ - float xa; - rctf box; - - if (font->flags & BLF_ASPECT) { - xa = font->aspect[0]; - } - else { - xa = 1.0f; - } +/** \} */ - if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); - } - else { - blf_font_boundbox(font, str, len, &box, r_info); - } - return BLI_rctf_size_x(&box) * xa; -} +/* -------------------------------------------------------------------- */ +/** \name Font Query: Attributes + * \{ */ -float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +int blf_font_height_max(FontBLF *font) { - float ya; - rctf box; + int height_max; - if (font->flags & BLF_ASPECT) { - ya = font->aspect[1]; - } - else { - ya = 1.0f; - } + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + height_max = gc->glyph_height_max; - if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); - } - else { - blf_font_boundbox(font, str, len, &box, r_info); - } - return BLI_rctf_size_y(&box) * ya; + blf_glyph_cache_release(font); + return height_max; } -float blf_font_fixed_width(FontBLF *font) +int blf_font_width_max(FontBLF *font) { - const unsigned int c = ' '; + int width_max; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF *g = blf_glyph_search(gc, c); - if (!g) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c); - - /* if we don't find the glyph. */ - if (!g) { - blf_glyph_cache_release(font); - return 0.0f; - } - } + width_max = gc->glyph_width_max; blf_glyph_cache_release(font); - return g->advance; + return width_max; } -/* -------------------------------------------------------------------- */ -/** \name Glyph Bound Box with Callback - * \{ */ - -static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, - GlyphCacheBLF *gc, - const char *str, - size_t len, - BLF_GlyphBoundsFn user_fn, - void *user_data, - struct ResultBLF *r_info, - int pen_y) +float blf_font_descender(FontBLF *font) { - unsigned int c, c_prev = BLI_UTF8_ERR; - GlyphBLF *g, *g_prev = NULL; - int pen_x = 0; - size_t i = 0, i_curr; - rcti gbox; - - if (len == 0) { - /* early output. */ - return; - } - - while ((i < len) && str[i]) { - i_curr = i; - g = blf_utf8_next_fast(font, gc, str, &i, &c); - - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } - if (UNLIKELY(g == NULL)) { - continue; - } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + float descender; - gbox.xmin = pen_x; - gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); - gbox.ymin = pen_y; - gbox.ymax = gbox.ymin - g->dims[1]; + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + descender = gc->descender; - pen_x += g->advance_i; + blf_glyph_cache_release(font); + return descender; +} - if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) { - break; - } +float blf_font_ascender(FontBLF *font) +{ + float ascender; - g_prev = g; - c_prev = c; - } + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + ascender = gc->ascender; - if (r_info) { - r_info->lines = 1; - r_info->width = pen_x; - } + blf_glyph_cache_release(font); + return ascender; } -void blf_font_boundbox_foreach_glyph(FontBLF *font, - const char *str, - size_t len, - BLF_GlyphBoundsFn user_fn, - void *user_data, - struct ResultBLF *r_info) + +char *blf_display_name(FontBLF *font) { - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_boundbox_foreach_glyph_ex(font, gc, str, len, user_fn, user_data, r_info, 0); - blf_glyph_cache_release(font); + if (!font->face->family_name) { + return NULL; + } + return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); } /** \} */ -int blf_font_count_missing_chars(FontBLF *font, - const char *str, - const size_t len, - int *r_tot_chars) -{ - int missing = 0; - size_t i = 0; - - *r_tot_chars = 0; - while (i < len) { - unsigned int c; +/* -------------------------------------------------------------------- */ +/** \name Font Subsystem Init/Exit + * \{ */ - if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) { - i++; - } - else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) { - if (FT_Get_Char_Index((font)->face, c) == 0) { - missing++; - } - } - (*r_tot_chars)++; - } - return missing; +int blf_font_init(void) +{ + memset(&g_batch, 0, sizeof(g_batch)); + BLI_spin_init(&ft_lib_mutex); + BLI_spin_init(&blf_glyph_cache_mutex); + return FT_Init_FreeType(&ft_lib); } -void blf_font_free(FontBLF *font) +void blf_font_exit(void) { - BLI_spin_lock(&blf_glyph_cache_mutex); - GlyphCacheBLF *gc; - - while ((gc = BLI_pophead(&font->cache))) { - blf_glyph_cache_free(gc); - } - - if (font->kerning_cache) { - MEM_freeN(font->kerning_cache); - } + FT_Done_FreeType(ft_lib); + BLI_spin_end(&ft_lib_mutex); + BLI_spin_end(&blf_glyph_cache_mutex); + blf_batch_draw_exit(); +} - FT_Done_Face(font->face); - if (font->filename) { - MEM_freeN(font->filename); - } - if (font->name) { - MEM_freeN(font->name); - } - MEM_freeN(font); +/** \} */ - BLI_spin_unlock(&blf_glyph_cache_mutex); -} +/* -------------------------------------------------------------------- */ +/** \name Font New/Free + * \{ */ static void blf_font_fill(FontBLF *font) { @@ -1366,54 +1414,69 @@ FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int m return font; } -int blf_font_height_max(FontBLF *font) +void blf_font_free(FontBLF *font) { - int height_max; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - height_max = gc->glyph_height_max; + BLI_spin_lock(&blf_glyph_cache_mutex); + GlyphCacheBLF *gc; - blf_glyph_cache_release(font); - return height_max; -} + while ((gc = BLI_pophead(&font->cache))) { + blf_glyph_cache_free(gc); + } -int blf_font_width_max(FontBLF *font) -{ - int width_max; + if (font->kerning_cache) { + MEM_freeN(font->kerning_cache); + } - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - width_max = gc->glyph_width_max; + FT_Done_Face(font->face); + if (font->filename) { + MEM_freeN(font->filename); + } + if (font->name) { + MEM_freeN(font->name); + } + MEM_freeN(font); - blf_glyph_cache_release(font); - return width_max; + BLI_spin_unlock(&blf_glyph_cache_mutex); } -float blf_font_descender(FontBLF *font) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Font Configure + * \{ */ + +void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) { - float descender; + GlyphCacheBLF *gc; + FT_Error err; - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - descender = gc->descender; + blf_glyph_cache_acquire(font); - blf_glyph_cache_release(font); - return descender; -} + gc = blf_glyph_cache_find(font, size, dpi); + if (gc) { + /* Optimization: do not call FT_Set_Char_Size if size did not change. */ + if (font->size == size && font->dpi == dpi) { + blf_glyph_cache_release(font); + return; + } + } -float blf_font_ascender(FontBLF *font) -{ - float ascender; + err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi); + if (err) { + /* FIXME: here we can go through the fixed size and choice a close one */ + printf("The current font don't support the size, %u and dpi, %u\n", size, dpi); - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - ascender = gc->ascender; + blf_glyph_cache_release(font); + return; + } - blf_glyph_cache_release(font); - return ascender; -} + font->size = size; + font->dpi = dpi; -char *blf_display_name(FontBLF *font) -{ - if (!font->face->family_name) { - return NULL; + if (!gc) { + blf_glyph_cache_new(font); } - return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); + blf_glyph_cache_release(font); } + +/** \} */ -- cgit v1.2.3 From 0b7947e85528bbf5c4e1f41354a41dc113937088 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Aug 2021 17:44:25 +1000 Subject: Cleanup: minor changes to blf_font.c - Use early return when kerning isn't used. - Remove early return that prevented matching acquire/release calls. --- source/blender/blenfont/intern/blf_font.c | 78 ++++++++++++++----------------- 1 file changed, 36 insertions(+), 42 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 47fa3544474..75a2e893119 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -328,29 +328,31 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, const uint c, int *pen_x_p) { - if (FT_HAS_KERNING(font->face) && g_prev != NULL) { - FT_Vector delta = {KERNING_ENTRY_UNSET}; + if (!FT_HAS_KERNING(font->face) || g_prev == NULL) { + return; + } - /* Get unscaled kerning value from our cache if ASCII. */ - if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - delta.x = font->kerning_cache->ascii_table[c][c_prev]; - } + FT_Vector delta = {KERNING_ENTRY_UNSET}; - /* If not ASCII or not found in cache, ask FreeType for kerning. */ - if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { - /* Note that this function sets delta values to zero on any error. */ - FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); - } + /* Get unscaled kerning value from our cache if ASCII. */ + if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { + delta.x = font->kerning_cache->ascii_table[c][c_prev]; + } - /* If ASCII we save this value to our cache for quicker access next time. */ - if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - font->kerning_cache->ascii_table[c][c_prev] = (int)delta.x; - } + /* If not ASCII or not found in cache, ask FreeType for kerning. */ + if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { + /* Note that this function sets delta values to zero on any error. */ + FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); + } - if (delta.x != 0) { - /* Convert unscaled design units to pixels and move pen. */ - *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x); - } + /* If ASCII we save this value to our cache for quicker access next time. */ + if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { + font->kerning_cache->ascii_table[c][c_prev] = (int)delta.x; + } + + if (delta.x != 0) { + /* Convert unscaled design units to pixels and move pen. */ + *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x); } } @@ -1447,35 +1449,27 @@ void blf_font_free(FontBLF *font) void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) { - GlyphCacheBLF *gc; - FT_Error err; - blf_glyph_cache_acquire(font); - gc = blf_glyph_cache_find(font, size, dpi); - if (gc) { + GlyphCacheBLF *gc = blf_glyph_cache_find(font, size, dpi); + if (gc && (font->size == size && font->dpi == dpi)) { /* Optimization: do not call FT_Set_Char_Size if size did not change. */ - if (font->size == size && font->dpi == dpi) { - blf_glyph_cache_release(font); - return; - } } - - err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi); - if (err) { - /* FIXME: here we can go through the fixed size and choice a close one */ - printf("The current font don't support the size, %u and dpi, %u\n", size, dpi); - - blf_glyph_cache_release(font); - return; + else { + const FT_Error err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi); + if (err) { + /* FIXME: here we can go through the fixed size and choice a close one */ + printf("The current font don't support the size, %u and dpi, %u\n", size, dpi); + } + else { + font->size = size; + font->dpi = dpi; + if (gc == NULL) { + blf_glyph_cache_new(font); + } + } } - font->size = size; - font->dpi = dpi; - - if (!gc) { - blf_glyph_cache_new(font); - } blf_glyph_cache_release(font); } -- cgit v1.2.3 From 34a05f39be2b79dd1c508c374a47cee6792174f9 Mon Sep 17 00:00:00 2001 From: Ankit Meel Date: Sat, 21 Aug 2021 14:02:50 +0530 Subject: Clang: warn about C++20 designated initializers With the ongoing transition to C++ files, Windows build breaks often because of designated initializers. Now we have two compilers to catch the MSVC build error on. Reviewed By: #platform_macos, brecht, campbellbarton Differential Revision: https://developer.blender.org/D11940 --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2868324bf46..5a555876d21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1598,6 +1598,10 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_UNUSED_PARAMETER -Wunused-parameter) ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_ALL -Wall) + # Designated initializer is a C++20 feature & breaks MSVC build. Dropping MSVC 2019 or + # updating to C++20 allows removing this. + ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_CXX20_DESIGNATOR -Wc++20-designator) + ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_AUTOLOGICAL_COMPARE -Wno-tautological-compare) ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_UNKNOWN_PRAGMAS -Wno-unknown-pragmas) ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_CHAR_SUBSCRIPTS -Wno-char-subscripts) -- cgit v1.2.3 From 2b170f16d6ded9b3bcb428121b27274ae8637555 Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Thu, 19 Aug 2021 23:57:00 +0200 Subject: Refactor low-level blendfile reading into separate files Instead of handling mmap, compression etc. all directly in readfile.c, refactor the code to use a generic FileReader. This makes it easier to add new compression methods or similar, and allows to reuse the logic in other places (e.g. thumbnail reading). Reviewed By: campbellbarton, brecht, mont29 Differential Revision: https://developer.blender.org/D5799 --- source/blender/blenlib/BLI_fileops.h | 2 + source/blender/blenlib/BLI_filereader.h | 79 ++++ source/blender/blenlib/CMakeLists.txt | 4 + source/blender/blenlib/intern/fileops.c | 7 + source/blender/blenlib/intern/filereader_file.c | 80 ++++ source/blender/blenlib/intern/filereader_gzip.c | 108 +++++ source/blender/blenlib/intern/filereader_memory.c | 145 +++++++ source/blender/blenloader/BLO_undofile.h | 14 + source/blender/blenloader/intern/readfile.c | 449 ++++----------------- source/blender/blenloader/intern/readfile.h | 32 +- source/blender/blenloader/intern/undofile.c | 95 +++++ source/blender/blenloader/intern/versioning_250.c | 3 +- .../blender/blenloader/intern/versioning_legacy.c | 3 +- source/blender/blenloader/intern/writefile.c | 2 +- source/blender/windowmanager/intern/wm_files.c | 88 ++-- 15 files changed, 667 insertions(+), 444 deletions(-) create mode 100644 source/blender/blenlib/BLI_filereader.h create mode 100644 source/blender/blenlib/intern/filereader_file.c create mode 100644 source/blender/blenlib/intern/filereader_gzip.c create mode 100644 source/blender/blenlib/intern/filereader_memory.c diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 7cfecc798a7..12fa73279c8 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -166,6 +166,8 @@ size_t BLI_gzip_mem_to_file_at_pos(void *buf, int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BLI_file_magic_is_gzip(const char header[4]); + size_t BLI_file_descriptor_size(int file) ATTR_WARN_UNUSED_RESULT; size_t BLI_file_size(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); diff --git a/source/blender/blenlib/BLI_filereader.h b/source/blender/blenlib/BLI_filereader.h new file mode 100644 index 00000000000..dbcaa7cb1b6 --- /dev/null +++ b/source/blender/blenlib/BLI_filereader.h @@ -0,0 +1,79 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup bli + * \brief Wrapper for reading from various sources (e.g. raw files, compressed files, memory...). + */ + +#pragma once + +#ifdef WIN32 +# include "BLI_winstuff.h" +#else +# include +#endif + +#include "BLI_compiler_attrs.h" +#include "BLI_utildefines.h" + +#if defined(_MSC_VER) || defined(__APPLE__) || defined(__HAIKU__) || defined(__NetBSD__) +typedef int64_t off64_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct FileReader; + +typedef ssize_t (*FileReaderReadFn)(struct FileReader *reader, void *buffer, size_t size); +typedef off64_t (*FileReaderSeekFn)(struct FileReader *reader, off64_t offset, int whence); +typedef void (*FileReaderCloseFn)(struct FileReader *reader); + +/* General structure for all FileReaders, implementations add custom fields at the end. */ +typedef struct FileReader { + FileReaderReadFn read; + FileReaderSeekFn seek; + FileReaderCloseFn close; + + off64_t offset; +} FileReader; + +/* Functions for opening the various types of FileReader. + * They either succeed and return a valid FileReader, or fail and return NULL. + * + * If a FileReader is created, it has to be cleaned up and freed by calling + * its close() function unless another FileReader has taken ownership - for example, + * Gzip takes over the base FileReader and will clean it up when their clean() is called. + */ + +/* Create FileReader from raw file descriptor. */ +FileReader *BLI_filereader_new_file(int filedes) ATTR_WARN_UNUSED_RESULT; +/* Create FileReader from raw file descriptor using memory-mapped IO. */ +FileReader *BLI_filereader_new_mmap(int filedes) ATTR_WARN_UNUSED_RESULT; +/* Create FileReader from a region of memory. */ +FileReader *BLI_filereader_new_memory(const void *data, size_t len) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); +/* Create FileReader from applying Gzip decompression on an underlying file. */ +FileReader *BLI_filereader_new_gzip(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index b6603dce378..d2ba9e74c90 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -75,6 +75,9 @@ set(SRC intern/endian_switch.c intern/expr_pylike_eval.c intern/fileops.c + intern/filereader_file.c + intern/filereader_gzip.c + intern/filereader_memory.c intern/fnmatch.c intern/freetypefont.c intern/gsqueue.c @@ -194,6 +197,7 @@ set(SRC BLI_enumerable_thread_specific.hh BLI_expr_pylike_eval.h BLI_fileops.h + BLI_filereader.h BLI_fileops_types.h BLI_float2.hh BLI_float3.hh diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index ac034d2b5cd..6fc2222241b 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -255,6 +255,13 @@ size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t g #undef CHUNK +bool BLI_file_magic_is_gzip(const char header[4]) +{ + /* GZIP itself starts with the magic bytes 0x1f 0x8b. + * The third byte indicates the compression method, which is 0x08 for DEFLATE. */ + return header[0] == 0x1f && header[1] == 0x8b && header[2] == 0x08; +} + /** * Returns true if the file with the specified name can be written. * This implementation uses access(2), which makes the check according diff --git a/source/blender/blenlib/intern/filereader_file.c b/source/blender/blenlib/intern/filereader_file.c new file mode 100644 index 00000000000..3a833871e27 --- /dev/null +++ b/source/blender/blenlib/intern/filereader_file.c @@ -0,0 +1,80 @@ +/* + * 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) 2004-2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#ifndef WIN32 +# include /* for read close */ +#else +# include "BLI_winstuff.h" +# include "winsock2.h" +# include /* for open close read */ +#endif + +#include "BLI_blenlib.h" +#include "BLI_filereader.h" + +#include "MEM_guardedalloc.h" + +typedef struct { + FileReader reader; + + int filedes; +} RawFileReader; + +static ssize_t file_read(FileReader *reader, void *buffer, size_t size) +{ + RawFileReader *rawfile = (RawFileReader *)reader; + ssize_t readsize = read(rawfile->filedes, buffer, size); + + if (readsize >= 0) { + rawfile->reader.offset += readsize; + } + + return readsize; +} + +static off64_t file_seek(FileReader *reader, off64_t offset, int whence) +{ + RawFileReader *rawfile = (RawFileReader *)reader; + rawfile->reader.offset = BLI_lseek(rawfile->filedes, offset, whence); + return rawfile->reader.offset; +} + +static void file_close(FileReader *reader) +{ + RawFileReader *rawfile = (RawFileReader *)reader; + close(rawfile->filedes); + MEM_freeN(rawfile); +} + +FileReader *BLI_filereader_new_file(int filedes) +{ + RawFileReader *rawfile = MEM_callocN(sizeof(RawFileReader), __func__); + + rawfile->filedes = filedes; + + rawfile->reader.read = file_read; + rawfile->reader.seek = file_seek; + rawfile->reader.close = file_close; + + return (FileReader *)rawfile; +} diff --git a/source/blender/blenlib/intern/filereader_gzip.c b/source/blender/blenlib/intern/filereader_gzip.c new file mode 100644 index 00000000000..72eb153a8b9 --- /dev/null +++ b/source/blender/blenlib/intern/filereader_gzip.c @@ -0,0 +1,108 @@ +/* + * 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) 2004-2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include + +#include "BLI_blenlib.h" +#include "BLI_filereader.h" + +#include "MEM_guardedalloc.h" + +typedef struct { + FileReader reader; + + FileReader *base; + + z_stream strm; + + void *in_buf; + size_t in_size; +} GzipReader; + +static ssize_t gzip_read(FileReader *reader, void *buffer, size_t size) +{ + GzipReader *gzip = (GzipReader *)reader; + + gzip->strm.avail_out = size; + gzip->strm.next_out = buffer; + + while (gzip->strm.avail_out > 0) { + if (gzip->strm.avail_in == 0) { + /* Ran out of buffered input data, read some more. */ + size_t readsize = gzip->base->read(gzip->base, gzip->in_buf, gzip->in_size); + + if (readsize > 0) { + /* We got some data, so mark the buffer as refilled. */ + gzip->strm.avail_in = readsize; + gzip->strm.next_in = gzip->in_buf; + } + else { + /* The underlying file is EOF, so return as much as we can. */ + break; + } + } + + int ret = inflate(&gzip->strm, Z_NO_FLUSH); + + if (ret != Z_OK && ret != Z_BUF_ERROR) { + break; + } + } + + ssize_t read_len = size - gzip->strm.avail_out; + gzip->reader.offset += read_len; + return read_len; +} + +static void gzip_close(FileReader *reader) +{ + GzipReader *gzip = (GzipReader *)reader; + + if (inflateEnd(&gzip->strm) != Z_OK) { + printf("close gzip stream error\n"); + } + MEM_freeN((void *)gzip->in_buf); + + gzip->base->close(gzip->base); + MEM_freeN(gzip); +} + +FileReader *BLI_filereader_new_gzip(FileReader *base) +{ + GzipReader *gzip = MEM_callocN(sizeof(GzipReader), __func__); + gzip->base = base; + + if (inflateInit2(&gzip->strm, 16 + MAX_WBITS) != Z_OK) { + MEM_freeN(gzip); + return NULL; + } + + gzip->in_size = 256 * 2014; + gzip->in_buf = MEM_mallocN(gzip->in_size, "gzip in buf"); + + gzip->reader.read = gzip_read; + gzip->reader.seek = NULL; + gzip->reader.close = gzip_close; + + return (FileReader *)gzip; +} diff --git a/source/blender/blenlib/intern/filereader_memory.c b/source/blender/blenlib/intern/filereader_memory.c new file mode 100644 index 00000000000..150fed7d1cc --- /dev/null +++ b/source/blender/blenlib/intern/filereader_memory.c @@ -0,0 +1,145 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2004-2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include + +#include "BLI_blenlib.h" +#include "BLI_filereader.h" +#include "BLI_mmap.h" + +#include "MEM_guardedalloc.h" + +/* This file implements both memory-backed and memory-mapped-file-backed reading. */ +typedef struct { + FileReader reader; + + const char *data; + BLI_mmap_file *mmap; + size_t length; +} MemoryReader; + +static ssize_t memory_read_raw(FileReader *reader, void *buffer, size_t size) +{ + MemoryReader *mem = (MemoryReader *)reader; + + /* Don't read more bytes than there are available in the buffer. */ + size_t readsize = MIN2(size, (size_t)(mem->length - mem->reader.offset)); + + memcpy(buffer, mem->data + mem->reader.offset, readsize); + mem->reader.offset += readsize; + + return readsize; +} + +static off64_t memory_seek(FileReader *reader, off64_t offset, int whence) +{ + MemoryReader *mem = (MemoryReader *)reader; + + off64_t new_pos; + if (whence == SEEK_CUR) { + new_pos = mem->reader.offset + offset; + } + else if (whence == SEEK_SET) { + new_pos = offset; + } + else if (whence == SEEK_END) { + new_pos = mem->length + offset; + } + else { + return -1; + } + + if (new_pos < 0 || new_pos > mem->length) { + return -1; + } + + mem->reader.offset = new_pos; + return mem->reader.offset; +} + +static void memory_close_raw(FileReader *reader) +{ + MEM_freeN(reader); +} + +FileReader *BLI_filereader_new_memory(const void *data, size_t len) +{ + MemoryReader *mem = MEM_callocN(sizeof(MemoryReader), __func__); + + mem->data = (const char *)data; + mem->length = len; + + mem->reader.read = memory_read_raw; + mem->reader.seek = memory_seek; + mem->reader.close = memory_close_raw; + + return (FileReader *)mem; +} + +/* Memory-mapped file reading. + * By using `mmap()`, we can map a file so that it can be treated like normal memory, + * meaning that we can just read from it with `memcpy()` etc. + * This avoids system call overhead and can significantly speed up file loading. + */ + +static ssize_t memory_read_mmap(FileReader *reader, void *buffer, size_t size) +{ + MemoryReader *mem = (MemoryReader *)reader; + + /* Don't read more bytes than there are available in the buffer. */ + size_t readsize = MIN2(size, (size_t)(mem->length - mem->reader.offset)); + + if (!BLI_mmap_read(mem->mmap, buffer, mem->reader.offset, readsize)) { + return 0; + } + + mem->reader.offset += readsize; + + return readsize; +} + +static void memory_close_mmap(FileReader *reader) +{ + MemoryReader *mem = (MemoryReader *)reader; + BLI_mmap_free(mem->mmap); + MEM_freeN(mem); +} + +FileReader *BLI_filereader_new_mmap(int filedes) +{ + BLI_mmap_file *mmap = BLI_mmap_open(filedes); + if (mmap == NULL) { + return NULL; + } + + MemoryReader *mem = MEM_callocN(sizeof(MemoryReader), __func__); + + mem->mmap = mmap; + mem->length = BLI_lseek(filedes, 0, SEEK_END); + + mem->reader.read = memory_read_mmap; + mem->reader.seek = memory_seek; + mem->reader.close = memory_close_mmap; + + return (FileReader *)mem; +} diff --git a/source/blender/blenloader/BLO_undofile.h b/source/blender/blenloader/BLO_undofile.h index fc41a6e832f..4e240e2462b 100644 --- a/source/blender/blenloader/BLO_undofile.h +++ b/source/blender/blenloader/BLO_undofile.h @@ -24,6 +24,8 @@ * \ingroup blenloader */ +#include "BLI_filereader.h" + struct GHash; struct Scene; @@ -65,6 +67,16 @@ typedef struct MemFileUndoData { size_t undo_size; } MemFileUndoData; +/* FileReader-compatible wrapper for reading MemFiles */ +typedef struct { + FileReader reader; + + MemFile *memfile; + int undo_direction; + + bool memchunk_identical; +} UndoReader; + /* actually only used writefile.c */ void BLO_memfile_write_init(MemFileWriteData *mem_data, @@ -84,3 +96,5 @@ extern struct Main *BLO_memfile_main_get(struct MemFile *memfile, struct Main *bmain, struct Scene **r_scene); extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename); + +FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index e48c305fc4b..82fbb2083df 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -21,8 +21,6 @@ * \ingroup blenloader */ -#include "zlib.h" - #include /* for isdigit. */ #include /* for open flags (O_BINARY, O_RDONLY). */ #include @@ -71,7 +69,6 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_mempool.h" -#include "BLI_mmap.h" #include "BLI_threads.h" #include "PIL_time.h" @@ -788,7 +785,7 @@ static BHeadN *get_bhead(FileData *fd) */ if (fd->flags & FD_FLAGS_FILE_POINTSIZE_IS_4) { bhead4.code = DATA; - readsize = fd->read(fd, &bhead4, sizeof(bhead4), NULL); + readsize = fd->file->read(fd->file, &bhead4, sizeof(bhead4)); if (readsize == sizeof(bhead4) || bhead4.code == ENDB) { if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { @@ -811,7 +808,7 @@ static BHeadN *get_bhead(FileData *fd) } else { bhead8.code = DATA; - readsize = fd->read(fd, &bhead8, sizeof(bhead8), NULL); + readsize = fd->file->read(fd->file, &bhead8, sizeof(bhead8)); if (readsize == sizeof(bhead8) || bhead8.code == ENDB) { if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { @@ -845,22 +842,22 @@ static BHeadN *get_bhead(FileData *fd) /* pass */ } #ifdef USE_BHEAD_READ_ON_DEMAND - else if (fd->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) { + else if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) { /* Delay reading bhead content. */ new_bhead = MEM_mallocN(sizeof(BHeadN), "new_bhead"); if (new_bhead) { new_bhead->next = new_bhead->prev = NULL; - new_bhead->file_offset = fd->file_offset; + new_bhead->file_offset = fd->file->offset; new_bhead->has_data = false; new_bhead->is_memchunk_identical = false; new_bhead->bhead = bhead; - off64_t seek_new = fd->seek(fd, bhead.len, SEEK_CUR); + off64_t seek_new = fd->file->seek(fd->file, bhead.len, SEEK_CUR); if (seek_new == -1) { fd->is_eof = true; MEM_freeN(new_bhead); new_bhead = NULL; } - BLI_assert(fd->file_offset == seek_new); + BLI_assert(fd->file->offset == seek_new); } else { fd->is_eof = true; @@ -878,14 +875,17 @@ static BHeadN *get_bhead(FileData *fd) new_bhead->is_memchunk_identical = false; new_bhead->bhead = bhead; - readsize = fd->read( - fd, new_bhead + 1, (size_t)bhead.len, &new_bhead->is_memchunk_identical); + readsize = fd->file->read(fd->file, new_bhead + 1, (size_t)bhead.len); - if (readsize != (ssize_t)bhead.len) { + if (readsize != bhead.len) { fd->is_eof = true; MEM_freeN(new_bhead); new_bhead = NULL; } + + if (fd->flags & FD_FLAGS_IS_MEMFILE) { + new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical; + } } else { fd->is_eof = true; @@ -964,17 +964,19 @@ static bool blo_bhead_read_data(FileData *fd, BHead *thisblock, void *buf) bool success = true; BHeadN *new_bhead = BHEADN_FROM_BHEAD(thisblock); BLI_assert(new_bhead->has_data == false && new_bhead->file_offset != 0); - off64_t offset_backup = fd->file_offset; - if (UNLIKELY(fd->seek(fd, new_bhead->file_offset, SEEK_SET) == -1)) { + off64_t offset_backup = fd->file->offset; + if (UNLIKELY(fd->file->seek(fd->file, new_bhead->file_offset, SEEK_SET) == -1)) { success = false; } else { - if (fd->read(fd, buf, (size_t)new_bhead->bhead.len, &new_bhead->is_memchunk_identical) != - (ssize_t)new_bhead->bhead.len) { + if (fd->file->read(fd->file, buf, (size_t)new_bhead->bhead.len) != new_bhead->bhead.len) { success = false; } + if (fd->flags & FD_FLAGS_IS_MEMFILE) { + new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical; + } } - if (fd->seek(fd, offset_backup, SEEK_SET) == -1) { + if (fd->file->seek(fd->file, offset_backup, SEEK_SET) == -1) { success = false; } return success; @@ -1017,7 +1019,7 @@ static void decode_blender_header(FileData *fd) ssize_t readsize; /* read in the header data */ - readsize = fd->read(fd, header, sizeof(header), NULL); + readsize = fd->file->read(fd->file, header, sizeof(header)); if (readsize == sizeof(header) && STREQLEN(header, "BLENDER", 7) && ELEM(header[7], '_', '-') && ELEM(header[8], 'v', 'V') && @@ -1147,210 +1149,12 @@ static int *read_file_thumbnail(FileData *fd) /** \} */ -/* -------------------------------------------------------------------- */ -/** \name File Data API - * \{ */ - -/* Regular file reading. */ - -static ssize_t fd_read_data_from_file(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - ssize_t readsize = read(filedata->filedes, buffer, size); - - if (readsize < 0) { - readsize = EOF; - } - else { - filedata->file_offset += readsize; - } - - return readsize; -} - -static off64_t fd_seek_data_from_file(FileData *filedata, off64_t offset, int whence) -{ - filedata->file_offset = BLI_lseek(filedata->filedes, offset, whence); - return filedata->file_offset; -} - -/* GZip file reading. */ - -static ssize_t fd_read_gzip_from_file(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - BLI_assert(size <= INT_MAX); - - ssize_t readsize = gzread(filedata->gzfiledes, buffer, (uint)size); - - if (readsize < 0) { - readsize = EOF; - } - else { - filedata->file_offset += readsize; - } - - return readsize; -} - -/* Memory reading. */ - -static ssize_t fd_read_from_memory(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - /* don't read more bytes than there are available in the buffer */ - ssize_t readsize = (ssize_t)MIN2(size, filedata->buffersize - (size_t)filedata->file_offset); - - memcpy(buffer, filedata->buffer + filedata->file_offset, (size_t)readsize); - filedata->file_offset += readsize; - - return readsize; -} - -/* Memory-mapped file reading. - * By using mmap(), we can map a file so that it can be treated like normal memory, - * meaning that we can just read from it with memcpy() etc. - * This avoids system call overhead and can significantly speed up file loading. - */ - -static ssize_t fd_read_from_mmap(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - /* don't read more bytes than there are available in the buffer */ - size_t readsize = MIN2(size, (size_t)(filedata->buffersize - filedata->file_offset)); - - if (!BLI_mmap_read(filedata->mmap_file, buffer, filedata->file_offset, readsize)) { - return 0; - } - - filedata->file_offset += readsize; - - return readsize; -} - -static off64_t fd_seek_from_mmap(FileData *filedata, off64_t offset, int whence) -{ - off64_t new_pos; - if (whence == SEEK_CUR) { - new_pos = filedata->file_offset + offset; - } - else if (whence == SEEK_SET) { - new_pos = offset; - } - else if (whence == SEEK_END) { - new_pos = filedata->buffersize + offset; - } - else { - return -1; - } - - if (new_pos < 0 || new_pos > filedata->buffersize) { - return -1; - } - - filedata->file_offset = new_pos; - return filedata->file_offset; -} - -/* MemFile reading. */ - -static ssize_t fd_read_from_memfile(FileData *filedata, - void *buffer, - size_t size, - bool *r_is_memchunck_identical) -{ - static size_t seek = SIZE_MAX; /* the current position */ - static size_t offset = 0; /* size of previous chunks */ - static MemFileChunk *chunk = NULL; - size_t chunkoffset, readsize, totread; - - if (r_is_memchunck_identical != NULL) { - *r_is_memchunck_identical = true; - } - - if (size == 0) { - return 0; - } - - if (seek != (size_t)filedata->file_offset) { - chunk = filedata->memfile->chunks.first; - seek = 0; - - while (chunk) { - if (seek + chunk->size > (size_t)filedata->file_offset) { - break; - } - seek += chunk->size; - chunk = chunk->next; - } - offset = seek; - seek = (size_t)filedata->file_offset; - } - - if (chunk) { - totread = 0; - - do { - /* first check if it's on the end if current chunk */ - if (seek - offset == chunk->size) { - offset += chunk->size; - chunk = chunk->next; - } - - /* debug, should never happen */ - if (chunk == NULL) { - CLOG_ERROR(&LOG, "Illegal read, got a NULL chunk"); - return 0; - } - - chunkoffset = seek - offset; - readsize = size - totread; - - /* data can be spread over multiple chunks, so clamp size - * to within this chunk, and then it will read further in - * the next chunk */ - if (chunkoffset + readsize > chunk->size) { - readsize = chunk->size - chunkoffset; - } - - memcpy(POINTER_OFFSET(buffer, totread), chunk->buf + chunkoffset, readsize); - totread += readsize; - filedata->file_offset += readsize; - seek += readsize; - if (r_is_memchunck_identical != NULL) { - /* `is_identical` of current chunk represents whether it changed compared to previous undo - * step. this is fine in redo case, but not in undo case, where we need an extra flag - * defined when saving the next (future) step after the one we want to restore, as we are - * supposed to 'come from' that future undo step, and not the one before current one. */ - *r_is_memchunck_identical &= filedata->undo_direction == STEP_REDO ? - chunk->is_identical : - chunk->is_identical_future; - } - } while (totread < size); - - return (ssize_t)totread; - } - - return 0; -} - static FileData *filedata_new(BlendFileReadReport *reports) { BLI_assert(reports != NULL); FileData *fd = MEM_callocN(sizeof(FileData), "FileData"); - fd->filedes = -1; - fd->gzfiledes = NULL; - fd->memsdna = DNA_sdna_current_get(); fd->datamap = oldnewmap_new(); @@ -1387,78 +1191,60 @@ static FileData *blo_decode_and_check(FileData *fd, ReportList *reports) static FileData *blo_filedata_from_file_descriptor(const char *filepath, BlendFileReadReport *reports, - int file) + int filedes) { - FileDataReadFn *read_fn = NULL; - FileDataSeekFn *seek_fn = NULL; /* Optional. */ - size_t buffersize = 0; - BLI_mmap_file *mmap_file = NULL; - - gzFile gzfile = (gzFile)Z_NULL; - char header[7]; + FileReader *rawfile = BLI_filereader_new_file(filedes); + FileReader *file = NULL; - /* Regular file. */ errno = 0; - if (read(file, header, sizeof(header)) != sizeof(header)) { + /* If opening the file failed or we can't read the header, give up. */ + if (rawfile == NULL || rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) { BKE_reportf(reports->reports, RPT_WARNING, "Unable to read '%s': %s", filepath, errno ? strerror(errno) : TIP_("insufficient content")); + if (rawfile) { + rawfile->close(rawfile); + } + else { + close(filedes); + } return NULL; } - /* Regular file. */ - if (memcmp(header, "BLENDER", sizeof(header)) == 0) { - read_fn = fd_read_data_from_file; - seek_fn = fd_seek_data_from_file; + /* Rewind the file after reading the header. */ + rawfile->seek(rawfile, 0, SEEK_SET); - mmap_file = BLI_mmap_open(file); - if (mmap_file != NULL) { - read_fn = fd_read_from_mmap; - seek_fn = fd_seek_from_mmap; - buffersize = BLI_lseek(file, 0, SEEK_END); + /* Check if we have a regular file. */ + if (memcmp(header, "BLENDER", sizeof(header)) == 0) { + /* Try opening the file with memory-mapped IO. */ + file = BLI_filereader_new_mmap(filedes); + if (file == NULL) { + /* mmap failed, so just keep using rawfile. */ + file = rawfile; + rawfile = NULL; } } - - BLI_lseek(file, 0, SEEK_SET); - - /* Gzip file. */ - errno = 0; - if ((read_fn == NULL) && - /* Check header magic. */ - (header[0] == 0x1f && header[1] == 0x8b)) { - gzfile = BLI_gzopen(filepath, "rb"); - if (gzfile == (gzFile)Z_NULL) { - BKE_reportf(reports->reports, - RPT_WARNING, - "Unable to open '%s': %s", - filepath, - errno ? strerror(errno) : TIP_("unknown error reading file")); - return NULL; + else if (BLI_file_magic_is_gzip(header)) { + file = BLI_filereader_new_gzip(rawfile); + if (file != NULL) { + rawfile = NULL; /* The Gzip FileReader takes ownership of `rawfile`. */ } - - /* 'seek_fn' is too slow for gzip, don't set it. */ - read_fn = fd_read_gzip_from_file; - /* Caller must close. */ - file = -1; } - if (read_fn == NULL) { + /* Clean up `rawfile` if it wasn't taken over. */ + if (rawfile != NULL) { + rawfile->close(rawfile); + } + if (file == NULL) { BKE_reportf(reports->reports, RPT_WARNING, "Unrecognized file format '%s'", filepath); return NULL; } FileData *fd = filedata_new(reports); - - fd->filedes = file; - fd->gzfiledes = gzfile; - - fd->read = read_fn; - fd->seek = seek_fn; - fd->mmap_file = mmap_file; - fd->buffersize = buffersize; + fd->file = file; return fd; } @@ -1475,11 +1261,7 @@ static FileData *blo_filedata_from_file_open(const char *filepath, BlendFileRead errno ? strerror(errno) : TIP_("unknown error reading file")); return NULL; } - FileData *fd = blo_filedata_from_file_descriptor(filepath, reports, file); - if ((fd == NULL) || (fd->filedes == -1)) { - close(file); - } - return fd; + return blo_filedata_from_file_descriptor(filepath, reports, file); } /* cannot be called with relative paths anymore! */ @@ -1513,50 +1295,6 @@ static FileData *blo_filedata_from_file_minimal(const char *filepath) return NULL; } -static ssize_t fd_read_gzip_from_memory(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - int err; - - filedata->strm.next_out = (Bytef *)buffer; - filedata->strm.avail_out = (uint)size; - - /* Inflate another chunk. */ - err = inflate(&filedata->strm, Z_SYNC_FLUSH); - - if (err == Z_STREAM_END) { - return 0; - } - if (err != Z_OK) { - CLOG_ERROR(&LOG, "ZLib error (code %d)", err); - return 0; - } - - filedata->file_offset += size; - - return (ssize_t)size; -} - -static int fd_read_gzip_from_memory_init(FileData *fd) -{ - - fd->strm.next_in = (Bytef *)fd->buffer; - fd->strm.avail_in = fd->buffersize; - fd->strm.total_out = 0; - fd->strm.zalloc = Z_NULL; - fd->strm.zfree = Z_NULL; - - if (inflateInit2(&fd->strm, (16 + MAX_WBITS)) != Z_OK) { - return 0; - } - - fd->read = fd_read_gzip_from_memory; - - return 1; -} - FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadReport *reports) { if (!mem || memsize < SIZEOFBLENDERHEADER) { @@ -1565,24 +1303,21 @@ FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadRe return NULL; } - FileData *fd = filedata_new(reports); - const char *cp = mem; - - fd->buffer = mem; - fd->buffersize = memsize; + FileReader *mem_file = BLI_filereader_new_memory(mem, memsize); + FileReader *file = mem_file; - /* test if gzip */ - if (cp[0] == 0x1f && cp[1] == 0x8b) { - if (0 == fd_read_gzip_from_memory_init(fd)) { - blo_filedata_free(fd); - return NULL; - } + if (BLI_file_magic_is_gzip(mem)) { + file = BLI_filereader_new_gzip(mem_file); } - else { - fd->read = fd_read_from_memory; + + if (file == NULL) { + /* Compression initialization failed. */ + mem_file->close(mem_file); + return NULL; } - fd->flags |= FD_FLAGS_NOT_MY_BUFFER; + FileData *fd = filedata_new(reports); + fd->file = file; return blo_decode_and_check(fd, reports->reports); } @@ -1597,11 +1332,9 @@ FileData *blo_filedata_from_memfile(MemFile *memfile, } FileData *fd = filedata_new(reports); - fd->memfile = memfile; + fd->file = BLO_memfile_new_filereader(memfile, params->undo_direction); fd->undo_direction = params->undo_direction; - - fd->read = fd_read_from_memfile; - fd->flags |= FD_FLAGS_NOT_MY_BUFFER; + fd->flags |= FD_FLAGS_IS_MEMFILE; return blo_decode_and_check(fd, reports->reports); } @@ -1609,30 +1342,7 @@ FileData *blo_filedata_from_memfile(MemFile *memfile, void blo_filedata_free(FileData *fd) { if (fd) { - if (fd->filedes != -1) { - close(fd->filedes); - } - - if (fd->gzfiledes != NULL) { - gzclose(fd->gzfiledes); - } - - if (fd->strm.next_in) { - int err = inflateEnd(&fd->strm); - if (err != Z_OK) { - CLOG_ERROR(&LOG, "Close gzip stream error (code %d)", err); - } - } - - if (fd->buffer && !(fd->flags & FD_FLAGS_NOT_MY_BUFFER)) { - MEM_freeN((void *)fd->buffer); - fd->buffer = NULL; - } - - if (fd->mmap_file) { - BLI_mmap_free(fd->mmap_file); - fd->mmap_file = NULL; - } + fd->file->close(fd->file); /* Free all BHeadN data blocks */ #ifndef NDEBUG @@ -1640,7 +1350,7 @@ void blo_filedata_free(FileData *fd) #else /* Sanity check we're not keeping memory we don't need. */ LISTBASE_FOREACH_MUTABLE (BHeadN *, new_bhead, &fd->bhead_list) { - if (fd->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) { + if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) { BLI_assert(new_bhead->has_data == 0); } MEM_freeN(new_bhead); @@ -2096,7 +1806,7 @@ static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id), void blo_cache_storage_init(FileData *fd, Main *bmain) { - if (fd->memfile != NULL) { + if (fd->flags & FD_FLAGS_IS_MEMFILE) { BLI_assert(fd->cache_storage == NULL); fd->cache_storage = MEM_mallocN(sizeof(*fd->cache_storage), __func__); fd->cache_storage->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); @@ -2261,7 +1971,7 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname) * undo since DNA must match. */ static const void *peek_struct_undo(FileData *fd, BHead *bhead) { - BLI_assert(fd->memfile != NULL); + BLI_assert(fd->flags & FD_FLAGS_IS_MEMFILE); UNUSED_VARS_NDEBUG(fd); return (bhead->len) ? (const void *)(bhead + 1) : NULL; } @@ -3679,7 +3389,7 @@ static BHead *read_libblock(FileData *fd, * When datablocks are changed but still exist, we restore them at the old * address and inherit recalc flags for the dependency graph. */ ID *id_old = NULL; - if (fd->memfile != NULL) { + if (fd->flags & FD_FLAGS_IS_MEMFILE) { if (read_libblock_undo_restore(fd, main, bhead, tag, &id_old)) { if (r_id) { *r_id = id_old; @@ -3980,13 +3690,14 @@ static void lib_link_all(FileData *fd, Main *bmain) continue; } - if (fd->memfile != NULL && GS(id->name) == ID_WM) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) && GS(id->name) == ID_WM) { /* No load UI for undo memfiles. * Only WM currently, SCR needs it still (see below), and so does WS? */ continue; } - if (fd->memfile != NULL && do_partial_undo && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) && do_partial_undo && + (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0) { /* This ID has been re-used from 'old' bmain. Since it was therefore unchanged across * current undo step, and old IDs re-use their old memory address, we do not need to liblink * it at all. */ @@ -4165,7 +3876,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) BlendFileData *bfd; ListBase mainlist = {NULL, NULL}; - if (fd->memfile != NULL) { + if (fd->flags & FD_FLAGS_IS_MEMFILE) { CLOG_INFO(&LOG_UNDO, 2, "UNDO: read step"); } @@ -4256,7 +3967,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) } /* do before read_libraries, but skip undo case */ - if (fd->memfile == NULL) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) { if ((fd->skip_flags & BLO_READ_SKIP_DATA) == 0) { do_versions(fd, NULL, bfd->main); } @@ -4278,7 +3989,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) fd->reports->duration.libraries = PIL_check_seconds_timer() - fd->reports->duration.libraries; /* Skip in undo case. */ - if (fd->memfile == NULL) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) { /* Note that we can't recompute user-counts at this point in undo case, we play too much with * IDs from different memory realms, and Main database is not in a fully valid state yet. */ @@ -4311,7 +4022,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) /* Now that all our data-blocks are loaded, * we can re-generate overrides from their references. */ - if (fd->memfile == NULL) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) { /* Do not apply in undo case! */ fd->reports->duration.lib_overrides = PIL_check_seconds_timer(); @@ -4391,7 +4102,7 @@ static void sort_bhead_old_map(FileData *fd) static BHead *find_previous_lib(FileData *fd, BHead *bhead) { /* Skip library data-blocks in undo, see comment in read_libblock. */ - if (fd->memfile) { + if (fd->flags & FD_FLAGS_IS_MEMFILE) { return NULL; } @@ -5850,7 +5561,7 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p) bool BLO_read_data_is_undo(BlendDataReader *reader) { - return reader->fd->memfile != NULL; + return (reader->fd->flags & FD_FLAGS_IS_MEMFILE); } void BLO_read_data_globmap_add(BlendDataReader *reader, void *oldaddr, void *newaddr) @@ -5870,7 +5581,7 @@ BlendFileReadReport *BLO_read_data_reports(BlendDataReader *reader) bool BLO_read_lib_is_undo(BlendLibReader *reader) { - return reader->fd->memfile != NULL; + return (reader->fd->flags & FD_FLAGS_IS_MEMFILE); } Main *BLO_read_lib_get_main(BlendLibReader *reader) diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index b04043f9641..beeed8e45ae 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -28,10 +28,10 @@ # include "BLI_winstuff.h" #endif +#include "BLI_filereader.h" #include "DNA_sdna_types.h" #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" /* for ReportType */ -#include "zlib.h" struct BLI_mmap_file; struct BLOCacheStorage; @@ -50,7 +50,7 @@ enum eFileDataFlag { FD_FLAGS_FILE_POINTSIZE_IS_4 = 1 << 1, FD_FLAGS_POINTSIZE_DIFFERS = 1 << 2, FD_FLAGS_FILE_OK = 1 << 3, - FD_FLAGS_NOT_MY_BUFFER = 1 << 4, + FD_FLAGS_IS_MEMFILE = 1 << 4, /* XXX Unused in practice (checked once but never set). */ FD_FLAGS_NOT_MY_LIBMAP = 1 << 5, }; @@ -60,44 +60,18 @@ enum eFileDataFlag { # pragma GCC poison off_t #endif -#if defined(_MSC_VER) || defined(__APPLE__) || defined(__HAIKU__) || defined(__NetBSD__) -typedef int64_t off64_t; -#endif - -typedef ssize_t(FileDataReadFn)(struct FileData *filedata, - void *buffer, - size_t size, - bool *r_is_memchunk_identical); -typedef off64_t(FileDataSeekFn)(struct FileData *filedata, off64_t offset, int whence); - typedef struct FileData { /** Linked list of BHeadN's. */ ListBase bhead_list; enum eFileDataFlag flags; bool is_eof; - size_t buffersize; - off64_t file_offset; - FileDataReadFn *read; - FileDataSeekFn *seek; + FileReader *file; - /** Regular file reading. */ - int filedes; - - /** Variables needed for reading from memory / stream / memory-mapped files. */ - const char *buffer; - struct BLI_mmap_file *mmap_file; - /** Variables needed for reading from memfile (undo). */ - struct MemFile *memfile; /** Whether we are undoing (< 0) or redoing (> 0), used to choose which 'unchanged' flag to use * to detect unchanged data from memfile. */ int undo_direction; /* eUndoStepDir */ - /** Variables needed for reading from file. */ - gzFile gzfiledes; - /** Gzip stream for memory decompression. */ - z_stream strm; - /** Now only in use for library appending. */ char relabase[FILE_MAX]; diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.c index 2eeeac2e8d7..62072cf7df5 100644 --- a/source/blender/blenloader/intern/undofile.c +++ b/source/blender/blenloader/intern/undofile.c @@ -48,6 +48,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_undo_system.h" /* keep last */ #include "BLI_strict_flags.h" @@ -273,3 +274,97 @@ bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename) } return true; } + +static ssize_t undo_read(FileReader *reader, void *buffer, size_t size) +{ + UndoReader *undo = (UndoReader *)reader; + + static size_t seek = SIZE_MAX; /* The current position. */ + static size_t offset = 0; /* Size of previous chunks. */ + static MemFileChunk *chunk = NULL; + size_t chunkoffset, readsize, totread; + + undo->memchunk_identical = true; + + if (size == 0) { + return 0; + } + + if (seek != (size_t)undo->reader.offset) { + chunk = undo->memfile->chunks.first; + seek = 0; + + while (chunk) { + if (seek + chunk->size > (size_t)undo->reader.offset) { + break; + } + seek += chunk->size; + chunk = chunk->next; + } + offset = seek; + seek = (size_t)undo->reader.offset; + } + + if (chunk) { + totread = 0; + + do { + /* First check if it's on the end if current chunk. */ + if (seek - offset == chunk->size) { + offset += chunk->size; + chunk = chunk->next; + } + + /* Debug, should never happen. */ + if (chunk == NULL) { + printf("illegal read, chunk zero\n"); + return 0; + } + + chunkoffset = seek - offset; + readsize = size - totread; + + /* Data can be spread over multiple chunks, so clamp size + * to within this chunk, and then it will read further in + * the next chunk. */ + if (chunkoffset + readsize > chunk->size) { + readsize = chunk->size - chunkoffset; + } + + memcpy(POINTER_OFFSET(buffer, totread), chunk->buf + chunkoffset, readsize); + totread += readsize; + undo->reader.offset += (off64_t)readsize; + seek += readsize; + + /* `is_identical` of current chunk represents whether it changed compared to previous undo + * step. this is fine in redo case, but not in undo case, where we need an extra flag + * defined when saving the next (future) step after the one we want to restore, as we are + * supposed to 'come from' that future undo step, and not the one before current one. */ + undo->memchunk_identical &= undo->undo_direction == STEP_REDO ? chunk->is_identical : + chunk->is_identical_future; + } while (totread < size); + + return (ssize_t)totread; + } + + return 0; +} + +static void undo_close(FileReader *reader) +{ + MEM_freeN(reader); +} + +FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction) +{ + UndoReader *undo = MEM_callocN(sizeof(UndoReader), __func__); + + undo->memfile = memfile; + undo->undo_direction = undo_direction; + + undo->reader.read = undo_read; + undo->reader.seek = NULL; + undo->reader.close = undo_close; + + return (FileReader *)undo; +} diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index e56c1995363..436645c2241 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -23,8 +23,7 @@ #else # include "BLI_winstuff.h" # include "winsock2.h" -# include /* for open close read */ -# include /* odd include order-issue */ +# include /* for open close read */ #endif /* allow readfile to use deprecated functionality */ diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 81371e1c1ed..6ba27b6ee9e 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -28,8 +28,7 @@ #else # include "BLI_winstuff.h" # include "winsock2.h" -# include /* for open close read */ -# include /* odd include order-issue */ +# include /* for open close read */ #endif /* allow readfile to use deprecated functionality */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 99246603e9a..4de8e35f402 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -78,12 +78,12 @@ #include #include #include +#include #ifdef WIN32 # include "BLI_winstuff.h" # include "winsock2.h" # include -# include /* odd include order-issue */ #else # include /* FreeBSD, for write() and close(). */ #endif diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index b53ad0ee927..c3789abf2dd 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -28,11 +28,10 @@ * winsock stuff. */ #include +#include /* for open flags (O_BINARY, O_RDONLY). */ #include #include -#include "zlib.h" /* wm_read_exotic() */ - #ifdef WIN32 /* Need to include windows.h so _WIN32_IE is defined. */ # include @@ -51,6 +50,7 @@ #include "BLI_blenlib.h" #include "BLI_fileops_types.h" +#include "BLI_filereader.h" #include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_system.h" @@ -481,53 +481,59 @@ static void wm_init_userdef(Main *bmain) /* intended to check for non-blender formats but for now it only reads blends */ static int wm_read_exotic(const char *name) { - int len; - gzFile gzfile; + /* make sure we're not trying to read a directory.... */ + + int namelen = strlen(name); + if (namelen > 0 && ELEM(name[namelen - 1], '/', '\\')) { + return BKE_READ_EXOTIC_FAIL_PATH; + } + + /* open the file. */ + const int filedes = BLI_open(name, O_BINARY | O_RDONLY, 0); + if (filedes == -1) { + return BKE_READ_EXOTIC_FAIL_OPEN; + } + + FileReader *rawfile = BLI_filereader_new_file(filedes); + if (rawfile == NULL) { + return BKE_READ_EXOTIC_FAIL_OPEN; + } + + /* read the header (7 bytes are enough to identify all known types). */ char header[7]; - int retval; + if (rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) { + rawfile->close(rawfile); + return BKE_READ_EXOTIC_FAIL_FORMAT; + } + rawfile->seek(rawfile, 0, SEEK_SET); - /* make sure we're not trying to read a directory.... */ + /* check for uncompressed .blend */ + if (STREQLEN(header, "BLENDER", 7)) { + rawfile->close(rawfile); + return BKE_READ_EXOTIC_OK_BLEND; + } - len = strlen(name); - if (len > 0 && ELEM(name[len - 1], '/', '\\')) { - retval = BKE_READ_EXOTIC_FAIL_PATH; + /* check for compressed .blend */ + FileReader *compressed_file = NULL; + if (BLI_file_magic_is_gzip(header)) { + compressed_file = BLI_filereader_new_gzip(rawfile); } - else { - gzfile = BLI_gzopen(name, "rb"); - if (gzfile == NULL) { - retval = BKE_READ_EXOTIC_FAIL_OPEN; - } - else { - len = gzread(gzfile, header, sizeof(header)); - gzclose(gzfile); - if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) { - retval = BKE_READ_EXOTIC_OK_BLEND; - } - else { - /* We may want to support loading other file formats - * from their header bytes or file extension. - * This used to be supported in the code below and may be added - * back at some point. */ -#if 0 - WM_cursor_wait(true); - if (is_foo_format(name)) { - read_foo(name); - retval = BKE_READ_EXOTIC_OK_OTHER; - } - else -#endif - { - retval = BKE_READ_EXOTIC_FAIL_FORMAT; - } -#if 0 - WM_cursor_wait(false); -#endif - } + /* If a compression signature matches, try decompressing the start and check if it's a .blend */ + if (compressed_file != NULL) { + size_t len = compressed_file->read(compressed_file, header, sizeof(header)); + compressed_file->close(compressed_file); + if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) { + return BKE_READ_EXOTIC_OK_BLEND; } } + else { + rawfile->close(rawfile); + } + + /* Add check for future file formats here. */ - return retval; + return BKE_READ_EXOTIC_FAIL_FORMAT; } /** \} */ -- cgit v1.2.3 From 2ea66af742bca4b427f88de13254414730a33776 Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Sat, 21 Aug 2021 03:15:31 +0200 Subject: Add support for Zstandard compression for .blend files Compressing blendfiles can help save a lot of disk space, but the slowdown while loading and saving is a major annoyance. Currently Blender uses Zlib (aka gzip aka Deflate) for compression, but there are now several more modern algorithms that outperform it in every way. In this patch, I decided for Zstandard aka Zstd for several reasons: - It is widely supported, both in other programs and libraries as well as in general-purpose compression utilities on Unix - It is extremely flexible - spanning several orders of magnitude of compression speeds depending on the level setting. - It is pretty much on the Pareto frontier for all of its configurations (meaning that no other algorithm is both faster and more efficient). One downside of course is that older versions of Blender will not be able to read these files, but one can always just re-save them without compression or decompress the file manually with an external tool. The implementation here saves additional metadata into the compressed file in order to allow for efficient seeking when loading. This is standard-compliant and will be ignored by other tools that support Zstd. If the metadata is not present (e.g. because you manually compressed a .blend file with another tool), Blender will fall back to sequential reading. Saving is multithreaded to improve performance. Loading is currently not multithreaded since it's not easy to predict the access patterns of the loading code when seeking is supported. In the future, we might want to look into making this more predictable or disabling seeking for the main .blend file, which would then allow for multiple background threads that decompress data ahead of time. The compression level was chosen to get sizes comparable to previous versions at much higher speeds. In the future, this could be exposed as an option. Reviewed By: campbellbarton, brecht, mont29 Differential Revision: https://developer.blender.org/D5799 --- build_files/cmake/Modules/FindZstd.cmake | 66 +++++ build_files/cmake/platform/platform_apple.cmake | 3 + build_files/cmake/platform/platform_unix.cmake | 1 + build_files/cmake/platform/platform_win32.cmake | 3 + source/blender/blenlib/BLI_fileops.h | 2 + source/blender/blenlib/BLI_filereader.h | 4 +- source/blender/blenlib/CMakeLists.txt | 3 + source/blender/blenlib/intern/fileops.c | 27 ++ source/blender/blenlib/intern/filereader_zstd.c | 335 ++++++++++++++++++++++++ source/blender/blenloader/CMakeLists.txt | 2 +- source/blender/blenloader/intern/readfile.c | 9 + source/blender/blenloader/intern/writefile.c | 297 ++++++++++++++++----- source/blender/windowmanager/CMakeLists.txt | 4 - source/blender/windowmanager/intern/wm_files.c | 5 + 14 files changed, 689 insertions(+), 72 deletions(-) create mode 100644 build_files/cmake/Modules/FindZstd.cmake create mode 100644 source/blender/blenlib/intern/filereader_zstd.c diff --git a/build_files/cmake/Modules/FindZstd.cmake b/build_files/cmake/Modules/FindZstd.cmake new file mode 100644 index 00000000000..84606d01f44 --- /dev/null +++ b/build_files/cmake/Modules/FindZstd.cmake @@ -0,0 +1,66 @@ +# - Find Zstd library +# Find the native Zstd includes and library +# This module defines +# ZSTD_INCLUDE_DIRS, where to find zstd.h, Set when +# ZSTD_INCLUDE_DIR is found. +# ZSTD_LIBRARIES, libraries to link against to use Zstd. +# ZSTD_ROOT_DIR, The base directory to search for Zstd. +# This can also be an environment variable. +# ZSTD_FOUND, If false, do not try to use Zstd. +# +# also defined, but not for general use are +# ZSTD_LIBRARY, where to find the Zstd library. + +#============================================================================= +# Copyright 2019 Blender Foundation. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= + +# If ZSTD_ROOT_DIR was defined in the environment, use it. +IF(NOT ZSTD_ROOT_DIR AND NOT $ENV{ZSTD_ROOT_DIR} STREQUAL "") + SET(ZSTD_ROOT_DIR $ENV{ZSTD_ROOT_DIR}) +ENDIF() + +SET(_zstd_SEARCH_DIRS + ${ZSTD_ROOT_DIR} +) + +FIND_PATH(ZSTD_INCLUDE_DIR + NAMES + zstd.h + HINTS + ${_zstd_SEARCH_DIRS} + PATH_SUFFIXES + include +) + +FIND_LIBRARY(ZSTD_LIBRARY + NAMES + zstd + HINTS + ${_zstd_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib + ) + +# handle the QUIETLY and REQUIRED arguments and set ZSTD_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Zstd DEFAULT_MSG + ZSTD_LIBRARY ZSTD_INCLUDE_DIR) + +IF(ZSTD_FOUND) + SET(ZSTD_LIBRARIES ${ZSTD_LIBRARY}) + SET(ZSTD_INCLUDE_DIRS ${ZSTD_INCLUDE_DIR}) +ENDIF() + +MARK_AS_ADVANCED( + ZSTD_INCLUDE_DIR + ZSTD_LIBRARY +) diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index dceafb236de..2bfdc84ec2a 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -441,6 +441,9 @@ if(WITH_HARU) endif() endif() +set(ZSTD_ROOT_DIR ${LIBDIR}/zstd) +find_package(Zstd REQUIRED) + if(EXISTS ${LIBDIR}) without_system_libs_end() endif() diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index 7f62399ac4f..fc0c37e4c8b 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -99,6 +99,7 @@ endif() find_package_wrapper(JPEG REQUIRED) find_package_wrapper(PNG REQUIRED) find_package_wrapper(ZLIB REQUIRED) +find_package_wrapper(Zstd REQUIRED) find_package_wrapper(Freetype REQUIRED) if(WITH_PYTHON) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index 3773aaaffed..d44ef691d1b 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -873,3 +873,6 @@ if(WITH_HARU) set(WITH_HARU OFF) endif() endif() + +set(ZSTD_INCLUDE_DIRS ${LIBDIR}/zstd/include) +set(ZSTD_LIBRARIES ${LIBDIR}/zstd/lib/zstd_static.lib) diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 12fa73279c8..377b7bc3bc2 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -168,6 +168,8 @@ size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t g ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BLI_file_magic_is_gzip(const char header[4]); +bool BLI_file_magic_is_zstd(const char header[4]); + size_t BLI_file_descriptor_size(int file) ATTR_WARN_UNUSED_RESULT; size_t BLI_file_size(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); diff --git a/source/blender/blenlib/BLI_filereader.h b/source/blender/blenlib/BLI_filereader.h index dbcaa7cb1b6..8d1fa3d1596 100644 --- a/source/blender/blenlib/BLI_filereader.h +++ b/source/blender/blenlib/BLI_filereader.h @@ -61,7 +61,7 @@ typedef struct FileReader { * * If a FileReader is created, it has to be cleaned up and freed by calling * its close() function unless another FileReader has taken ownership - for example, - * Gzip takes over the base FileReader and will clean it up when their clean() is called. + * Zstd and Gzip take over the base FileReader and will clean it up when their clean() is called. */ /* Create FileReader from raw file descriptor. */ @@ -71,6 +71,8 @@ FileReader *BLI_filereader_new_mmap(int filedes) ATTR_WARN_UNUSED_RESULT; /* Create FileReader from a region of memory. */ FileReader *BLI_filereader_new_memory(const void *data, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +/* Create FileReader from applying Zstd decompression on an underlying file. */ +FileReader *BLI_filereader_new_zstd(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); /* Create FileReader from applying Gzip decompression on an underlying file. */ FileReader *BLI_filereader_new_gzip(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index d2ba9e74c90..f98d15ad08b 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -31,6 +31,7 @@ set(INC set(INC_SYS ${ZLIB_INCLUDE_DIRS} + ${ZSTD_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS} ${GMP_INCLUDE_DIRS} ) @@ -78,6 +79,7 @@ set(SRC intern/filereader_file.c intern/filereader_gzip.c intern/filereader_memory.c + intern/filereader_zstd.c intern/fnmatch.c intern/freetypefont.c intern/gsqueue.c @@ -327,6 +329,7 @@ set(LIB ${FREETYPE_LIBRARY} ${ZLIB_LIBRARIES} + ${ZSTD_LIBRARIES} ) if(WITH_MEM_VALGRIND) diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 6fc2222241b..31825c69737 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -262,6 +262,33 @@ bool BLI_file_magic_is_gzip(const char header[4]) return header[0] == 0x1f && header[1] == 0x8b && header[2] == 0x08; } +bool BLI_file_magic_is_zstd(const char header[4]) +{ + /* ZSTD files consist of concatenated frames, each either a Zstd frame or a skippable frame. + * Both types of frames start with a magic number: 0xFD2FB528 for Zstd frames and 0x184D2A5* + * for skippable frames, with the * being anything from 0 to F. + * + * To check whether a file is Zstd-compressed, we just check whether the first frame matches + * either. Seeking through the file until a Zstd frame is found would make things more + * complicated and the probability of a false positive is rather low anyways. + * + * Note that LZ4 uses a compatible format, so even though its compressed frames have a + * different magic number, a valid LZ4 file might also start with a skippable frame matching + * the second check here. + * + * For more details, see https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md + */ + + uint32_t magic = *((uint32_t *)header); + if (magic == 0xFD2FB528) { + return true; + } + if ((magic >> 4) == 0x184D2A5) { + return true; + } + return false; +} + /** * Returns true if the file with the specified name can be written. * This implementation uses access(2), which makes the check according diff --git a/source/blender/blenlib/intern/filereader_zstd.c b/source/blender/blenlib/intern/filereader_zstd.c new file mode 100644 index 00000000000..785a40cd1a1 --- /dev/null +++ b/source/blender/blenlib/intern/filereader_zstd.c @@ -0,0 +1,335 @@ +/* + * 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) 2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include +#include + +#include "BLI_blenlib.h" +#include "BLI_endian_switch.h" +#include "BLI_filereader.h" +#include "BLI_math_base.h" + +#include "MEM_guardedalloc.h" + +typedef struct { + FileReader reader; + + FileReader *base; + + ZSTD_DCtx *ctx; + ZSTD_inBuffer in_buf; + size_t in_buf_max_size; + + struct { + int num_frames; + size_t *compressed_ofs; + size_t *uncompressed_ofs; + + char *cached_content; + int cached_frame; + } seek; +} ZstdReader; + +static bool zstd_read_u32(FileReader *base, uint32_t *val) +{ + if (base->read(base, val, sizeof(uint32_t)) != sizeof(uint32_t)) { + return false; + } +#ifdef __BIG_ENDIAN__ + BLI_endian_switch_uint32(val); +#endif + return true; +} + +static bool zstd_read_seek_table(ZstdReader *zstd) +{ + FileReader *base = zstd->base; + + /* The seek table frame is at the end of the file, so seek there + * and verify that there is enough data. */ + if (base->seek(base, -4, SEEK_END) < 13) { + return false; + } + uint32_t magic; + if (!zstd_read_u32(base, &magic) || magic != 0x8F92EAB1) { + return false; + } + + uint8_t flags; + if (base->seek(base, -5, SEEK_END) < 0 || base->read(base, &flags, 1) != 1) { + return false; + } + /* Bit 7 indicates checksums. Bits 5 and 6 must be zero. */ + bool has_checksums = (flags & 0x80); + if (flags & 0x60) { + return false; + } + + uint32_t num_frames; + if (base->seek(base, -9, SEEK_END) < 0 || !zstd_read_u32(base, &num_frames)) { + return false; + } + + /* Each frame has either 2 or 3 uint32_t, and after that we have + * num_frames, flags and magic for another 9 bytes. */ + uint32_t expected_frame_length = num_frames * (has_checksums ? 12 : 8) + 9; + /* The frame starts with another magic number and its length, but these + * two fields are not included when counting length. */ + off64_t frame_start_ofs = 8 + expected_frame_length; + /* Sanity check: Before the start of the seek table frame, + * there must be num_frames frames, each of which at least 8 bytes long. */ + off64_t seek_frame_start = base->seek(base, -frame_start_ofs, SEEK_END); + if (seek_frame_start < num_frames * 8) { + return false; + } + + if (!zstd_read_u32(base, &magic) || magic != 0x184D2A5E) { + return false; + } + + uint32_t frame_length; + if (!zstd_read_u32(base, &frame_length) || frame_length != expected_frame_length) { + return false; + } + + zstd->seek.num_frames = num_frames; + zstd->seek.compressed_ofs = MEM_malloc_arrayN(num_frames + 1, sizeof(size_t), __func__); + zstd->seek.uncompressed_ofs = MEM_malloc_arrayN(num_frames + 1, sizeof(size_t), __func__); + + size_t compressed_ofs = 0; + size_t uncompressed_ofs = 0; + for (int i = 0; i < num_frames; i++) { + uint32_t compressed_size, uncompressed_size; + if (!zstd_read_u32(base, &compressed_size) || !zstd_read_u32(base, &uncompressed_size)) { + break; + } + if (has_checksums && base->seek(base, 4, SEEK_CUR) < 0) { + break; + } + zstd->seek.compressed_ofs[i] = compressed_ofs; + zstd->seek.uncompressed_ofs[i] = uncompressed_ofs; + compressed_ofs += compressed_size; + uncompressed_ofs += uncompressed_size; + } + zstd->seek.compressed_ofs[num_frames] = compressed_ofs; + zstd->seek.uncompressed_ofs[num_frames] = uncompressed_ofs; + + /* Seek to the end of the previous frame for the following BHead frame detection. */ + if (seek_frame_start != compressed_ofs || base->seek(base, seek_frame_start, SEEK_SET) < 0) { + MEM_freeN(zstd->seek.compressed_ofs); + MEM_freeN(zstd->seek.uncompressed_ofs); + memset(&zstd->seek, 0, sizeof(zstd->seek)); + return false; + } + + zstd->seek.cached_frame = -1; + + return true; +} + +/* Find out which frame contains the given position in the uncompressed stream. + * Basically just bisection. */ +static int zstd_frame_from_pos(ZstdReader *zstd, size_t pos) +{ + int low = 0, high = zstd->seek.num_frames; + + if (pos >= zstd->seek.uncompressed_ofs[zstd->seek.num_frames]) { + return -1; + } + + while (low + 1 < high) { + int mid = low + ((high - low) >> 1); + if (zstd->seek.uncompressed_ofs[mid] <= pos) { + low = mid; + } + else { + high = mid; + } + } + + return low; +} + +/* Ensure that the currently loaded frame is the correct one. */ +static const char *zstd_ensure_cache(ZstdReader *zstd, int frame) +{ + if (zstd->seek.cached_frame == frame) { + /* Cached frame matches, so just return it. */ + return zstd->seek.cached_content; + } + + /* Cached frame doesn't match, so discard it and cache the wanted one onstead. */ + MEM_SAFE_FREE(zstd->seek.cached_content); + + size_t compressed_size = zstd->seek.compressed_ofs[frame + 1] - zstd->seek.compressed_ofs[frame]; + size_t uncompressed_size = zstd->seek.uncompressed_ofs[frame + 1] - + zstd->seek.uncompressed_ofs[frame]; + + char *uncompressed_data = MEM_mallocN(uncompressed_size, __func__); + char *compressed_data = MEM_mallocN(compressed_size, __func__); + if (zstd->base->seek(zstd->base, zstd->seek.compressed_ofs[frame], SEEK_SET) < 0 || + zstd->base->read(zstd->base, compressed_data, compressed_size) < compressed_size) { + MEM_freeN(compressed_data); + MEM_freeN(uncompressed_data); + return NULL; + } + + size_t res = ZSTD_decompressDCtx( + zstd->ctx, uncompressed_data, uncompressed_size, compressed_data, compressed_size); + MEM_freeN(compressed_data); + if (ZSTD_isError(res) || res < uncompressed_size) { + MEM_freeN(uncompressed_data); + return NULL; + } + + zstd->seek.cached_frame = frame; + zstd->seek.cached_content = uncompressed_data; + return uncompressed_data; +} + +static ssize_t zstd_read_seekable(FileReader *reader, void *buffer, size_t size) +{ + ZstdReader *zstd = (ZstdReader *)reader; + + size_t end_offset = zstd->reader.offset + size, read_len = 0; + while (zstd->reader.offset < end_offset) { + int frame = zstd_frame_from_pos(zstd, zstd->reader.offset); + if (frame < 0) { + /* EOF is reached, so return as much as we can. */ + break; + } + + const char *framedata = zstd_ensure_cache(zstd, frame); + if (framedata == NULL) { + /* Error while reading the frame, so return as much as we can. */ + break; + } + + size_t frame_end_offset = min_zz(zstd->seek.uncompressed_ofs[frame + 1], end_offset); + size_t frame_read_len = frame_end_offset - zstd->reader.offset; + + size_t offset_in_frame = zstd->reader.offset - zstd->seek.uncompressed_ofs[frame]; + memcpy((char *)buffer + read_len, framedata + offset_in_frame, frame_read_len); + read_len += frame_read_len; + zstd->reader.offset = frame_end_offset; + } + + return read_len; +} + +static off64_t zstd_seek(FileReader *reader, off64_t offset, int whence) +{ + ZstdReader *zstd = (ZstdReader *)reader; + off64_t new_pos; + if (whence == SEEK_SET) { + new_pos = offset; + } + else if (whence == SEEK_END) { + new_pos = zstd->seek.uncompressed_ofs[zstd->seek.num_frames] + offset; + } + else { + new_pos = zstd->reader.offset + offset; + } + + if (new_pos < 0 || new_pos > zstd->seek.uncompressed_ofs[zstd->seek.num_frames]) { + return -1; + } + zstd->reader.offset = new_pos; + return zstd->reader.offset; +} + +static ssize_t zstd_read(FileReader *reader, void *buffer, size_t size) +{ + ZstdReader *zstd = (ZstdReader *)reader; + ZSTD_outBuffer output = {buffer, size, 0}; + + while (output.pos < output.size) { + if (zstd->in_buf.pos == zstd->in_buf.size) { + /* Ran out of buffered input data, read some more. */ + zstd->in_buf.pos = 0; + ssize_t readsize = zstd->base->read( + zstd->base, (char *)zstd->in_buf.src, zstd->in_buf_max_size); + + if (readsize > 0) { + /* We got some data, so mark the buffer as refilled. */ + zstd->in_buf.size = readsize; + } + else { + /* The underlying file is EOF, so return as much as we can. */ + break; + } + } + + if (ZSTD_isError(ZSTD_decompressStream(zstd->ctx, &output, &zstd->in_buf))) { + break; + } + } + + zstd->reader.offset += output.pos; + return output.pos; +} + +static void zstd_close(FileReader *reader) +{ + ZstdReader *zstd = (ZstdReader *)reader; + + ZSTD_freeDCtx(zstd->ctx); + if (zstd->reader.seek) { + MEM_freeN(zstd->seek.uncompressed_ofs); + MEM_freeN(zstd->seek.compressed_ofs); + MEM_freeN(zstd->seek.cached_content); + } + else { + MEM_freeN((void *)zstd->in_buf.src); + } + + zstd->base->close(zstd->base); + MEM_freeN(zstd); +} + +FileReader *BLI_filereader_new_zstd(FileReader *base) +{ + ZstdReader *zstd = MEM_callocN(sizeof(ZstdReader), __func__); + + zstd->ctx = ZSTD_createDCtx(); + zstd->base = base; + + if (zstd_read_seek_table(zstd)) { + zstd->reader.read = zstd_read_seekable; + zstd->reader.seek = zstd_seek; + } + else { + zstd->reader.read = zstd_read; + zstd->reader.seek = NULL; + + zstd->in_buf_max_size = ZSTD_DStreamInSize(); + zstd->in_buf.src = MEM_mallocN(zstd->in_buf_max_size, "zstd in buf"); + zstd->in_buf.size = zstd->in_buf_max_size; + /* This signals that the buffer has run out, + * which will make the read function refill it on the first call. */ + zstd->in_buf.pos = zstd->in_buf_max_size; + } + zstd->reader.close = zstd_close; + + return (FileReader *)zstd; +} diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index f5baf0dcb83..89631588ed0 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -42,7 +42,7 @@ set(INC ) set(INC_SYS - ${ZLIB_INCLUDE_DIRS} + ${ZSTD_INCLUDE_DIRS} ) set(SRC diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 82fbb2083df..49c3497f996 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1233,6 +1233,12 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, rawfile = NULL; /* The Gzip FileReader takes ownership of `rawfile`. */ } } + else if (BLI_file_magic_is_zstd(header)) { + file = BLI_filereader_new_zstd(rawfile); + if (file != NULL) { + rawfile = NULL; /* The Zstd FileReader takes ownership of `rawfile`. */ + } + } /* Clean up `rawfile` if it wasn't taken over. */ if (rawfile != NULL) { @@ -1309,6 +1315,9 @@ FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadRe if (BLI_file_magic_is_gzip(mem)) { file = BLI_filereader_new_gzip(mem_file); } + else if (BLI_file_magic_is_zstd(mem)) { + file = BLI_filereader_new_zstd(mem_file); + } if (file == NULL) { /* Compression initialization failed. */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 4de8e35f402..6f43fbf1fa0 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -101,7 +101,12 @@ #include "BLI_bitmap.h" #include "BLI_blenlib.h" #include "BLI_endian_defines.h" +#include "BLI_endian_switch.h" +#include "BLI_link_utils.h" +#include "BLI_linklist.h" +#include "BLI_math_base.h" #include "BLI_mempool.h" +#include "BLI_threads.h" #include "MEM_guardedalloc.h" /* MEM_freeN */ #include "BKE_blender_version.h" @@ -129,14 +134,21 @@ #include +#include + /* Make preferences read-only. */ #define U (*((const UserDef *)&U)) /* ********* my write, buffered writing with minimum size chunks ************ */ /* Use optimal allocation since blocks of this size are kept in memory for undo. */ -#define MYWRITE_BUFFER_SIZE (MEM_SIZE_OPTIMAL(1 << 17)) /* 128kb */ -#define MYWRITE_MAX_CHUNK (MEM_SIZE_OPTIMAL(1 << 15)) /* ~32kb */ +#define MEM_BUFFER_SIZE (MEM_SIZE_OPTIMAL(1 << 17)) /* 128kb */ +#define MEM_CHUNK_SIZE (MEM_SIZE_OPTIMAL(1 << 15)) /* ~32kb */ + +#define ZSTD_BUFFER_SIZE (1 << 21) /* 2mb */ +#define ZSTD_CHUNK_SIZE (1 << 20) /* 1mb */ + +#define ZSTD_COMPRESSION_LEVEL 3 /** Use if we want to store how many bytes have been written to the file. */ // #define USE_WRITE_DATA_LEN @@ -147,9 +159,16 @@ typedef enum { WW_WRAP_NONE = 1, - WW_WRAP_ZLIB, + WW_WRAP_ZSTD, } eWriteWrapType; +typedef struct ZstdFrame { + struct ZstdFrame *next, *prev; + + uint32_t compressed_size; + uint32_t uncompressed_size; +} ZstdFrame; + typedef struct WriteWrap WriteWrap; struct WriteWrap { /* callbacks */ @@ -161,15 +180,23 @@ struct WriteWrap { bool use_buf; /* internal */ - union { - int file_handle; - gzFile gz_handle; - } _user_data; + int file_handle; + struct { + ListBase threadpool; + ListBase tasks; + ThreadMutex mutex; + ThreadCondition condition; + int next_frame; + int num_frames; + + int level; + ListBase frames; + + bool write_error; + } zstd; }; /* none */ -#define FILE_HANDLE(ww) (ww)->_user_data.file_handle - static bool ww_open_none(WriteWrap *ww, const char *filepath) { int file; @@ -177,7 +204,7 @@ static bool ww_open_none(WriteWrap *ww, const char *filepath) file = BLI_open(filepath, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666); if (file != -1) { - FILE_HANDLE(ww) = file; + ww->file_handle = file; return true; } @@ -185,39 +212,170 @@ static bool ww_open_none(WriteWrap *ww, const char *filepath) } static bool ww_close_none(WriteWrap *ww) { - return (close(FILE_HANDLE(ww)) != -1); + return (close(ww->file_handle) != -1); } static size_t ww_write_none(WriteWrap *ww, const char *buf, size_t buf_len) { - return write(FILE_HANDLE(ww), buf, buf_len); + return write(ww->file_handle, buf, buf_len); } -#undef FILE_HANDLE -/* zlib */ -#define FILE_HANDLE(ww) (ww)->_user_data.gz_handle +/* zstd */ + +typedef struct { + struct ZstdWriteBlockTask *next, *prev; + void *data; + size_t size; + int frame_number; + WriteWrap *ww; +} ZstdWriteBlockTask; -static bool ww_open_zlib(WriteWrap *ww, const char *filepath) +static void *zstd_write_task(void *userdata) { - gzFile file; + ZstdWriteBlockTask *task = userdata; + WriteWrap *ww = task->ww; - file = BLI_gzopen(filepath, "wb1"); + size_t out_buf_len = ZSTD_compressBound(task->size); + void *out_buf = MEM_mallocN(out_buf_len, "Zstd out buffer"); + size_t out_size = ZSTD_compress( + out_buf, out_buf_len, task->data, task->size, ZSTD_COMPRESSION_LEVEL); - if (file != Z_NULL) { - FILE_HANDLE(ww) = file; - return true; + MEM_freeN(task->data); + + BLI_mutex_lock(&ww->zstd.mutex); + + while (ww->zstd.next_frame != task->frame_number) { + BLI_condition_wait(&ww->zstd.condition, &ww->zstd.mutex); } - return false; + if (ZSTD_isError(out_size)) { + ww->zstd.write_error = true; + } + else { + if (ww_write_none(ww, out_buf, out_size) == out_size) { + ZstdFrame *frameinfo = MEM_mallocN(sizeof(ZstdFrame), "zstd frameinfo"); + frameinfo->uncompressed_size = task->size; + frameinfo->compressed_size = out_size; + BLI_addtail(&ww->zstd.frames, frameinfo); + } + else { + ww->zstd.write_error = true; + } + } + + ww->zstd.next_frame++; + + BLI_mutex_unlock(&ww->zstd.mutex); + BLI_condition_notify_all(&ww->zstd.condition); + + MEM_freeN(out_buf); + return NULL; +} + +static bool ww_open_zstd(WriteWrap *ww, const char *filepath) +{ + if (!ww_open_none(ww, filepath)) { + return false; + } + + /* Leave one thread open for the main writing logic, unless we only have one HW thread. */ + int num_threads = max_ii(1, BLI_system_thread_count() - 1); + BLI_threadpool_init(&ww->zstd.threadpool, zstd_write_task, num_threads); + BLI_mutex_init(&ww->zstd.mutex); + BLI_condition_init(&ww->zstd.condition); + + return true; } -static bool ww_close_zlib(WriteWrap *ww) + +static void zstd_write_u32_le(WriteWrap *ww, uint32_t val) +{ +#ifdef __BIG_ENDIAN__ + BLI_endian_switch_uint32(&val); +#endif + ww_write_none(ww, (char *)&val, sizeof(uint32_t)); +} + +/* In order to implement efficient seeking when reading the .blend, we append + * a skippable frame that encodes information about the other frames present + * in the file. + * The format here follows the upstream spec for seekable files: + * https://github.com/facebook/zstd/blob/master/contrib/seekable_format/zstd_seekable_compression_format.md + * If this information is not present in a file (e.g. if it was compressed + * with external tools), it can still be opened in Blender, but seeking will + * not be supported, so more memory might be needed. */ +static void zstd_write_seekable_frames(WriteWrap *ww) +{ + /* Write seek table header (magic number and frame size). */ + zstd_write_u32_le(ww, 0x184D2A5E); + + /* The actual frame number might not match ww->zstd.num_frames if there was a write error. */ + const uint32_t num_frames = BLI_listbase_count(&ww->zstd.frames); + /* Each frame consists of two u32, so 8 bytes each. + * After the frames, a footer containing two u32 and one byte (9 bytes total) is written. */ + const uint32_t frame_size = num_frames * 8 + 9; + zstd_write_u32_le(ww, frame_size); + + /* Write seek table entries. */ + LISTBASE_FOREACH (ZstdFrame *, frame, &ww->zstd.frames) { + zstd_write_u32_le(ww, frame->compressed_size); + zstd_write_u32_le(ww, frame->uncompressed_size); + } + + /* Write seek table footer (number of frames, option flags and second magic number). */ + zstd_write_u32_le(ww, num_frames); + const char flags = 0; /* We don't store checksums for each frame. */ + ww_write_none(ww, &flags, 1); + zstd_write_u32_le(ww, 0x8F92EAB1); +} + +static bool ww_close_zstd(WriteWrap *ww) { - return (gzclose(FILE_HANDLE(ww)) == Z_OK); + BLI_threadpool_end(&ww->zstd.threadpool); + BLI_freelistN(&ww->zstd.tasks); + + BLI_mutex_end(&ww->zstd.mutex); + BLI_condition_end(&ww->zstd.condition); + + zstd_write_seekable_frames(ww); + BLI_freelistN(&ww->zstd.frames); + + return ww_close_none(ww) && !ww->zstd.write_error; } -static size_t ww_write_zlib(WriteWrap *ww, const char *buf, size_t buf_len) + +static size_t ww_write_zstd(WriteWrap *ww, const char *buf, size_t buf_len) { - return gzwrite(FILE_HANDLE(ww), buf, buf_len); + if (ww->zstd.write_error) { + return 0; + } + + ZstdWriteBlockTask *task = MEM_mallocN(sizeof(ZstdWriteBlockTask), __func__); + task->data = MEM_mallocN(buf_len, __func__); + memcpy(task->data, buf, buf_len); + task->size = buf_len; + task->frame_number = ww->zstd.num_frames++; + task->ww = ww; + + BLI_mutex_lock(&ww->zstd.mutex); + BLI_addtail(&ww->zstd.tasks, task); + + /* If there's a free worker thread, just push the block into that thread. + * Otherwise, we wait for the earliest thread to finish. + * We look up the earliest thread while holding the mutex, but release it + * before joining the thread to prevent a deadlock. */ + ZstdWriteBlockTask *first_task = ww->zstd.tasks.first; + BLI_mutex_unlock(&ww->zstd.mutex); + if (!BLI_available_threads(&ww->zstd.threadpool)) { + BLI_threadpool_remove(&ww->zstd.threadpool, first_task); + + /* If the task list was empty before we pushed our task, there should + * always be a free thread. */ + BLI_assert(first_task != task); + BLI_remlink(&ww->zstd.tasks, first_task); + MEM_freeN(first_task); + } + BLI_threadpool_insert(&ww->zstd.threadpool, task); + + return buf_len; } -#undef FILE_HANDLE /* --- end compression types --- */ @@ -226,11 +384,11 @@ static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww) memset(r_ww, 0, sizeof(*r_ww)); switch (ww_type) { - case WW_WRAP_ZLIB: { - r_ww->open = ww_open_zlib; - r_ww->close = ww_close_zlib; - r_ww->write = ww_write_zlib; - r_ww->use_buf = false; + case WW_WRAP_ZSTD: { + r_ww->open = ww_open_zstd; + r_ww->close = ww_close_zstd; + r_ww->write = ww_write_zstd; + r_ww->use_buf = true; break; } default: { @@ -252,10 +410,17 @@ static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww) typedef struct { const struct SDNA *sdna; - /** Use for file and memory writing (fixed size of #MYWRITE_BUFFER_SIZE). */ - uchar *buf; - /** Number of bytes used in #WriteData.buf (flushed when exceeded). */ - size_t buf_used_len; + struct { + /** Use for file and memory writing (size stored in max_size). */ + uchar *buf; + /** Number of bytes used in #WriteData.buf (flushed when exceeded). */ + size_t used_len; + + /** Maximum size of the buffer. */ + size_t max_size; + /** Threshold above which writes get their own chunk. */ + size_t chunk_size; + } buffer; #ifdef USE_WRITE_DATA_LEN /** Total number of bytes written. */ @@ -271,7 +436,7 @@ typedef struct { bool use_memfile; /** - * Wrap writing, so we can use zlib or + * Wrap writing, so we can use zstd or * other compression types later, see: G_FILE_COMPRESS * Will be NULL for UNDO. */ @@ -291,7 +456,15 @@ static WriteData *writedata_new(WriteWrap *ww) wd->ww = ww; if ((ww == NULL) || (ww->use_buf)) { - wd->buf = MEM_mallocN(MYWRITE_BUFFER_SIZE, "wd->buf"); + if (ww == NULL) { + wd->buffer.max_size = MEM_BUFFER_SIZE; + wd->buffer.chunk_size = MEM_CHUNK_SIZE; + } + else { + wd->buffer.max_size = ZSTD_BUFFER_SIZE; + wd->buffer.chunk_size = ZSTD_CHUNK_SIZE; + } + wd->buffer.buf = MEM_mallocN(wd->buffer.max_size, "wd->buffer.buf"); } return wd; @@ -325,8 +498,8 @@ static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen) static void writedata_free(WriteData *wd) { - if (wd->buf) { - MEM_freeN(wd->buf); + if (wd->buffer.buf) { + MEM_freeN(wd->buffer.buf); } MEM_freeN(wd); } @@ -343,9 +516,9 @@ static void writedata_free(WriteData *wd) */ static void mywrite_flush(WriteData *wd) { - if (wd->buf_used_len != 0) { - writedata_do_write(wd, wd->buf, wd->buf_used_len); - wd->buf_used_len = 0; + if (wd->buffer.used_len != 0) { + writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len); + wd->buffer.used_len = 0; } } @@ -369,20 +542,20 @@ static void mywrite(WriteData *wd, const void *adr, size_t len) wd->write_len += len; #endif - if (wd->buf == NULL) { + if (wd->buffer.buf == NULL) { writedata_do_write(wd, adr, len); } else { /* if we have a single big chunk, write existing data in * buffer and write out big chunk in smaller pieces */ - if (len > MYWRITE_MAX_CHUNK) { - if (wd->buf_used_len != 0) { - writedata_do_write(wd, wd->buf, wd->buf_used_len); - wd->buf_used_len = 0; + if (len > wd->buffer.chunk_size) { + if (wd->buffer.used_len != 0) { + writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len); + wd->buffer.used_len = 0; } do { - size_t writelen = MIN2(len, MYWRITE_MAX_CHUNK); + size_t writelen = MIN2(len, wd->buffer.chunk_size); writedata_do_write(wd, adr, writelen); adr = (const char *)adr + writelen; len -= writelen; @@ -392,14 +565,14 @@ static void mywrite(WriteData *wd, const void *adr, size_t len) } /* if data would overflow buffer, write out the buffer */ - if (len + wd->buf_used_len > MYWRITE_BUFFER_SIZE - 1) { - writedata_do_write(wd, wd->buf, wd->buf_used_len); - wd->buf_used_len = 0; + if (len + wd->buffer.used_len > wd->buffer.max_size - 1) { + writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len); + wd->buffer.used_len = 0; } /* append data at end of buffer */ - memcpy(&wd->buf[wd->buf_used_len], adr, len); - wd->buf_used_len += len; + memcpy(&wd->buffer.buf[wd->buffer.used_len], adr, len); + wd->buffer.used_len += len; } } @@ -430,9 +603,9 @@ static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *curren */ static bool mywrite_end(WriteData *wd) { - if (wd->buf_used_len != 0) { - writedata_do_write(wd, wd->buf, wd->buf_used_len); - wd->buf_used_len = 0; + if (wd->buffer.used_len != 0) { + writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len); + wd->buffer.used_len = 0; } if (wd->use_memfile) { @@ -1150,7 +1323,6 @@ bool BLO_write_file(Main *mainvar, ReportList *reports) { char tempname[FILE_MAX + 1]; - eWriteWrapType ww_type; WriteWrap ww; eBLO_WritePathRemap remap_mode = params->remap_mode; @@ -1172,14 +1344,7 @@ bool BLO_write_file(Main *mainvar, /* open temporary file, so we preserve the original in case we crash */ BLI_snprintf(tempname, sizeof(tempname), "%s@", filepath); - if (write_flags & G_FILE_COMPRESS) { - ww_type = WW_WRAP_ZLIB; - } - else { - ww_type = WW_WRAP_NONE; - } - - ww_handle_init(ww_type, &ww); + ww_handle_init((write_flags & G_FILE_COMPRESS) ? WW_WRAP_ZSTD : WW_WRAP_NONE, &ww); if (ww.open(&ww, tempname) == false) { BKE_reportf( diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index b7fbb9bb82b..4d65726fe2b 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -48,10 +48,6 @@ set(INC ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern ) -set(INC_SYS - ${ZLIB_INCLUDE_DIRS} -) - set(SRC intern/wm.c intern/wm_cursors.c diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index c3789abf2dd..f83511e76f0 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -516,8 +516,13 @@ static int wm_read_exotic(const char *name) /* check for compressed .blend */ FileReader *compressed_file = NULL; if (BLI_file_magic_is_gzip(header)) { + /* In earlier versions of Blender (before 3.0), compressed files used Gzip instead of Zstd. + * While these files will no longer be written, there still needs to be reading support. */ compressed_file = BLI_filereader_new_gzip(rawfile); } + else if (BLI_file_magic_is_zstd(header)) { + compressed_file = BLI_filereader_new_zstd(rawfile); + } /* If a compression signature matches, try decompressing the start and check if it's a .blend */ if (compressed_file != NULL) { -- cgit v1.2.3 From 67c29bc5a273b66e278bd20c18187b425acf1869 Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Sat, 21 Aug 2021 03:26:57 +0200 Subject: Use Zstandard compression for the sequencer cache Reviewed By: campbellbarton, brecht, mont29 Differential Revision: https://developer.blender.org/D5799 --- source/blender/blenlib/BLI_fileops.h | 19 +-- source/blender/blenlib/intern/fileops.c | 217 +++++++------------------- source/blender/sequencer/intern/image_cache.c | 32 ++-- 3 files changed, 82 insertions(+), 186 deletions(-) diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 377b7bc3bc2..906a56ce909 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -154,20 +154,15 @@ bool BLI_file_is_writable(const char *file) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL bool BLI_file_touch(const char *file) ATTR_NONNULL(); bool BLI_file_alias_target(const char *filepath, char *r_targetpath) ATTR_WARN_UNUSED_RESULT; -#if 0 /* UNUSED */ -int BLI_file_gzip(const char *from, const char *to) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -#endif -char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size) ATTR_WARN_UNUSED_RESULT - ATTR_NONNULL(); -size_t BLI_gzip_mem_to_file_at_pos(void *buf, - size_t len, - FILE *file, - size_t gz_stream_offset, - int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset) - ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BLI_file_magic_is_gzip(const char header[4]); +size_t BLI_file_zstd_from_mem_at_pos(void *buf, + size_t len, + FILE *file, + size_t file_offset, + int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset) + ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BLI_file_magic_is_zstd(const char header[4]); size_t BLI_file_descriptor_size(int file) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 31825c69737..532a29b8147 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -31,6 +31,7 @@ #include #include "zlib.h" +#include "zstd.h" #ifdef WIN32 # include "BLI_fileops_types.h" @@ -61,200 +62,90 @@ #include "BLI_sys_types.h" /* for intptr_t support */ #include "BLI_utildefines.h" -#if 0 /* UNUSED */ -/* gzip the file in from and write it to "to". - * return -1 if zlib fails, -2 if the originating file does not exist - * NOTE: will remove the "from" file - */ -int BLI_file_gzip(const char *from, const char *to) +size_t BLI_file_zstd_from_mem_at_pos( + void *buf, size_t len, FILE *file, size_t file_offset, int compression_level) { - char buffer[10240]; - int file; - int readsize = 0; - int rval = 0, err; - gzFile gzfile; + fseek(file, file_offset, SEEK_SET); - /* level 1 is very close to 3 (the default) in terms of file size, - * but about twice as fast, best use for speedy saving - campbell */ - gzfile = BLI_gzopen(to, "wb1"); - if (gzfile == NULL) { - return -1; - } - file = BLI_open(from, O_BINARY | O_RDONLY, 0); - if (file == -1) { - return -2; - } + ZSTD_CCtx *ctx = ZSTD_createCCtx(); + ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, compression_level); + + ZSTD_inBuffer input = {buf, len, 0}; - while (1) { - readsize = read(file, buffer, sizeof(buffer)); + size_t out_len = ZSTD_CStreamOutSize(); + void *out_buf = MEM_mallocN(out_len, __func__); + size_t total_written = 0; - if (readsize < 0) { - rval = -2; /* error happened in reading */ - fprintf(stderr, "Error reading file %s: %s.\n", from, strerror(errno)); + /* Compress block and write it out until the input has been consumed. */ + while (input.pos < input.size) { + ZSTD_outBuffer output = {out_buf, out_len, 0}; + size_t ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_continue); + if (ZSTD_isError(ret)) { break; } - else if (readsize == 0) { - break; /* done reading */ - } - - if (gzwrite(gzfile, buffer, readsize) <= 0) { - rval = -1; /* error happened in writing */ - fprintf(stderr, "Error writing gz file %s: %s.\n", to, gzerror(gzfile, &err)); + if (fwrite(out_buf, 1, output.pos, file) != output.pos) { break; } + total_written += output.pos; } - gzclose(gzfile); - close(file); - - return rval; -} -#endif - -/* gzip the file in from_file and write it to memory to_mem, at most size bytes. - * return the unzipped size - */ -char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size) -{ - gzFile gzfile; - int readsize, size, alloc_size = 0; - char *mem = NULL; - const int chunk_size = 512 * 1024; - - size = 0; - - gzfile = BLI_gzopen(from_file, "rb"); - for (;;) { - if (mem == NULL) { - mem = MEM_callocN(chunk_size, "BLI_ungzip_to_mem"); - alloc_size = chunk_size; - } - else { - mem = MEM_reallocN(mem, size + chunk_size); - alloc_size += chunk_size; - } - - readsize = gzread(gzfile, mem + size, chunk_size); - if (readsize > 0) { - size += readsize; + /* Finalize the Zstd frame. */ + size_t ret = 1; + while (ret != 0) { + ZSTD_outBuffer output = {out_buf, out_len, 0}; + ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_end); + if (ZSTD_isError(ret)) { + break; } - else { + if (fwrite(out_buf, 1, output.pos, file) != output.pos) { break; } + total_written += output.pos; } - gzclose(gzfile); - - if (size == 0) { - MEM_freeN(mem); - mem = NULL; - } - else if (alloc_size != size) { - mem = MEM_reallocN(mem, size); - } - - *r_size = size; + MEM_freeN(out_buf); + ZSTD_freeCCtx(ctx); - return mem; + return ZSTD_isError(ret) ? 0 : total_written; } -#define CHUNK (256 * 1024) - -/* gzip byte array from memory and write it to file at certain position. - * return size of gzip stream. - */ -size_t BLI_gzip_mem_to_file_at_pos( - void *buf, size_t len, FILE *file, size_t gz_stream_offset, int compression_level) +size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset) { - int ret, flush; - unsigned have; - z_stream strm; - unsigned char out[CHUNK]; - - BLI_fseek(file, gz_stream_offset, 0); - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - ret = deflateInit(&strm, compression_level); - if (ret != Z_OK) { - return 0; - } - - strm.avail_in = len; - strm.next_in = (Bytef *)buf; - flush = Z_FINISH; - - do { - strm.avail_out = CHUNK; - strm.next_out = out; - ret = deflate(&strm, flush); - if (ret == Z_STREAM_ERROR) { - return 0; - } - have = CHUNK - strm.avail_out; - if (fwrite(out, 1, have, file) != have || ferror(file)) { - deflateEnd(&strm); - return 0; - } - } while (strm.avail_out == 0); + fseek(file, file_offset, SEEK_SET); - if (strm.avail_in != 0 || ret != Z_STREAM_END) { - return 0; - } + ZSTD_DCtx *ctx = ZSTD_createDCtx(); - deflateEnd(&strm); - return (size_t)strm.total_out; -} + size_t in_len = ZSTD_DStreamInSize(); + void *in_buf = MEM_mallocN(in_len, __func__); + ZSTD_inBuffer input = {in_buf, in_len, 0}; -/* read and decompress gzip stream from file at certain position to buffer. - * return size of decompressed data. - */ -size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset) -{ - int ret; - z_stream strm; - size_t chunk = 256 * 1024; - unsigned char in[CHUNK]; - - BLI_fseek(file, gz_stream_offset, 0); - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = 0; - strm.next_in = Z_NULL; - ret = inflateInit(&strm); - if (ret != Z_OK) { - return 0; - } + ZSTD_outBuffer output = {buf, len, 0}; - do { - strm.avail_in = fread(in, 1, chunk, file); - strm.next_in = in; - if (ferror(file)) { - inflateEnd(&strm); - return 0; + size_t ret = 0; + /* Read and decompress chunks of input data until we have enough output. */ + while (output.pos < output.size && !ZSTD_isError(ret)) { + input.size = fread(in_buf, 1, in_len, file); + if (input.size == 0) { + break; } - do { - strm.avail_out = len; - strm.next_out = (Bytef *)buf + strm.total_out; + /* Consume input data until we run out or have enough output. */ + input.pos = 0; + while (input.pos < input.size && output.pos < output.size) { + ret = ZSTD_decompressStream(ctx, &output, &input); - ret = inflate(&strm, Z_NO_FLUSH); - if (ret == Z_STREAM_ERROR) { - return 0; + if (ZSTD_isError(ret)) { + break; } - } while (strm.avail_out == 0); + } + } - } while (ret != Z_STREAM_END); + MEM_freeN(in_buf); + ZSTD_freeDCtx(ctx); - inflateEnd(&strm); - return (size_t)strm.total_out; + return ZSTD_isError(ret) ? 0 : output.pos; } -#undef CHUNK - bool BLI_file_magic_is_gzip(const char header[4]) { /* GZIP itself starts with the magic bytes 0x1f 0x8b. diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index 604c9900355..86bd840ce31 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -102,7 +102,7 @@ /* -x-%()-.dcf */ #define DCACHE_FNAME_FORMAT "%d-%dx%d-%d%%(%d)-%d.dcf" #define DCACHE_IMAGES_PER_FILE 100 -#define DCACHE_CURRENT_VERSION 1 +#define DCACHE_CURRENT_VERSION 2 #define COLORSPACE_NAME_MAX 64 /* XXX: defined in imb intern */ typedef struct DiskCacheHeaderEntry { @@ -496,24 +496,34 @@ static size_t deflate_imbuf_to_file(ImBuf *ibuf, int level, DiskCacheHeaderEntry *header_entry) { - if (ibuf->rect) { - return BLI_gzip_mem_to_file_at_pos( - ibuf->rect, header_entry->size_raw, file, header_entry->offset, level); + void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; + + /* Apply compression if wanted, otherwise just write directly to the file. */ + if (level > 0) { + return BLI_file_zstd_from_mem_at_pos( + data, header_entry->size_raw, file, header_entry->offset, level); } - return BLI_gzip_mem_to_file_at_pos( - ibuf->rect_float, header_entry->size_raw, file, header_entry->offset, level); + fseek(file, header_entry->offset, SEEK_SET); + return fwrite(data, 1, header_entry->size_raw, file); } static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntry *header_entry) { - if (ibuf->rect) { - return BLI_ungzip_file_to_mem_at_pos( - ibuf->rect, header_entry->size_raw, file, header_entry->offset); + void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; + char header[4]; + fseek(file, header_entry->offset, SEEK_SET); + if (fread(header, 1, sizeof(header), file) != sizeof(header)) { + return 0; + } + + /* Check if the data is compressed or raw. */ + if (BLI_file_magic_is_zstd(header)) { + return BLI_file_unzstd_to_mem_at_pos(data, header_entry->size_raw, file, header_entry->offset); } - return BLI_ungzip_file_to_mem_at_pos( - ibuf->rect_float, header_entry->size_raw, file, header_entry->offset); + fseek(file, header_entry->offset, SEEK_SET); + return fread(data, 1, header_entry->size_raw, file); } static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) -- cgit v1.2.3