Add support for attributes storage in simulation state #107133

Merged
76 changed files with 1476 additions and 692 deletions
Showing only changes of commit c40bfcdead - Show all commits

View File

@ -550,7 +550,7 @@ static void log_kernel_features(const uint features)
<< "\n"; << "\n";
VLOG_INFO << "Use Shader Raytrace " << string_from_bool(features & KERNEL_FEATURE_NODE_RAYTRACE) VLOG_INFO << "Use Shader Raytrace " << string_from_bool(features & KERNEL_FEATURE_NODE_RAYTRACE)
<< "\n"; << "\n";
VLOG_INFO << "Use MNEE" << string_from_bool(features & KERNEL_FEATURE_MNEE) << "\n"; VLOG_INFO << "Use MNEE " << string_from_bool(features & KERNEL_FEATURE_MNEE) << "\n";
VLOG_INFO << "Use Transparent " << string_from_bool(features & KERNEL_FEATURE_TRANSPARENT) VLOG_INFO << "Use Transparent " << string_from_bool(features & KERNEL_FEATURE_TRANSPARENT)
<< "\n"; << "\n";
VLOG_INFO << "Use Denoising " << string_from_bool(features & KERNEL_FEATURE_DENOISING) << "\n"; VLOG_INFO << "Use Denoising " << string_from_bool(features & KERNEL_FEATURE_DENOISING) << "\n";

View File

@ -5,6 +5,10 @@ from bpy.types import Operator
from bpy.app.translations import pgettext_data as data_ from bpy.app.translations import pgettext_data as data_
from bpy.props import (
EnumProperty,
)
def build_default_empty_geometry_node_group(name): def build_default_empty_geometry_node_group(name):
group = bpy.data.node_groups.new(name, 'GeometryNodeTree') group = bpy.data.node_groups.new(name, 'GeometryNodeTree')
@ -237,8 +241,102 @@ class NewGeometryNodeTreeAssign(Operator):
return {'FINISHED'} return {'FINISHED'}
class SimulationZoneOperator():
input_node_type = 'GeometryNodeSimulationInput'
output_node_type = 'GeometryNodeSimulationOutput'
@classmethod
def get_output_node(cls, context):
node = context.active_node
if node.bl_idname == cls.input_node_type:
return node.paired_output
if node.bl_idname == cls.output_node_type:
return node
@classmethod
def poll(cls, context):
space = context.space_data
# Needs active node editor and a tree.
if not space or space.type != 'NODE_EDITOR' or not space.edit_tree or space.edit_tree.library:
return False
node = context.active_node
if node is None or node.bl_idname not in [cls.input_node_type, cls.output_node_type]:
return False
if cls.get_output_node(context) is None:
return False
return True
class SimulationZoneItemAddOperator(SimulationZoneOperator, Operator):
'''Add a state item to the simulation zone'''
bl_idname = "node.simulation_zone_item_add"
bl_label = "Add State Item"
bl_options = {'REGISTER', 'UNDO'}
default_socket_type = 'GEOMETRY'
def execute(self, context):
node = self.get_output_node(context)
state_items = node.state_items
# Remember index to move the item.
dst_index = min(node.active_index + 1, len(state_items))
# Empty name so it is based on the type only.
state_items.new(self.default_socket_type, "")
state_items.move(len(state_items) - 1, dst_index)
node.active_index = dst_index
return {'FINISHED'}
class SimulationZoneItemRemoveOperator(SimulationZoneOperator, Operator):
'''Remove a state item from the simulation zone'''
bl_idname = "node.simulation_zone_item_remove"
bl_label = "Remove State Item"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
node = self.get_output_node(context)
state_items = node.state_items
if node.active_item:
state_items.remove(node.active_item)
node.active_index = min(node.active_index, len(state_items) - 1)
return {'FINISHED'}
class SimulationZoneItemMoveOperator(SimulationZoneOperator, Operator):
'''Move a simulation state item up or down in the list'''
bl_idname = "node.simulation_zone_item_move"
bl_label = "Move State Item"
bl_options = {'REGISTER', 'UNDO'}
direction: EnumProperty(
name="Direction",
items=[('UP', "Up", ""), ('DOWN', "Down", "")],
default = 'UP',
)
def execute(self, context):
node = self.get_output_node(context)
state_items = node.state_items
if self.direction == 'UP' and node.active_index > 0:
state_items.move(node.active_index, node.active_index - 1)
node.active_index = node.active_index - 1
elif self.direction == 'DOWN' and node.active_index < len(state_items) - 1:
state_items.move(node.active_index, node.active_index + 1)
node.active_index = node.active_index + 1
return {'FINISHED'}
classes = ( classes = (
NewGeometryNodesModifier, NewGeometryNodesModifier,
NewGeometryNodeTreeAssign, NewGeometryNodeTreeAssign,
MoveModifierToNodes, MoveModifierToNodes,
SimulationZoneItemAddOperator,
SimulationZoneItemRemoveOperator,
SimulationZoneItemMoveOperator,
) )

View File

@ -954,6 +954,76 @@ class NODE_PT_node_tree_interface_outputs(NodeTreeInterfacePanel):
self.draw_socket_list(context, "OUT", "outputs", "active_output") self.draw_socket_list(context, "OUT", "outputs", "active_output")
class NODE_UL_simulation_zone_items(bpy.types.UIList):
def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index):
if self.layout_type in {'DEFAULT', 'COMPACT'}:
row = layout.row(align=True)
row.template_node_socket(color=item.color)
row.prop(item, "name", text="", emboss=False, icon_value=icon)
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.template_node_socket(color=item.color)
class NODE_PT_simulation_zone_items(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"
bl_label = "Simulation State"
input_node_type = 'GeometryNodeSimulationInput'
output_node_type = 'GeometryNodeSimulationOutput'
@classmethod
def get_output_node(cls, context):
node = context.active_node
if node.bl_idname == cls.input_node_type:
return node.paired_output
if node.bl_idname == cls.output_node_type:
return node
@classmethod
def poll(cls, context):
snode = context.space_data
if snode is None:
return False
node = context.active_node
if node is None or node.bl_idname not in [cls.input_node_type, cls.output_node_type]:
return False
if cls.get_output_node(context) is None:
return False
return True
def draw(self, context):
layout = self.layout
output_node = self.get_output_node(context)
split = layout.row()
split.template_list("NODE_UL_simulation_zone_items", "", output_node, "state_items", output_node, "active_index")
ops_col = split.column()
add_remove_col = ops_col.column(align=True)
add_remove_col.operator("node.simulation_zone_item_add", icon='ADD', text="")
add_remove_col.operator("node.simulation_zone_item_remove", icon='REMOVE', text="")
ops_col.separator()
up_down_col = ops_col.column(align=True)
props = up_down_col.operator("node.simulation_zone_item_move", icon='TRIA_UP', text="")
props.direction = 'UP'
props = up_down_col.operator("node.simulation_zone_item_move", icon='TRIA_DOWN', text="")
props.direction = 'DOWN'
active_item = output_node.active_item
if active_item is not None:
layout.prop(active_item, "socket_type")
layout.prop(active_item, "name")
# Grease Pencil properties # Grease Pencil properties
class NODE_PT_annotation(AnnotationDataPanel, Panel): class NODE_PT_annotation(AnnotationDataPanel, Panel):
bl_space_type = 'NODE_EDITOR' bl_space_type = 'NODE_EDITOR'
@ -1018,6 +1088,8 @@ classes = (
NODE_UL_interface_sockets, NODE_UL_interface_sockets,
NODE_PT_node_tree_interface_inputs, NODE_PT_node_tree_interface_inputs,
NODE_PT_node_tree_interface_outputs, NODE_PT_node_tree_interface_outputs,
NODE_UL_simulation_zone_items,
NODE_PT_simulation_zone_items,
node_panel(EEVEE_MATERIAL_PT_settings), node_panel(EEVEE_MATERIAL_PT_settings),
node_panel(MATERIAL_PT_viewport), node_panel(MATERIAL_PT_viewport),

View File

@ -23,7 +23,7 @@ std::string normalize_directory_path(StringRef directory)
directory.data(), directory.data(),
/* + 1 for null terminator. */ /* + 1 for null terminator. */
std::min(directory.size() + 1, int64_t(sizeof(dir_normalized)))); std::min(directory.size() + 1, int64_t(sizeof(dir_normalized))));
BLI_path_normalize_dir(nullptr, dir_normalized, sizeof(dir_normalized)); BLI_path_normalize_dir(dir_normalized, sizeof(dir_normalized));
return std::string(dir_normalized); return std::string(dir_normalized);
} }
@ -34,7 +34,7 @@ std::string normalize_path(StringRefNull path, int64_t max_len)
char *buf = BLI_strdupn(path.c_str(), len); char *buf = BLI_strdupn(path.c_str(), len);
BLI_path_slash_native(buf); BLI_path_slash_native(buf);
BLI_path_normalize(nullptr, buf); BLI_path_normalize(buf);
std::string normalized_path = buf; std::string normalized_path = buf;
MEM_freeN(buf); MEM_freeN(buf);

View File

@ -11,8 +11,6 @@
# include <mutex> # include <mutex>
# include "MEM_guardedalloc.h"
# include "BLI_array.hh" # include "BLI_array.hh"
# include "BLI_bit_vector.hh" # include "BLI_bit_vector.hh"
# include "BLI_bounds_types.hh" # include "BLI_bounds_types.hh"
@ -22,7 +20,6 @@
# include "BLI_span.hh" # include "BLI_span.hh"
# include "BLI_vector.hh" # include "BLI_vector.hh"
# include "DNA_customdata_types.h"
# include "DNA_meshdata_types.h" # include "DNA_meshdata_types.h"
struct BVHCache; struct BVHCache;
@ -175,7 +172,7 @@ struct MeshRuntime {
SharedCache<LooseEdgeCache> loose_edges_cache; SharedCache<LooseEdgeCache> loose_edges_cache;
/** Cache of data about vertices not used by edges. See #Mesh::loose_verts(). */ /** Cache of data about vertices not used by edges. See #Mesh::loose_verts(). */
SharedCache<LooseVertCache> loose_verts_cache; SharedCache<LooseVertCache> loose_verts_cache;
/** Cache of data about vertices not used by faces. See #Mesh::loose_verts(). */ /** Cache of data about vertices not used by faces. See #Mesh::verts_no_face(). */
SharedCache<LooseVertCache> verts_no_face_cache; SharedCache<LooseVertCache> verts_no_face_cache;
/** /**

View File

@ -386,7 +386,7 @@ static bool get_path_local_ex(char *targetpath,
char osx_resourses[FILE_MAX + 4 + 9]; char osx_resourses[FILE_MAX + 4 + 9];
BLI_path_join(osx_resourses, sizeof(osx_resourses), g_app.program_dirname, "..", "Resources"); BLI_path_join(osx_resourses, sizeof(osx_resourses), g_app.program_dirname, "..", "Resources");
/* Remove the '/../' added above. */ /* Remove the '/../' added above. */
BLI_path_normalize(NULL, osx_resourses); BLI_path_normalize(osx_resourses);
path_base = osx_resourses; path_base = osx_resourses;
#endif #endif
return test_path(targetpath, return test_path(targetpath,
@ -871,7 +871,7 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name)
BLI_path_program_search(fullname, maxlen, name); BLI_path_program_search(fullname, maxlen, name);
} }
/* Remove "/./" and "/../" so string comparisons can be used on the path. */ /* Remove "/./" and "/../" so string comparisons can be used on the path. */
BLI_path_normalize(NULL, fullname); BLI_path_normalize(fullname);
# if defined(DEBUG) # if defined(DEBUG)
if (!STREQ(name, fullname)) { if (!STREQ(name, fullname)) {
@ -890,7 +890,7 @@ void BKE_appdir_program_path_init(const char *argv0)
* which must point to the Python module for data-files to be detected. */ * which must point to the Python module for data-files to be detected. */
STRNCPY(g_app.program_filepath, argv0); STRNCPY(g_app.program_filepath, argv0);
BLI_path_abs_from_cwd(g_app.program_filepath, sizeof(g_app.program_filepath)); BLI_path_abs_from_cwd(g_app.program_filepath, sizeof(g_app.program_filepath));
BLI_path_normalize(NULL, g_app.program_filepath); BLI_path_normalize(g_app.program_filepath);
if (g_app.program_dirname[0] == '\0') { if (g_app.program_dirname[0] == '\0') {
/* First time initializing, the file binary path isn't valid from a Python module. /* First time initializing, the file binary path isn't valid from a Python module.

View File

@ -441,7 +441,7 @@ static bool relative_rebase_foreach_path_cb(BPathForeachPathData *bpath_data,
return false; return false;
} }
BLI_path_normalize(NULL, filepath); BLI_path_normalize(filepath);
/* This may fail, if so it's fine to leave absolute since the path is still valid. */ /* This may fail, if so it's fine to leave absolute since the path is still valid. */
BLI_path_rel(filepath, data->basedir_dst); BLI_path_rel(filepath, data->basedir_dst);

View File

@ -137,7 +137,7 @@ static bool lib_id_library_local_paths_callback(BPathForeachPathData *bpath_data
/* Path was relative and is now absolute. Remap. /* Path was relative and is now absolute. Remap.
* Important BLI_path_normalize runs before the path is made relative * Important BLI_path_normalize runs before the path is made relative
* because it won't work for paths that start with "//../" */ * because it won't work for paths that start with "//../" */
BLI_path_normalize(base_new, filepath); BLI_path_normalize(filepath);
BLI_path_rel(filepath, base_new); BLI_path_rel(filepath, base_new);
BLI_strncpy(r_path_dst, filepath, FILE_MAX); BLI_strncpy(r_path_dst, filepath, FILE_MAX);
return true; return true;

View File

@ -115,14 +115,14 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
/* This is a direct copy of a main mesh, so for now it has the same topology. */ /* This is a direct copy of a main mesh, so for now it has the same topology. */
mesh_dst->runtime->deformed_only = true; mesh_dst->runtime->deformed_only = true;
} }
/* This option is set for run-time meshes that have been copied from the current objects mode. /* This option is set for run-time meshes that have been copied from the current object's mode.
* Currently this is used for edit-mesh although it could be used for sculpt or other * Currently this is used for edit-mesh although it could be used for sculpt or other
* kinds of data specific to an objects mode. * kinds of data specific to an object's mode.
* *
* The flag signals that the mesh hasn't been modified from the data that generated it, * The flag signals that the mesh hasn't been modified from the data that generated it,
* allowing us to use the object-mode data for drawing. * allowing us to use the object-mode data for drawing.
* *
* While this could be the callers responsibility, keep here since it's * While this could be the caller's responsibility, keep here since it's
* highly unlikely we want to create a duplicate and not use it for drawing. */ * highly unlikely we want to create a duplicate and not use it for drawing. */
mesh_dst->runtime->is_original_bmesh = false; mesh_dst->runtime->is_original_bmesh = false;

View File

@ -136,10 +136,10 @@ static void bit_vector_with_reset_bits_or_empty(const Span<int> indices_to_reset
*/ */
static void try_tag_verts_no_face_none(const Mesh &mesh) static void try_tag_verts_no_face_none(const Mesh &mesh)
{ {
if (mesh.runtime->loose_edges_cache.is_cached() || mesh.loose_edges().count > 0) { if (!mesh.runtime->loose_edges_cache.is_cached() || mesh.loose_edges().count > 0) {
return; return;
} }
if (mesh.runtime->loose_verts_cache.is_cached() || mesh.loose_verts().count > 0) { if (!mesh.runtime->loose_verts_cache.is_cached() || mesh.loose_verts().count > 0) {
return; return;
} }
mesh.runtime->verts_no_face_cache.ensure([&](LooseVertCache &r_data) { mesh.runtime->verts_no_face_cache.ensure([&](LooseVertCache &r_data) {

View File

@ -795,7 +795,7 @@ static void foreach_vertex_of_loose_edge(const SubdivForeachContext *foreach_con
static void store_edge(MultiresReshapeSmoothContext *reshape_smooth_context, static void store_edge(MultiresReshapeSmoothContext *reshape_smooth_context,
const int subdiv_v1, const int subdiv_v1,
const int subdiv_v2, const int subdiv_v2,
const char crease) const float crease)
{ {
/* This is a bit overhead to use atomics in such a simple function called from many threads, /* This is a bit overhead to use atomics in such a simple function called from many threads,
* but this allows to save quite measurable amount of memory. */ * but this allows to save quite measurable amount of memory. */
@ -805,7 +805,7 @@ static void store_edge(MultiresReshapeSmoothContext *reshape_smooth_context,
Edge *edge = &reshape_smooth_context->geometry.edges[edge_index]; Edge *edge = &reshape_smooth_context->geometry.edges[edge_index];
edge->v1 = subdiv_v1; edge->v1 = subdiv_v1;
edge->v2 = subdiv_v2; edge->v2 = subdiv_v2;
edge->sharpness = BKE_subdiv_crease_to_sharpness_char(crease); edge->sharpness = BKE_subdiv_crease_to_sharpness_f(crease);
} }
static void foreach_edge(const SubdivForeachContext *foreach_context, static void foreach_edge(const SubdivForeachContext *foreach_context,
@ -821,7 +821,7 @@ static void foreach_edge(const SubdivForeachContext *foreach_context,
if (reshape_smooth_context->smoothing_type == MULTIRES_SUBDIVIDE_LINEAR) { if (reshape_smooth_context->smoothing_type == MULTIRES_SUBDIVIDE_LINEAR) {
if (!is_loose) { if (!is_loose) {
store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, char(255)); store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, 1.0f);
} }
return; return;
} }
@ -837,8 +837,8 @@ static void foreach_edge(const SubdivForeachContext *foreach_context,
return; return;
} }
/* Edges without crease are to be ignored as well. */ /* Edges without crease are to be ignored as well. */
const char crease = get_effective_crease(reshape_smooth_context, coarse_edge_index); const float crease = get_effective_crease(reshape_smooth_context, coarse_edge_index);
if (crease == 0) { if (crease == 0.0f) {
return; return;
} }
store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, crease); store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, crease);

View File

@ -2459,29 +2459,7 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree,
return node_dst; return node_dst;
} }
static void for_each_node_group_instance(Main &bmain, void node_socket_move_default_value(Main & /*bmain*/,
const bNodeTree &node_group,
const Span<int> tree_types_to_lookup,
const FunctionRef<void(bNode &)> func)
{
LISTBASE_FOREACH (bNodeTree *, other_group, &bmain.nodetrees) {
if (!tree_types_to_lookup.contains(other_group->type)) {
continue;
}
if (other_group == &node_group) {
continue;
}
other_group->ensure_topology_cache();
for (bNode *node : other_group->group_nodes()) {
if (node->id == &node_group.id) {
func(*node);
}
}
}
}
void node_socket_move_default_value(Main &bmain,
bNodeTree &tree, bNodeTree &tree,
bNodeSocket &src, bNodeSocket &src,
bNodeSocket &dst) bNodeSocket &dst)
@ -2519,14 +2497,6 @@ void node_socket_move_default_value(Main &bmain,
dst_values.append(&dst_node.id); dst_values.append(&dst_node.id);
break; break;
} }
case NODE_GROUP_INPUT: {
for_each_node_group_instance(bmain, tree, {NTREE_GEOMETRY}, [&](bNode &node_group) {
bNodeSocket &socket = node_group.input_by_identifier(dst.identifier);
Image **tmp_dst_value = &socket.default_value_typed<bNodeSocketValueImage>()->value;
dst_values.append(reinterpret_cast<ID **>(tmp_dst_value));
});
break;
}
default: { default: {
break; break;
} }

View File

@ -572,6 +572,15 @@ class NodeTreeMainUpdater {
return true; return true;
} }
} }
/* Check paired simulation zone nodes. */
if (node.type == GEO_NODE_SIMULATION_INPUT) {
const NodeGeometrySimulationInput *data = static_cast<const NodeGeometrySimulationInput *>(node.storage);
if (const bNode *output_node = ntree.node_by_id(data->output_node_id)) {
if (output_node->runtime->changed_flag & NTREE_CHANGED_NODE_PROPERTY) {
return true;
}
}
}
return false; return false;
} }

View File

@ -161,7 +161,7 @@ static float get_edge_sharpness(const OpenSubdiv_Converter *converter, int manif
return 10.0f; return 10.0f;
} }
#endif #endif
if (!storage->settings.use_creases || storage->cd_edge_crease == nullptr) { if (storage->cd_edge_crease == nullptr) {
return 0.0f; return 0.0f;
} }
const int edge_index = storage->manifold_edge_index_reverse[manifold_edge_index]; const int edge_index = storage->manifold_edge_index_reverse[manifold_edge_index];
@ -187,7 +187,7 @@ static bool is_infinite_sharp_vertex(const OpenSubdiv_Converter *converter,
static float get_vertex_sharpness(const OpenSubdiv_Converter *converter, int manifold_vertex_index) static float get_vertex_sharpness(const OpenSubdiv_Converter *converter, int manifold_vertex_index)
{ {
ConverterStorage *storage = static_cast<ConverterStorage *>(converter->user_data); ConverterStorage *storage = static_cast<ConverterStorage *>(converter->user_data);
if (!storage->settings.use_creases || storage->cd_vertex_crease == nullptr) { if (storage->cd_vertex_crease == nullptr) {
return 0.0f; return 0.0f;
} }
const int vertex_index = storage->manifold_vertex_index_reverse[manifold_vertex_index]; const int vertex_index = storage->manifold_vertex_index_reverse[manifold_vertex_index];
@ -214,16 +214,15 @@ static void precalc_uv_layer(const OpenSubdiv_Converter *converter, const int la
storage->loop_uv_indices = static_cast<int *>( storage->loop_uv_indices = static_cast<int *>(
MEM_malloc_arrayN(mesh->totloop, sizeof(int), "loop uv vertex index")); MEM_malloc_arrayN(mesh->totloop, sizeof(int), "loop uv vertex index"));
} }
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create( UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(storage->polys,
storage->polys, nullptr,
(const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"), nullptr,
(const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".select_poly"), storage->corner_verts.data(),
storage->corner_verts.data(), mloopuv,
mloopuv, num_vert,
num_vert, limit,
limit, false,
false, true);
true);
/* NOTE: First UV vertex is supposed to be always marked as separate. */ /* NOTE: First UV vertex is supposed to be always marked as separate. */
storage->num_uv_coordinates = -1; storage->num_uv_coordinates = -1;
for (int vertex_index = 0; vertex_index < num_vert; vertex_index++) { for (int vertex_index = 0; vertex_index < num_vert; vertex_index++) {
@ -395,10 +394,12 @@ static void init_user_data(OpenSubdiv_Converter *converter,
user_data->polys = mesh->polys(); user_data->polys = mesh->polys();
user_data->corner_verts = mesh->corner_verts(); user_data->corner_verts = mesh->corner_verts();
user_data->corner_edges = mesh->corner_edges(); user_data->corner_edges = mesh->corner_edges();
user_data->cd_vertex_crease = static_cast<const float *>( if (settings->use_creases) {
CustomData_get_layer(&mesh->vdata, CD_CREASE)); user_data->cd_vertex_crease = static_cast<const float *>(
user_data->cd_edge_crease = static_cast<const float *>( CustomData_get_layer(&mesh->vdata, CD_CREASE));
CustomData_get_layer(&mesh->edata, CD_CREASE)); user_data->cd_edge_crease = static_cast<const float *>(
CustomData_get_layer(&mesh->edata, CD_CREASE));
}
user_data->loop_uv_indices = nullptr; user_data->loop_uv_indices = nullptr;
initialize_manifold_indices(user_data); initialize_manifold_indices(user_data);
converter->user_data = user_data; converter->user_data = user_data;

View File

@ -91,9 +91,3 @@ BLI_INLINE float BKE_subdiv_crease_to_sharpness_f(float edge_crease)
{ {
return edge_crease * edge_crease * 10.0f; return edge_crease * edge_crease * 10.0f;
} }
BLI_INLINE float BKE_subdiv_crease_to_sharpness_char(char edge_crease)
{
const float edge_crease_f = edge_crease / 255.0f;
return BKE_subdiv_crease_to_sharpness_f(edge_crease_f);
}

View File

@ -1192,11 +1192,19 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv,
BitVector<> &bit_vector = result->runtime->subsurf_optimal_display_edges; BitVector<> &bit_vector = result->runtime->subsurf_optimal_display_edges;
bit_vector.clear(); bit_vector.clear();
bit_vector.resize(subdiv_context.subdiv_display_edges.size()); bit_vector.resize(subdiv_context.subdiv_display_edges.size());
threading::parallel_for_aligned(span.index_range(), 4096, 64, [&](const IndexRange range) { threading::parallel_for_aligned(
for (const int i : range) { span.index_range(), 4096, bits::BitsPerInt, [&](const IndexRange range) {
bit_vector[i].set(span[i]); for (const int i : range) {
} bit_vector[i].set(span[i]);
}); }
});
}
if (coarse_mesh->verts_no_face().count == 0) {
result->tag_loose_verts_none();
}
if (coarse_mesh->loose_edges().count == 0) {
result->loose_edges_tag_none();
} }
if (subdiv->settings.is_simple) { if (subdiv->settings.is_simple) {

View File

@ -170,12 +170,9 @@ Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(SubsurfRuntimeData *runtim
return runtime_data->subdiv_gpu = BKE_subdiv_update_from_mesh( return runtime_data->subdiv_gpu = BKE_subdiv_update_from_mesh(
runtime_data->subdiv_gpu, &runtime_data->settings, mesh); runtime_data->subdiv_gpu, &runtime_data->settings, mesh);
} }
else { runtime_data->used_cpu = 2;
runtime_data->used_cpu = 2; return runtime_data->subdiv_cpu = BKE_subdiv_update_from_mesh(
runtime_data->subdiv_cpu, &runtime_data->settings, mesh);
return runtime_data->subdiv_cpu = BKE_subdiv_update_from_mesh(
runtime_data->subdiv_cpu, &runtime_data->settings, mesh);
}
} }
int BKE_subsurf_modifier_eval_required_mode(bool is_final_render, bool is_edit_mode) int BKE_subsurf_modifier_eval_required_mode(bool is_final_render, bool is_edit_mode)

View File

@ -625,7 +625,7 @@ fn::GField DataTypeConversions::try_convert(fn::GField field, const CPPType &to_
} }
const mf::MultiFunction &fn = *this->get_conversion_multi_function( const mf::MultiFunction &fn = *this->get_conversion_multi_function(
mf::DataType::ForSingle(from_type), mf::DataType::ForSingle(to_type)); mf::DataType::ForSingle(from_type), mf::DataType::ForSingle(to_type));
return {std::make_shared<fn::FieldOperation>(fn, Vector<fn::GField>{std::move(field)})}; return {fn::FieldOperation::Create(fn, {std::move(field)})};
} }
} // namespace blender::bke } // namespace blender::bke

View File

@ -15,6 +15,14 @@ namespace blender::array_utils {
* grain-size. * grain-size.
*/ */
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size = 4096); void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size = 4096);
template<typename T>
inline void copy(const VArray<T> &src, MutableSpan<T> dst, const int64_t grain_size = 4096)
{
BLI_assert(src.size() == dst.size());
threading::parallel_for(src.index_range(), grain_size, [&](const IndexRange range) {
src.materialize_to_uninitialized(range, dst);
});
}
/** /**
* Fill the destination span by copying all values from the `src` array. Threaded based on * Fill the destination span by copying all values from the `src` array. Threaded based on
@ -28,6 +36,7 @@ inline void copy(const Span<T> src, MutableSpan<T> dst, const int64_t grain_size
dst.slice(range).copy_from(src.slice(range)); dst.slice(range).copy_from(src.slice(range));
}); });
} }
/** /**
* Fill the destination span by copying masked values from the `src` array. Threaded based on * Fill the destination span by copying masked values from the `src` array. Threaded based on
* grain-size. * grain-size.

View File

@ -323,19 +323,18 @@ void BLI_path_sequence_encode(
/** /**
* Remove redundant characters from \a path and optionally make absolute. * Remove redundant characters from \a path and optionally make absolute.
* *
* \param relabase: The path this is relative to, or ignored when NULL.
* \param path: Can be any input, and this function converts it to a regular full path. * \param path: Can be any input, and this function converts it to a regular full path.
* Also removes garbage from directory paths, like `/../` or double slashes etc. * Also removes garbage from directory paths, like `/../` or double slashes etc.
* *
* \note \a path isn't protected for max string names. * \note \a path isn't protected for max string names.
*/ */
void BLI_path_normalize(const char *relabase, char *path) ATTR_NONNULL(2); void BLI_path_normalize(char *path) ATTR_NONNULL(1);
/** /**
* Cleanup file-path ensuring a trailing slash. * Cleanup file-path ensuring a trailing slash.
* *
* \note Same as #BLI_path_normalize but adds a trailing slash. * \note Same as #BLI_path_normalize but adds a trailing slash.
*/ */
void BLI_path_normalize_dir(const char *relabase, char *dir, size_t dir_maxlen) ATTR_NONNULL(2); void BLI_path_normalize_dir(char *dir, size_t dir_maxlen) ATTR_NONNULL(1);
/** /**
* Make given name safe to be used in paths. * Make given name safe to be used in paths.

View File

@ -112,21 +112,54 @@ void BLI_path_sequence_encode(
BLI_sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail); BLI_sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail);
} }
void BLI_path_normalize(const char *relabase, char *path) void BLI_path_normalize(char *path)
{ {
const char *path_orig = path;
int path_len;
ptrdiff_t a; ptrdiff_t a;
char *start, *eind; char *start, *eind;
if (relabase) {
BLI_path_abs(path, relabase); path_len = strlen(path);
if (path[0] == '/' && path[1] == '/') {
path = path + 2; /* Leave the initial `//` untouched. */
path_len -= 2;
/* Strip leading slashes, as they will interfere with the absolute/relative check
* (besides being redundant). */
int i = 0;
while (path[i] == SEP) {
i++;
}
if (i != 0) {
memmove(path, path + i, (path_len - i) + 1);
path_len -= i;
}
BLI_assert(path_len == strlen(path));
} }
else {
if (path[0] == '/' && path[1] == '/') { #ifdef WIN32
if (path[2] == '\0') { /* Skip to the first slash of the drive or UNC path,
return; /* Path is `//` - can't clean it. */ * so additional slashes are treated as doubles. */
} if (path_orig == path) {
path = path + 2; /* Leave the initial `//` untouched. */ int path_unc_len = BLI_path_unc_prefix_len(path);
if (path_unc_len) {
path_unc_len -= 1;
BLI_assert(path_unc_len > 0 && path[path_unc_len] == SEP);
path += path_unc_len;
path_len -= path_unc_len;
}
else if (isalpha(path[0]) && (path[1] == ':')) {
path += 2;
path_len -= 2;
} }
} }
#endif /* WIN32 */
/* Works on WIN32 as well, because the drive component is skipped. */
const bool is_relative = path[0] && (path[0] != SEP);
/* NOTE(@ideasman42): /* NOTE(@ideasman42):
* `memmove(start, eind, strlen(eind) + 1);` * `memmove(start, eind, strlen(eind) + 1);`
@ -135,58 +168,83 @@ void BLI_path_normalize(const char *relabase, char *path)
* except `strcpy` should not be used because there is overlap, * except `strcpy` should not be used because there is overlap,
* so use `memmove` 's slightly more obscure syntax. */ * so use `memmove` 's slightly more obscure syntax. */
#ifdef WIN32 /* Inline replacement:
* - `/./` -> `/`.
* - `//` -> `/`.
* Performed until no more replacements can be made. */
if (path_len > 1) {
for (int i = path_len - 1; i > 0; i--) {
/* Calculate the redundant slash span (if any). */
if (path[i] == SEP) {
const int i_end = i;
do {
/* Stepping over elements assumes 'i' references a separator. */
BLI_assert(path[i] == SEP);
if (path[i - 1] == SEP) {
i -= 1; /* Found `//`, replace with `/`. */
}
else if (i >= 2 && path[i - 1] == '.' && path[i - 2] == SEP) {
i -= 2; /* Found `/./`, replace with `/`. */
}
else {
break;
}
while ((start = strstr(path, "\\.\\"))) { } while (i > 0);
eind = start + strlen("\\.\\") - 1;
memmove(start, eind, strlen(eind) + 1);
}
/* Remove two consecutive backslashes, but skip the UNC prefix, if (i < i_end) {
* which needs to be preserved. */ memmove(path + i, path + i_end, (path_len - i_end) + 1);
while ((start = strstr(path + BLI_path_unc_prefix_len(path), "\\\\"))) { path_len -= i_end - i;
eind = start + strlen("\\\\") - 1; BLI_assert(strlen(path) == path_len);
memmove(start, eind, strlen(eind) + 1); }
}
while ((start = strstr(path, "\\..\\"))) {
eind = start + strlen("\\..\\") - 1;
a = start - path - 1;
while (a > 0) {
if (path[a] == '\\') {
break;
} }
a--;
}
if (a < 0) {
break;
}
else {
memmove(path + a, eind, strlen(eind) + 1);
} }
} }
#else /* Remove redundant `./` prefix, while it could be kept, it confuses the loop below. */
if (is_relative) {
while ((start = strstr(path, "/./"))) { if ((path_len > 2) && (path[0] == '.') && (path[1] == SEP)) {
eind = start + (3 - 1) /* `strlen("/./") - 1` */; memmove(path, path + 2, (path_len - 2) + 1);
memmove(start, eind, strlen(eind) + 1); path_len -= 2;
}
} }
while ((start = strstr(path, "//"))) { const ptrdiff_t a_start = is_relative ? 0 : 1;
eind = start + (2 - 1) /* `strlen("//") - 1` */; start = path;
memmove(start, eind, strlen(eind) + 1); while ((start = strstr(start, SEP_STR ".."))) {
} if (!ELEM(start[3], SEP, '\0')) {
start += 3;
continue;
}
while ((start = strstr(path, "/../"))) { a = (start - path) - 1;
a = start - path - 1; if (a >= a_start) {
if (a > 0) {
/* `<prefix>/<parent>/../<postfix> => <prefix>/<postfix>`. */ /* `<prefix>/<parent>/../<postfix> => <prefix>/<postfix>`. */
eind = start + (4 - 1) /* `strlen("/../") - 1` */; /* Strip "/.." and keep last "/". */ eind = start + (4 - 1) /* `strlen("/../") - 1` */; /* Strip "/.." and keep the char after. */
while (a > 0 && path[a] != '/') { /* Find start of `<parent>`. */ while (a > 0 && path[a] != SEP) { /* Find start of `<parent>`. */
a--; a--;
} }
memmove(path + a, eind, strlen(eind) + 1);
if (is_relative && (a == 0) && *eind) {
/* When the path does not start with a slash, don't copy the first `/` to the destination
* as it will make a relative path into an absolute path. */
eind += 1;
}
const size_t eind_len = path_len - (eind - path);
BLI_assert(eind_len == strlen(eind));
/* Only remove the parent if it's not also a `..`. */
if (is_relative && STRPREFIX(path + ((path[a] == SEP) ? a + 1 : a), ".." SEP_STR)) {
start += 3 /* `strlen("/..")` */;
}
else {
start = path + a;
BLI_assert(start < eind);
memmove(start, eind, eind_len + 1);
path_len -= (eind - start);
BLI_assert(strlen(path) == path_len);
BLI_assert(!is_relative || (path[0] != SEP));
}
} }
else { else {
/* Support for odd paths: eg `/../home/me` --> `/home/me` /* Support for odd paths: eg `/../home/me` --> `/home/me`
@ -198,21 +256,32 @@ void BLI_path_normalize(const char *relabase, char *path)
* which meant that the `/../home/me` example actually became `home/me`. * which meant that the `/../home/me` example actually became `home/me`.
* Using offset of 3 gives behavior consistent with the aforementioned * Using offset of 3 gives behavior consistent with the aforementioned
* Python routine. */ * Python routine. */
memmove(path, path + 3, strlen(path + 3) + 1); eind = start + 3;
const size_t eind_len = path_len - (eind - path);
memmove(start, eind, eind_len + 1);
path_len -= 3;
BLI_assert(strlen(path) == path_len);
BLI_assert(!is_relative || (path[0] != SEP));
} }
} }
#endif if (is_relative && path_len == 0 && (path == path_orig)) {
path[0] = '.';
path[1] = '\0';
path_len += 1;
}
BLI_assert(strlen(path) == path_len);
} }
void BLI_path_normalize_dir(const char *relabase, char *dir, size_t dir_maxlen) void BLI_path_normalize_dir(char *dir, size_t dir_maxlen)
{ {
/* Would just create an unexpected "/" path, just early exit entirely. */ /* Would just create an unexpected "/" path, just early exit entirely. */
if (dir[0] == '\0') { if (dir[0] == '\0') {
return; return;
} }
BLI_path_normalize(relabase, dir); BLI_path_normalize(dir);
BLI_path_slash_ensure(dir, dir_maxlen); BLI_path_slash_ensure(dir, dir_maxlen);
} }
@ -504,8 +573,8 @@ void BLI_path_rel(char *file, const char *relfile)
BLI_str_replace_char(file + BLI_path_unc_prefix_len(file), '\\', '/'); BLI_str_replace_char(file + BLI_path_unc_prefix_len(file), '\\', '/');
/* Remove `/./` which confuse the following slash counting. */ /* Remove `/./` which confuse the following slash counting. */
BLI_path_normalize(NULL, file); BLI_path_normalize(file);
BLI_path_normalize(NULL, temp); BLI_path_normalize(temp);
/* The last slash in the file indicates where the path part ends. */ /* The last slash in the file indicates where the path part ends. */
lslash = BLI_path_slash_rfind(temp); lslash = BLI_path_slash_rfind(temp);
@ -906,7 +975,7 @@ bool BLI_path_abs(char *path, const char *basepath)
BLI_strncpy(base, basepath, sizeof(base)); BLI_strncpy(base, basepath, sizeof(base));
/* File component is ignored, so don't bother with the trailing slash. */ /* File component is ignored, so don't bother with the trailing slash. */
BLI_path_normalize(NULL, base); BLI_path_normalize(base);
lslash = BLI_path_slash_rfind(base); lslash = BLI_path_slash_rfind(base);
BLI_str_replace_char(base + BLI_path_unc_prefix_len(base), '\\', '/'); BLI_str_replace_char(base + BLI_path_unc_prefix_len(base), '\\', '/');
@ -938,7 +1007,7 @@ bool BLI_path_abs(char *path, const char *basepath)
#endif #endif
/* Ensure this is after correcting for path switch. */ /* Ensure this is after correcting for path switch. */
BLI_path_normalize(NULL, path); BLI_path_normalize(path);
return wasrelative; return wasrelative;
} }
@ -1638,8 +1707,8 @@ bool BLI_path_contains(const char *container_path, const char *containee_path)
BLI_path_slash_native(container_native); BLI_path_slash_native(container_native);
BLI_path_slash_native(containee_native); BLI_path_slash_native(containee_native);
BLI_path_normalize(NULL, container_native); BLI_path_normalize(container_native);
BLI_path_normalize(NULL, containee_native); BLI_path_normalize(containee_native);
#ifdef WIN32 #ifdef WIN32
BLI_str_tolower_ascii(container_native, PATH_MAX); BLI_str_tolower_ascii(container_native, PATH_MAX);
@ -1742,8 +1811,8 @@ int BLI_path_cmp_normalized(const char *p1, const char *p2)
BLI_path_slash_native(norm_p1); BLI_path_slash_native(norm_p1);
BLI_path_slash_native(norm_p2); BLI_path_slash_native(norm_p2);
BLI_path_normalize(NULL, norm_p1); BLI_path_normalize(norm_p1);
BLI_path_normalize(NULL, norm_p2); BLI_path_normalize(norm_p2);
return BLI_path_cmp(norm_p1, norm_p2); return BLI_path_cmp(norm_p1, norm_p2);
} }

View File

@ -42,29 +42,41 @@ static char *str_replace_char_strdup(const char *str, char src, char dst)
/** \name Tests for: #BLI_path_normalize /** \name Tests for: #BLI_path_normalize
* \{ */ * \{ */
#define NORMALIZE_WITH_BASEDIR(input, input_base, output_expect) \ #define NORMALIZE(input, output_expect) \
{ \ { \
char path[FILE_MAX] = input; \ char path[FILE_MAX] = input; \
const char *input_base_test = input_base; \
if (SEP == '\\') { \ if (SEP == '\\') { \
str_replace_char_with_relative_exception(path, '/', '\\'); \ str_replace_char_with_relative_exception(path, '/', '\\'); \
input_base_test = str_replace_char_strdup(input_base_test, '/', '\\'); \
} \ } \
BLI_path_normalize(input_base_test, path); \ BLI_path_normalize(path); \
if (SEP == '\\') { \ if (SEP == '\\') { \
BLI_str_replace_char(path, '\\', '/'); \ BLI_str_replace_char(path, '\\', '/'); \
if (input_base_test) { \
free((void *)input_base_test); \
} \
} \ } \
EXPECT_STREQ(path, output_expect); \ EXPECT_STREQ(path, output_expect); \
} \ } \
((void)0) ((void)0)
#define NORMALIZE(input, output_expect) NORMALIZE_WITH_BASEDIR(input, nullptr, output_expect) /* #BLI_path_normalize: do nothing. */
TEST(path_util, Normalize_Nop)
{
NORMALIZE(".", ".");
NORMALIZE("./", "./");
NORMALIZE("/", "/");
NORMALIZE("//", "//");
NORMALIZE("//a", "//a");
}
TEST(path_util, Normalize_NopRelative)
{
NORMALIZE("..", "..");
NORMALIZE("../", "../");
NORMALIZE("../", "../");
NORMALIZE("../..", "../..");
NORMALIZE("../../", "../../");
}
/* #BLI_path_normalize: "/./" -> "/" */ /* #BLI_path_normalize: "/./" -> "/" */
TEST(path_util, Clean_Dot) TEST(path_util, Normalize_Dot)
{ {
NORMALIZE("/./", "/"); NORMALIZE("/./", "/");
NORMALIZE("/a/./b/./c/./", "/a/b/c/"); NORMALIZE("/a/./b/./c/./", "/a/b/c/");
@ -72,28 +84,68 @@ TEST(path_util, Clean_Dot)
NORMALIZE("/a/./././b/", "/a/b/"); NORMALIZE("/a/./././b/", "/a/b/");
} }
/* #BLI_path_normalize: complex "/./" -> "/", "//" -> "/", "./path/../" -> "./". */ /* #BLI_path_normalize: complex "/./" -> "/", "//" -> "/", "./path/../" -> "./". */
TEST(path_util, Clean_Complex) TEST(path_util, Normalize_Complex)
{ {
NORMALIZE("/a/./b/./c/./.././.././", "/a/"); NORMALIZE("/a/./b/./c/./.././.././", "/a/");
NORMALIZE("/a//.//b//.//c//.//..//.//..//.//", "/a/"); NORMALIZE("/a//.//b//.//c//.//..//.//..//.//", "/a/");
} }
/* #BLI_path_normalize: "//" -> "/" */ /* #BLI_path_normalize: "//" -> "/" */
TEST(path_util, Clean_DoubleSlash) TEST(path_util, Normalize_DoubleSlash)
{ {
NORMALIZE("//", "//"); /* Exception, double forward slash. */ NORMALIZE("//", "//"); /* Exception, double forward slash. */
NORMALIZE(".//", "./"); NORMALIZE(".//", "./");
NORMALIZE("a////", "a/"); NORMALIZE("a////", "a/");
NORMALIZE("./a////", "./a/"); NORMALIZE("./a////", "a/");
} }
/* #BLI_path_normalize: "foo/bar/../" -> "foo/" */ /* #BLI_path_normalize: "foo/bar/../" -> "foo/" */
TEST(path_util, Clean_Parent) TEST(path_util, Normalize_Parent)
{ {
NORMALIZE("/a/b/c/../../../", "/"); NORMALIZE("/a/b/c/../../../", "/");
NORMALIZE("/a/../a/b/../b/c/../c/", "/a/b/c/"); NORMALIZE("/a/../a/b/../b/c/../c/", "/a/b/c/");
NORMALIZE_WITH_BASEDIR("//../", "/a/b/c/", "/a/b/"); }
/* #BLI_path_normalize: with too many "/../", match Python's behavior. */
TEST(path_util, Normalize_UnbalancedAbsolute)
{
NORMALIZE("/../", "/");
NORMALIZE("/../a", "/a");
NORMALIZE("/a/b/c/../../../../../d", "/d");
NORMALIZE("/a/b/c/../../../../d", "/d");
NORMALIZE("/a/b/c/../../../d", "/d");
}
/* #BLI_path_normalize: with relative paths that result in leading "../". */
TEST(path_util, Normalize_UnbalancedRelative)
{
NORMALIZE("./a/b/c/../../../", ".");
NORMALIZE("a/b/c/../../../", ".");
NORMALIZE("//a/b/c/../../../", "//");
NORMALIZE("./a/../../../", "../../");
NORMALIZE("a/../../../", "../../");
NORMALIZE("///a/../../../", "//../../");
NORMALIZE("//./a/../../../", "//../../");
NORMALIZE("../a/../../../", "../../../");
NORMALIZE("a/b/../c/../../d/../../../e/../../../../f", "../../../../../f");
NORMALIZE(".../.../a/.../b/../c/../../d/../../../e/../../../.../../f", "../f");
}
TEST(path_util, Normalize_UnbalancedRelativeTrailing)
{
NORMALIZE("./a/b/c/../../..", ".");
NORMALIZE("a/b/c/../../..", ".");
NORMALIZE("//a/b/c/../../..", "//");
NORMALIZE("./a/../../..", "../..");
NORMALIZE("a/../../..", "../..");
NORMALIZE("///a/../../..", "//../..");
NORMALIZE("//./a/../../..", "//../..");
NORMALIZE("../a/../../..", "../../..");
} }
#undef NORMALIZE_WITH_BASEDIR
#undef NORMALIZE #undef NORMALIZE
/** \} */ /** \} */

View File

@ -516,7 +516,8 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab
char name1[FILE_MAX]; char name1[FILE_MAX];
BLI_strncpy(name1, filepath, sizeof(name1)); BLI_strncpy(name1, filepath, sizeof(name1));
BLI_path_normalize(relabase, name1); BLI_path_abs(name1, relabase);
BLI_path_normalize(name1);
// printf("blo_find_main: relabase %s\n", relabase); // printf("blo_find_main: relabase %s\n", relabase);
// printf("blo_find_main: original in %s\n", filepath); // printf("blo_find_main: original in %s\n", filepath);
@ -2695,7 +2696,8 @@ static void direct_link_library(FileData *fd, Library *lib, Main *main)
/* Make sure we have full path in lib->filepath_abs */ /* Make sure we have full path in lib->filepath_abs */
BLI_strncpy(lib->filepath_abs, lib->filepath, sizeof(lib->filepath)); BLI_strncpy(lib->filepath_abs, lib->filepath, sizeof(lib->filepath));
BLI_path_normalize(fd->relabase, lib->filepath_abs); BLI_path_abs(lib->filepath_abs, fd->relabase);
BLI_path_normalize(lib->filepath_abs);
// printf("direct_link_library: filepath %s\n", lib->filepath); // printf("direct_link_library: filepath %s\n", lib->filepath);
// printf("direct_link_library: filepath_abs %s\n", lib->filepath_abs); // printf("direct_link_library: filepath_abs %s\n", lib->filepath_abs);

View File

@ -4308,6 +4308,70 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
/* Rename Grease Pencil weight draw brush. */ /* Rename Grease Pencil weight draw brush. */
do_versions_rename_id(bmain, ID_BR, "Draw Weight", "Weight Draw"); do_versions_rename_id(bmain, ID_BR, "Draw Weight", "Weight Draw");
/* Identifier generation for simulation node sockets changed.
* Update identifiers so links are not removed during validation. */
if (!DNA_struct_elem_find(fd->filesdna, "NodeSimulationItem", "int", "identifier")) {
static auto set_socket_identifiers = [](bNode *node, const bNode *output_node, int extra_outputs) {
const NodeGeometrySimulationOutput *output_data = static_cast<const NodeGeometrySimulationOutput *>(output_node->storage);
/* Includes extension socket. */
BLI_assert(BLI_listbase_count(&node->inputs) == output_data->items_num + 1);
/* Includes extension socket. */
BLI_assert(BLI_listbase_count(&node->outputs) == output_data->items_num + 1 + extra_outputs);
int i;
LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->inputs, i) {
/* Skip extension socket. */
if (i >= output_data->items_num) {
break;
}
BLI_snprintf(sock->identifier, sizeof(sock->identifier), "Item_%d", output_data->items[i].identifier);
}
LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) {
const int item_i = i - extra_outputs;
/* Skip extra outputs. */
if (i < extra_outputs) {
continue;
}
/* Skip extension socket. */
if (item_i >= output_data->items_num) {
break;
}
BLI_snprintf(sock->identifier, sizeof(sock->identifier), "Item_%d", output_data->items[i - extra_outputs].identifier);
}
};
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type == NTREE_GEOMETRY) {
/* Initialize item identifiers. */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_SIMULATION_OUTPUT) {
NodeGeometrySimulationOutput *data = static_cast<NodeGeometrySimulationOutput *>(node->storage);
for (int i = 0; i < data->items_num; ++i) {
data->items[i].identifier = i;
}
data->next_identifier = data->items_num;
}
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_SIMULATION_INPUT) {
const NodeGeometrySimulationInput *input_data = static_cast<const NodeGeometrySimulationInput *>(node->storage);
LISTBASE_FOREACH (bNode *, output_node, &ntree->nodes) {
if (output_node->identifier == input_data->output_node_id) {
/* 'Delta Time' socket in input nodes */
set_socket_identifiers(node, output_node, 1);
break;
}
}
}
if (node->type == GEO_NODE_SIMULATION_OUTPUT) {
set_socket_identifiers(node, node, 0);
}
}
}
}
}
} }
/** /**

View File

@ -1474,13 +1474,13 @@ bool BLO_write_file(Main *mainvar,
/* Normalize the paths in case there is some subtle difference (so they can be compared). */ /* Normalize the paths in case there is some subtle difference (so they can be compared). */
if (relbase_valid) { if (relbase_valid) {
BLI_split_dir_part(mainvar->filepath, dir_src, sizeof(dir_src)); BLI_split_dir_part(mainvar->filepath, dir_src, sizeof(dir_src));
BLI_path_normalize(nullptr, dir_src); BLI_path_normalize(dir_src);
} }
else { else {
dir_src[0] = '\0'; dir_src[0] = '\0';
} }
BLI_split_dir_part(filepath, dir_dst, sizeof(dir_dst)); BLI_split_dir_part(filepath, dir_dst, sizeof(dir_dst));
BLI_path_normalize(nullptr, dir_dst); BLI_path_normalize(dir_dst);
/* Only for relative, not relative-all, as this means making existing paths relative. */ /* Only for relative, not relative-all, as this means making existing paths relative. */
if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) { if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {

View File

@ -73,6 +73,7 @@ void ED_init_node_socket_type_virtual(struct bNodeSocketType *stype);
void ED_node_sample_set(const float col[4]); void ED_node_sample_set(const float col[4]);
void ED_node_draw_snap( void ED_node_draw_snap(
struct View2D *v2d, const float cent[2], float size, NodeBorder border, unsigned int pos); struct View2D *v2d, const float cent[2], float size, NodeBorder border, unsigned int pos);
void ED_node_type_draw_color(const char *idname, float *r_color);
/* node_draw.cc */ /* node_draw.cc */

View File

@ -198,11 +198,12 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
} }
else if (file->redirection_path) { else if (file->redirection_path) {
BLI_strncpy(params->dir, file->redirection_path, sizeof(params->dir)); BLI_strncpy(params->dir, file->redirection_path, sizeof(params->dir));
BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir, sizeof(params->dir)); BLI_path_abs(params->dir, BKE_main_blendfile_path(bmain));
BLI_path_slash_ensure(params->dir, sizeof(params->dir)); BLI_path_normalize_dir(params->dir, sizeof(params->dir));
} }
else { else {
BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir, sizeof(params->dir)); BLI_path_abs(params->dir, BKE_main_blendfile_path(bmain));
BLI_path_normalize_dir(params->dir, sizeof(params->dir));
BLI_path_append_dir(params->dir, sizeof(params->dir), file->relpath); BLI_path_append_dir(params->dir, sizeof(params->dir), file->relpath);
} }
@ -1095,7 +1096,8 @@ static int bookmark_select_exec(bContext *C, wmOperator *op)
RNA_property_string_get(op->ptr, prop, entry); RNA_property_string_get(op->ptr, prop, entry);
BLI_strncpy(params->dir, entry, sizeof(params->dir)); BLI_strncpy(params->dir, entry, sizeof(params->dir));
BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir, sizeof(params->dir)); BLI_path_abs(params->dir, BKE_main_blendfile_path(bmain));
BLI_path_normalize_dir(params->dir, sizeof(params->dir));
ED_file_change_dir(C); ED_file_change_dir(C);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
@ -2058,7 +2060,8 @@ static bool file_execute(bContext *C, SpaceFile *sfile)
BLI_path_parent_dir(params->dir); BLI_path_parent_dir(params->dir);
} }
else { else {
BLI_path_normalize(BKE_main_blendfile_path(bmain), params->dir); BLI_path_abs(params->dir, BKE_main_blendfile_path(bmain));
BLI_path_normalize(params->dir);
BLI_path_append_dir(params->dir, sizeof(params->dir), file->relpath); BLI_path_append_dir(params->dir, sizeof(params->dir), file->relpath);
} }
ED_file_change_dir(C); ED_file_change_dir(C);
@ -2222,7 +2225,8 @@ static int file_parent_exec(bContext *C, wmOperator *UNUSED(unused))
if (params) { if (params) {
if (BLI_path_parent_dir(params->dir)) { if (BLI_path_parent_dir(params->dir)) {
BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir, sizeof(params->dir)); BLI_path_abs(params->dir, BKE_main_blendfile_path(bmain));
BLI_path_normalize_dir(params->dir, sizeof(params->dir));
ED_file_change_dir(C); ED_file_change_dir(C);
if (params->recursion_level > 1) { if (params->recursion_level > 1) {
/* Disable 'dirtree' recursion when going up in tree. */ /* Disable 'dirtree' recursion when going up in tree. */
@ -2805,7 +2809,8 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN
} }
} }
BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir, sizeof(params->dir)); BLI_path_abs(params->dir, BKE_main_blendfile_path(bmain));
BLI_path_normalize_dir(params->dir, sizeof(params->dir));
if (filelist_is_dir(sfile->files, params->dir)) { if (filelist_is_dir(sfile->files, params->dir)) {
if (!STREQ(params->dir, old_dir)) { /* Avoids flickering when nothing's changed. */ if (!STREQ(params->dir, old_dir)) { /* Avoids flickering when nothing's changed. */
@ -2892,7 +2897,8 @@ void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg
/* if directory, open it and empty filename field */ /* if directory, open it and empty filename field */
if (filelist_is_dir(sfile->files, filepath)) { if (filelist_is_dir(sfile->files, filepath)) {
BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), filepath, sizeof(filepath)); BLI_path_abs(filepath, BKE_main_blendfile_path(bmain));
BLI_path_normalize_dir(filepath, sizeof(filepath));
BLI_strncpy(params->dir, filepath, sizeof(params->dir)); BLI_strncpy(params->dir, filepath, sizeof(params->dir));
params->file[0] = '\0'; params->file[0] = '\0';
ED_file_change_dir(C); ED_file_change_dir(C);

View File

@ -1984,7 +1984,8 @@ void filelist_setdir(FileList *filelist, char *r_dir)
const bool allow_invalid = filelist->asset_library_ref != nullptr; const bool allow_invalid = filelist->asset_library_ref != nullptr;
BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA); BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA);
BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir, FILE_MAX_LIBEXTRA); BLI_path_abs(r_dir, BKE_main_blendfile_path_from_global());
BLI_path_normalize_dir(r_dir, FILE_MAX_LIBEXTRA);
const bool is_valid_path = filelist->check_dir_fn(filelist, r_dir, !allow_invalid); const bool is_valid_path = filelist->check_dir_fn(filelist, r_dir, !allow_invalid);
BLI_assert(is_valid_path || allow_invalid); BLI_assert(is_valid_path || allow_invalid);
UNUSED_VARS_NDEBUG(is_valid_path); UNUSED_VARS_NDEBUG(is_valid_path);
@ -2916,7 +2917,7 @@ struct TodoDir {
struct FileListReadJob { struct FileListReadJob {
ThreadMutex lock; ThreadMutex lock;
char main_name[FILE_MAX]; char main_filepath[FILE_MAX];
Main *current_main; Main *current_main;
FileList *filelist; FileList *filelist;
@ -2981,7 +2982,7 @@ static int filelist_readjob_list_dir(FileListReadJob *job_params,
ListBase *entries, ListBase *entries,
const char *filter_glob, const char *filter_glob,
const bool do_lib, const bool do_lib,
const char *main_name, const char *main_filepath,
const bool skip_currpar) const bool skip_currpar)
{ {
direntry *files; direntry *files;
@ -3045,7 +3046,7 @@ static int filelist_readjob_list_dir(FileListReadJob *job_params,
/* If we are considering .blend files as libraries, promote them to directory status. */ /* If we are considering .blend files as libraries, promote them to directory status. */
entry->typeflag = FILE_TYPE_BLENDER; entry->typeflag = FILE_TYPE_BLENDER;
/* prevent current file being used as acceptable dir */ /* prevent current file being used as acceptable dir */
if (BLI_path_cmp(main_name, target) != 0) { if (BLI_path_cmp(main_filepath, target) != 0) {
entry->typeflag |= FILE_TYPE_DIR; entry->typeflag |= FILE_TYPE_DIR;
} }
} }
@ -3586,7 +3587,8 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
BLI_strncpy(dir, filelist->filelist.root, sizeof(dir)); BLI_strncpy(dir, filelist->filelist.root, sizeof(dir));
BLI_strncpy(filter_glob, filelist->filter_data.filter_glob, sizeof(filter_glob)); BLI_strncpy(filter_glob, filelist->filter_data.filter_glob, sizeof(filter_glob));
BLI_path_normalize_dir(job_params->main_name, dir, sizeof(dir)); BLI_path_abs(dir, job_params->main_filepath);
BLI_path_normalize_dir(dir, sizeof(dir));
td_dir->dir = BLI_strdup(dir); td_dir->dir = BLI_strdup(dir);
/* Init the file indexer. */ /* Init the file indexer. */
@ -3617,7 +3619,8 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
* Note that in the end, this means we 'cache' valid relative subdir once here, * Note that in the end, this means we 'cache' valid relative subdir once here,
* this is actually better. */ * this is actually better. */
BLI_strncpy(rel_subdir, subdir, sizeof(rel_subdir)); BLI_strncpy(rel_subdir, subdir, sizeof(rel_subdir));
BLI_path_normalize_dir(root, rel_subdir, sizeof(rel_subdir)); BLI_path_abs(rel_subdir, root);
BLI_path_normalize_dir(rel_subdir, sizeof(rel_subdir));
BLI_path_rel(rel_subdir, root); BLI_path_rel(rel_subdir, root);
/* Update the current relative base path within the filelist root. */ /* Update the current relative base path within the filelist root. */
@ -3649,8 +3652,13 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
} }
if (!is_lib && BLI_is_dir(subdir)) { if (!is_lib && BLI_is_dir(subdir)) {
entries_num = filelist_readjob_list_dir( entries_num = filelist_readjob_list_dir(job_params,
job_params, subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar); subdir,
&entries,
filter_glob,
do_lib,
job_params->main_filepath,
skip_currpar);
} }
LISTBASE_FOREACH (FileListInternEntry *, entry, &entries) { LISTBASE_FOREACH (FileListInternEntry *, entry, &entries) {
@ -3663,7 +3671,8 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
/* We have a directory we want to list, add it to todo list! /* We have a directory we want to list, add it to todo list!
* Using #BLI_path_join works but isn't needed as `root` has a trailing slash. */ * Using #BLI_path_join works but isn't needed as `root` has a trailing slash. */
BLI_string_join(dir, sizeof(dir), root, entry->relpath); BLI_string_join(dir, sizeof(dir), root, entry->relpath);
BLI_path_normalize_dir(job_params->main_name, dir, sizeof(dir)); BLI_path_abs(dir, job_params->main_filepath);
BLI_path_normalize_dir(dir, sizeof(dir));
td_dir = static_cast<TodoDir *>(BLI_stack_push_r(todo_dirs)); td_dir = static_cast<TodoDir *>(BLI_stack_push_r(todo_dirs));
td_dir->level = recursion_level + 1; td_dir->level = recursion_level + 1;
td_dir->dir = BLI_strdup(dir); td_dir->dir = BLI_strdup(dir);
@ -4088,7 +4097,7 @@ void filelist_readjob_start(FileList *filelist, const int space_notifier, const
flrj = MEM_cnew<FileListReadJob>(__func__); flrj = MEM_cnew<FileListReadJob>(__func__);
flrj->filelist = filelist; flrj->filelist = filelist;
flrj->current_main = bmain; flrj->current_main = bmain;
BLI_strncpy(flrj->main_name, BKE_main_blendfile_path(bmain), sizeof(flrj->main_name)); BLI_strncpy(flrj->main_filepath, BKE_main_blendfile_path(bmain), sizeof(flrj->main_filepath));
if ((filelist->flags & FL_FORCE_RESET_MAIN_FILES) && !(filelist->flags & FL_FORCE_RESET)) { if ((filelist->flags & FL_FORCE_RESET_MAIN_FILES) && !(filelist->flags & FL_FORCE_RESET)) {
flrj->only_main_data = true; flrj->only_main_data = true;
} }

View File

@ -203,8 +203,8 @@ static FileSelectParams *fileselect_ensure_updated_file_params(SpaceFile *sfile)
} }
if (params->dir[0]) { if (params->dir[0]) {
BLI_path_normalize_dir(blendfile_path, params->dir, sizeof(params->dir));
BLI_path_abs(params->dir, blendfile_path); BLI_path_abs(params->dir, blendfile_path);
BLI_path_normalize_dir(params->dir, sizeof(params->dir));
} }
params->flag = 0; params->flag = 0;

View File

@ -1497,6 +1497,23 @@ void ED_init_node_socket_type_virtual(bNodeSocketType *stype)
stype->draw_color = node_socket_virtual_draw_color; stype->draw_color = node_socket_virtual_draw_color;
} }
void ED_node_type_draw_color(const char *idname, float *r_color)
{
using namespace blender::ed::space_node;
const bNodeSocketType *typeinfo = nodeSocketTypeFind(idname);
if (!typeinfo || typeinfo->type == SOCK_CUSTOM) {
r_color[0] = 0.0f;
r_color[1] = 0.0f;
r_color[2] = 0.0f;
r_color[3] = 0.0f;
return;
}
BLI_assert(typeinfo->type < ARRAY_SIZE(std_node_socket_colors));
copy_v4_v4(r_color, std_node_socket_colors[typeinfo->type]);
}
namespace blender::ed::space_node { namespace blender::ed::space_node {
/* ************** Generic drawing ************** */ /* ************** Generic drawing ************** */

View File

@ -571,7 +571,7 @@ static Mesh *subdivide_edit_mesh(const Object *object,
mesh_settings.resolution = (1 << smd->levels) + 1; mesh_settings.resolution = (1 << smd->levels) + 1;
mesh_settings.use_optimal_display = (smd->flags & eSubsurfModifierFlag_ControlEdges); mesh_settings.use_optimal_display = (smd->flags & eSubsurfModifierFlag_ControlEdges);
Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &settings, me_from_em); Subdiv *subdiv = BKE_subdiv_new_from_mesh(&settings, me_from_em);
Mesh *result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, me_from_em); Mesh *result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, me_from_em);
BKE_id_free(nullptr, me_from_em); BKE_id_free(nullptr, me_from_em);
BKE_subdiv_free(subdiv); BKE_subdiv_free(subdiv);

View File

@ -331,7 +331,7 @@ int Controller::LoadMesh(Render *re, ViewLayer *view_layer, Depsgraph *depsgraph
soc string basename((const char *)qfi.fileName().toAscii().data()); soc string basename((const char *)qfi.fileName().toAscii().data());
char cleaned[FILE_MAX]; char cleaned[FILE_MAX];
BLI_strncpy(cleaned, iFileName, FILE_MAX); BLI_strncpy(cleaned, iFileName, FILE_MAX);
BLI_path_normalize(NULL, cleaned); BLI_path_normalize(cleaned);
string basename = string(cleaned); string basename = string(cleaned);
#endif #endif

View File

@ -32,7 +32,7 @@ void getPathName(const string &path, const string &base, vector<string> &pathnam
dir = path.substr(pos, sep - pos); dir = path.substr(pos, sep - pos);
BLI_strncpy(cleaned, dir.c_str(), FILE_MAX); BLI_strncpy(cleaned, dir.c_str(), FILE_MAX);
BLI_path_normalize(nullptr, cleaned); BLI_path_normalize(cleaned);
res = string(cleaned); res = string(cleaned);
if (!base.empty()) { if (!base.empty()) {

View File

@ -522,7 +522,7 @@ Field<bool> invert_boolean_field(const Field<bool> &field)
{ {
static auto not_fn = mf::build::SI1_SO<bool, bool>( static auto not_fn = mf::build::SI1_SO<bool, bool>(
"Not", [](bool a) { return !a; }, mf::build::exec_presets::AllSpanOrSingle()); "Not", [](bool a) { return !a; }, mf::build::exec_presets::AllSpanOrSingle());
auto not_op = std::make_shared<FieldOperation>(FieldOperation(not_fn, {field})); auto not_op = FieldOperation::Create(not_fn, {field});
return Field<bool>(not_op); return Field<bool>(not_op);
} }

View File

@ -11,10 +11,8 @@ namespace blender::fn::tests {
TEST(field, ConstantFunction) TEST(field, ConstantFunction)
{ {
/* TODO: Figure out how to not use another "FieldOperation(" inside of std::make_shared. */ GField constant_field{
GField constant_field{std::make_shared<FieldOperation>( FieldOperation::Create(std::make_unique<mf::CustomMF_Constant<int>>(10), {}), 0};
FieldOperation(std::make_unique<mf::CustomMF_Constant<int>>(10), {})),
0};
Array<int> result(4); Array<int> result(4);
@ -103,8 +101,7 @@ TEST(field, InputAndFunction)
GField index_field{std::make_shared<IndexFieldInput>()}; GField index_field{std::make_shared<IndexFieldInput>()};
auto add_fn = mf::build::SI2_SO<int, int, int>("add", [](int a, int b) { return a + b; }); auto add_fn = mf::build::SI2_SO<int, int, int>("add", [](int a, int b) { return a + b; });
GField output_field{ GField output_field{FieldOperation::Create(add_fn, {index_field, index_field}), 0};
std::make_shared<FieldOperation>(FieldOperation(add_fn, {index_field, index_field})), 0};
Array<int> result(10); Array<int> result(10);
@ -126,11 +123,10 @@ TEST(field, TwoFunctions)
GField index_field{std::make_shared<IndexFieldInput>()}; GField index_field{std::make_shared<IndexFieldInput>()};
auto add_fn = mf::build::SI2_SO<int, int, int>("add", [](int a, int b) { return a + b; }); auto add_fn = mf::build::SI2_SO<int, int, int>("add", [](int a, int b) { return a + b; });
GField add_field{ GField add_field{FieldOperation::Create(add_fn, {index_field, index_field}), 0};
std::make_shared<FieldOperation>(FieldOperation(add_fn, {index_field, index_field})), 0};
auto add_10_fn = mf::build::SI1_SO<int, int>("add_10", [](int a) { return a + 10; }); auto add_10_fn = mf::build::SI1_SO<int, int>("add_10", [](int a) { return a + 10; });
GField result_field{std::make_shared<FieldOperation>(FieldOperation(add_10_fn, {add_field})), 0}; GField result_field{FieldOperation::Create(add_10_fn, {add_field}), 0};
Array<int> result(10); Array<int> result(10);
@ -181,8 +177,8 @@ TEST(field, FunctionTwoOutputs)
GField index_field_1{std::make_shared<IndexFieldInput>()}; GField index_field_1{std::make_shared<IndexFieldInput>()};
GField index_field_2{std::make_shared<IndexFieldInput>()}; GField index_field_2{std::make_shared<IndexFieldInput>()};
std::shared_ptr<FieldOperation> fn = std::make_shared<FieldOperation>( std::shared_ptr<FieldOperation> fn = FieldOperation::Create(
FieldOperation(std::make_unique<TwoOutputFunction>(), {index_field_1, index_field_2})); std::make_unique<TwoOutputFunction>(), {index_field_1, index_field_2});
GField result_field_1{fn, 0}; GField result_field_1{fn, 0};
GField result_field_2{fn, 1}; GField result_field_2{fn, 1};
@ -212,8 +208,8 @@ TEST(field, TwoFunctionsTwoOutputs)
{ {
GField index_field{std::make_shared<IndexFieldInput>()}; GField index_field{std::make_shared<IndexFieldInput>()};
std::shared_ptr<FieldOperation> fn = std::make_shared<FieldOperation>( std::shared_ptr<FieldOperation> fn = FieldOperation::Create(
FieldOperation(std::make_unique<TwoOutputFunction>(), {index_field, index_field})); std::make_unique<TwoOutputFunction>(), {index_field, index_field});
Array<int64_t> mask_indices = {2, 4, 6, 8}; Array<int64_t> mask_indices = {2, 4, 6, 8};
IndexMask mask = mask_indices.as_span(); IndexMask mask = mask_indices.as_span();
@ -222,8 +218,7 @@ TEST(field, TwoFunctionsTwoOutputs)
Field<int> intermediate_field{fn, 1}; Field<int> intermediate_field{fn, 1};
auto add_10_fn = mf::build::SI1_SO<int, int>("add_10", [](int a) { return a + 10; }); auto add_10_fn = mf::build::SI1_SO<int, int>("add_10", [](int a) { return a + 10; });
Field<int> result_field_2{ Field<int> result_field_2{FieldOperation::Create(add_10_fn, {intermediate_field}), 0};
std::make_shared<FieldOperation>(FieldOperation(add_10_fn, {intermediate_field})), 0};
FieldContext field_context; FieldContext field_context;
FieldEvaluator field_evaluator{field_context, &mask}; FieldEvaluator field_evaluator{field_context, &mask};
@ -245,8 +240,8 @@ TEST(field, TwoFunctionsTwoOutputs)
TEST(field, SameFieldTwice) TEST(field, SameFieldTwice)
{ {
GField constant_field{ GField constant_field{FieldOperation::Create(std::make_unique<mf::CustomMF_Constant<int>>(10)),
std::make_shared<FieldOperation>(std::make_unique<mf::CustomMF_Constant<int>>(10)), 0}; 0};
FieldContext field_context; FieldContext field_context;
IndexMask mask{IndexRange(2)}; IndexMask mask{IndexRange(2)};
@ -266,7 +261,7 @@ TEST(field, SameFieldTwice)
TEST(field, IgnoredOutput) TEST(field, IgnoredOutput)
{ {
static mf::tests::OptionalOutputsFunction fn; static mf::tests::OptionalOutputsFunction fn;
Field<int> field{std::make_shared<FieldOperation>(fn), 0}; Field<int> field{FieldOperation::Create(fn), 0};
FieldContext field_context; FieldContext field_context;
FieldEvaluator field_evaluator{field_context, 10}; FieldEvaluator field_evaluator{field_context, 10};

View File

@ -21,10 +21,7 @@ static fn::Field<int> get_count_input_max_one(const fn::Field<int> &count_field)
"Clamp Above One", "Clamp Above One",
[](int value) { return std::max(1, value); }, [](int value) { return std::max(1, value); },
mf::build::exec_presets::AllSpanOrSingle()); mf::build::exec_presets::AllSpanOrSingle());
auto clamp_op = std::make_shared<fn::FieldOperation>( return fn::Field<int>(fn::FieldOperation::Create(max_one_fn, {count_field}));
fn::FieldOperation(max_one_fn, {count_field}));
return fn::Field<int>(std::move(clamp_op));
} }
static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length_field) static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length_field)
@ -39,9 +36,9 @@ static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length
}, },
mf::build::exec_presets::AllSpanOrSingle()); mf::build::exec_presets::AllSpanOrSingle());
auto get_count_op = std::make_shared<fn::FieldOperation>(fn::FieldOperation( auto get_count_op = fn::FieldOperation::Create(
get_count_fn, get_count_fn,
{fn::Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), length_field})); {fn::Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), length_field});
return fn::Field<int>(std::move(get_count_op)); return fn::Field<int>(std::move(get_count_op));
} }

View File

@ -93,7 +93,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies)
/* make absolute source path */ /* make absolute source path */
BLI_strncpy(source_path, image->filepath, sizeof(source_path)); BLI_strncpy(source_path, image->filepath, sizeof(source_path));
BLI_path_abs(source_path, ID_BLEND_PATH_FROM_GLOBAL(&image->id)); BLI_path_abs(source_path, ID_BLEND_PATH_FROM_GLOBAL(&image->id));
BLI_path_normalize(nullptr, source_path); BLI_path_normalize(source_path);
if (use_copies) { if (use_copies) {

View File

@ -16,7 +16,7 @@ std::string path_reference(StringRefNull filepath,
char filepath_abs[PATH_MAX]; char filepath_abs[PATH_MAX];
BLI_strncpy(filepath_abs, filepath.c_str(), PATH_MAX); BLI_strncpy(filepath_abs, filepath.c_str(), PATH_MAX);
BLI_path_abs(filepath_abs, base_src.c_str()); BLI_path_abs(filepath_abs, base_src.c_str());
BLI_path_normalize(nullptr, filepath_abs); BLI_path_normalize(filepath_abs);
/* Figure out final mode to be used. */ /* Figure out final mode to be used. */
if (mode == PATH_REFERENCE_MATCH) { if (mode == PATH_REFERENCE_MATCH) {

View File

@ -82,7 +82,7 @@ static std::string copy_asset_to_directory(const char *src_path,
char dest_file_path[FILE_MAX]; char dest_file_path[FILE_MAX];
BLI_path_join(dest_file_path, sizeof(dest_file_path), dest_dir_path, base_name.c_str()); BLI_path_join(dest_file_path, sizeof(dest_file_path), dest_dir_path, base_name.c_str());
BLI_path_normalize(NULL, dest_file_path); BLI_path_normalize(dest_file_path);
if (name_collision_mode == USD_TEX_NAME_COLLISION_USE_EXISTING && BLI_is_file(dest_file_path)) { if (name_collision_mode == USD_TEX_NAME_COLLISION_USE_EXISTING && BLI_is_file(dest_file_path)) {
return dest_file_path; return dest_file_path;
@ -278,7 +278,8 @@ std::string import_asset(const char *src,
} }
} }
BLI_path_normalize(basepath, dest_dir_path); BLI_path_abs(dest_dir_path, basepath);
BLI_path_normalize(dest_dir_path);
if (!BLI_dir_create_recursive(dest_dir_path)) { if (!BLI_dir_create_recursive(dest_dir_path)) {
WM_reportf( WM_reportf(

View File

@ -444,7 +444,7 @@ static void get_absolute_path(Image *ima, char *r_path)
/* Make absolute source path. */ /* Make absolute source path. */
BLI_strncpy(r_path, ima->filepath, FILE_MAX); BLI_strncpy(r_path, ima->filepath, FILE_MAX);
BLI_path_abs(r_path, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); BLI_path_abs(r_path, ID_BLEND_PATH_FROM_GLOBAL(&ima->id));
BLI_path_normalize(nullptr, r_path); BLI_path_normalize(r_path);
} }
static pxr::TfToken get_node_tex_image_color_space(bNode *node) static pxr::TfToken get_node_tex_image_color_space(bNode *node)

View File

@ -666,7 +666,7 @@ void MTLWriter::write_materials(const char *blen_filepath,
char blen_filedir[PATH_MAX]; char blen_filedir[PATH_MAX];
BLI_split_dir_part(blen_filepath, blen_filedir, PATH_MAX); BLI_split_dir_part(blen_filepath, blen_filedir, PATH_MAX);
BLI_path_slash_native(blen_filedir); BLI_path_slash_native(blen_filedir);
BLI_path_normalize(nullptr, blen_filedir); BLI_path_normalize(blen_filedir);
std::sort(mtlmaterials_.begin(), std::sort(mtlmaterials_.begin(),
mtlmaterials_.end(), mtlmaterials_.end(),

View File

@ -292,7 +292,7 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co
BLI_strncpy(dest_dir, export_params.file_base_for_tests, PATH_MAX); BLI_strncpy(dest_dir, export_params.file_base_for_tests, PATH_MAX);
} }
BLI_path_slash_native(dest_dir); BLI_path_slash_native(dest_dir);
BLI_path_normalize(nullptr, dest_dir); BLI_path_normalize(dest_dir);
mtl_writer->write_materials(export_params.blen_filepath, mtl_writer->write_materials(export_params.blen_filepath,
export_params.path_mode, export_params.path_mode,
dest_dir, dest_dir,

View File

@ -16,6 +16,8 @@
#ifdef __cplusplus #ifdef __cplusplus
namespace blender { namespace blender {
template<typename T> class Span; template<typename T> class Span;
template<typename T> class MutableSpan;
class IndexRange;
class StringRef; class StringRef;
class StringRefNull; class StringRefNull;
} // namespace blender } // namespace blender
@ -1600,7 +1602,9 @@ typedef struct NodeSimulationItem {
/* #eNodeSocketDatatype. */ /* #eNodeSocketDatatype. */
/* TODO: Use a different enum instead to support Byte colors, etc. */ /* TODO: Use a different enum instead to support Byte colors, etc. */
short socket_type; short socket_type;
char _pad[6]; short _pad;
/* Generates unique identifier for sockets. */
int identifier;
} NodeSimulationItem; } NodeSimulationItem;
typedef struct NodeGeometrySimulationInput { typedef struct NodeGeometrySimulationInput {
@ -1610,7 +1614,16 @@ typedef struct NodeGeometrySimulationInput {
typedef struct NodeGeometrySimulationOutput { typedef struct NodeGeometrySimulationOutput {
NodeSimulationItem *items; NodeSimulationItem *items;
int items_num; int items_num;
char _pad[4]; int active_index;
/* Number to give unique IDs to state items. */
int next_identifier;
int _pad;
#ifdef __cplusplus
blender::Span<NodeSimulationItem> items_span() const;
blender::MutableSpan<NodeSimulationItem> items_span_for_write();
blender::IndexRange items_range() const;
#endif
} NodeGeometrySimulationOutput; } NodeGeometrySimulationOutput;
typedef struct NodeGeometryDistributePointsInVolume { typedef struct NodeGeometryDistributePointsInVolume {

View File

@ -52,6 +52,8 @@
#include "RE_texture.h" #include "RE_texture.h"
#include "NOD_composite.h" #include "NOD_composite.h"
#include "NOD_geometry.h"
#include "NOD_socket.h"
#include "DEG_depsgraph.h" #include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h" #include "DEG_depsgraph_query.h"
@ -4087,6 +4089,60 @@ static void rna_NodeCryptomatte_update_remove(Main *bmain, Scene *scene, Pointer
rna_Node_update(bmain, scene, ptr); rna_Node_update(bmain, scene, ptr);
} }
static void rna_SimulationStateItem_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
NodeSimulationItem *item = (NodeSimulationItem *)ptr->data;
bNode *node = NOD_geometry_simulation_output_find_node_by_item(ntree, item);
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
}
static bool rna_SimulationStateItem_socket_type_supported(const EnumPropertyItem *item)
{
return NOD_geometry_simulation_output_item_socket_type_supported((eNodeSocketDatatype)item->value);
}
static const EnumPropertyItem *rna_SimulationStateItem_socket_type_itemf(bContext *UNUSED(C),
PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
*r_free = true;
return itemf_function_check(node_socket_data_type_items, rna_SimulationStateItem_socket_type_supported);
}
static void rna_SimulationStateItem_name_set(PointerRNA *ptr, const char *value)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
NodeSimulationItem *item = (NodeSimulationItem *)ptr->data;
bNode *node = NOD_geometry_simulation_output_find_node_by_item(ntree, item);
NodeGeometrySimulationOutput *sim = (NodeGeometrySimulationOutput *)node->storage;
const char *defname = nodeStaticSocketLabel(item->socket_type, 0);
NOD_geometry_simulation_output_item_set_unique_name(sim, item, value, defname);
}
static void rna_SimulationStateItem_color_get(PointerRNA *ptr, float *values)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
NodeSimulationItem *item = (NodeSimulationItem *)ptr->data;
const char *socket_type_idname = nodeStaticSocketType(item->socket_type, 0);
node_type_draw_color(socket_type_idname, values);
}
static PointerRNA rna_NodeGeometrySimulationInput_paired_output_get(PointerRNA *ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNode *node = (bNode *)ptr->data;
bNode *output_node = NOD_geometry_simulation_input_get_paired_output(ntree, node);
PointerRNA r_ptr;
RNA_pointer_create(&ntree->id, &RNA_Node, output_node, &r_ptr);
return r_ptr;
}
static bool rna_GeometryNodeSimulationInput_pair_with_output( static bool rna_GeometryNodeSimulationInput_pair_with_output(
ID *id, bNode *node, bContext *C, ReportList *reports, bNode *output_node) ID *id, bNode *node, bContext *C, ReportList *reports, bNode *output_node)
{ {
@ -4108,6 +4164,100 @@ static bool rna_GeometryNodeSimulationInput_pair_with_output(
return true; return true;
} }
static NodeSimulationItem *rna_NodeGeometrySimulationOutput_items_new(
ID *id,
bNode *node,
Main *bmain,
ReportList *reports,
int socket_type,
const char *name)
{
NodeGeometrySimulationOutput *sim = (NodeGeometrySimulationOutput *)node->storage;
NodeSimulationItem *item = NOD_geometry_simulation_output_add_item(sim, (short)socket_type, name);
if (item == NULL) {
BKE_report(reports, RPT_ERROR, "Unable to create socket");
}
else {
bNodeTree *ntree = (bNodeTree *)id;
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
return item;
}
static void rna_NodeGeometrySimulationOutput_items_remove(ID *id,
bNode *node,
Main *bmain,
ReportList *reports,
NodeSimulationItem *item)
{
NodeGeometrySimulationOutput *sim = (NodeGeometrySimulationOutput *)node->storage;
if (!NOD_geometry_simulation_output_contains_item(sim, item)) {
BKE_reportf(reports, RPT_ERROR, "Unable to locate item '%s' in node", item->name);
}
else {
NOD_geometry_simulation_output_remove_item(sim, item);
bNodeTree *ntree = (bNodeTree *)id;
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
}
static void rna_NodeGeometrySimulationOutput_items_clear(ID *id,
bNode *node,
Main *bmain)
{
NodeGeometrySimulationOutput *sim = (NodeGeometrySimulationOutput *)node->storage;
NOD_geometry_simulation_output_clear_items(sim);
bNodeTree *ntree = (bNodeTree *)id;
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
static void rna_NodeGeometrySimulationOutput_items_move(ID *id,
bNode *node,
Main *bmain,
int from_index,
int to_index)
{
NodeGeometrySimulationOutput *sim = (NodeGeometrySimulationOutput *)node->storage;
if (from_index < 0 || from_index >= sim->items_num || to_index < 0 || to_index >= sim->items_num) {
return;
}
NOD_geometry_simulation_output_move_item(sim, from_index, to_index);
bNodeTree *ntree = (bNodeTree *)id;
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
static PointerRNA rna_NodeGeometrySimulationOutput_active_item_get(PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
NodeGeometrySimulationOutput *sim = (NodeGeometrySimulationOutput *)node->storage;
NodeSimulationItem *item = NOD_geometry_simulation_output_get_active_item(sim);
PointerRNA r_ptr;
RNA_pointer_create(ptr->owner_id, &RNA_SimulationStateItem, item, &r_ptr);
return r_ptr;
}
static void rna_NodeGeometrySimulationOutput_active_item_set(PointerRNA *ptr, PointerRNA value)
{
bNode *node = (bNode *)ptr->data;
NodeGeometrySimulationOutput *sim = (NodeGeometrySimulationOutput *)node->storage;
NOD_geometry_simulation_output_set_active_item(sim, (NodeSimulationItem *)value.data);
}
/* ******** Node Socket Types ******** */ /* ******** Node Socket Types ******** */
static PointerRNA rna_NodeOutputFile_slot_layer_get(CollectionPropertyIterator *iter) static PointerRNA rna_NodeOutputFile_slot_layer_get(CollectionPropertyIterator *iter)
@ -9759,9 +9909,19 @@ static void def_geo_set_curve_normal(StructRNA *srna)
static void def_geo_simulation_input(StructRNA *srna) static void def_geo_simulation_input(StructRNA *srna)
{ {
PropertyRNA *prop;
FunctionRNA *func; FunctionRNA *func;
PropertyRNA *parm; PropertyRNA *parm;
RNA_def_struct_sdna_from(srna, "NodeGeometrySimulationInput", "storage");
prop = RNA_def_property(srna, "paired_output", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Node");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_pointer_funcs(
prop, "rna_NodeGeometrySimulationInput_paired_output_get", NULL, NULL, NULL);
RNA_def_property_ui_text(prop, "Paired Output", "Simulation output node that this input node is paired with");
func = RNA_def_function( func = RNA_def_function(
srna, "pair_with_output", "rna_GeometryNodeSimulationInput_pair_with_output"); srna, "pair_with_output", "rna_GeometryNodeSimulationInput_pair_with_output");
RNA_def_function_ui_description(func, "Pair a simulation input node with an output node."); RNA_def_function_ui_description(func, "Pair a simulation input node with an output node.");
@ -9777,16 +9937,71 @@ static void def_geo_simulation_input(StructRNA *srna)
static void rna_def_simulation_state_item(BlenderRNA *brna) static void rna_def_simulation_state_item(BlenderRNA *brna)
{ {
StructRNA *srna = RNA_def_struct(brna, "SimulationStateItem", NULL);
RNA_def_struct_ui_text(srna, "Simulation Sate Item", "");
RNA_def_struct_sdna(srna, "NodeSimulationItem");
PropertyRNA *prop; PropertyRNA *prop;
StructRNA *srna = RNA_def_struct(brna, "SimulationStateItem", NULL);
RNA_def_struct_ui_text(srna, "Simulation Item", "");
RNA_def_struct_sdna(srna, "NodeSimulationItem");
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_SimulationStateItem_name_set");
RNA_def_property_ui_text(prop, "Name", ""); RNA_def_property_ui_text(prop, "Name", "");
RNA_def_struct_name_property(srna, prop); RNA_def_struct_name_property(srna, prop);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocket_update"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_SimulationStateItem_update");
prop = RNA_def_property(srna, "socket_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, node_socket_data_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_SimulationStateItem_socket_type_itemf");
RNA_def_property_ui_text(prop, "Socket Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_SimulationStateItem_update");
prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_array(prop, 4);
RNA_def_property_float_funcs(prop, "rna_SimulationStateItem_color_get", NULL, NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Color", "Socket color");
}
static void rna_def_geo_simulation_output_items(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *parm;
FunctionRNA *func;
srna = RNA_def_struct(brna, "NodeGeometrySimulationOutputItems", NULL);
RNA_def_struct_sdna(srna, "bNode");
RNA_def_struct_ui_text(srna, "Items", "Collection of simulation items");
func = RNA_def_function(srna, "new", "rna_NodeGeometrySimulationOutput_items_new");
RNA_def_function_ui_description(func, "Add a item to this simulation zone");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_enum(func, "socket_type", node_socket_data_type_items, SOCK_GEOMETRY, "Socket Type", "Socket type of the item");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/* return value */
parm = RNA_def_pointer(func, "item", "SimulationStateItem", "Item", "New item");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove", "rna_NodeGeometrySimulationOutput_items_remove");
RNA_def_function_ui_description(func, "Remove an item from this simulation zone");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_pointer(func, "item", "SimulationStateItem", "Item", "The item to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
func = RNA_def_function(srna, "clear", "rna_NodeGeometrySimulationOutput_items_clear");
RNA_def_function_ui_description(func, "Remove all items from this simulation zone");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
func = RNA_def_function(srna, "move", "rna_NodeGeometrySimulationOutput_items_move");
RNA_def_function_ui_description(func, "Move an item to another position");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
parm = RNA_def_int(
func, "from_index", -1, 0, INT_MAX, "From Index", "Index of the item to move", 0, 10000);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_int(
func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the item", 0, 10000);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
} }
static void def_geo_simulation_output(StructRNA *srna) static void def_geo_simulation_output(StructRNA *srna)
@ -9798,7 +10013,24 @@ static void def_geo_simulation_output(StructRNA *srna)
prop = RNA_def_property(srna, "state_items", PROP_COLLECTION, PROP_NONE); prop = RNA_def_property(srna, "state_items", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "items", "items_num"); RNA_def_property_collection_sdna(prop, NULL, "items", "items_num");
RNA_def_property_struct_type(prop, "SimulationStateItem"); RNA_def_property_struct_type(prop, "SimulationStateItem");
RNA_def_property_ui_text(prop, "Inputs", ""); RNA_def_property_ui_text(prop, "Items", "");
RNA_def_property_srna(prop, "NodeGeometrySimulationOutputItems");
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "active_index");
RNA_def_property_ui_text(prop, "Active Item Index", "Index of the active item");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_NODE, NULL);
prop = RNA_def_property(srna, "active_item", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "SimulationStateItem");
RNA_def_property_pointer_funcs(prop,
"rna_NodeGeometrySimulationOutput_active_item_get",
"rna_NodeGeometrySimulationOutput_active_item_set",
NULL,
NULL);
RNA_def_property_ui_text(prop, "Active Item Index", "Index of the active item");
RNA_def_property_update(prop, NC_NODE, NULL);
} }
static void def_geo_curve_handle_type_selection(StructRNA *srna) static void def_geo_curve_handle_type_selection(StructRNA *srna)
@ -13222,6 +13454,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
/* special socket types */ /* special socket types */
rna_def_cmp_output_file_slot_file(brna); rna_def_cmp_output_file_slot_file(brna);
rna_def_cmp_output_file_slot_layer(brna); rna_def_cmp_output_file_slot_layer(brna);
rna_def_geo_simulation_output_items(brna);
rna_def_node_instance_hash(brna); rna_def_node_instance_hash(brna);
} }

View File

@ -13,16 +13,70 @@ extern struct bNodeTreeType *ntreeType_Geometry;
void register_node_type_geo_custom_group(bNodeType *ntype); void register_node_type_geo_custom_group(bNodeType *ntype);
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name Simulation Node API /** \name Simulation Input Node
* \{ */ * \{ */
struct bNode *NOD_geometry_simulation_input_get_paired_output(
struct bNodeTree *node_tree, const struct bNode *simulation_input_node);
/** /**
* Pair a simulation input node with an output node. * Pair a simulation input node with an output node.
* \return True if pairing the node was successful. * \return True if pairing the node was successful.
*/ */
bool NOD_geometry_simulation_input_pair_with_output(const struct bNodeTree *node_tree, bool NOD_geometry_simulation_input_pair_with_output(const struct bNodeTree *node_tree,
struct bNode *sim_input_node, struct bNode *simulation_input_node,
const struct bNode *sim_output_node); const struct bNode *simulation_output_node);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Simulation Output Node
* \{ */
bool NOD_geometry_simulation_output_item_socket_type_supported(eNodeSocketDatatype socket_type);
/**
* Set a unique item name.
* \return True if the unique name differs from the original name.
*/
bool NOD_geometry_simulation_output_item_set_unique_name(struct NodeGeometrySimulationOutput *sim,
struct NodeSimulationItem *item,
const char *name,
const char *defname);
/**
* Find the node owning this simulation state item.
*/
bNode *NOD_geometry_simulation_output_find_node_by_item(struct bNodeTree *ntree,
const struct NodeSimulationItem *item);
bool NOD_geometry_simulation_output_contains_item(struct NodeGeometrySimulationOutput *sim,
const struct NodeSimulationItem *item);
struct NodeSimulationItem *NOD_geometry_simulation_output_get_active_item(
struct NodeGeometrySimulationOutput *sim);
void NOD_geometry_simulation_output_set_active_item(struct NodeGeometrySimulationOutput *sim,
struct NodeSimulationItem *item);
struct NodeSimulationItem *NOD_geometry_simulation_output_find_item(
struct NodeGeometrySimulationOutput *sim, const char *name);
struct NodeSimulationItem *NOD_geometry_simulation_output_add_item(
struct NodeGeometrySimulationOutput *sim, short socket_type, const char *name);
struct NodeSimulationItem *NOD_geometry_simulation_output_insert_item(
struct NodeGeometrySimulationOutput *sim, short socket_type, const char *name, int index);
struct NodeSimulationItem *NOD_geometry_simulation_output_add_item_from_socket(
struct NodeGeometrySimulationOutput *sim,
const struct bNode *from_node,
const struct bNodeSocket *from_sock);
struct NodeSimulationItem *NOD_geometry_simulation_output_insert_item_from_socket(
struct NodeGeometrySimulationOutput *sim,
const struct bNode *from_node,
const struct bNodeSocket *from_sock,
int index);
void NOD_geometry_simulation_output_remove_item(struct NodeGeometrySimulationOutput *sim,
struct NodeSimulationItem *item);
void NOD_geometry_simulation_output_clear_items(struct NodeGeometrySimulationOutput *sim);
void NOD_geometry_simulation_output_move_item(struct NodeGeometrySimulationOutput *sim,
int from_index,
int to_index);
/** \} */ /** \} */

View File

@ -42,23 +42,6 @@ using fn::ValueOrField;
using geo_eval_log::NamedAttributeUsage; using geo_eval_log::NamedAttributeUsage;
using geo_eval_log::NodeWarningType; using geo_eval_log::NodeWarningType;
/**
* An anonymous attribute created by a node.
*/
class NodeAnonymousAttributeID : public AnonymousAttributeID {
std::string long_name_;
std::string socket_name_;
public:
NodeAnonymousAttributeID(const Object &object,
const ComputeContext &compute_context,
const bNode &bnode,
const StringRef identifier,
const StringRef name);
std::string user_name() const override;
};
class GeoNodeExecParams { class GeoNodeExecParams {
private: private:
const bNode &node_; const bNode &node_;
@ -66,18 +49,22 @@ class GeoNodeExecParams {
const lf::Context &lf_context_; const lf::Context &lf_context_;
const Span<int> lf_input_for_output_bsocket_usage_; const Span<int> lf_input_for_output_bsocket_usage_;
const Span<int> lf_input_for_attribute_propagation_to_output_; const Span<int> lf_input_for_attribute_propagation_to_output_;
const FunctionRef<AnonymousAttributeIDPtr(int)> get_output_attribute_id_;
public: public:
GeoNodeExecParams(const bNode &node, GeoNodeExecParams(const bNode &node,
lf::Params &params, lf::Params &params,
const lf::Context &lf_context, const lf::Context &lf_context,
const Span<int> lf_input_for_output_bsocket_usage, const Span<int> lf_input_for_output_bsocket_usage,
const Span<int> lf_input_for_attribute_propagation_to_output) const Span<int> lf_input_for_attribute_propagation_to_output,
const FunctionRef<AnonymousAttributeIDPtr(int)> get_output_attribute_id)
: node_(node), : node_(node),
params_(params), params_(params),
lf_context_(lf_context), lf_context_(lf_context),
lf_input_for_output_bsocket_usage_(lf_input_for_output_bsocket_usage), lf_input_for_output_bsocket_usage_(lf_input_for_output_bsocket_usage),
lf_input_for_attribute_propagation_to_output_(lf_input_for_attribute_propagation_to_output) lf_input_for_attribute_propagation_to_output_(
lf_input_for_attribute_propagation_to_output),
get_output_attribute_id_(get_output_attribute_id)
{ {
} }
@ -240,8 +227,6 @@ class GeoNodeExecParams {
*/ */
void error_message_add(const NodeWarningType type, StringRef message) const; void error_message_add(const NodeWarningType type, StringRef message) const;
std::string attribute_producer_name() const;
void set_default_remaining_outputs(); void set_default_remaining_outputs();
void used_named_attribute(StringRef attribute_name, NamedAttributeUsage usage); void used_named_attribute(StringRef attribute_name, NamedAttributeUsage usage);
@ -268,14 +253,7 @@ class GeoNodeExecParams {
return {}; return {};
} }
const bNodeSocket &output_socket = node_.output_by_identifier(output_identifier); const bNodeSocket &output_socket = node_.output_by_identifier(output_identifier);
const GeoNodesLFUserData &user_data = *this->user_data(); return get_output_attribute_id_(output_socket.index());
const ComputeContext &compute_context = *user_data.compute_context;
return MEM_new<NodeAnonymousAttributeID>(__func__,
*user_data.modifier_data->self_object,
compute_context,
node_,
output_identifier,
output_socket.name);
} }
/** /**

View File

@ -322,7 +322,9 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
/** /**
* For inputs this means that the input field is evaluated on all geometry inputs. For outputs * For inputs this means that the input field is evaluated on all geometry inputs. For outputs
* it means that this contains an anonymous attribute reference that is available on all geometry * it means that this contains an anonymous attribute reference that is available on all geometry
* outputs. * outputs. This sockets value does not have to be output manually in the node. It's done
* automatically by #LazyFunctionForGeometryNode. This allows outputting this field even if the
* geometry output does not have to be computed.
*/ */
Self &field_on_all() Self &field_on_all()
{ {

View File

@ -22,6 +22,8 @@ struct bNodeTree;
extern "C" { extern "C" {
#endif #endif
void node_type_draw_color(const char *idname, float *r_color);
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree, struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node, struct bNode *node,
struct bNodeSocketTemplate *stemp, struct bNodeSocketTemplate *stemp,

View File

@ -139,9 +139,6 @@ void socket_declarations_for_simulation_items(Span<NodeSimulationItem> items,
NodeDeclaration &r_declaration); NodeDeclaration &r_declaration);
const CPPType &get_simulation_item_cpp_type(eNodeSocketDatatype socket_type); const CPPType &get_simulation_item_cpp_type(eNodeSocketDatatype socket_type);
const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item); const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item);
/** \warning Return value will be reallocated when items are added or removed. */
NodeSimulationItem *simulation_item_add_from_socket(NodeGeometrySimulationOutput &storage,
const bNodeSocket &socket);
void copy_simulation_state_to_output_param(lf::Params &params, void copy_simulation_state_to_output_param(lf::Params &params,
int index, int index,
eNodeSocketDatatype socket_type, eNodeSocketDatatype socket_type,

View File

@ -174,8 +174,6 @@ static void node_geo_exec(GeoNodeExecParams params)
break; break;
} }
const CPPType &type = field.cpp_type();
/* Run on the instances component separately to only affect the top level of instances. */ /* Run on the instances component separately to only affect the top level of instances. */
if (domain == ATTR_DOMAIN_INSTANCE) { if (domain == ATTR_DOMAIN_INSTANCE) {
if (geometry_set.has_instances()) { if (geometry_set.has_instances()) {
@ -198,34 +196,6 @@ static void node_geo_exec(GeoNodeExecParams params)
}); });
} }
GField output_field{std::make_shared<bke::AnonymousAttributeFieldInput>(
std::move(attribute_id), type, params.attribute_producer_name())};
switch (data_type) {
case CD_PROP_FLOAT: {
params.set_output(output_identifier, Field<float>(output_field));
break;
}
case CD_PROP_FLOAT3: {
params.set_output(output_identifier, Field<float3>(output_field));
break;
}
case CD_PROP_COLOR: {
params.set_output(output_identifier, Field<ColorGeometry4f>(output_field));
break;
}
case CD_PROP_BOOL: {
params.set_output(output_identifier, Field<bool>(output_field));
break;
}
case CD_PROP_INT32: {
params.set_output(output_identifier, Field<int>(output_field));
break;
}
default:
break;
}
params.set_output("Geometry", geometry_set); params.set_output("Geometry", geometry_set);
} }

View File

@ -160,11 +160,6 @@ static void node_geo_exec(GeoNodeExecParams params)
selection.span[i] = true; selection.span[i] = true;
} }
selection.finish(); selection.finish();
params.set_output(
"Intersecting Edges",
AnonymousAttributeFieldInput::Create<bool>(
std::move(attribute_outputs.intersecting_edges_id), params.attribute_producer_name()));
} }
params.set_output("Mesh", GeometrySet::create_with_mesh(result)); params.set_output("Mesh", GeometrySet::create_with_mesh(result));

View File

@ -81,9 +81,6 @@ static void node_geo_exec(GeoNodeExecParams params)
if (AnonymousAttributeIDPtr outer_points_id = params.get_output_anonymous_attribute_id_if_needed( if (AnonymousAttributeIDPtr outer_points_id = params.get_output_anonymous_attribute_id_if_needed(
"Outer Points")) { "Outer Points")) {
create_selection_output(output.get_component_for_write<CurveComponent>(), outer_points_id); create_selection_output(output.get_component_for_write<CurveComponent>(), outer_points_id);
params.set_output("Outer Points",
AnonymousAttributeFieldInput::Create<bool>(
std::move(outer_points_id), params.attribute_producer_name()));
} }
params.set_output("Curve", std::move(output)); params.set_output("Curve", std::move(output));
} }

View File

@ -200,21 +200,6 @@ static void node_geo_exec(GeoNodeExecParams params)
} }
params.set_output("Points", std::move(geometry_set)); params.set_output("Points", std::move(geometry_set));
if (tangent_anonymous_id) {
params.set_output("Tangent",
AnonymousAttributeFieldInput::Create<float3>(
std::move(tangent_anonymous_id), params.attribute_producer_name()));
}
if (normal_anonymous_id) {
params.set_output("Normal",
AnonymousAttributeFieldInput::Create<float3>(
std::move(normal_anonymous_id), params.attribute_producer_name()));
}
if (rotation_anonymous_id) {
params.set_output("Rotation",
AnonymousAttributeFieldInput::Create<float3>(
std::move(rotation_anonymous_id), params.attribute_producer_name()));
}
} }
} // namespace blender::nodes::node_geo_curve_to_points_cc } // namespace blender::nodes::node_geo_curve_to_points_cc

View File

@ -590,19 +590,6 @@ static void node_geo_exec(GeoNodeExecParams params)
}); });
params.set_output("Points", std::move(geometry_set)); params.set_output("Points", std::move(geometry_set));
if (attribute_outputs.normal_id) {
params.set_output(
"Normal",
AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id),
params.attribute_producer_name()));
}
if (attribute_outputs.rotation_id) {
params.set_output(
"Rotation",
AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id),
params.attribute_producer_name()));
}
} }
} // namespace blender::nodes::node_geo_distribute_points_on_faces_cc } // namespace blender::nodes::node_geo_distribute_points_on_faces_cc

View File

@ -1098,12 +1098,6 @@ static void node_geo_exec(GeoNodeExecParams params)
return; return;
} }
if (attribute_outputs.duplicate_index) {
params.set_output(
"Duplicate Index",
AnonymousAttributeFieldInput::Create<int>(std::move(attribute_outputs.duplicate_index),
params.attribute_producer_name()));
}
params.set_output("Geometry", std::move(geometry_set)); params.set_output("Geometry", std::move(geometry_set));
} }

View File

@ -1343,9 +1343,8 @@ static void node_geo_exec(GeoNodeExecParams params)
"Scale", "Scale",
[](const float3 &offset, const float scale) { return offset * scale; }, [](const float3 &offset, const float scale) { return offset * scale; },
mf::build::exec_presets::AllSpanOrSingle()); mf::build::exec_presets::AllSpanOrSingle());
std::shared_ptr<FieldOperation> multiply_op = std::make_shared<FieldOperation>( const Field<float3> final_offset{
FieldOperation(multiply_fn, {std::move(offset_field), std::move(scale_field)})); FieldOperation::Create(multiply_fn, {std::move(offset_field), std::move(scale_field)})};
const Field<float3> final_offset{std::move(multiply_op)};
AttributeOutputs attribute_outputs; AttributeOutputs attribute_outputs;
attribute_outputs.top_id = params.get_output_anonymous_attribute_id_if_needed("Top"); attribute_outputs.top_id = params.get_output_anonymous_attribute_id_if_needed("Top");
@ -1377,16 +1376,6 @@ static void node_geo_exec(GeoNodeExecParams params)
}); });
params.set_output("Mesh", std::move(geometry_set)); params.set_output("Mesh", std::move(geometry_set));
if (attribute_outputs.top_id) {
params.set_output("Top",
AnonymousAttributeFieldInput::Create<bool>(
std::move(attribute_outputs.top_id), params.attribute_producer_name()));
}
if (attribute_outputs.side_id) {
params.set_output("Side",
AnonymousAttributeFieldInput::Create<bool>(
std::move(attribute_outputs.side_id), params.attribute_producer_name()));
}
} }
} // namespace blender::nodes::node_geo_extrude_mesh_cc } // namespace blender::nodes::node_geo_extrude_mesh_cc

View File

@ -415,8 +415,7 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float3> vector_field = params.extract_input<Field<float3>>("Vector"); Field<float3> vector_field = params.extract_input<Field<float3>>("Vector");
auto image_op = std::make_shared<FieldOperation>( auto image_op = FieldOperation::Create(std::move(image_fn), {std::move(vector_field)});
FieldOperation(std::move(image_fn), {std::move(vector_field)}));
params.set_output("Color", Field<ColorGeometry4f>(image_op, 0)); params.set_output("Color", Field<ColorGeometry4f>(image_op, 0));
params.set_output("Alpha", Field<float>(image_op, 1)); params.set_output("Alpha", Field<float>(image_op, 1));

View File

@ -836,16 +836,6 @@ static void node_geo_exec(GeoNodeExecParams params)
} }
params.set_output("Curves", std::move(new_curves)); params.set_output("Curves", std::move(new_curves));
if (index_attribute_id) {
params.set_output("Closest Index",
AnonymousAttributeFieldInput::Create<int>(std::move(index_attribute_id),
params.attribute_producer_name()));
}
if (weight_attribute_id) {
params.set_output("Closest Weight",
AnonymousAttributeFieldInput::Create<float>(
std::move(weight_attribute_id), params.attribute_producer_name()));
}
} }
} // namespace blender::nodes::node_geo_interpolate_curves_cc } // namespace blender::nodes::node_geo_interpolate_curves_cc

View File

@ -851,29 +851,6 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Transform the mesh so that the base of the cone is at the origin. */ /* Transform the mesh so that the base of the cone is at the origin. */
BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false); BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false);
if (attribute_outputs.top_id) {
params.set_output("Top",
AnonymousAttributeFieldInput::Create<bool>(
std::move(attribute_outputs.top_id), params.attribute_producer_name()));
}
if (attribute_outputs.bottom_id) {
params.set_output(
"Bottom",
AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.bottom_id),
params.attribute_producer_name()));
}
if (attribute_outputs.side_id) {
params.set_output("Side",
AnonymousAttributeFieldInput::Create<bool>(
std::move(attribute_outputs.side_id), params.attribute_producer_name()));
}
if (attribute_outputs.uv_map_id) {
params.set_output(
"UV Map",
AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.uv_map_id),
params.attribute_producer_name()));
}
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
} }

View File

@ -112,12 +112,6 @@ static void node_geo_exec(GeoNodeExecParams params)
Mesh *mesh = create_cube_mesh(size, verts_x, verts_y, verts_z, uv_map_id.get()); Mesh *mesh = create_cube_mesh(size, verts_x, verts_y, verts_z, uv_map_id.get());
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
if (uv_map_id) {
params.set_output("UV Map",
AnonymousAttributeFieldInput::Create<float3>(
std::move(uv_map_id), params.attribute_producer_name()));
}
} }
} // namespace blender::nodes::node_geo_mesh_primitive_cube_cc } // namespace blender::nodes::node_geo_mesh_primitive_cube_cc

View File

@ -122,29 +122,6 @@ static void node_geo_exec(GeoNodeExecParams params)
fill, fill,
attribute_outputs); attribute_outputs);
if (attribute_outputs.top_id) {
params.set_output("Top",
AnonymousAttributeFieldInput::Create<bool>(
std::move(attribute_outputs.top_id), params.attribute_producer_name()));
}
if (attribute_outputs.bottom_id) {
params.set_output(
"Bottom",
AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.bottom_id),
params.attribute_producer_name()));
}
if (attribute_outputs.side_id) {
params.set_output("Side",
AnonymousAttributeFieldInput::Create<bool>(
std::move(attribute_outputs.side_id), params.attribute_producer_name()));
}
if (attribute_outputs.uv_map_id) {
params.set_output(
"UV Map",
AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.uv_map_id),
params.attribute_producer_name()));
}
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
} }

View File

@ -205,12 +205,6 @@ static void node_geo_exec(GeoNodeExecParams params)
BKE_id_material_eval_ensure_default_slot(&mesh->id); BKE_id_material_eval_ensure_default_slot(&mesh->id);
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
if (uv_map_id) {
params.set_output("UV Map",
AnonymousAttributeFieldInput::Create<float3>(
std::move(uv_map_id), params.attribute_producer_name()));
}
} }
} // namespace blender::nodes::node_geo_mesh_primitive_grid_cc } // namespace blender::nodes::node_geo_mesh_primitive_grid_cc

View File

@ -115,12 +115,6 @@ static void node_geo_exec(GeoNodeExecParams params)
Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius, uv_map_id.get()); Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius, uv_map_id.get());
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
if (uv_map_id) {
params.set_output("UV Map",
AnonymousAttributeFieldInput::Create<float3>(
std::move(uv_map_id), params.attribute_producer_name()));
}
} }
} // namespace blender::nodes::node_geo_mesh_primitive_ico_sphere_cc } // namespace blender::nodes::node_geo_mesh_primitive_ico_sphere_cc

View File

@ -363,11 +363,6 @@ static void node_geo_exec(GeoNodeExecParams params)
Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num, uv_map_id.get()); Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num, uv_map_id.get());
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
if (uv_map_id) {
params.set_output("UV Map",
AnonymousAttributeFieldInput::Create<float3>(
std::move(uv_map_id), params.attribute_producer_name()));
}
} }
} // namespace blender::nodes::node_geo_mesh_primitive_uv_sphere_cc } // namespace blender::nodes::node_geo_mesh_primitive_uv_sphere_cc

View File

@ -18,14 +18,8 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Mesh")).propagate_all(); b.add_output<decl::Geometry>(N_("Mesh")).propagate_all();
} }
static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level) static Mesh *simple_subdivide_mesh(const Mesh &mesh, const int level)
{ {
if (!geometry_set.has_mesh()) {
return;
}
const Mesh *mesh_in = geometry_set.get_mesh_for_read();
/* Initialize mesh settings. */ /* Initialize mesh settings. */
SubdivToMeshSettings mesh_settings; SubdivToMeshSettings mesh_settings;
mesh_settings.resolution = (1 << level) + 1; mesh_settings.resolution = (1 << level) + 1;
@ -42,43 +36,38 @@ static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int lev
subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(0); subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(0);
/* Apply subdivision from mesh. */ /* Apply subdivision from mesh. */
Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); Subdiv *subdiv = BKE_subdiv_new_from_mesh(&subdiv_settings, &mesh);
if (!subdiv) {
/* In case of bad topology, skip to input mesh. */ return nullptr;
if (subdiv == nullptr) {
return;
} }
Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); Mesh *result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh);
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_component.replace(mesh_out);
BKE_subdiv_free(subdiv); BKE_subdiv_free(subdiv);
return result;
} }
static void node_geo_exec(GeoNodeExecParams params) static void node_geo_exec(GeoNodeExecParams params)
{ {
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
#ifdef WITH_OPENSUBDIV
#ifndef WITH_OPENSUBDIV
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
params.set_default_remaining_outputs();
return;
#endif
/* See CCGSUBSURF_LEVEL_MAX for max limit. */ /* See CCGSUBSURF_LEVEL_MAX for max limit. */
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 11); const int level = clamp_i(params.extract_input<int>("Level"), 0, 11);
if (level == 0) {
if (subdiv_level == 0) {
params.set_output("Mesh", std::move(geometry_set)); params.set_output("Mesh", std::move(geometry_set));
return; return;
} }
geometry_set.modify_geometry_sets( geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
[&](GeometrySet &geometry_set) { geometry_set_mesh_subdivide(geometry_set, subdiv_level); }); if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
geometry_set.replace_mesh(simple_subdivide_mesh(*mesh, level));
}
});
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
#endif
params.set_output("Mesh", std::move(geometry_set)); params.set_output("Mesh", std::move(geometry_set));
} }

View File

@ -47,9 +47,9 @@ static void node_init(bNodeTree * /*tree*/, bNode *node)
} }
static void geometry_set_mesh_to_points(GeometrySet &geometry_set, static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
Field<float3> &position_field, const Field<float3> &position_field,
Field<float> &radius_field, const Field<float> &radius_field,
Field<bool> &selection_field, const Field<bool> &selection_field,
const eAttrDomain domain, const eAttrDomain domain,
const AnonymousAttributePropagationInfo &propagation_info) const AnonymousAttributePropagationInfo &propagation_info)
{ {
@ -151,9 +151,7 @@ static void node_geo_exec(GeoNodeExecParams params)
__func__, __func__,
[](float value) { return std::max(0.0f, value); }, [](float value) { return std::max(0.0f, value); },
mf::build::exec_presets::AllSpanOrSingle()); mf::build::exec_presets::AllSpanOrSingle());
auto max_zero_op = std::make_shared<FieldOperation>( const Field<float> positive_radius(FieldOperation::Create(max_zero_fn, {std::move(radius)}), 0);
FieldOperation(max_zero_fn, {std::move(radius)}));
Field<float> positive_radius(std::move(max_zero_op), 0);
const NodeGeometryMeshToPoints &storage = node_storage(params.node()); const NodeGeometryMeshToPoints &storage = node_storage(params.node());
const GeometryNodeMeshToPointsMode mode = (GeometryNodeMeshToPointsMode)storage.mode; const GeometryNodeMeshToPointsMode mode = (GeometryNodeMeshToPointsMode)storage.mode;

View File

@ -198,10 +198,10 @@ class ProximityFunction : public mf::MultiFunction {
static void node_geo_exec(GeoNodeExecParams params) static void node_geo_exec(GeoNodeExecParams params)
{ {
GeometrySet geometry_set_target = params.extract_input<GeometrySet>("Target"); GeometrySet target = params.extract_input<GeometrySet>("Target");
geometry_set_target.ensure_owns_direct_data(); target.ensure_owns_direct_data();
if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) { if (!target.has_mesh() && !target.has_pointcloud()) {
params.set_default_remaining_outputs(); params.set_default_remaining_outputs();
return; return;
} }
@ -210,9 +210,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float3> position_field = params.extract_input<Field<float3>>("Source Position"); Field<float3> position_field = params.extract_input<Field<float3>>("Source Position");
auto proximity_fn = std::make_unique<ProximityFunction>( auto proximity_fn = std::make_unique<ProximityFunction>(
std::move(geometry_set_target), GeometryNodeProximityTargetType(storage.target_element)); std::move(target), GeometryNodeProximityTargetType(storage.target_element));
auto proximity_op = std::make_shared<FieldOperation>( auto proximity_op = FieldOperation::Create(std::move(proximity_fn), {std::move(position_field)});
FieldOperation(std::move(proximity_fn), {std::move(position_field)}));
params.set_output("Position", Field<float3>(proximity_op, 0)); params.set_output("Position", Field<float3>(proximity_op, 0));
params.set_output("Distance", Field<float>(proximity_op, 1)); params.set_output("Distance", Field<float>(proximity_op, 1));

View File

@ -407,9 +407,9 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float> length_field = params.extract_input<Field<float>>("Ray Length"); Field<float> length_field = params.extract_input<Field<float>>("Ray Length");
auto fn = std::make_unique<RaycastFunction>(std::move(target), std::move(field), mapping); auto fn = std::make_unique<RaycastFunction>(std::move(target), std::move(field), mapping);
auto op = std::make_shared<FieldOperation>(FieldOperation( auto op = FieldOperation::Create(
std::move(fn), std::move(fn),
{std::move(position_field), std::move(direction_field), std::move(length_field)})); {std::move(position_field), std::move(direction_field), std::move(length_field)});
params.set_output("Is Hit", Field<bool>(op, 0)); params.set_output("Is Hit", Field<bool>(op, 0));
params.set_output("Hit Position", Field<float3>(op, 1)); params.set_output("Hit Position", Field<float3>(op, 1));

View File

@ -8,6 +8,7 @@
#include "UI_interface.h" #include "UI_interface.h"
#include "UI_resources.h" #include "UI_resources.h"
#include "NOD_geometry.h"
#include "NOD_socket.h" #include "NOD_socket.h"
#include "node_geometry_util.hh" #include "node_geometry_util.hh"
@ -43,6 +44,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
public: public:
LazyFunctionForSimulationInputNode(const bNodeTree &node_tree, const bNode &node) LazyFunctionForSimulationInputNode(const bNodeTree &node_tree, const bNode &node)
{ {
debug_name_ = "Simulation Input";
LukasTonne marked this conversation as resolved Outdated

remove dead code

remove dead code
output_node_id_ = node_storage(node).output_node_id; output_node_id_ = node_storage(node).output_node_id;
const bNode &output_node = *node_tree.node_by_id(output_node_id_); const bNode &output_node = *node_tree.node_by_id(output_node_id_);
const NodeGeometrySimulationOutput &storage = *static_cast<NodeGeometrySimulationOutput *>( const NodeGeometrySimulationOutput &storage = *static_cast<NodeGeometrySimulationOutput *>(
@ -160,8 +162,8 @@ static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
if (link->tonode == node) { if (link->tonode == node) {
if (link->tosock->identifier == StringRef("__extend__")) { if (link->tosock->identifier == StringRef("__extend__")) {
if (const NodeSimulationItem *item = simulation_item_add_from_socket(storage, if (const NodeSimulationItem *item = NOD_geometry_simulation_output_add_item_from_socket(
*link->fromsock)) { &storage, link->fromnode, link->fromsock)) {
update_node_declaration_and_sockets(*ntree, *node); update_node_declaration_and_sockets(*ntree, *node);
link->tosock = nodeFindSocket(node, SOCK_IN, item->name); link->tosock = nodeFindSocket(node, SOCK_IN, item->name);
} }
@ -173,8 +175,8 @@ static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
else { else {
BLI_assert(link->fromnode == node); BLI_assert(link->fromnode == node);
if (link->fromsock->identifier == StringRef("__extend__")) { if (link->fromsock->identifier == StringRef("__extend__")) {
if (const NodeSimulationItem *item = simulation_item_add_from_socket(storage, if (const NodeSimulationItem *item = NOD_geometry_simulation_output_add_item_from_socket(
*link->tosock)) { &storage, link->tonode, link->tosock)) {
update_node_declaration_and_sockets(*ntree, *node); update_node_declaration_and_sockets(*ntree, *node);
link->fromsock = nodeFindSocket(node, SOCK_OUT, item->name); link->fromsock = nodeFindSocket(node, SOCK_OUT, item->name);
} }
@ -204,6 +206,15 @@ void register_node_type_geo_simulation_input()
nodeRegisterType(&ntype); nodeRegisterType(&ntype);
} }
bNode *NOD_geometry_simulation_input_get_paired_output(bNodeTree *node_tree,
const bNode *simulation_input_node)
{
namespace file_ns = blender::nodes::node_geo_simulation_input_cc;
const NodeGeometrySimulationInput &data = file_ns::node_storage(*simulation_input_node);
return node_tree->node_by_id(data.output_node_id);
}
bool NOD_geometry_simulation_input_pair_with_output(const bNodeTree *node_tree, bool NOD_geometry_simulation_input_pair_with_output(const bNodeTree *node_tree,
bNode *sim_input_node, bNode *sim_input_node,
const bNode *sim_output_node) const bNode *sim_output_node)

View File

@ -8,7 +8,6 @@
#include "DEG_depsgraph_query.h" #include "DEG_depsgraph_query.h"
#include "UI_interface.h" #include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_common.h" #include "NOD_common.h"
#include "NOD_socket.h" #include "NOD_socket.h"
@ -17,22 +16,10 @@
namespace blender::nodes { namespace blender::nodes {
bool is_simulation_item_type_supported(const eNodeSocketDatatype socket_type)
{
return ELEM(socket_type,
SOCK_FLOAT,
SOCK_VECTOR,
SOCK_RGBA,
SOCK_BOOLEAN,
SOCK_INT,
SOCK_STRING,
SOCK_GEOMETRY);
}
static std::unique_ptr<SocketDeclaration> socket_declaration_for_simulation_item( static std::unique_ptr<SocketDeclaration> socket_declaration_for_simulation_item(
const NodeSimulationItem &item, const eNodeSocketInOut in_out, const int index) const NodeSimulationItem &item, const eNodeSocketInOut in_out, const int index)
{ {
BLI_assert(is_simulation_item_type_supported(eNodeSocketDatatype(item.socket_type))); BLI_assert(NOD_geometry_simulation_output_item_socket_type_supported(eNodeSocketDatatype(item.socket_type)));
std::unique_ptr<SocketDeclaration> decl; std::unique_ptr<SocketDeclaration> decl;
switch (eNodeSocketDatatype(item.socket_type)) { switch (eNodeSocketDatatype(item.socket_type)) {
@ -71,8 +58,8 @@ static std::unique_ptr<SocketDeclaration> socket_declaration_for_simulation_item
BLI_assert_unreachable(); BLI_assert_unreachable();
} }
decl->name = item.name; decl->name = item.name ? item.name : "";
decl->identifier = item.name; decl->identifier = "Item_" + std::to_string(item.identifier);
decl->in_out = in_out; decl->in_out = in_out;
return decl; return decl;
} }
@ -89,13 +76,20 @@ void socket_declarations_for_simulation_items(const Span<NodeSimulationItem> ite
r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT)); r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT));
} }
struct SimulationItemsUniqueNameArgs {
NodeGeometrySimulationOutput *sim;
const NodeSimulationItem *item;
};
static bool simulation_items_unique_name_check(void *arg, const char *name) static bool simulation_items_unique_name_check(void *arg, const char *name)
{ {
const NodeGeometrySimulationOutput &storage = *static_cast<const NodeGeometrySimulationOutput *>( const SimulationItemsUniqueNameArgs &args = *static_cast<const SimulationItemsUniqueNameArgs *>(
arg); arg);
for (const NodeSimulationItem &item : Span(storage.items, storage.items_num)) { for (const NodeSimulationItem &item : args.sim->items_span()) {
if (STREQ(item.name, name)) { if (&item != args.item) {
return true; if (STREQ(item.name, name)) {
return true;
}
} }
} }
if (STREQ(name, "Delta Time")) { if (STREQ(name, "Delta Time")) {
@ -104,38 +98,6 @@ static bool simulation_items_unique_name_check(void *arg, const char *name)
return false; return false;
} }
NodeSimulationItem *simulation_item_add_from_socket(NodeGeometrySimulationOutput &storage,
const bNodeSocket &socket)
{
if (!is_simulation_item_type_supported(eNodeSocketDatatype(socket.type))) {
return nullptr;
}
char unique_name[MAX_NAME + 4] = "";
BLI_uniquename_cb(simulation_items_unique_name_check,
&storage,
socket.name,
'.',
unique_name,
ARRAY_SIZE(unique_name));
NodeSimulationItem *old_items = storage.items;
storage.items = MEM_cnew_array<NodeSimulationItem>(storage.items_num + 1, __func__);
for (const int i : IndexRange(storage.items_num)) {
storage.items[i].name = old_items[i].name;
storage.items[i].socket_type = old_items[i].socket_type;
}
NodeSimulationItem &added_item = storage.items[storage.items_num];
added_item.name = BLI_strdup(unique_name);
added_item.socket_type = socket.type;
storage.items_num++;
MEM_SAFE_FREE(old_items);
return &added_item;
}
const CPPType &get_simulation_item_cpp_type(const eNodeSocketDatatype socket_type) const CPPType &get_simulation_item_cpp_type(const eNodeSocketDatatype socket_type)
{ {
const char *socket_idname = nodeStaticSocketType(socket_type, 0); const char *socket_idname = nodeStaticSocketType(socket_type, 0);
@ -206,6 +168,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
public: public:
LazyFunctionForSimulationOutputNode(const bNode &node) : node_id_(node.identifier) LazyFunctionForSimulationOutputNode(const bNode &node) : node_id_(node.identifier)
{ {
debug_name_ = "Simulation Output";
const NodeGeometrySimulationOutput &storage = node_storage(node); const NodeGeometrySimulationOutput &storage = node_storage(node);
simulation_items_ = {storage.items, storage.items_num}; simulation_items_ = {storage.items, storage.items_num};
for (const NodeSimulationItem &item : Span(storage.items, storage.items_num)) { for (const NodeSimulationItem &item : Span(storage.items, storage.items_num)) {
@ -359,10 +322,15 @@ static void node_declare_dynamic(const bNodeTree & /*node_tree*/,
static void node_init(bNodeTree * /*tree*/, bNode *node) static void node_init(bNodeTree * /*tree*/, bNode *node)
{ {
NodeGeometrySimulationOutput *data = MEM_cnew<NodeGeometrySimulationOutput>(__func__); NodeGeometrySimulationOutput *data = MEM_cnew<NodeGeometrySimulationOutput>(__func__);
data->next_identifier = 0;
data->items = MEM_cnew_array<NodeSimulationItem>(1, __func__); data->items = MEM_cnew_array<NodeSimulationItem>(1, __func__);
data->items[0].name = BLI_strdup(DATA_("Geometry")); data->items[0].name = BLI_strdup(DATA_("Geometry"));
data->items[0].socket_type = SOCK_GEOMETRY; data->items[0].socket_type = SOCK_GEOMETRY;
data->items[0].identifier = data->next_identifier++;
data->items_num = 1; data->items_num = 1;
node->storage = data; node->storage = data;
} }
@ -386,8 +354,11 @@ static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const b
dst_storage->items = MEM_cnew_array<NodeSimulationItem>(src_storage.items_num, __func__); dst_storage->items = MEM_cnew_array<NodeSimulationItem>(src_storage.items_num, __func__);
dst_storage->items_num = src_storage.items_num; dst_storage->items_num = src_storage.items_num;
dst_storage->active_index = src_storage.active_index;
dst_storage->next_identifier = src_storage.next_identifier;
for (const int i : IndexRange(src_storage.items_num)) { for (const int i : IndexRange(src_storage.items_num)) {
if (char *name = src_storage.items[i].name) { if (char *name = src_storage.items[i].name) {
dst_storage->items[i].identifier = src_storage.items[i].identifier;
dst_storage->items[i].name = BLI_strdup(name); dst_storage->items[i].name = BLI_strdup(name);
dst_storage->items[i].socket_type = src_storage.items[i].socket_type; dst_storage->items[i].socket_type = src_storage.items[i].socket_type;
} }
@ -401,8 +372,8 @@ static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
NodeGeometrySimulationOutput &storage = node_storage(*node); NodeGeometrySimulationOutput &storage = node_storage(*node);
if (link->tonode == node) { if (link->tonode == node) {
if (link->tosock->identifier == StringRef("__extend__")) { if (link->tosock->identifier == StringRef("__extend__")) {
if (const NodeSimulationItem *item = simulation_item_add_from_socket(storage, if (const NodeSimulationItem *item = NOD_geometry_simulation_output_add_item_from_socket(
*link->fromsock)) { &storage, link->fromnode, link->fromsock)) {
update_node_declaration_and_sockets(*ntree, *node); update_node_declaration_and_sockets(*ntree, *node);
link->tosock = nodeFindSocket(node, SOCK_IN, item->name); link->tosock = nodeFindSocket(node, SOCK_IN, item->name);
} }
@ -414,8 +385,8 @@ static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
else { else {
BLI_assert(link->fromnode == node); BLI_assert(link->fromnode == node);
if (link->fromsock->identifier == StringRef("__extend__")) { if (link->fromsock->identifier == StringRef("__extend__")) {
if (const NodeSimulationItem *item = simulation_item_add_from_socket(storage, if (const NodeSimulationItem *item = NOD_geometry_simulation_output_add_item_from_socket(
*link->tosock)) { &storage, link->fromnode, link->tosock)) {
update_node_declaration_and_sockets(*ntree, *node); update_node_declaration_and_sockets(*ntree, *node);
link->fromsock = nodeFindSocket(node, SOCK_OUT, item->name); link->fromsock = nodeFindSocket(node, SOCK_OUT, item->name);
} }
@ -446,3 +417,209 @@ void register_node_type_geo_simulation_output()
file_ns::node_copy_storage); file_ns::node_copy_storage);
nodeRegisterType(&ntype); nodeRegisterType(&ntype);
} }
blender::Span<NodeSimulationItem> NodeGeometrySimulationOutput::items_span() const
{
return blender::Span<NodeSimulationItem>(items, items_num);
}
blender::MutableSpan<NodeSimulationItem> NodeGeometrySimulationOutput::items_span_for_write()
{
return blender::MutableSpan<NodeSimulationItem>(items, items_num);
}
blender::IndexRange NodeGeometrySimulationOutput::items_range() const
{
return blender::IndexRange(items_num);
}
bool NOD_geometry_simulation_output_item_socket_type_supported(
const eNodeSocketDatatype socket_type)
{
return ELEM(socket_type,
SOCK_FLOAT,
SOCK_VECTOR,
SOCK_RGBA,
SOCK_BOOLEAN,
SOCK_INT,
SOCK_STRING,
SOCK_GEOMETRY);
}
bNode *NOD_geometry_simulation_output_find_node_by_item(bNodeTree *ntree,
const NodeSimulationItem *item)
{
ntree->ensure_topology_cache();
for (bNode *node : ntree->nodes_by_type("GeometryNodeSimulationOutput")) {
NodeGeometrySimulationOutput *sim = static_cast<NodeGeometrySimulationOutput *>(node->storage);
if (sim->items_span().contains_ptr(item)) {
return node;
}
}
return nullptr;
}
bool NOD_geometry_simulation_output_item_set_unique_name(NodeGeometrySimulationOutput *sim,
NodeSimulationItem *item,
const char *name,
const char *defname)
{
char unique_name[MAX_NAME + 4];
BLI_strncpy(unique_name, name, sizeof(unique_name));
blender::nodes::SimulationItemsUniqueNameArgs args{sim, item};
const bool name_changed = BLI_uniquename_cb(blender::nodes::simulation_items_unique_name_check,
&args,
defname,
'.',
unique_name,
ARRAY_SIZE(unique_name));
item->name = BLI_strdup(unique_name);
return name_changed;
}
bool NOD_geometry_simulation_output_contains_item(NodeGeometrySimulationOutput *sim,
const NodeSimulationItem *item)
{
return sim->items_span().contains_ptr(item);
}
NodeSimulationItem *NOD_geometry_simulation_output_get_active_item(
NodeGeometrySimulationOutput *sim)
{
if (!sim->items_range().contains(sim->active_index)) {
return nullptr;
}
return &sim->items[sim->active_index];
}
void NOD_geometry_simulation_output_set_active_item(NodeGeometrySimulationOutput *sim,
NodeSimulationItem *item)
{
if (sim->items_span().contains_ptr(item)) {
sim->active_index = item - sim->items;
}
}
NodeSimulationItem *NOD_geometry_simulation_output_find_item(NodeGeometrySimulationOutput *sim,
const char *name)
{
for (NodeSimulationItem &item : sim->items_span_for_write()) {
if (STREQ(item.name, name)) {
return &item;
}
}
return nullptr;
}
NodeSimulationItem *NOD_geometry_simulation_output_add_item(NodeGeometrySimulationOutput *sim,
const short socket_type,
const char *name)
{
return NOD_geometry_simulation_output_insert_item(sim, socket_type, name, sim->items_num);
}
NodeSimulationItem *NOD_geometry_simulation_output_insert_item(NodeGeometrySimulationOutput *sim,
const short socket_type,
const char *name,
int index)
{
NodeSimulationItem *old_items = sim->items;
sim->items = MEM_cnew_array<NodeSimulationItem>(sim->items_num + 1, __func__);
for (const int i : blender::IndexRange(index)) {
sim->items[i] = old_items[i];
}
for (const int i : blender::IndexRange(index, sim->items_num - index)) {
sim->items[i + 1] = old_items[i];
}
const char *defname = nodeStaticSocketLabel(socket_type, 0);
NodeSimulationItem &added_item = sim->items[index];
added_item.identifier = sim->next_identifier++;
NOD_geometry_simulation_output_item_set_unique_name(sim, &added_item, name, defname);
added_item.socket_type = socket_type;
sim->items_num++;
MEM_SAFE_FREE(old_items);
return &added_item;
}
NodeSimulationItem *NOD_geometry_simulation_output_add_item_from_socket(
NodeGeometrySimulationOutput *sim, const bNode * /*from_node*/, const bNodeSocket *from_sock)
{
if (from_sock->type != SOCK_GEOMETRY) {
return nullptr;
}
return NOD_geometry_simulation_output_insert_item(
sim, from_sock->type, from_sock->name, sim->items_num);
}
NodeSimulationItem *NOD_geometry_simulation_output_insert_item_from_socket(
NodeGeometrySimulationOutput *sim,
const bNode * /*from_node*/,
const bNodeSocket *from_sock,
int index)
{
return NOD_geometry_simulation_output_insert_item(sim, from_sock->type, from_sock->name, index);
}
void NOD_geometry_simulation_output_remove_item(NodeGeometrySimulationOutput *sim,
NodeSimulationItem *item)
{
const int index = item - sim->items;
if (index < 0 || index >= sim->items_num) {
return;
}
NodeSimulationItem *old_items = sim->items;
sim->items = MEM_cnew_array<NodeSimulationItem>(sim->items_num - 1, __func__);
for (const int i : blender::IndexRange(index)) {
sim->items[i] = old_items[i];
}
for (const int i : blender::IndexRange(index, sim->items_num - index).drop_front(1)) {
sim->items[i - 1] = old_items[i];
}
MEM_SAFE_FREE(old_items[index].name);
sim->items_num--;
MEM_SAFE_FREE(old_items);
}
void NOD_geometry_simulation_output_clear_items(struct NodeGeometrySimulationOutput *sim)
{
for (NodeSimulationItem &item : sim->items_span_for_write()) {
MEM_SAFE_FREE(item.name);
}
MEM_SAFE_FREE(sim->items);
sim->items = nullptr;
sim->items_num = 0;
}
void NOD_geometry_simulation_output_move_item(NodeGeometrySimulationOutput *sim,
int from_index,
int to_index)
{
BLI_assert(from_index >= 0 && from_index < sim->items_num);
BLI_assert(to_index >= 0 && to_index < sim->items_num);
if (from_index == to_index) {
return;
}
if (from_index < to_index) {
const NodeSimulationItem tmp = sim->items[from_index];
for (int i = from_index; i < to_index; ++i) {
sim->items[i] = sim->items[i + 1];
}
sim->items[to_index] = tmp;
}
else /* from_index > to_index */ {
const NodeSimulationItem tmp = sim->items[from_index];
for (int i = from_index; i > to_index; --i) {
sim->items[i] = sim->items[i - 1];
}
sim->items[to_index] = tmp;
}
}

View File

@ -348,9 +348,6 @@ static void create_attributes(GeoNodeExecParams &params,
*line_id, ATTR_DOMAIN_INSTANCE); *line_id, ATTR_DOMAIN_INSTANCE);
line_attribute.span.copy_from(layout.line_numbers); line_attribute.span.copy_from(layout.line_numbers);
line_attribute.finish(); line_attribute.finish();
params.set_output("Line",
AnonymousAttributeFieldInput::Create<int>(std::move(line_id),
params.attribute_producer_name()));
} }
if (AnonymousAttributeIDPtr pivot_id = params.get_output_anonymous_attribute_id_if_needed( if (AnonymousAttributeIDPtr pivot_id = params.get_output_anonymous_attribute_id_if_needed(
@ -363,9 +360,6 @@ static void create_attributes(GeoNodeExecParams &params,
} }
pivot_attribute.finish(); pivot_attribute.finish();
params.set_output("Pivot Point",
AnonymousAttributeFieldInput::Create<float3>(
std::move(pivot_id), params.attribute_producer_name()));
} }
} }

View File

@ -1,12 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */ /* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array_utils.hh"
#include "BLI_task.hh" #include "BLI_task.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h" #include "DNA_modifier_types.h"
#include "BKE_attribute.hh" #include "BKE_attribute.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.hh" #include "BKE_mesh.hh"
#include "BKE_subdiv.h" #include "BKE_subdiv.h"
#include "BKE_subdiv_mesh.hh" #include "BKE_subdiv_mesh.hh"
@ -54,136 +54,136 @@ static void node_init(bNodeTree * /*tree*/, bNode *node)
} }
#ifdef WITH_OPENSUBDIV #ifdef WITH_OPENSUBDIV
static void materialize_and_clamp_creases(const VArray<float> &crease_varray,
MutableSpan<float> creases) static void write_vert_creases(Mesh &mesh, const VArray<float> &creases)
{ {
threading::parallel_for(creases.index_range(), 1024, [&](IndexRange range) { CustomData_free_layers(&mesh.vdata, CD_CREASE, mesh.totvert);
crease_varray.materialize(range, creases); float *layer = static_cast<float *>(
for (const int i : range) { CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_CONSTRUCT, mesh.totvert));
creases[i] = std::clamp(creases[i], 0.0f, 1.0f); array_utils::copy(creases, {layer, mesh.totvert});
}
});
} }
static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray) static void write_edge_creases(Mesh &mesh, const VArray<float> &creases)
{ {
float *crease; bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
if (CustomData_has_layer(&mesh.vdata, CD_CREASE)) { attributes.remove("crease");
crease = static_cast<float *>( attributes.add<float>("crease", ATTR_DOMAIN_EDGE, bke::AttributeInitVArray(creases));
CustomData_get_layer_for_write(&mesh.vdata, CD_CREASE, mesh.totvert)); }
static bool varray_is_single_zero(const VArray<float> &varray)
{
if (const std::optional<float> value = varray.get_if_single()) {
return *value == 0.0f;
} }
else { return false;
crease = static_cast<float *>( }
CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_CONSTRUCT, mesh.totvert));
static fn::Field<float> clamp_crease(fn::Field<float> crease_field)
{
static auto clamp_fn = mf::build::SI1_SO<float, float>(
"Clamp",
[](float value) { return std::clamp(value, 0.0f, 1.0f); },
mf::build::exec_presets::AllSpanOrSingle());
return fn::Field<float>(fn::FieldOperation::Create(clamp_fn, {std::move(crease_field)}));
}
static Mesh *mesh_subsurf_calc(const Mesh *mesh,
const int level,
const Field<float> &vert_crease_field,
const Field<float> &edge_crease_field,
const int boundary_smooth,
const int uv_smooth)
{
const bke::MeshFieldContext point_context{*mesh, ATTR_DOMAIN_POINT};
FieldEvaluator point_evaluator(point_context, mesh->totvert);
point_evaluator.add(clamp_crease(vert_crease_field));
point_evaluator.evaluate();
const bke::MeshFieldContext edge_context{*mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator edge_evaluator(edge_context, mesh->totedge);
edge_evaluator.add(clamp_crease(edge_crease_field));
edge_evaluator.evaluate();
const VArray<float> vert_creases = point_evaluator.get_evaluated<float>(0);
const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0);
const bool use_creases = !varray_is_single_zero(vert_creases) ||
!varray_is_single_zero(edge_creases);
Mesh *mesh_copy = nullptr;
if (use_creases) {
/* Due to the "BKE_subdiv" API, the crease layers must be on the input mesh. But in this node
* they are provided as separate inputs, not as custom data layers. When needed, retrieve the
* mesh with write access and store the new crease values there. */
mesh_copy = BKE_mesh_copy_for_eval(mesh);
write_vert_creases(*mesh_copy, vert_creases);
write_edge_creases(*mesh_copy, edge_creases);
mesh = mesh_copy;
} }
materialize_and_clamp_creases(crease_varray, {crease, mesh.totvert});
SubdivToMeshSettings mesh_settings;
mesh_settings.resolution = (1 << level) + 1;
mesh_settings.use_optimal_display = false;
SubdivSettings subdiv_settings;
subdiv_settings.is_simple = false;
subdiv_settings.is_adaptive = false;
subdiv_settings.use_creases = use_creases;
subdiv_settings.level = level;
subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf(
boundary_smooth);
subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
uv_smooth);
Subdiv *subdiv = BKE_subdiv_new_from_mesh(&subdiv_settings, mesh);
if (!subdiv) {
return nullptr;
}
Mesh *result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh);
BKE_subdiv_free(subdiv);
if (use_creases) {
/* Remove the layer in case it was created by the node from the field input. The fact
* that this node uses #CD_CREASE to input creases to the subdivision code is meant to be
* an implementation detail ideally. */
CustomData_free_layers(&result->edata, CD_CREASE, result->totedge);
}
if (mesh_copy) {
BKE_id_free(nullptr, mesh_copy);
}
return result;
} }
static void write_edge_creases(Mesh &mesh, const VArray<float> &crease_varray)
{
bke::SpanAttributeWriter<float> attribute =
mesh.attributes_for_write().lookup_or_add_for_write_only_span<float>("crease",
ATTR_DOMAIN_EDGE);
materialize_and_clamp_creases(crease_varray, attribute.span);
attribute.finish();
}
static bool varray_is_nonzero(const VArray<float> &varray)
{
return !(varray.is_single() && varray.get_internal_single() == 0.0f);
}
#endif #endif
static void node_geo_exec(GeoNodeExecParams params) static void node_geo_exec(GeoNodeExecParams params)
{ {
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
#ifndef WITH_OPENSUBDIV #ifdef WITH_OPENSUBDIV
params.error_message_add(NodeWarningType::Error, const Field<float> vert_crease = params.extract_input<Field<float>>("Vertex Crease");
TIP_("Disabled, Blender was compiled without OpenSubdiv")); const Field<float> edge_crease = params.extract_input<Field<float>>("Edge Crease");
#else
Field<float> edge_crease_field = params.extract_input<Field<float>>("Edge Crease");
Field<float> vertex_crease_field = params.extract_input<Field<float>>("Vertex Crease");
const NodeGeometrySubdivisionSurface &storage = node_storage(params.node()); const NodeGeometrySubdivisionSurface &storage = node_storage(params.node());
const int uv_smooth = storage.uv_smooth; const int uv_smooth = storage.uv_smooth;
const int boundary_smooth = storage.boundary_smooth; const int boundary_smooth = storage.boundary_smooth;
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30); const int level = std::clamp(params.extract_input<int>("Level"), 0, 11);
if (level == 0) {
/* Only process subdivision if level is greater than 0. */
if (subdiv_level == 0) {
params.set_output("Mesh", std::move(geometry_set)); params.set_output("Mesh", std::move(geometry_set));
return; return;
} }
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
const Mesh *mesh = geometry_set.get_mesh_for_read(); if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
if (!mesh) { geometry_set.replace_mesh(
return; mesh_subsurf_calc(mesh, level, vert_crease, edge_crease, boundary_smooth, uv_smooth));
} }
if (mesh->totvert == 0 || mesh->totedge == 0) {
return;
}
bke::MeshFieldContext point_context{*mesh, ATTR_DOMAIN_POINT};
FieldEvaluator point_evaluator(point_context, mesh->totvert);
point_evaluator.add(vertex_crease_field);
point_evaluator.evaluate();
const VArray<float> vertex_creases = point_evaluator.get_evaluated<float>(0);
bke::MeshFieldContext edge_context{*mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator edge_evaluator(edge_context, mesh->totedge);
edge_evaluator.add(edge_crease_field);
edge_evaluator.evaluate();
const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0);
const bool use_creases = varray_is_nonzero(vertex_creases) || varray_is_nonzero(edge_creases);
if (use_creases) {
/* Due to the "BKE_subdiv" API, the crease layers must be on the input mesh. But in this node
* they are provided as separate inputs, not as custom data layers. When needed, retrieve the
* mesh with write access and store the new crease values there. */
Mesh &mesh_for_write = *geometry_set.get_mesh_for_write();
write_vertex_creases(mesh_for_write, vertex_creases);
write_edge_creases(mesh_for_write, edge_creases);
mesh = &mesh_for_write;
}
/* Initialize mesh settings. */
SubdivToMeshSettings mesh_settings;
mesh_settings.resolution = (1 << subdiv_level) + 1;
mesh_settings.use_optimal_display = false;
/* Initialize subdivision settings. */
SubdivSettings subdiv_settings;
subdiv_settings.is_simple = false;
subdiv_settings.is_adaptive = false;
subdiv_settings.use_creases = use_creases;
subdiv_settings.level = subdiv_level;
subdiv_settings.vtx_boundary_interpolation =
BKE_subdiv_vtx_boundary_interpolation_from_subsurf(boundary_smooth);
subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
uv_smooth);
/* Apply subdivision to mesh. */
Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh);
/* In case of bad topology, skip to input mesh. */
if (subdiv == nullptr) {
return;
}
Mesh *result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh);
BKE_subdiv_free(subdiv);
if (use_creases) {
/* Remove the layer in case it was created by the node from the field input. The fact
* that this node uses #CD_CREASE to input creases to the subdivision code is meant to be
* an implementation detail ideally. */
CustomData_free_layers(&result->edata, CD_CREASE, result->totedge);
}
geometry_set.replace_mesh(result);
}); });
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
#endif #endif
params.set_output("Mesh", std::move(geometry_set)); params.set_output("Mesh", std::move(geometry_set));
} }

View File

@ -23,6 +23,7 @@
#include "BLI_cpp_types.hh" #include "BLI_cpp_types.hh"
#include "BLI_dot_export.hh" #include "BLI_dot_export.hh"
#include "BLI_hash.h" #include "BLI_hash.h"
#include "BLI_hash_md5.h"
#include "BLI_lazy_threading.hh" #include "BLI_lazy_threading.hh"
#include "BLI_map.hh" #include "BLI_map.hh"
@ -104,6 +105,43 @@ static void lazy_function_interface_from_node(const bNode &node,
} }
} }
/**
* An anonymous attribute created by a node.
*/
class NodeAnonymousAttributeID : public AnonymousAttributeID {
std::string long_name_;
std::string socket_name_;
public:
NodeAnonymousAttributeID(const Object &object,
const ComputeContext &compute_context,
const bNode &bnode,
const StringRef identifier,
const StringRef name)
: socket_name_(name)
{
const ComputeContextHash &hash = compute_context.hash();
{
std::stringstream ss;
ss << hash << "_" << object.id.name << "_" << bnode.identifier << "_" << identifier;
long_name_ = ss.str();
}
{
uint64_t hash_result[2];
BLI_hash_md5_buffer(long_name_.data(), long_name_.size(), hash_result);
std::stringstream ss;
ss << ".a_" << std::hex << hash_result[0] << hash_result[1];
name_ = ss.str();
BLI_assert(name_.size() < MAX_CUSTOMDATA_LAYER_NAME);
}
}
std::string user_name() const override
{
return socket_name_;
}
};
/** /**
* Used for most normal geometry nodes like Subdivision Surface and Set Position. * Used for most normal geometry nodes like Subdivision Surface and Set Position.
*/ */
@ -119,6 +157,25 @@ class LazyFunctionForGeometryNode : public LazyFunction {
* propagated to the output. * propagated to the output.
*/ */
const Span<int> lf_input_for_attribute_propagation_to_output_; const Span<int> lf_input_for_attribute_propagation_to_output_;
/**
* Maps #bNodeSocket::index_in_tree to input/output indices of the current lazy-function.
*/
const Span<int> lf_index_by_bsocket_;
/**
* A bool for every output bsocket. If true, the socket just outputs a field containing an
* anonymous attribute id. If only such outputs are requested by other nodes, the node itself
* does not have to execute.
*/
Vector<bool> is_attribute_output_bsocket_;
struct OutputAttributeID {
int bsocket_index;
AnonymousAttributeIDPtr attribute_id;
};
struct Storage {
Vector<OutputAttributeID, 1> attributes;
};
public: public:
LazyFunctionForGeometryNode(const bNode &node, LazyFunctionForGeometryNode(const bNode &node,
@ -128,7 +185,9 @@ class LazyFunctionForGeometryNode : public LazyFunction {
: node_(node), : node_(node),
lf_input_for_output_bsocket_usage_(r_lf_input_for_output_bsocket_usage), lf_input_for_output_bsocket_usage_(r_lf_input_for_output_bsocket_usage),
lf_input_for_attribute_propagation_to_output_( lf_input_for_attribute_propagation_to_output_(
r_lf_input_for_attribute_propagation_to_output) r_lf_input_for_attribute_propagation_to_output),
lf_index_by_bsocket_(r_lf_index_by_bsocket),
is_attribute_output_bsocket_(node.output_sockets().size(), false)
{ {
BLI_assert(node.typeinfo->geometry_node_execute != nullptr); BLI_assert(node.typeinfo->geometry_node_execute != nullptr);
debug_name_ = node.name; debug_name_ = node.name;
@ -139,6 +198,16 @@ class LazyFunctionForGeometryNode : public LazyFunction {
if (relations == nullptr) { if (relations == nullptr) {
return; return;
} }
if (!relations->available_relations.is_empty()) {
/* Inputs are only used when an output is used that is not just outputting an anonymous
* attribute field. */
for (lf::Input &input : inputs_) {
input.usage = lf::ValueUsage::Maybe;
}
for (const aal::AvailableRelation &relation : relations->available_relations) {
is_attribute_output_bsocket_[relation.field_output] = true;
}
}
Vector<const bNodeSocket *> handled_field_outputs; Vector<const bNodeSocket *> handled_field_outputs;
for (const aal::AvailableRelation &relation : relations->available_relations) { for (const aal::AvailableRelation &relation : relations->available_relations) {
const bNodeSocket &output_bsocket = node.output_socket(relation.field_output); const bNodeSocket &output_bsocket = node.output_socket(relation.field_output);
@ -162,16 +231,90 @@ class LazyFunctionForGeometryNode : public LazyFunction {
} }
} }
void *init_storage(LinearAllocator<> &allocator) const override
{
return allocator.construct<Storage>().release();
}
void destruct_storage(void *storage) const override
{
Storage *s = static_cast<Storage *>(storage);
std::destroy_at(s);
}
void execute_impl(lf::Params &params, const lf::Context &context) const override void execute_impl(lf::Params &params, const lf::Context &context) const override
{ {
Storage *storage = static_cast<Storage *>(context.storage);
GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
BLI_assert(user_data != nullptr); BLI_assert(user_data != nullptr);
/* Lazily create the required anonymous attribute ids. */
auto get_output_attribute_id = [&](const int output_bsocket_index) -> AnonymousAttributeIDPtr {
for (const OutputAttributeID &node_output_attribute : storage->attributes) {
if (node_output_attribute.bsocket_index == output_bsocket_index) {
return node_output_attribute.attribute_id;
}
}
const bNodeSocket &bsocket = node_.output_socket(output_bsocket_index);
AnonymousAttributeIDPtr attribute_id = MEM_new<NodeAnonymousAttributeID>(
__func__,
*user_data->modifier_data->self_object,
*user_data->compute_context,
node_,
bsocket.identifier,
bsocket.name);
storage->attributes.append({output_bsocket_index, attribute_id});
return attribute_id;
};
bool used_non_attribute_output_exists = false;
for (const int output_bsocket_index : node_.output_sockets().index_range()) {
const bNodeSocket &output_bsocket = node_.output_socket(output_bsocket_index);
const int lf_index = lf_index_by_bsocket_[output_bsocket.index_in_tree()];
if (lf_index == -1) {
continue;
}
const lf::ValueUsage output_usage = params.get_output_usage(lf_index);
if (output_usage == lf::ValueUsage::Unused) {
continue;
}
if (is_attribute_output_bsocket_[output_bsocket_index]) {
if (params.output_was_set(lf_index)) {
continue;
}
this->output_anonymous_attribute_field(
params, lf_index, get_output_attribute_id(output_bsocket_index));
}
else {
if (output_usage == lf::ValueUsage::Used) {
used_non_attribute_output_exists = true;
}
}
}
if (!used_non_attribute_output_exists) {
/* Only attribute outputs are used currently, no need to evaluate the full node and its
* inputs. */
return;
}
bool missing_input = false;
for (const int lf_index : inputs_.index_range()) {
if (params.try_get_input_data_ptr_or_request(lf_index) == nullptr) {
missing_input = true;
}
}
if (missing_input) {
/* Wait until all inputs are available. */
return;
}
GeoNodeExecParams geo_params{node_, GeoNodeExecParams geo_params{node_,
params, params,
context, context,
lf_input_for_output_bsocket_usage_, lf_input_for_output_bsocket_usage_,
lf_input_for_attribute_propagation_to_output_}; lf_input_for_attribute_propagation_to_output_,
get_output_attribute_id};
geo_eval_log::TimePoint start_time = geo_eval_log::Clock::now(); geo_eval_log::TimePoint start_time = geo_eval_log::Clock::now();
node_.typeinfo->geometry_node_execute(geo_params); node_.typeinfo->geometry_node_execute(geo_params);
@ -184,6 +327,24 @@ class LazyFunctionForGeometryNode : public LazyFunction {
} }
} }
/**
* Output the given anonymous attribute id as a field.
*/
void output_anonymous_attribute_field(lf::Params &params,
const int lf_index,
AnonymousAttributeIDPtr attribute_id) const
{
const ValueOrFieldCPPType &value_or_field_cpp_type = *ValueOrFieldCPPType::get_from_self(
*outputs_[lf_index].type);
GField output_field{
std::make_shared<AnonymousAttributeFieldInput>(std::move(attribute_id),
value_or_field_cpp_type.value,
node_.label_or_name() + TIP_(" node"))};
void *r_value = params.get_output_data_ptr(lf_index);
value_or_field_cpp_type.construct_from_field(r_value, std::move(output_field));
params.output_set(lf_index);
}
std::string input_name(const int index) const override std::string input_name(const int index) const override
{ {
for (const bNodeSocket *bsocket : node_.output_sockets()) { for (const bNodeSocket *bsocket : node_.output_sockets()) {
@ -343,10 +504,10 @@ static void execute_multi_function_on_value_or_field(
/* Construct the new field node. */ /* Construct the new field node. */
std::shared_ptr<fn::FieldOperation> operation; std::shared_ptr<fn::FieldOperation> operation;
if (owned_fn) { if (owned_fn) {
operation = std::make_shared<fn::FieldOperation>(owned_fn, std::move(input_fields)); operation = fn::FieldOperation::Create(owned_fn, std::move(input_fields));
} }
else { else {
operation = std::make_shared<fn::FieldOperation>(fn, std::move(input_fields)); operation = fn::FieldOperation::Create(fn, std::move(input_fields));
} }
/* Store the new fields in the output. */ /* Store the new fields in the output. */

View File

@ -9,40 +9,10 @@
#include "NOD_geometry_exec.hh" #include "NOD_geometry_exec.hh"
#include "BLI_hash_md5.h"
#include "node_geometry_util.hh" #include "node_geometry_util.hh"
namespace blender::nodes { namespace blender::nodes {
NodeAnonymousAttributeID::NodeAnonymousAttributeID(const Object &object,
const ComputeContext &compute_context,
const bNode &bnode,
const StringRef identifier,
const StringRef name)
: socket_name_(name)
{
const ComputeContextHash &hash = compute_context.hash();
{
std::stringstream ss;
ss << hash << "_" << object.id.name << "_" << bnode.identifier << "_" << identifier;
long_name_ = ss.str();
}
{
uint64_t hash_result[2];
BLI_hash_md5_buffer(long_name_.data(), long_name_.size(), hash_result);
std::stringstream ss;
ss << ".a_" << std::hex << hash_result[0] << hash_result[1];
name_ = ss.str();
BLI_assert(name_.size() < MAX_CUSTOMDATA_LAYER_NAME);
}
}
std::string NodeAnonymousAttributeID::user_name() const
{
return socket_name_;
}
void GeoNodeExecParams::error_message_add(const NodeWarningType type, void GeoNodeExecParams::error_message_add(const NodeWarningType type,
const StringRef message) const const StringRef message) const
{ {
@ -153,11 +123,6 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name
return nullptr; return nullptr;
} }
std::string GeoNodeExecParams::attribute_producer_name() const
{
return node_.label_or_name() + TIP_(" node");
}
void GeoNodeExecParams::set_default_remaining_outputs() void GeoNodeExecParams::set_default_remaining_outputs()
{ {
params_.set_default_remaining_outputs(); params_.set_default_remaining_outputs();

View File

@ -39,6 +39,13 @@ using namespace blender;
using blender::fn::ValueOrField; using blender::fn::ValueOrField;
using blender::nodes::SocketDeclarationPtr; using blender::nodes::SocketDeclarationPtr;
extern "C" void ED_node_type_draw_color(const char *idname, float *r_color);
void node_type_draw_color(const char *idname, float *r_color)
{
ED_node_type_draw_color(idname, r_color);
}
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree, struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node, struct bNode *node,
struct bNodeSocketTemplate *stemp, struct bNodeSocketTemplate *stemp,

View File

@ -2060,7 +2060,7 @@ static int arg_handle_load_file(int UNUSED(argc), const char **argv, void *data)
BLI_strncpy(filepath, argv[0], sizeof(filepath)); BLI_strncpy(filepath, argv[0], sizeof(filepath));
BLI_path_slash_native(filepath); BLI_path_slash_native(filepath);
BLI_path_abs_from_cwd(filepath, sizeof(filepath)); BLI_path_abs_from_cwd(filepath, sizeof(filepath));
BLI_path_normalize(NULL, filepath); BLI_path_normalize(filepath);
/* load the file */ /* load the file */
BKE_reports_init(&reports, RPT_PRINT); BKE_reports_init(&reports, RPT_PRINT);