2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 #include "DNA_modifier_types.h"
19 #include "BKE_node_ui_storage.hh"
21 #include "DEG_depsgraph_query.h"
23 #include "NOD_derived_node_tree.hh"
24 #include "NOD_geometry_exec.hh"
25 #include "NOD_type_callbacks.hh"
27 #include "node_geometry_util.hh"
29 namespace blender::nodes {
31 void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const
33 bNodeTree *btree_cow = node_.node_ref().tree().btree();
34 BLI_assert(btree_cow != nullptr);
35 if (btree_cow == nullptr) {
38 bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
40 const NodeTreeEvaluationContext context(*self_object_, *modifier_);
42 BKE_nodetree_error_message_add(
43 *btree_original, context, *node_.bnode(), type, std::move(message));
46 const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
48 for (const DSocket *socket : node_.inputs()) {
49 if (socket->is_available() && socket->name() == name) {
50 return socket->bsocket();
57 ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
58 const GeometryComponent &component,
59 const AttributeDomain domain,
60 const CustomDataType type,
61 const void *default_value) const
63 const bNodeSocket *found_socket = this->find_available_socket(name);
64 BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */
65 if (found_socket == nullptr) {
66 return component.attribute_get_constant_for_read(domain, type, default_value);
69 if (found_socket->type == SOCK_STRING) {
70 const std::string name = this->get_input<std::string>(found_socket->identifier);
71 /* Try getting the attribute without the default value. */
72 ReadAttributePtr attribute = component.attribute_try_get_for_read(name, domain, type);
77 /* If the attribute doesn't exist, use the default value and output an error message
78 * (except when the field is empty, to avoid spamming error messages, and not when
79 * the domain is empty and we don't expect an attribute anyway). */
80 if (!name.empty() && component.attribute_domain_size(domain) != 0) {
81 this->error_message_add(NodeWarningType::Error,
82 std::string("No attribute with name '") + name + "'.");
84 return component.attribute_get_constant_for_read(domain, type, default_value);
86 if (found_socket->type == SOCK_FLOAT) {
87 const float value = this->get_input<float>(found_socket->identifier);
88 return component.attribute_get_constant_for_read_converted(
89 domain, CD_PROP_FLOAT, type, &value);
91 if (found_socket->type == SOCK_VECTOR) {
92 const float3 value = this->get_input<float3>(found_socket->identifier);
93 return component.attribute_get_constant_for_read_converted(
94 domain, CD_PROP_FLOAT3, type, &value);
96 if (found_socket->type == SOCK_RGBA) {
97 const Color4f value = this->get_input<Color4f>(found_socket->identifier);
98 return component.attribute_get_constant_for_read_converted(
99 domain, CD_PROP_COLOR, type, &value);
102 return component.attribute_get_constant_for_read(domain, type, default_value);
105 CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
106 const StringRef name,
107 const GeometryComponent &component,
108 const CustomDataType default_type) const
110 const bNodeSocket *found_socket = this->find_available_socket(name);
111 BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */
112 if (found_socket == nullptr) {
116 if (found_socket->type == SOCK_STRING) {
117 const std::string name = this->get_input<std::string>(found_socket->identifier);
118 ReadAttributePtr attribute = component.attribute_try_get_for_read(name);
122 return attribute->custom_data_type();
124 if (found_socket->type == SOCK_FLOAT) {
125 return CD_PROP_FLOAT;
127 if (found_socket->type == SOCK_VECTOR) {
128 return CD_PROP_FLOAT3;
130 if (found_socket->type == SOCK_RGBA) {
131 return CD_PROP_COLOR;
133 if (found_socket->type == SOCK_BOOLEAN) {
142 * If any of the corresponding input sockets are attributes instead of single values,
143 * use the highest priority attribute domain from among them.
144 * Otherwise return the default domain.
146 AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
147 Span<std::string> names,
148 const GeometryComponent &component,
149 const AttributeDomain default_domain) const
151 Vector<AttributeDomain, 8> input_domains;
152 for (const std::string &name : names) {
153 const bNodeSocket *found_socket = this->find_available_socket(name);
154 BLI_assert(found_socket != nullptr); /* A socket should be available socket for the name. */
155 if (found_socket == nullptr) {
159 if (found_socket->type == SOCK_STRING) {
160 const std::string name = this->get_input<std::string>(found_socket->identifier);
161 ReadAttributePtr attribute = component.attribute_try_get_for_read(name);
163 input_domains.append(attribute->domain());
168 if (input_domains.size() > 0) {
169 return bke::attribute_domain_highest_priority(input_domains);
172 return default_domain;
175 void GeoNodeExecParams::check_extract_input(StringRef identifier,
176 const CPPType *requested_type) const
178 bNodeSocket *found_socket = nullptr;
179 for (const DSocket *socket : node_.inputs()) {
180 if (socket->identifier() == identifier) {
181 found_socket = socket->bsocket();
186 if (found_socket == nullptr) {
187 std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n";
188 std::cout << "Possible identifiers are: ";
189 for (const DSocket *socket : node_.inputs()) {
190 if (socket->is_available()) {
191 std::cout << "'" << socket->identifier() << "', ";
197 else if (found_socket->flag & SOCK_UNAVAIL) {
198 std::cout << "The socket corresponding to the identifier '" << identifier
199 << "' is disabled.\n";
202 else if (!input_values_.contains(identifier)) {
203 std::cout << "The identifier '" << identifier
204 << "' is valid, but there is no value for it anymore.\n";
205 std::cout << "Most likely it has been extracted before.\n";
208 else if (requested_type != nullptr) {
209 const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
210 if (*requested_type != expected_type) {
211 std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '"
212 << expected_type.name() << "'.\n";
218 void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &value_type) const
220 bNodeSocket *found_socket = nullptr;
221 for (const DSocket *socket : node_.outputs()) {
222 if (socket->identifier() == identifier) {
223 found_socket = socket->bsocket();
228 if (found_socket == nullptr) {
229 std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n";
230 std::cout << "Possible identifiers are: ";
231 for (const DSocket *socket : node_.outputs()) {
232 if (socket->is_available()) {
233 std::cout << "'" << socket->identifier() << "', ";
239 else if (found_socket->flag & SOCK_UNAVAIL) {
240 std::cout << "The socket corresponding to the identifier '" << identifier
241 << "' is disabled.\n";
244 else if (output_values_.contains(identifier)) {
245 std::cout << "The identifier '" << identifier << "' has been set already.\n";
249 const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
250 if (value_type != expected_type) {
251 std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '"
252 << expected_type.name() << "'.\n";
258 } // namespace blender::nodes