From 53cb09357e74a936f25d66b751fc5ea1c0431f14 Mon Sep 17 00:00:00 2001 From: Jason Fielder Date: Mon, 19 Jun 2023 20:28:51 +0200 Subject: [PATCH 01/36] Fix #108792: Ensure Metal buffers correctly freed on exit Replaces vector of allocations with dynamic linked list. Bug caused by previously freed buffers still having been in the list. Linked list enables fast removal of already-released buffers. Also ensured that the memory manager classes are included in memory tracking. Authored by Apple: Michael Parkin-White Pull Request: https://projects.blender.org/blender/blender/pulls/108940 --- source/blender/gpu/metal/mtl_memory.hh | 23 ++++- source/blender/gpu/metal/mtl_memory.mm | 123 ++++++++++++++++++++++--- 2 files changed, 130 insertions(+), 16 deletions(-) diff --git a/source/blender/gpu/metal/mtl_memory.hh b/source/blender/gpu/metal/mtl_memory.hh index 0142f5d248a5..22cb3616ec2b 100644 --- a/source/blender/gpu/metal/mtl_memory.hh +++ b/source/blender/gpu/metal/mtl_memory.hh @@ -107,6 +107,11 @@ class MTLUniformBuf; /* MTLBuffer allocation wrapper. */ class MTLBuffer { + public: + /* NOTE: ListBase API is not used due to cutsom destructor operation required to release + * Metal objective C buffer resource. */ + gpu::MTLBuffer *next, *prev; + private: /* Metal resource. */ id metal_buffer_; @@ -177,6 +182,8 @@ class MTLBuffer { /* Safety check to ensure buffers are not used after free. */ void debug_ensure_used(); + + MEM_CXX_CLASS_ALLOC_FUNCS("MTLBuffer"); }; /* View into part of an MTLBuffer. */ @@ -333,6 +340,8 @@ class MTLSafeFreeList { } } } + + MEM_CXX_CLASS_ALLOC_FUNCS("MTLSafeFreeList"); }; /* MTLBuffer pools. */ @@ -362,7 +371,7 @@ class MTLBufferPool { #endif /* Metal resources. */ - bool ensure_initialised_ = false; + bool initialized_ = false; id device_ = nil; /* The buffer selection aims to pick a buffer which meets the minimum size requirements. @@ -389,7 +398,10 @@ class MTLBufferPool { std::mutex buffer_pool_lock_; blender::Map buffer_pools_; - blender::Vector allocations_; + + /* Linked list to track all existing allocations. Prioritizing fast insert/deletion. */ + gpu::MTLBuffer *allocations_list_base_; + uint allocations_list_size_; /* Maintain a queue of all MTLSafeFreeList's that have been released * by the GPU and are ready to have their buffers re-inserted into the @@ -432,6 +444,11 @@ class MTLBufferPool { void ensure_buffer_pool(MTLResourceOptions options); void insert_buffer_into_pool(MTLResourceOptions options, gpu::MTLBuffer *buffer); void free(); + + /* Allocations list. */ + void allocations_list_insert(gpu::MTLBuffer *buffer); + void allocations_list_delete(gpu::MTLBuffer *buffer); + void allocations_list_delete_all(); }; /* Scratch buffers are circular-buffers used for temporary data within the current frame. @@ -492,6 +509,8 @@ class MTLScratchBufferManager { * This call will perform a partial flush of the buffer starting from * the last offset the data was flushed from, to the current offset. */ void flush_active_scratch_buffer(); + + MEM_CXX_CLASS_ALLOC_FUNCS("MTLBufferPool"); }; /** \} */ diff --git a/source/blender/gpu/metal/mtl_memory.mm b/source/blender/gpu/metal/mtl_memory.mm index 73b167a9ce30..9219d80a7b26 100644 --- a/source/blender/gpu/metal/mtl_memory.mm +++ b/source/blender/gpu/metal/mtl_memory.mm @@ -25,9 +25,9 @@ void MTLBufferPool::init(id mtl_device) { - if (!ensure_initialised_) { + if (!initialized_) { BLI_assert(mtl_device); - ensure_initialised_ = true; + initialized_ = true; device_ = mtl_device; #if MTL_DEBUG_MEMORY_STATISTICS == 1 @@ -39,6 +39,10 @@ /* Track pool allocation size. */ allocations_in_pool_ = 0; + /* Live allocations list. */ + allocations_list_base_ = nullptr; + allocations_list_size_ = 0; + /* Free pools -- Create initial safe free pool */ BLI_assert(current_free_list_ == nullptr); this->begin_new_safe_list(); @@ -53,17 +57,29 @@ void MTLBufferPool::free() { buffer_pool_lock_.lock(); - for (auto buffer : allocations_) { - BLI_assert(buffer); - delete buffer; + + /* Delete all existing allocations. */ + allocations_list_delete_all(); + + /* Release safe free lists. */ + for (int safe_pool_free_index = 0; safe_pool_free_index < completed_safelist_queue_.size(); + safe_pool_free_index++) + { + delete completed_safelist_queue_[safe_pool_free_index]; + } + completed_safelist_queue_.clear(); + if (current_free_list_ != nullptr) { + delete current_free_list_; + current_free_list_ = nullptr; } - allocations_.clear(); + /* Clear and release memory pools. */ for (std::multiset *buffer_pool : buffer_pools_.values()) { delete buffer_pool; } + buffer_pools_.clear(); buffer_pool_lock_.unlock(); } @@ -155,10 +171,7 @@ new_buffer = new gpu::MTLBuffer(device_, size, options, alignment); /* Track allocation in context. */ - allocations_.append(new_buffer); -#if MTL_DEBUG_MEMORY_STATISTICS == 1 - total_allocation_bytes_ += aligned_alloc_size; -#endif + allocations_list_insert(new_buffer); } else { /* Re-use suitable buffer. */ @@ -289,6 +302,7 @@ * animation. * If memory is continually used, then we do not want to free this memory as it will be * re-allocated during a short time period. */ + const time_t time_now = std::time(nullptr); for (auto buffer_pool_list : buffer_pools_.items()) { MTLBufferPoolOrderedList *pool_allocations = buffer_pool_list.value; @@ -323,12 +337,13 @@ if (time_passed > deletion_time_threshold_s) { - /* Delete allocation. */ - delete handle.buffer; + /* Remove buffer from global allocations list and release resource. */ + allocations_list_delete(handle.buffer); + + /* Remove buffer from pool and update pool statistics. */ pool_iterator = pool_allocations->erase(pool_iterator); allocations_in_pool_ -= handle.buffer_size; #if MTL_DEBUG_MEMORY_STATISTICS == 1 - total_allocation_bytes_ -= handle.buffer_size; buffers_in_pool_--; #endif continue; @@ -343,7 +358,7 @@ uint framealloc = (uint)per_frame_allocation_count_; printf(" Allocations in frame: %u\n", framealloc); - printf(" Total Buffers allocated: %u\n", (uint)allocations_.size()); + printf(" Total Buffers allocated: %u\n", allocations_list_size_); printf(" Total Memory allocated: %u MB\n", (uint)total_allocation_bytes_ / (1024 * 1024)); uint allocs = (uint)(allocations_in_pool_) / 1024 / 2024; @@ -453,6 +468,80 @@ #endif } +void MTLBufferPool::allocations_list_insert(gpu::MTLBuffer *buffer) +{ + /* NOTE: Function should only be called while buffer_pool_lock_ is acquired. */ + BLI_assert(initialized_); + BLI_assert(buffer != nullptr); + + /* Insert buffer at base of allocations list. */ + gpu::MTLBuffer *current_head = allocations_list_base_; + buffer->next = current_head; + buffer->prev = nullptr; + + if (current_head != nullptr) { + current_head->prev = buffer; + } + + allocations_list_base_ = buffer; + allocations_list_size_++; + +#if MTL_DEBUG_MEMORY_STATISTICS == 1 + total_allocation_bytes_ += buffer->get_size(); +#endif +} + +void MTLBufferPool::allocations_list_delete(gpu::MTLBuffer *buffer) +{ + /* NOTE: Function should only be called while buffer_pool_lock_ is acquired. */ + /* Remove a buffer link in the allocations chain. */ + BLI_assert(initialized_); + BLI_assert(buffer != nullptr); + BLI_assert(allocations_list_size_ >= 1); + + gpu::MTLBuffer *next = buffer->next; + gpu::MTLBuffer *prev = buffer->prev; + + if (prev != nullptr) { + BLI_assert(prev->next == buffer); + prev->next = next; + } + + if (next != nullptr) { + BLI_assert(next->prev == buffer); + next->prev = prev; + } + + if (allocations_list_base_ == buffer) { + allocations_list_base_ = next; + BLI_assert(prev == nullptr); + } + allocations_list_size_--; + +#if MTL_DEBUG_MEMORY_STATISTICS == 1 + total_allocation_bytes_ -= buffer->get_size(); +#endif + + /* Delete buffer. */ + delete buffer; +} + +void MTLBufferPool::allocations_list_delete_all() +{ + gpu::MTLBuffer *current = allocations_list_base_; + while (current != nullptr) { + gpu::MTLBuffer *next = current->next; + delete current; + current = next; + } + allocations_list_size_ = 0; + allocations_list_base_ = nullptr; + +#if MTL_DEBUG_MEMORY_STATISTICS == 1 + total_allocation_bytes_ = 0; +#endif +} + MTLSafeFreeList::MTLSafeFreeList() { reference_count_ = 1; @@ -565,6 +654,9 @@ else { data_ = nullptr; } + + /* Linked resources. */ + next = prev = nullptr; } MTLBuffer::MTLBuffer(id external_buffer) @@ -584,6 +676,9 @@ this->set_usage_size(size_); data_ = [metal_buffer_ contents]; in_use_ = true; + + /* Linked resources. */ + next = prev = nullptr; } gpu::MTLBuffer::~MTLBuffer() From c3a12704de112696c9d273cff3a5e6b0040b85bd Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 19 Jun 2023 21:33:14 -0400 Subject: [PATCH 02/36] Fix: Torus primitive shaded smooth after mesh refactor See #109070 (full fix will contain a more general change). --- scripts/startup/bl_operators/add_mesh_torus.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/startup/bl_operators/add_mesh_torus.py b/scripts/startup/bl_operators/add_mesh_torus.py index de23917b2a05..1fa7428983ae 100644 --- a/scripts/startup/bl_operators/add_mesh_torus.py +++ b/scripts/startup/bl_operators/add_mesh_torus.py @@ -243,6 +243,7 @@ def execute(self, context): mesh.vertices.foreach_set("co", verts_loc) mesh.polygons.foreach_set("loop_start", range(0, nbr_loops, 4)) mesh.loops.foreach_set("vertex_index", faces) + mesh.shade_flat() if self.generate_uvs: add_uvs(mesh, self.minor_segments, self.major_segments) From ee352c968fd165fafd17adcc696e98bb0efa844a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 19 Jun 2023 21:40:59 -0400 Subject: [PATCH 03/36] Fix #109070: Creating mesh from Python skips setting faces sharp The default value of the "sharp_face" attribute is False like other boolean attributes. That mistakenly changed behavior in addons that created meshes with `Mesh.from_pydata`. To fix, add an argument with a default value that maintains the behavior from before, and add convenience, `shade_smooth` and `shade_flat` methods. --- scripts/modules/bpy_types.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/scripts/modules/bpy_types.py b/scripts/modules/bpy_types.py index 9d67805a4815..9fb0e390b56a 100644 --- a/scripts/modules/bpy_types.py +++ b/scripts/modules/bpy_types.py @@ -534,7 +534,7 @@ def ord_ind(i1, i2): class Mesh(bpy_types.ID): __slots__ = () - def from_pydata(self, vertices, edges, faces): + def from_pydata(self, vertices, edges, faces, shade_flat=True): """ Make a mesh from a list of vertices/edges/faces Until we have a nicer way to make geometry, use this. @@ -591,6 +591,9 @@ def from_pydata(self, vertices, edges, faces): self.polygons.foreach_set("loop_start", loop_starts) self.polygons.foreach_set("vertices", vertex_indices) + if shade_flat: + self.shade_flat() + if edges_len or faces_len: self.update( # Needed to either: @@ -605,6 +608,26 @@ def from_pydata(self, vertices, edges, faces): def edge_keys(self): return [ed.key for ed in self.edges] + def shade_flat(self): + """ + Render and display faces uniform, using face normals, + setting the "sharp_face" attribute true for every face + """ + sharp_faces = self.attributes.new("sharp_face", 'BOOLEAN', 'FACE') + for value in sharp_faces.data: + value.value = True + + def shade_smooth(self): + """ + Render and display faces smooth, using interpolated vertex normals, + removing the "sharp_face" attribute + """ + try: + attribute = self.attributes["sharp_face"] + self.attributes.remove(attribute) + except KeyError: + pass + class MeshEdge(StructRNA): __slots__ = () From 75ac92ed85e6b04678ecae0029941efca4a52fa9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 20 Jun 2023 11:45:45 +1000 Subject: [PATCH 04/36] Fix #109061: UI list generic template fails with error --- scripts/templates_py/ui_list_generic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/templates_py/ui_list_generic.py b/scripts/templates_py/ui_list_generic.py index d33075ddd650..1827cae53d6b 100644 --- a/scripts/templates_py/ui_list_generic.py +++ b/scripts/templates_py/ui_list_generic.py @@ -18,8 +18,8 @@ def draw(self, context): draw_ui_list( layout, context, - list_context_path="scene.my_list", - active_index_context_path="scene.my_list_active_index" + list_path="scene.my_list", + active_index_path="scene.my_list_active_index", ) From 1d4ce828e5dbf2379930e979e81b63870d724208 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 20 Jun 2023 11:48:41 +1000 Subject: [PATCH 05/36] PyAPI: require a unique_id for bl_ui.generic_ui_list An empty unique_id would generate a warning when drawing. --- scripts/startup/bl_ui/generic_ui_list.py | 4 ++-- scripts/templates_py/ui_list_generic.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/startup/bl_ui/generic_ui_list.py b/scripts/startup/bl_ui/generic_ui_list.py index 8e58983d38e4..d249c55016f4 100644 --- a/scripts/startup/bl_ui/generic_ui_list.py +++ b/scripts/startup/bl_ui/generic_ui_list.py @@ -29,7 +29,7 @@ def draw_ui_list( context, class_name="UI_UL_list", *, - unique_id="", + unique_id, list_path, active_index_path, insertion_operators=True, @@ -46,7 +46,7 @@ def draw_ui_list( :type context: :class:`Context` :arg class_name: Name of the UIList class to draw. The default is the UIList class that ships with Blender. :type class_name: str - :arg unique_id: Optional identifier, in case wanting to draw multiple unique copies of a list. + :arg unique_id: Unique identifier to differentiate this from other UI lists. :type unique_id: str :arg list_path: Data path of the list relative to context, eg. "object.vertex_groups". :type list_path: str diff --git a/scripts/templates_py/ui_list_generic.py b/scripts/templates_py/ui_list_generic.py index 1827cae53d6b..4113a99d6811 100644 --- a/scripts/templates_py/ui_list_generic.py +++ b/scripts/templates_py/ui_list_generic.py @@ -20,6 +20,7 @@ def draw(self, context): context, list_path="scene.my_list", active_index_path="scene.my_list_active_index", + unique_id="my_list_id", ) From a474fdece502b306b277286d2ad9fcbcef3fa79a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 20 Jun 2023 12:06:51 +1000 Subject: [PATCH 06/36] Fix every call to Mesh.shade_flat(..) adding a new layer Also avoid exception handling for checking if the layer exists. --- scripts/modules/bpy_types.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/scripts/modules/bpy_types.py b/scripts/modules/bpy_types.py index 9fb0e390b56a..168ab5626dbf 100644 --- a/scripts/modules/bpy_types.py +++ b/scripts/modules/bpy_types.py @@ -613,8 +613,19 @@ def shade_flat(self): Render and display faces uniform, using face normals, setting the "sharp_face" attribute true for every face """ - sharp_faces = self.attributes.new("sharp_face", 'BOOLEAN', 'FACE') - for value in sharp_faces.data: + attr = self.attributes.get("sharp_face") + if attr is None: + pass + elif attr.data_type == 'BOOLEAN' and attr.domain == 'FACE': + pass + else: + self.attributes.remove(attr) + attr = None + + if attr is None: + attr = self.attributes.new("sharp_face", 'BOOLEAN', 'FACE') + + for value in attr.data: value.value = True def shade_smooth(self): @@ -622,11 +633,9 @@ def shade_smooth(self): Render and display faces smooth, using interpolated vertex normals, removing the "sharp_face" attribute """ - try: - attribute = self.attributes["sharp_face"] - self.attributes.remove(attribute) - except KeyError: - pass + attr = self.attributes.get("sharp_face") + if attr is not None: + self.attributes.remove(attr) class MeshEdge(StructRNA): From e516f25e8f79ae15debf0029aeb9de6acb6768ea Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 19 Jun 2023 22:27:39 -0400 Subject: [PATCH 07/36] Fix #109060: Crash with mask modifier smooth option A mistake similar to the one fixed in 17aaff69c68b7903b905. Also remove the DNA deprecated define added in the last fix. --- source/blender/modifiers/intern/MOD_mask.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 8091b7bc31ad..c9ef85d40764 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -5,8 +5,6 @@ * \ingroup modifiers */ -#define DNA_DEPRECATED_ALLOW - #include "MEM_guardedalloc.h" #include "BLI_utildefines.h" @@ -296,7 +294,7 @@ static void compute_interpolated_polys(const Mesh *mesh, } if (0 < in_count && in_count < poly_src.size()) { /* Ring search starting at a vertex which is not included in the mask. */ - int last_corner_vert = corner_verts[start]; + int last_corner_vert = poly_verts_src[start]; bool v_loop_in_mask_last = vertex_mask[last_corner_vert]; for (const int j : poly_verts_src.index_range()) { const int corner_vert = poly_verts_src[(start + 1 + j) % poly_src.size()]; From 2100ebca7a26efa47f7965e6739f99700ea06e2f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 20 Jun 2023 13:23:30 +1000 Subject: [PATCH 08/36] Cleanup: use the term "value" for RNA get/set functions This is already used in most functions. --- source/blender/blenlib/intern/path_util.c | 4 +-- source/blender/makesrna/intern/rna_object.cc | 6 ++-- source/blender/makesrna/intern/rna_particle.c | 28 +++++++++---------- source/blender/makesrna/intern/rna_texture.c | 6 ++-- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 7d3b6f4c095d..cf6f2dab6cbd 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -196,8 +196,8 @@ static int path_normalize_impl(char *path, bool check_blend_relative_prefix) /* NOTE(@ideasman42): * `memmove(start, eind, strlen(eind) + 1);` * is the same as - * `strcpy(start, eind);` - * except `strcpy` should not be used because there is overlap, + * `BLI_strncpy(start, eind, ...);` + * except string-copy should not be used because there is overlap, * so use `memmove` 's slightly more obscure syntax. */ /* Inline replacement: diff --git a/source/blender/makesrna/intern/rna_object.cc b/source/blender/makesrna/intern/rna_object.cc index 17ce3afe6342..dc2ed421fc91 100644 --- a/source/blender/makesrna/intern/rna_object.cc +++ b/source/blender/makesrna/intern/rna_object.cc @@ -1357,7 +1357,7 @@ static int rna_MaterialSlot_name_length(PointerRNA *ptr) return 0; } -static void rna_MaterialSlot_name_get(PointerRNA *ptr, char *str) +static void rna_MaterialSlot_name_get(PointerRNA *ptr, char *value) { Object *ob = reinterpret_cast(ptr->owner_id); Material *ma; @@ -1366,10 +1366,10 @@ static void rna_MaterialSlot_name_get(PointerRNA *ptr, char *str) ma = BKE_object_material_get(ob, index + 1); if (ma) { - strcpy(str, ma->id.name + 2); + strcpy(value, ma->id.name + 2); } else { - str[0] = '\0'; + value[0] = '\0'; } } diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index b88cbc16826f..176070ef52d5 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -1168,7 +1168,7 @@ static void rna_ParticleSystem_active_particle_target_index_set(struct PointerRN } } -static void rna_ParticleTarget_name_get(PointerRNA *ptr, char *str) +static void rna_ParticleTarget_name_get(PointerRNA *ptr, char *value) { ParticleTarget *pt = ptr->data; @@ -1185,28 +1185,28 @@ static void rna_ParticleTarget_name_get(PointerRNA *ptr, char *str) if (psys) { if (pt->ob) { - BLI_sprintf(str, "%s: %s", pt->ob->id.name + 2, psys->name); + BLI_sprintf(value, "%s: %s", pt->ob->id.name + 2, psys->name); } else { - strcpy(str, psys->name); + strcpy(value, psys->name); } } else { - strcpy(str, TIP_("Invalid target!")); + strcpy(value, TIP_("Invalid target!")); } } else { - strcpy(str, TIP_("Invalid target!")); + strcpy(value, TIP_("Invalid target!")); } } static int rna_ParticleTarget_name_length(PointerRNA *ptr) { - char tstr[MAX_ID_NAME + MAX_ID_NAME + 64]; + char tvalue[MAX_ID_NAME + MAX_ID_NAME + 64]; - rna_ParticleTarget_name_get(ptr, tstr); + rna_ParticleTarget_name_get(ptr, tvalue); - return strlen(tstr); + return strlen(tvalue); } static int particle_id_check(const PointerRNA *ptr) @@ -1304,7 +1304,7 @@ static void rna_ParticleDupliWeight_active_index_set(struct PointerRNA *ptr, int } } -static void rna_ParticleDupliWeight_name_get(PointerRNA *ptr, char *str) +static void rna_ParticleDupliWeight_name_get(PointerRNA *ptr, char *value) { ParticleSettings *part = (ParticleSettings *)ptr->owner_id; psys_find_group_weights(part); @@ -1312,20 +1312,20 @@ static void rna_ParticleDupliWeight_name_get(PointerRNA *ptr, char *str) ParticleDupliWeight *dw = ptr->data; if (dw->ob) { - BLI_sprintf(str, "%s: %i", dw->ob->id.name + 2, dw->count); + BLI_sprintf(value, "%s: %i", dw->ob->id.name + 2, dw->count); } else { - strcpy(str, "No object"); + strcpy(value, "No object"); } } static int rna_ParticleDupliWeight_name_length(PointerRNA *ptr) { - char tstr[MAX_ID_NAME + 64]; + char tvalue[MAX_ID_NAME + 64]; - rna_ParticleDupliWeight_name_get(ptr, tstr); + rna_ParticleDupliWeight_name_get(ptr, tvalue); - return strlen(tstr); + return strlen(tvalue); } static const EnumPropertyItem *rna_Particle_type_itemf(bContext *UNUSED(C), diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index a5764af75bfa..923651bdaac8 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -349,15 +349,15 @@ static int rna_TextureSlot_name_length(PointerRNA *ptr) return 0; } -static void rna_TextureSlot_name_get(PointerRNA *ptr, char *str) +static void rna_TextureSlot_name_get(PointerRNA *ptr, char *value) { MTex *mtex = ptr->data; if (mtex->tex) { - strcpy(str, mtex->tex->id.name + 2); + strcpy(value, mtex->tex->id.name + 2); } else { - str[0] = '\0'; + value[0] = '\0'; } } From 69d92bd3deec67544c36ecfbd6630f4f4661cb7a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 20 Jun 2023 13:23:33 +1000 Subject: [PATCH 09/36] Cleanup: remove strcpy usage Remove strcpy use in: - bone_autoside_name - BLI_string_flip_side_name - datatoc_icon utility. - RNA define error messages. - RNA UI registration. - extern/xdnd. --- extern/xdnd/xdnd.c | 5 +- intern/ghost/intern/GHOST_DropTargetWin32.cc | 5 +- .../blender/blenkernel/intern/action_test.cc | 10 ++-- source/blender/blenkernel/intern/armature.c | 28 +++++------ source/blender/blenlib/intern/string_utils.c | 36 +++++++------- source/blender/datatoc/datatoc_icon.c | 43 ++++++++-------- source/blender/makesrna/intern/rna_define.c | 49 +++++++++---------- .../blender/makesrna/intern/rna_object_api.cc | 7 ++- source/blender/makesrna/intern/rna_ui.cc | 6 +-- 9 files changed, 99 insertions(+), 90 deletions(-) diff --git a/extern/xdnd/xdnd.c b/extern/xdnd/xdnd.c index e8c51377e278..afd7156978fe 100644 --- a/extern/xdnd/xdnd.c +++ b/extern/xdnd/xdnd.c @@ -464,8 +464,9 @@ static char *concat_string_list (char **t, int *bytes) break; if (!(t[n][0])) break; - strcpy (s + l, t[n]); - l += strlen (t[n]) + 1; + int t_size = strlen (t[n]) + 1; + memcpy (s + l, t[n], t_size); + l += t_size; } *bytes = l; s[l] = '\0'; diff --git a/intern/ghost/intern/GHOST_DropTargetWin32.cc b/intern/ghost/intern/GHOST_DropTargetWin32.cc index a7f642cdbb04..513f61b0b552 100644 --- a/intern/ghost/intern/GHOST_DropTargetWin32.cc +++ b/intern/ghost/intern/GHOST_DropTargetWin32.cc @@ -270,10 +270,11 @@ void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *p_data_object) if (p_data_object->QueryGetData(&fmtetc) == S_OK) { if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) { char *str = (char *)::GlobalLock(stgmed.hGlobal); + int str_size = ::strlen(str) + 1; - tmp_string = (char *)::malloc(::strlen(str) + 1); + tmp_string = (char *)::malloc(str_size); if (tmp_string) { - ::strcpy(tmp_string, str); + ::memcpy(tmp_string, str, str_size); } /* Free memory. */ ::GlobalUnlock(stgmed.hGlobal); diff --git a/source/blender/blenkernel/intern/action_test.cc b/source/blender/blenkernel/intern/action_test.cc index 58c144057925..7f46f2c8cd83 100644 --- a/source/blender/blenkernel/intern/action_test.cc +++ b/source/blender/blenkernel/intern/action_test.cc @@ -2,6 +2,8 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_string.h" + #include "BKE_action.h" #include "DNA_action_types.h" @@ -53,10 +55,10 @@ TEST(action_groups, ReconstructGroupsWithReordering) bActionGroup groupB = {nullptr}; bActionGroup groupC = {nullptr}; bActionGroup groupD = {nullptr}; - strcpy(groupA.name, "groupA"); - strcpy(groupB.name, "groupB"); - strcpy(groupC.name, "groupC"); - strcpy(groupD.name, "groupD"); + STRNCPY(groupA.name, "groupA"); + STRNCPY(groupB.name, "groupB"); + STRNCPY(groupC.name, "groupC"); + STRNCPY(groupD.name, "groupD"); BLI_addtail(&action.groups, &groupA); BLI_addtail(&action.groups, &groupB); diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 75f95c20998c..c0b551e684d6 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -703,7 +703,7 @@ bool bone_autoside_name( { uint len; char basename[MAXBONENAME] = ""; - char extension[5] = ""; + const char *extension = NULL; len = strlen(name); if (len == 0) { @@ -723,18 +723,18 @@ bool bone_autoside_name( /* z-axis - vertical (top/bottom) */ if (IS_EQF(head, 0.0f)) { if (tail < 0) { - strcpy(extension, "Bot"); + extension = "Bot"; } else if (tail > 0) { - strcpy(extension, "Top"); + extension = "Top"; } } else { if (head < 0) { - strcpy(extension, "Bot"); + extension = "Bot"; } else { - strcpy(extension, "Top"); + extension = "Top"; } } } @@ -742,18 +742,18 @@ bool bone_autoside_name( /* y-axis - depth (front/back) */ if (IS_EQF(head, 0.0f)) { if (tail < 0) { - strcpy(extension, "Fr"); + extension = "Fr"; } else if (tail > 0) { - strcpy(extension, "Bk"); + extension = "Bk"; } } else { if (head < 0) { - strcpy(extension, "Fr"); + extension = "Fr"; } else { - strcpy(extension, "Bk"); + extension = "Bk"; } } } @@ -761,19 +761,19 @@ bool bone_autoside_name( /* x-axis - horizontal (left/right) */ if (IS_EQF(head, 0.0f)) { if (tail < 0) { - strcpy(extension, "R"); + extension = "R"; } else if (tail > 0) { - strcpy(extension, "L"); + extension = "L"; } } else { if (head < 0) { - strcpy(extension, "R"); + extension = "R"; /* XXX Shouldn't this be simple else, as for z and y axes? */ } else if (head > 0) { - strcpy(extension, "L"); + extension = "L"; } } } @@ -782,7 +782,7 @@ bool bone_autoside_name( * - truncate if there is an extension and it wouldn't be able to fit * - otherwise, just append to end */ - if (extension[0]) { + if (extension) { bool changed = true; while (changed) { /* remove extensions */ diff --git a/source/blender/blenlib/intern/string_utils.c b/source/blender/blenlib/intern/string_utils.c index f9d918663be8..efc098c16f80 100644 --- a/source/blender/blenlib/intern/string_utils.c +++ b/source/blender/blenlib/intern/string_utils.c @@ -133,14 +133,14 @@ size_t BLI_string_flip_side_name(char *name_dst, BLI_string_debug_size(name_dst, name_dst_maxncpy); size_t len; - char *prefix = alloca(name_dst_maxncpy); /* The part before the facing */ - char *suffix = alloca(name_dst_maxncpy); /* The part after the facing */ - char *replace = alloca(name_dst_maxncpy); /* The replacement string */ - char *number = alloca(name_dst_maxncpy); /* The number extension string */ + char *prefix = alloca(name_dst_maxncpy); /* The part before the facing */ + char *suffix = alloca(name_dst_maxncpy); /* The part after the facing */ + char *number = alloca(name_dst_maxncpy); /* The number extension string */ + const char *replace = NULL; char *index = NULL; bool is_set = false; - *prefix = *suffix = *replace = *number = '\0'; + *prefix = *suffix = *number = '\0'; /* always copy the name, since this can be called with an uninitialized string */ len = BLI_strncpy_rlen(name_dst, name_src, name_dst_maxncpy); @@ -169,19 +169,19 @@ size_t BLI_string_flip_side_name(char *name_dst, switch (name_dst[len - 1]) { case 'l': prefix[len - 1] = 0; - strcpy(replace, "r"); + replace = "r"; break; case 'r': prefix[len - 1] = 0; - strcpy(replace, "l"); + replace = "l"; break; case 'L': prefix[len - 1] = 0; - strcpy(replace, "R"); + replace = "R"; break; case 'R': prefix[len - 1] = 0; - strcpy(replace, "L"); + replace = "L"; break; default: is_set = false; @@ -193,22 +193,22 @@ size_t BLI_string_flip_side_name(char *name_dst, is_set = true; switch (name_dst[0]) { case 'l': - strcpy(replace, "r"); + replace = "r"; BLI_strncpy(suffix, name_dst + 1, name_dst_maxncpy); prefix[0] = 0; break; case 'r': - strcpy(replace, "l"); + replace = "l"; BLI_strncpy(suffix, name_dst + 1, name_dst_maxncpy); prefix[0] = 0; break; case 'L': - strcpy(replace, "R"); + replace = "R"; BLI_strncpy(suffix, name_dst + 1, name_dst_maxncpy); prefix[0] = 0; break; case 'R': - strcpy(replace, "L"); + replace = "L"; BLI_strncpy(suffix, name_dst + 1, name_dst_maxncpy); prefix[0] = 0; break; @@ -222,10 +222,10 @@ size_t BLI_string_flip_side_name(char *name_dst, if (((index = BLI_strcasestr(prefix, "right")) == prefix) || (index == prefix + len - 5)) { is_set = true; if (index[0] == 'r') { - strcpy(replace, "left"); + replace = "left"; } else { - strcpy(replace, (index[1] == 'I') ? "LEFT" : "Left"); + replace = (index[1] == 'I' ? "LEFT" : "Left"); } *index = 0; BLI_strncpy(suffix, index + 5, name_dst_maxncpy); @@ -233,10 +233,10 @@ size_t BLI_string_flip_side_name(char *name_dst, else if (((index = BLI_strcasestr(prefix, "left")) == prefix) || (index == prefix + len - 4)) { is_set = true; if (index[0] == 'l') { - strcpy(replace, "right"); + replace = "right"; } else { - strcpy(replace, (index[1] == 'E') ? "RIGHT" : "Right"); + replace = (index[1] == 'E' ? "RIGHT" : "Right"); } *index = 0; BLI_strncpy(suffix, index + 4, name_dst_maxncpy); @@ -244,7 +244,7 @@ size_t BLI_string_flip_side_name(char *name_dst, } return BLI_snprintf_rlen( - name_dst, name_dst_maxncpy, "%s%s%s%s", prefix, replace, suffix, number); + name_dst, name_dst_maxncpy, "%s%s%s%s", prefix, replace ? replace : "", suffix, number); } /* Unique name utils. */ diff --git a/source/blender/datatoc/datatoc_icon.c b/source/blender/datatoc/datatoc_icon.c index 9b603945a906..74c076f06045 100644 --- a/source/blender/datatoc/datatoc_icon.c +++ b/source/blender/datatoc/datatoc_icon.c @@ -36,17 +36,6 @@ /* -------------------------------------------------------------------- */ /* Utility functions */ -static int path_ensure_slash(char *path) -{ - int len = strlen(path); - if (len == 0 || path[len - 1] != SEP) { - path[len] = SEP; - path[len + 1] = '\0'; - return len + 1; - } - return len; -} - static bool path_test_extension(const char *filepath, const char *ext) { const size_t a = strlen(filepath); @@ -81,6 +70,25 @@ static const char *path_basename(const char *path) return filename ? filename + 1 : path; } +static bool path_join(char *filepath, + size_t filepath_maxncpy, + const char *dirpath, + const char *filename) +{ + int dirpath_len = strlen(dirpath); + if (dirpath_len && dirpath[dirpath_len - 1] == SEP) { + dirpath_len--; + } + const int filename_len = strlen(filename); + if (dirpath_len + 1 + filename_len + 1 > filepath_maxncpy) { + return false; + } + memcpy(filepath, dirpath, dirpath_len); + filepath[dirpath_len] = SEP; + memcpy(filepath + dirpath_len + 1, filename, filename_len + 1); + return true; +} + /* -------------------------------------------------------------------- */ /* Write a PNG from RGBA pixels */ @@ -392,8 +400,6 @@ static bool icondir_to_png(const char *path_src, const char *file_dst) DIR *dir; const struct dirent *fname; char filepath[1024]; - char *filename; - int path_str_len; int found = 0, fail = 0; struct IconMergeContext context; @@ -411,15 +417,12 @@ static bool icondir_to_png(const char *path_src, const char *file_dst) return false; } - strcpy(filepath, path_src); - path_str_len = path_ensure_slash(filepath); - filename = &filepath[path_str_len]; - while ((fname = readdir(dir)) != NULL) { if (path_test_extension(fname->d_name, ".dat")) { - - strcpy(filename, fname->d_name); - + if (!path_join(filepath, sizeof(filepath), path_src, fname->d_name)) { + printf("%s: path is too long (%s, %s)\n", __func__, path_src, fname->d_name); + return false; + } if (icon_merge(&context, filepath, &pixels_canvas, &canvas_w, &canvas_h)) { found++; } diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index b14d5b3bd4d4..48586d83b856 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -526,7 +526,7 @@ static int rna_find_sdna_member(SDNA *sdna, return 0; } -static int rna_validate_identifier(const char *identifier, char *error, bool property) +static bool rna_validate_identifier(const char *identifier, bool property, const char **r_error) { int a = 0; @@ -548,15 +548,15 @@ static int rna_validate_identifier(const char *identifier, char *error, bool pro }; if (!isalpha(identifier[0])) { - strcpy(error, "first character failed isalpha() check"); - return 0; + *r_error = "first character failed isalpha() check"; + return false; } for (a = 0; identifier[a]; a++) { if (DefRNA.preprocess && property) { if (isalpha(identifier[a]) && isupper(identifier[a])) { - strcpy(error, "property names must contain lower case characters only"); - return 0; + *r_error = "property names must contain lower case characters only"; + return false; } } @@ -565,20 +565,20 @@ static int rna_validate_identifier(const char *identifier, char *error, bool pro } if (identifier[a] == ' ') { - strcpy(error, "spaces are not okay in identifier names"); - return 0; + *r_error = "spaces are not okay in identifier names"; + return false; } if (isalnum(identifier[a]) == 0) { - strcpy(error, "one of the characters failed an isalnum() check and is not an underscore"); - return 0; + *r_error = "one of the characters failed an isalnum() check and is not an underscore"; + return false; } } for (a = 0; kwlist[a]; a++) { if (STREQ(identifier, kwlist[a])) { - strcpy(error, "this keyword is reserved by Python"); - return 0; + *r_error = "this keyword is reserved by Python"; + return false; } } @@ -594,13 +594,13 @@ static int rna_validate_identifier(const char *identifier, char *error, bool pro for (a = 0; kwlist_prop[a]; a++) { if (STREQ(identifier, kwlist_prop[a])) { - strcpy(error, "this keyword is reserved by Python"); - return 0; + *r_error = "this keyword is reserved by Python"; + return false; } } } - return 1; + return true; } void RNA_identifier_sanitize(char *identifier, int property) @@ -907,9 +907,9 @@ StructRNA *RNA_def_struct_ptr(BlenderRNA *brna, const char *identifier, StructRN PropertyRNA *prop; if (DefRNA.preprocess) { - char error[512]; + const char *error = NULL; - if (rna_validate_identifier(identifier, error, false) == 0) { + if (!rna_validate_identifier(identifier, false, &error)) { CLOG_ERROR(&LOG, "struct identifier \"%s\" error - %s", identifier, error); DefRNA.error = true; } @@ -1269,9 +1269,9 @@ PropertyRNA *RNA_def_property(StructOrFunctionRNA *cont_, PropertyRNA *prop; if (DefRNA.preprocess) { - char error[512]; + const char *error = NULL; - if (rna_validate_identifier(identifier, error, true) == 0) { + if (!rna_validate_identifier(identifier, true, &error)) { CLOG_ERROR( &LOG, "property identifier \"%s.%s\" - %s", CONTAINER_RNA_ID(cont), identifier, error); DefRNA.error = true; @@ -1290,8 +1290,8 @@ PropertyRNA *RNA_def_property(StructOrFunctionRNA *cont_, } else { #ifndef NDEBUG - char error[512]; - if (rna_validate_identifier(identifier, error, true) == 0) { + const char *error = NULL; + if (!rna_validate_identifier(identifier, true, &error)) { CLOG_ERROR(&LOG, "runtime property identifier \"%s.%s\" - %s", CONTAINER_RNA_ID(cont), @@ -3477,8 +3477,8 @@ void RNA_def_property_collection_funcs(PropertyRNA *prop, void RNA_def_property_srna(PropertyRNA *prop, const char *type) { - char error[512]; - if (rna_validate_identifier(type, error, false) == 0) { + const char *error = NULL; + if (!rna_validate_identifier(type, false, &error)) { CLOG_ERROR(&LOG, "struct identifier \"%s\" error - %s", type, error); DefRNA.error = true; return; @@ -4247,9 +4247,8 @@ static FunctionRNA *rna_def_function(StructRNA *srna, const char *identifier) FunctionDefRNA *dfunc; if (DefRNA.preprocess) { - char error[512]; - - if (rna_validate_identifier(identifier, error, false) == 0) { + const char *error = NULL; + if (!rna_validate_identifier(identifier, false, &error)) { CLOG_ERROR(&LOG, "function identifier \"%s\" - %s", identifier, error); DefRNA.error = true; } diff --git a/source/blender/makesrna/intern/rna_object_api.cc b/source/blender/makesrna/intern/rna_object_api.cc index 33c59ccada90..a880b84b9d7a 100644 --- a/source/blender/makesrna/intern/rna_object_api.cc +++ b/source/blender/makesrna/intern/rna_object_api.cc @@ -30,6 +30,8 @@ #include "rna_internal.h" /* own include */ +#define MESH_DM_INFO_STR_MAX 16384 + static const EnumPropertyItem space_items[] = { {CONSTRAINT_SPACE_WORLD, "WORLD", 0, "World Space", "The most global space in Blender"}, {CONSTRAINT_SPACE_POSE, @@ -763,7 +765,7 @@ void rna_Object_me_eval_info( if (me_eval) { ret = BKE_mesh_debug_info(me_eval); if (ret) { - strcpy(result, ret); + BLI_strncpy(result, ret, MESH_DM_INFO_STR_MAX); MEM_freeN(ret); } } @@ -1329,7 +1331,8 @@ void RNA_api_object(StructRNA *srna) "(only needed if current Context's depsgraph is not suitable)"); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_RNAPTR); /* weak!, no way to return dynamic string type */ - parm = RNA_def_string(func, "result", nullptr, 16384, "", "Requested information"); + parm = RNA_def_string( + func, "result", nullptr, MESH_DM_INFO_STR_MAX, "", "Requested information"); RNA_def_parameter_flags( parm, PROP_THICK_WRAP, ParameterFlag(0)); /* needed for string return value */ RNA_def_function_output(func, parm); diff --git a/source/blender/makesrna/intern/rna_ui.cc b/source/blender/makesrna/intern/rna_ui.cc index 15fe02e1ac7a..06e3655ccafc 100644 --- a/source/blender/makesrna/intern/rna_ui.cc +++ b/source/blender/makesrna/intern/rna_ui.cc @@ -266,7 +266,7 @@ static StructRNA *rna_Panel_register(Main *bmain, RNA_pointer_create(nullptr, &RNA_Panel, &dummy_panel, &dummy_panel_ptr); /* We have to set default context! Else we get a void string... */ - strcpy(dummy_pt.translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + STRNCPY(dummy_pt.translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); /* validate the python class */ if (validate(&dummy_panel_ptr, data, have_function) != 0) { @@ -286,7 +286,7 @@ static StructRNA *rna_Panel_register(Main *bmain, if ((1 << dummy_pt.region_type) & RGN_TYPE_HAS_CATEGORY_MASK) { if (dummy_pt.category[0] == '\0') { /* Use a fallback, otherwise an empty value will draw the panel in every category. */ - strcpy(dummy_pt.category, PNL_CATEGORY_FALLBACK); + STRNCPY(dummy_pt.category, PNL_CATEGORY_FALLBACK); # ifndef NDEBUG printf("%s '%s' misses category, please update the script\n", error_prefix, dummy_pt.idname); # endif @@ -989,7 +989,7 @@ static StructRNA *rna_Menu_register(Main *bmain, RNA_pointer_create(nullptr, &RNA_Menu, &dummy_menu, &dummy_menu_ptr); /* We have to set default context! Else we get a void string... */ - strcpy(dummy_mt.translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + STRNCPY(dummy_mt.translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); /* validate the python class */ if (validate(&dummy_menu_ptr, data, have_function) != 0) { From 2e087374d958dbec739859c55dbe04615607f74b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 20 Jun 2023 14:14:16 +1000 Subject: [PATCH 10/36] BLI_rect: support a zero limit for BLI_rctf_compare Use <= comparison for BLI_rctf_compare so two rectangles which are exactly the same return true with a limit of zero. Matches compare_ff, compare_v3v3 etc. In practice, this shouldn't result in user visible functional changes. --- source/blender/blenlib/intern/rct.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/blenlib/intern/rct.c b/source/blender/blenlib/intern/rct.c index aa752046b27b..aef289a5718a 100644 --- a/source/blender/blenlib/intern/rct.c +++ b/source/blender/blenlib/intern/rct.c @@ -825,10 +825,10 @@ bool BLI_rcti_clamp(rcti *rect, const rcti *rect_bounds, int r_xy[2]) bool BLI_rctf_compare(const rctf *rect_a, const rctf *rect_b, const float limit) { - if (fabsf(rect_a->xmin - rect_b->xmin) < limit) { - if (fabsf(rect_a->xmax - rect_b->xmax) < limit) { - if (fabsf(rect_a->ymin - rect_b->ymin) < limit) { - if (fabsf(rect_a->ymax - rect_b->ymax) < limit) { + if (fabsf(rect_a->xmin - rect_b->xmin) <= limit) { + if (fabsf(rect_a->xmax - rect_b->xmax) <= limit) { + if (fabsf(rect_a->ymin - rect_b->ymin) <= limit) { + if (fabsf(rect_a->ymax - rect_b->ymax) <= limit) { return true; } } From 4f3c09c5ee338f7e38853fe087ec3b2ff76d55e3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 20 Jun 2023 14:21:44 +1000 Subject: [PATCH 11/36] Cleanup: replace memcmp with BLI_rctf_compare While in this case it's likely not an issue, in general using memcmp for floating point values should be avoided. --- source/blender/geometry/intern/uv_pack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/geometry/intern/uv_pack.cc b/source/blender/geometry/intern/uv_pack.cc index 828f2d03b0c4..10abc60dd71b 100644 --- a/source/blender/geometry/intern/uv_pack.cc +++ b/source/blender/geometry/intern/uv_pack.cc @@ -1855,7 +1855,7 @@ static float pack_islands_scale_margin(const Span islands, rotate_inside_square(slow_aabbs, islands, params, scale, margin, r_phis, &extent); } - if (!memcmp(&extent, &fast_extent, sizeof(rctf))) { + if (BLI_rctf_compare(&extent, &fast_extent, 0.0f)) { /* The fast packer was the best so far. Lets just use the fast packer for everything. */ slow_aabbs = slow_aabbs.take_front(locked_island_count); extent = locked_bounds; From 58b5d38824de753e3189f339c13328cd8999f92f Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 20 Jun 2023 08:09:35 +0200 Subject: [PATCH 12/36] Metal: Fix operator precedence bug Backporting commit 31c986998bf1711a0a86672fe642fd4e446346fd to 3.6 release branch Pull Request: https://projects.blender.org/blender/blender/pulls/109147 --- source/blender/gpu/metal/mtl_storage_buffer.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/gpu/metal/mtl_storage_buffer.mm b/source/blender/gpu/metal/mtl_storage_buffer.mm index 9c5dbc138615..fbc6504626f4 100644 --- a/source/blender/gpu/metal/mtl_storage_buffer.mm +++ b/source/blender/gpu/metal/mtl_storage_buffer.mm @@ -220,9 +220,9 @@ if (ctx) { /* If all 4 bytes within clear value are equal, use the builtin fast-path for clearing. */ uint clear_byte = clear_value & 0xFF; - bool clear_value_bytes_equal = (clear_byte == (clear_value >> 8) & 0xFF) && - (clear_byte == (clear_value >> 16) & 0xFF) && - (clear_byte == (clear_value >> 24) & 0xFF); + bool clear_value_bytes_equal = (clear_byte == ((clear_value >> 8) & 0xFF)) && + (clear_byte == ((clear_value >> 16) & 0xFF)) && + (clear_byte == ((clear_value >> 24) & 0xFF)); if (clear_value_bytes_equal) { id blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder(); From 7b93431c2d67e647c9e3e7747e42bb9a1aed1f76 Mon Sep 17 00:00:00 2001 From: Iliya Katueshenock Date: Tue, 20 Jun 2023 09:54:32 +0200 Subject: [PATCH 13/36] BLI: add assert for negative indices in MutableSpan Pull Request: https://projects.blender.org/blender/blender/pulls/109143 --- source/blender/blenlib/BLI_span.hh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index 608ef8e2e551..93dbd9756815 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -599,7 +599,8 @@ template class MutableSpan { constexpr T &operator[](const int64_t index) const { - BLI_assert(index < this->size()); + BLI_assert(index >= 0); + BLI_assert(index < size_); return data_[index]; } From 98d675ac6cb6a885337140fce06a80ed2339983d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 20 Jun 2023 09:50:44 +0200 Subject: [PATCH 14/36] Geometry Nodes: make evaluation and logging system aware of zones This refactors how a geometry nodes node tree is converted to a lazy-function graph. Previously, all nodes were inserted into a single graph. This was fine because every node was evaluated at most once per node group evaluation. However, loops (#108896) break this assumption since now nodes may be evaluated multiple times and thus a single flat graph does not work anymore. Now, a separate lazy-function is build for every zone which gives us much more flexibility for what can happen in a zone. Right now, the change only applies to simulation zones since that's the only kind of zone we have. Technically, those zones could be inlined, but turning them into a separate lazy-function also does not hurt and makes it possible to test this refactor without implementing loops first. Also, having them as separate functions might help in the future if we integrate a substep loop directly into the simulation zone. The most tricky part here is to just link everything up correctly, especially with respect to deterministic anonymous attribute lifetimes. Fortunately, correctness can be checked visually by looking at the generated graphs. The logging/viewer system also had to be refactored a bit, because now there can be multiple different `ComputeContext` in a single node tree. Each zone is in a separate `ComputeContext`. To make it work, the `ViewerPath` system now explicitly supports zones and drawing code will look up the right logger for showing inspection data. No functional changes are expected, except that the spreadsheet now shows "Simulation Zone" in the context path if the viewer is in a simulation. --- scripts/startup/bl_ui/space_spreadsheet.py | 8 +- .../blenkernel/BKE_compute_contexts.hh | 19 + .../blender/blenkernel/BKE_node_tree_zones.hh | 19 + source/blender/blenkernel/BKE_viewer_path.h | 4 +- .../blenkernel/intern/compute_contexts.cc | 26 + .../blenkernel/intern/node_tree_zones.cc | 40 +- .../blender/blenkernel/intern/viewer_path.cc | 137 +- .../blender/editors/include/ED_viewer_path.hh | 6 +- .../editors/screen/workspace_listen.cc | 6 +- .../blender/editors/space_node/node_draw.cc | 85 +- .../node_geometry_attribute_search.cc | 27 +- source/blender/editors/util/ed_viewer_path.cc | 200 +- source/blender/makesdna/DNA_node_types.h | 6 + .../blender/makesdna/DNA_viewer_path_types.h | 27 +- source/blender/makesrna/intern/rna_space.cc | 51 +- source/blender/modifiers/intern/MOD_nodes.cc | 132 +- .../nodes/NOD_geometry_nodes_lazy_function.hh | 30 +- .../blender/nodes/NOD_geometry_nodes_log.hh | 9 +- .../intern/geometry_nodes_lazy_function.cc | 2532 ++++++++++------- .../nodes/intern/geometry_nodes_log.cc | 99 +- 20 files changed, 2154 insertions(+), 1309 deletions(-) diff --git a/scripts/startup/bl_ui/space_spreadsheet.py b/scripts/startup/bl_ui/space_spreadsheet.py index 3ba713e019e1..a4997ceda183 100644 --- a/scripts/startup/bl_ui/space_spreadsheet.py +++ b/scripts/startup/bl_ui/space_spreadsheet.py @@ -87,8 +87,12 @@ def draw_spreadsheet_context(self, layout, ctx): layout.label(text="Invalid id") elif ctx.type == 'MODIFIER': layout.label(text=ctx.modifier_name, icon='MODIFIER') - elif ctx.type == 'NODE': - layout.label(text=ctx.node_name, icon='NODE') + elif ctx.type == 'GROUP_NODE': + layout.label(text=ctx.ui_name, icon='NODE') + elif ctx.type == 'SIMULATION_ZONE': + layout.label(text="Simulation Zone") + elif ctx.type == 'VIEWER_NODE': + layout.label(text=ctx.ui_name) def draw_spreadsheet_viewer_path_icon(self, layout, space, icon='RIGHTARROW_THIN'): layout.prop(space, "display_viewer_path_collapsed", icon_only=True, emboss=False, icon=icon) diff --git a/source/blender/blenkernel/BKE_compute_contexts.hh b/source/blender/blenkernel/BKE_compute_contexts.hh index fb4702b27152..a1015c7b7f22 100644 --- a/source/blender/blenkernel/BKE_compute_contexts.hh +++ b/source/blender/blenkernel/BKE_compute_contexts.hh @@ -59,4 +59,23 @@ class NodeGroupComputeContext : public ComputeContext { void print_current_in_line(std::ostream &stream) const override; }; +class SimulationZoneComputeContext : public ComputeContext { + private: + static constexpr const char *s_static_type = "SIMULATION_ZONE"; + + int32_t output_node_id_; + + public: + SimulationZoneComputeContext(const ComputeContext *parent, int output_node_id); + SimulationZoneComputeContext(const ComputeContext *parent, const bNode &node); + + int32_t output_node_id() const + { + return output_node_id_; + } + + private: + void print_current_in_line(std::ostream &stream) const override; +}; + } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_node_tree_zones.hh b/source/blender/blenkernel/BKE_node_tree_zones.hh index bbc8770ff1cd..2a5908ae57b7 100644 --- a/source/blender/blenkernel/BKE_node_tree_zones.hh +++ b/source/blender/blenkernel/BKE_node_tree_zones.hh @@ -14,6 +14,8 @@ namespace blender::bke::node_tree_zones { +class TreeZones; + struct TreeZone { TreeZones *owner = nullptr; /** Index of the zone in the array of all zones in a node tree. */ @@ -53,8 +55,25 @@ class TreeZones { * in a different zone than its output sockets. */ const TreeZone *get_zone_by_socket(const bNodeSocket &socket) const; + + /** + * Get the deepest zone that the node is in. Note that the e.g. Simulation Input and Output nodes + * are considered to be inside of the zone they create. + */ + const TreeZone *get_zone_by_node(const int32_t node_id) const; + + /** + * Get a sorted list of zones that the node is in. First comes the root zone and last the most + * nested zone. For nodes that are at the root level, the returned list is empty. + */ + Vector get_zone_stack_for_node(const int32_t node_id) const; }; const TreeZones *get_tree_zones(const bNodeTree &tree); } // namespace blender::bke::node_tree_zones + +inline const blender::bke::node_tree_zones::TreeZones *bNodeTree::zones() const +{ + return blender::bke::node_tree_zones::get_tree_zones(*this); +} diff --git a/source/blender/blenkernel/BKE_viewer_path.h b/source/blender/blenkernel/BKE_viewer_path.h index 230caefebe58..8707e2c96c33 100644 --- a/source/blender/blenkernel/BKE_viewer_path.h +++ b/source/blender/blenkernel/BKE_viewer_path.h @@ -50,7 +50,9 @@ void BKE_viewer_path_id_remap(ViewerPath *viewer_path, const struct IDRemapper * ViewerPathElem *BKE_viewer_path_elem_new(ViewerPathElemType type); IDViewerPathElem *BKE_viewer_path_elem_new_id(void); ModifierViewerPathElem *BKE_viewer_path_elem_new_modifier(void); -NodeViewerPathElem *BKE_viewer_path_elem_new_node(void); +GroupNodeViewerPathElem *BKE_viewer_path_elem_new_group_node(void); +SimulationZoneViewerPathElem *BKE_viewer_path_elem_new_simulation_zone(void); +ViewerNodeViewerPathElem *BKE_viewer_path_elem_new_viewer_node(void); ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src); bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, const ViewerPathElem *b); void BKE_viewer_path_elem_free(ViewerPathElem *elem); diff --git a/source/blender/blenkernel/intern/compute_contexts.cc b/source/blender/blenkernel/intern/compute_contexts.cc index e8b4304bf63c..b943aefcfebb 100644 --- a/source/blender/blenkernel/intern/compute_contexts.cc +++ b/source/blender/blenkernel/intern/compute_contexts.cc @@ -62,4 +62,30 @@ void NodeGroupComputeContext::print_current_in_line(std::ostream &stream) const stream << "Node ID: " << node_id_; } +SimulationZoneComputeContext::SimulationZoneComputeContext(const ComputeContext *parent, + const int32_t output_node_id) + : ComputeContext(s_static_type, parent), output_node_id_(output_node_id) +{ + /* Mix static type and node id into a single buffer so that only a single call to #mix_in is + * necessary. */ + const int type_size = strlen(s_static_type); + const int buffer_size = type_size + 1 + sizeof(int32_t); + DynamicStackBuffer<64, 8> buffer_owner(buffer_size, 8); + char *buffer = static_cast(buffer_owner.buffer()); + memcpy(buffer, s_static_type, type_size + 1); + memcpy(buffer + type_size + 1, &output_node_id_, sizeof(int32_t)); + hash_.mix_in(buffer, buffer_size); +} + +SimulationZoneComputeContext::SimulationZoneComputeContext(const ComputeContext *parent, + const bNode &node) + : SimulationZoneComputeContext(parent, node.identifier) +{ +} + +void SimulationZoneComputeContext::print_current_in_line(std::ostream &stream) const +{ + stream << "Simulation Zone ID: " << output_node_id_; +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/node_tree_zones.cc b/source/blender/blenkernel/intern/node_tree_zones.cc index 785a38a9ddbc..7c9160d9695d 100644 --- a/source/blender/blenkernel/intern/node_tree_zones.cc +++ b/source/blender/blenkernel/intern/node_tree_zones.cc @@ -309,22 +309,44 @@ bool TreeZone::contains_zone_recursively(const TreeZone &other_zone) const const TreeZone *TreeZones::get_zone_by_socket(const bNodeSocket &socket) const { const bNode &node = socket.owner_node(); - const int zone_i = this->zone_by_node_id.lookup_default(node.identifier, -1); - if (zone_i == -1) { - return nullptr; + const TreeZone *zone = this->get_zone_by_node(node.identifier); + if (zone == nullptr) { + return zone; } - const TreeZone &zone = *this->zones[zone_i]; - if (zone.input_node == &node) { + if (zone->input_node == &node) { if (socket.is_input()) { - return zone.parent_zone; + return zone->parent_zone; } } - if (zone.output_node == &node) { + if (zone->output_node == &node) { if (socket.is_output()) { - return zone.parent_zone; + return zone->parent_zone; } } - return &zone; + return zone; +} + +const TreeZone *TreeZones::get_zone_by_node(const int32_t node_id) const +{ + const int zone_i = this->zone_by_node_id.lookup_default(node_id, -1); + if (zone_i == -1) { + return nullptr; + } + return this->zones[zone_i].get(); +} + +Vector TreeZones::get_zone_stack_for_node(const int node_id) const +{ + const TreeZone *zone = this->get_zone_by_node(node_id); + if (zone == nullptr) { + return {}; + } + Vector zone_stack; + for (; zone; zone = zone->parent_zone) { + zone_stack.append(zone); + } + std::reverse(zone_stack.begin(), zone_stack.end()); + return zone_stack; } } // namespace blender::bke::node_tree_zones diff --git a/source/blender/blenkernel/intern/viewer_path.cc b/source/blender/blenkernel/intern/viewer_path.cc index e7b3b5d6e57c..04cd874d5679 100644 --- a/source/blender/blenkernel/intern/viewer_path.cc +++ b/source/blender/blenkernel/intern/viewer_path.cc @@ -73,13 +73,23 @@ void BKE_viewer_path_blend_write(BlendWriter *writer, const ViewerPath *viewer_p BLO_write_string(writer, typed_elem->modifier_name); break; } - case VIEWER_PATH_ELEM_TYPE_NODE: { - const auto *typed_elem = reinterpret_cast(elem); - BLO_write_struct(writer, NodeViewerPathElem, typed_elem); - BLO_write_string(writer, typed_elem->node_name); + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: { + const auto *typed_elem = reinterpret_cast(elem); + BLO_write_struct(writer, GroupNodeViewerPathElem, typed_elem); + break; + } + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: { + const auto *typed_elem = reinterpret_cast(elem); + BLO_write_struct(writer, SimulationZoneViewerPathElem, typed_elem); + break; + } + case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: { + const auto *typed_elem = reinterpret_cast(elem); + BLO_write_struct(writer, ViewerNodeViewerPathElem, typed_elem); break; } } + BLO_write_string(writer, elem->ui_name); } } @@ -87,7 +97,11 @@ void BKE_viewer_path_blend_read_data(BlendDataReader *reader, ViewerPath *viewer { BLO_read_list(reader, &viewer_path->path); LISTBASE_FOREACH (ViewerPathElem *, elem, &viewer_path->path) { + BLO_read_data_address(reader, &elem->ui_name); switch (ViewerPathElemType(elem->type)) { + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: + case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: case VIEWER_PATH_ELEM_TYPE_ID: { break; } @@ -96,11 +110,6 @@ void BKE_viewer_path_blend_read_data(BlendDataReader *reader, ViewerPath *viewer BLO_read_data_address(reader, &typed_elem->modifier_name); break; } - case VIEWER_PATH_ELEM_TYPE_NODE: { - auto *typed_elem = reinterpret_cast(elem); - BLO_read_data_address(reader, &typed_elem->node_name); - break; - } } } } @@ -115,7 +124,9 @@ void BKE_viewer_path_blend_read_lib(BlendLibReader *reader, ID *self_id, ViewerP break; } case VIEWER_PATH_ELEM_TYPE_MODIFIER: - case VIEWER_PATH_ELEM_TYPE_NODE: { + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: + case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: { break; } } @@ -132,7 +143,9 @@ void BKE_viewer_path_foreach_id(LibraryForeachIDData *data, ViewerPath *viewer_p break; } case VIEWER_PATH_ELEM_TYPE_MODIFIER: - case VIEWER_PATH_ELEM_TYPE_NODE: { + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: + case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: { break; } } @@ -149,30 +162,39 @@ void BKE_viewer_path_id_remap(ViewerPath *viewer_path, const IDRemapper *mapping break; } case VIEWER_PATH_ELEM_TYPE_MODIFIER: - case VIEWER_PATH_ELEM_TYPE_NODE: { + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: + case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: { break; } } } } +template static T *make_elem(const ViewerPathElemType type) +{ + T *elem = MEM_cnew(__func__); + elem->base.type = type; + return elem; +} + ViewerPathElem *BKE_viewer_path_elem_new(const ViewerPathElemType type) { switch (type) { case VIEWER_PATH_ELEM_TYPE_ID: { - IDViewerPathElem *elem = MEM_cnew(__func__); - elem->base.type = type; - return &elem->base; + return &make_elem(type)->base; } case VIEWER_PATH_ELEM_TYPE_MODIFIER: { - ModifierViewerPathElem *elem = MEM_cnew(__func__); - elem->base.type = type; - return &elem->base; + return &make_elem(type)->base; + } + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: { + return &make_elem(type)->base; } - case VIEWER_PATH_ELEM_TYPE_NODE: { - NodeViewerPathElem *elem = MEM_cnew(__func__); - elem->base.type = type; - return &elem->base; + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: { + return &make_elem(type)->base; + } + case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: { + return &make_elem(type)->base; } } BLI_assert_unreachable(); @@ -190,15 +212,30 @@ ModifierViewerPathElem *BKE_viewer_path_elem_new_modifier() BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_MODIFIER)); } -NodeViewerPathElem *BKE_viewer_path_elem_new_node() +GroupNodeViewerPathElem *BKE_viewer_path_elem_new_group_node() +{ + return reinterpret_cast( + BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_GROUP_NODE)); +} + +SimulationZoneViewerPathElem *BKE_viewer_path_elem_new_simulation_zone() { - return reinterpret_cast( - BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_NODE)); + return reinterpret_cast( + BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE)); +} + +ViewerNodeViewerPathElem *BKE_viewer_path_elem_new_viewer_node() +{ + return reinterpret_cast( + BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_VIEWER_NODE)); } ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src) { ViewerPathElem *dst = BKE_viewer_path_elem_new(ViewerPathElemType(src->type)); + if (src->ui_name) { + dst->ui_name = BLI_strdup(src->ui_name); + } switch (ViewerPathElemType(src->type)) { case VIEWER_PATH_ELEM_TYPE_ID: { const auto *old_elem = reinterpret_cast(src); @@ -214,13 +251,22 @@ ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src) } break; } - case VIEWER_PATH_ELEM_TYPE_NODE: { - const auto *old_elem = reinterpret_cast(src); - auto *new_elem = reinterpret_cast(dst); + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: { + const auto *old_elem = reinterpret_cast(src); + auto *new_elem = reinterpret_cast(dst); + new_elem->node_id = old_elem->node_id; + break; + } + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: { + const auto *old_elem = reinterpret_cast(src); + auto *new_elem = reinterpret_cast(dst); + new_elem->sim_output_node_id = old_elem->sim_output_node_id; + break; + } + case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: { + const auto *old_elem = reinterpret_cast(src); + auto *new_elem = reinterpret_cast(dst); new_elem->node_id = old_elem->node_id; - if (old_elem->node_name != nullptr) { - new_elem->node_name = BLI_strdup(old_elem->node_name); - } break; } } @@ -243,9 +289,19 @@ bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, const ViewerPathElem *b const auto *b_elem = reinterpret_cast(b); return StringRef(a_elem->modifier_name) == StringRef(b_elem->modifier_name); } - case VIEWER_PATH_ELEM_TYPE_NODE: { - const auto *a_elem = reinterpret_cast(a); - const auto *b_elem = reinterpret_cast(b); + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: { + const auto *a_elem = reinterpret_cast(a); + const auto *b_elem = reinterpret_cast(b); + return a_elem->node_id == b_elem->node_id; + } + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: { + const auto *a_elem = reinterpret_cast(a); + const auto *b_elem = reinterpret_cast(b); + return a_elem->sim_output_node_id == b_elem->sim_output_node_id; + } + case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: { + const auto *a_elem = reinterpret_cast(a); + const auto *b_elem = reinterpret_cast(b); return a_elem->node_id == b_elem->node_id; } } @@ -255,7 +311,10 @@ bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, const ViewerPathElem *b void BKE_viewer_path_elem_free(ViewerPathElem *elem) { switch (ViewerPathElemType(elem->type)) { - case VIEWER_PATH_ELEM_TYPE_ID: { + case VIEWER_PATH_ELEM_TYPE_ID: + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: + case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: { break; } case VIEWER_PATH_ELEM_TYPE_MODIFIER: { @@ -263,11 +322,9 @@ void BKE_viewer_path_elem_free(ViewerPathElem *elem) MEM_SAFE_FREE(typed_elem->modifier_name); break; } - case VIEWER_PATH_ELEM_TYPE_NODE: { - auto *typed_elem = reinterpret_cast(elem); - MEM_SAFE_FREE(typed_elem->node_name); - break; - } + } + if (elem->ui_name) { + MEM_freeN(elem->ui_name); } MEM_freeN(elem); } diff --git a/source/blender/editors/include/ED_viewer_path.hh b/source/blender/editors/include/ED_viewer_path.hh index 87c228e108d6..4d9f02b58873 100644 --- a/source/blender/editors/include/ED_viewer_path.hh +++ b/source/blender/editors/include/ED_viewer_path.hh @@ -37,7 +37,8 @@ Object *parse_object_only(const ViewerPath &viewer_path); struct ViewerPathForGeometryNodesViewer { Object *object; blender::StringRefNull modifier_name; - blender::Vector group_node_ids; + /* Contains only group node and simulation zone elements. */ + blender::Vector node_path; int32_t viewer_node_id; }; @@ -65,7 +66,6 @@ bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed * Checks if the node referenced by the viewer and its entire context is still active, i.e. some * editor is showing it. */ -bool is_active_geometry_nodes_viewer(const bContext &C, - const ViewerPathForGeometryNodesViewer &parsed_viewer_path); +bool is_active_geometry_nodes_viewer(const bContext &C, const ViewerPath &viewer_path); } // namespace blender::ed::viewer_path diff --git a/source/blender/editors/screen/workspace_listen.cc b/source/blender/editors/screen/workspace_listen.cc index 7acf51336c46..9dd8303c6a5a 100644 --- a/source/blender/editors/screen/workspace_listen.cc +++ b/source/blender/editors/screen/workspace_listen.cc @@ -20,11 +20,7 @@ static void validate_viewer_paths(bContext &C, WorkSpace &workspace) return; } - const std::optional parsed_path = - blender::ed::viewer_path::parse_geometry_nodes_viewer(workspace.viewer_path); - if (parsed_path.has_value() && - blender::ed::viewer_path::is_active_geometry_nodes_viewer(C, *parsed_path)) - { + if (blender::ed::viewer_path::is_active_geometry_nodes_viewer(C, workspace.viewer_path)) { /* The current viewer path is still valid and active. */ return; } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index b31b97d81f20..b06ad1276b14 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -113,7 +113,7 @@ struct TreeDrawContext { * Geometry nodes logs various data during execution. The logged data that corresponds to the * currently drawn node tree can be retrieved from the log below. */ - geo_log::GeoTreeLog *geo_tree_log = nullptr; + blender::Map geo_log_by_zone; /** * True if there is an active realtime compositor using the node tree, false otherwise. */ @@ -1068,8 +1068,8 @@ static void create_inspection_string_for_geometry_socket(std::stringstream &ss, } } -static std::optional create_socket_inspection_string(TreeDrawContext &tree_draw_ctx, - const bNodeSocket &socket) +static std::optional create_socket_inspection_string( + geo_log::GeoTreeLog &geo_tree_log, const bNodeSocket &socket) { using namespace blender::nodes::geo_eval_log; @@ -1077,8 +1077,8 @@ static std::optional create_socket_inspection_string(TreeDrawContex return std::nullopt; } - tree_draw_ctx.geo_tree_log->ensure_socket_values(); - ValueLog *value_log = tree_draw_ctx.geo_tree_log->find_socket_value_log(socket); + geo_tree_log.ensure_socket_values(); + ValueLog *value_log = geo_tree_log.find_socket_value_log(socket); std::stringstream ss; if (const geo_log::GenericValueLog *generic_value_log = dynamic_cast(value_log)) @@ -1131,7 +1131,8 @@ static char *node_socket_get_tooltip(const SpaceNode *snode, TreeDrawContext tree_draw_ctx; if (snode != nullptr) { if (ntree.type == NTREE_GEOMETRY) { - tree_draw_ctx.geo_tree_log = geo_log::GeoModifierLog::get_tree_log_for_node_editor(*snode); + tree_draw_ctx.geo_log_by_zone = + geo_log::GeoModifierLog::get_tree_log_by_zone_for_node_editor(*snode); } } @@ -1144,13 +1145,22 @@ static char *node_socket_get_tooltip(const SpaceNode *snode, } } - if (ntree.type == NTREE_GEOMETRY && tree_draw_ctx.geo_tree_log != nullptr) { + geo_log::GeoTreeLog *geo_tree_log = [&]() -> geo_log::GeoTreeLog * { + const TreeZones *zones = ntree.zones(); + if (!zones) { + return nullptr; + } + const TreeZone *zone = zones->get_zone_by_socket(socket); + return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr); + }(); + + if (ntree.type == NTREE_GEOMETRY && geo_tree_log != nullptr) { if (!output.str().empty()) { output << ".\n\n"; } std::optional socket_inspection_str = create_socket_inspection_string( - tree_draw_ctx, socket); + *geo_tree_log, socket); if (socket_inspection_str.has_value()) { output << *socket_inspection_str; } @@ -1738,9 +1748,18 @@ static void node_add_error_message_button(const TreeDrawContext &tree_draw_ctx, return; } + geo_log::GeoTreeLog *geo_tree_log = [&]() -> geo_log::GeoTreeLog * { + const TreeZones *zones = node.owner_tree().zones(); + if (!zones) { + return nullptr; + } + const TreeZone *zone = zones->get_zone_by_node(node.identifier); + return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr); + }(); + Span warnings; - if (tree_draw_ctx.geo_tree_log) { - geo_log::GeoNodeLog *node_log = tree_draw_ctx.geo_tree_log->nodes.lookup_ptr(node.identifier); + if (geo_tree_log) { + geo_log::GeoNodeLog *node_log = geo_tree_log->nodes.lookup_ptr(node.identifier); if (node_log != nullptr) { warnings = node_log->warnings; } @@ -1778,7 +1797,15 @@ static void node_add_error_message_button(const TreeDrawContext &tree_draw_ctx, static std::optional node_get_execution_time( const TreeDrawContext &tree_draw_ctx, const bNodeTree &ntree, const bNode &node) { - const geo_log::GeoTreeLog *tree_log = tree_draw_ctx.geo_tree_log; + geo_log::GeoTreeLog *tree_log = [&]() -> geo_log::GeoTreeLog * { + const TreeZones *zones = ntree.zones(); + if (!zones) { + return nullptr; + } + const TreeZone *zone = zones->get_zone_by_node(node.identifier); + return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr); + }(); + if (tree_log == nullptr) { return std::nullopt; } @@ -1936,7 +1963,15 @@ static NodeExtraInfoRow row_from_used_named_attribute( static std::optional node_get_accessed_attributes_row( TreeDrawContext &tree_draw_ctx, const bNode &node) { - if (tree_draw_ctx.geo_tree_log == nullptr) { + geo_log::GeoTreeLog *geo_tree_log = [&]() -> geo_log::GeoTreeLog * { + const TreeZones *zones = node.owner_tree().zones(); + if (!zones) { + return nullptr; + } + const TreeZone *zone = zones->get_zone_by_node(node.identifier); + return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr); + }(); + if (geo_tree_log == nullptr) { return std::nullopt; } if (ELEM(node.type, @@ -1953,8 +1988,8 @@ static std::optional node_get_accessed_attributes_row( } } } - tree_draw_ctx.geo_tree_log->ensure_used_named_attributes(); - geo_log::GeoNodeLog *node_log = tree_draw_ctx.geo_tree_log->nodes.lookup_ptr(node.identifier); + geo_tree_log->ensure_used_named_attributes(); + geo_log::GeoNodeLog *node_log = geo_tree_log->nodes.lookup_ptr(node.identifier); if (node_log == nullptr) { return std::nullopt; } @@ -1998,7 +2033,16 @@ static Vector node_get_extra_info(TreeDrawContext &tree_draw_c } if (snode.edittree->type == NTREE_GEOMETRY) { - if (geo_log::GeoTreeLog *tree_log = tree_draw_ctx.geo_tree_log) { + geo_log::GeoTreeLog *tree_log = [&]() -> geo_log::GeoTreeLog * { + const TreeZones *tree_zones = node.owner_tree().zones(); + if (!tree_zones) { + return nullptr; + } + const TreeZone *zone = tree_zones->get_zone_by_node(node.identifier); + return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr); + }(); + + if (tree_log) { tree_log->ensure_debug_messages(); const geo_log::GeoNodeLog *node_log = tree_log->nodes.lookup_ptr(node.identifier); if (node_log != nullptr) { @@ -3126,7 +3170,7 @@ static void node_draw_zones(TreeDrawContext & /*tree_draw_ctx*/, const SpaceNode &snode, const bNodeTree &ntree) { - const TreeZones *zones = bke::node_tree_zones::get_tree_zones(ntree); + const TreeZones *zones = ntree.zones(); if (zones == nullptr) { return; } @@ -3368,10 +3412,11 @@ static void draw_nodetree(const bContext &C, TreeDrawContext tree_draw_ctx; if (ntree.type == NTREE_GEOMETRY) { - tree_draw_ctx.geo_tree_log = geo_log::GeoModifierLog::get_tree_log_for_node_editor(*snode); - if (tree_draw_ctx.geo_tree_log != nullptr) { - tree_draw_ctx.geo_tree_log->ensure_node_warnings(); - tree_draw_ctx.geo_tree_log->ensure_node_run_time(); + tree_draw_ctx.geo_log_by_zone = geo_log::GeoModifierLog::get_tree_log_by_zone_for_node_editor( + *snode); + for (geo_log::GeoTreeLog *log : tree_draw_ctx.geo_log_by_zone.values()) { + log->ensure_node_warnings(); + log->ensure_node_run_time(); } const WorkSpace *workspace = CTX_wm_workspace(&C); tree_draw_ctx.active_geometry_nodes_viewer = viewer_path::find_geometry_nodes_viewer( diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index 62a1fbb092ad..2b6d63cdceef 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -18,6 +18,7 @@ #include "BKE_context.h" #include "BKE_node_runtime.hh" #include "BKE_node_tree_update.h" +#include "BKE_node_tree_zones.hh" #include "BKE_object.h" #include "RNA_access.h" @@ -41,6 +42,8 @@ using blender::nodes::geo_eval_log::GeometryAttributeInfo; namespace blender::ed::space_node { +using namespace bke::node_tree_zones; + struct AttributeSearchData { int32_t node_id; char socket_identifier[MAX_NAME]; @@ -69,23 +72,33 @@ static Vector get_attribute_info_from_context( BLI_assert_unreachable(); return {}; } - GeoTreeLog *tree_log = GeoModifierLog::get_tree_log_for_node_editor(*snode); - if (tree_log == nullptr) { + const TreeZones *tree_zones = node_tree->zones(); + if (!tree_zones) { return {}; } - tree_log->ensure_socket_values(); + const Map log_by_zone = + GeoModifierLog::get_tree_log_by_zone_for_node_editor(*snode); /* For the attribute input node, collect attribute information from all nodes in the group. */ if (node->type == GEO_NODE_INPUT_NAMED_ATTRIBUTE) { - tree_log->ensure_existing_attributes(); Vector attributes; - for (const GeometryAttributeInfo *attribute : tree_log->existing_attributes) { - if (bke::allow_procedural_attribute_access(attribute->name)) { - attributes.append(attribute); + for (GeoTreeLog *tree_log : log_by_zone.values()) { + tree_log->ensure_socket_values(); + tree_log->ensure_existing_attributes(); + for (const GeometryAttributeInfo *attribute : tree_log->existing_attributes) { + if (bke::allow_procedural_attribute_access(attribute->name)) { + attributes.append(attribute); + } } } return attributes; } + const TreeZone *zone = tree_zones->get_zone_by_node(node->identifier); + GeoTreeLog *tree_log = log_by_zone.lookup_default(zone, nullptr); + if (!tree_log) { + return {}; + } + tree_log->ensure_socket_values(); GeoNodeLog *node_log = tree_log->nodes.lookup_ptr(node->identifier); if (node_log == nullptr) { return {}; diff --git a/source/blender/editors/util/ed_viewer_path.cc b/source/blender/editors/util/ed_viewer_path.cc index b3351902312d..31f125d31721 100644 --- a/source/blender/editors/util/ed_viewer_path.cc +++ b/source/blender/editors/util/ed_viewer_path.cc @@ -8,6 +8,7 @@ #include "BKE_context.h" #include "BKE_main.h" #include "BKE_node_runtime.hh" +#include "BKE_node_tree_zones.hh" #include "BKE_workspace.h" #include "BLI_listbase.h" @@ -22,6 +23,8 @@ namespace blender::ed::viewer_path { +using namespace bke::node_tree_zones; + static void viewer_path_for_geometry_node(const SpaceNode &snode, const bNode &node, ViewerPath &r_dst) @@ -55,27 +58,56 @@ static void viewer_path_for_geometry_node(const SpaceNode &snode, modifier = nmd; } } - if (modifier != nullptr) { - ModifierViewerPathElem *modifier_elem = BKE_viewer_path_elem_new_modifier(); - modifier_elem->modifier_name = BLI_strdup(modifier->modifier.name); - BLI_addtail(&r_dst.path, modifier_elem); + if (modifier == nullptr) { + return; } + ModifierViewerPathElem *modifier_elem = BKE_viewer_path_elem_new_modifier(); + modifier_elem->modifier_name = BLI_strdup(modifier->modifier.name); + BLI_addtail(&r_dst.path, modifier_elem); Vector tree_path = snode.treepath; for (const int i : tree_path.index_range().drop_back(1)) { + bNodeTree *tree = tree_path[i]->nodetree; /* The tree path contains the name of the node but not its ID. */ - const bNode *node = nodeFindNodebyName(tree_path[i]->nodetree, tree_path[i + 1]->node_name); + const char *node_name = tree_path[i + 1]->node_name; + const bNode *node = nodeFindNodebyName(tree, node_name); /* The name in the tree path should match a group node in the tree. */ BLI_assert(node != nullptr); - NodeViewerPathElem *node_elem = BKE_viewer_path_elem_new_node(); + + tree->ensure_topology_cache(); + const TreeZones *tree_zones = tree->zones(); + if (!tree_zones) { + return; + } + const Vector zone_stack = tree_zones->get_zone_stack_for_node( + node->identifier); + for (const TreeZone *zone : zone_stack) { + SimulationZoneViewerPathElem *node_elem = BKE_viewer_path_elem_new_simulation_zone(); + node_elem->sim_output_node_id = zone->output_node->identifier; + BLI_addtail(&r_dst.path, node_elem); + } + + GroupNodeViewerPathElem *node_elem = BKE_viewer_path_elem_new_group_node(); node_elem->node_id = node->identifier; - node_elem->node_name = BLI_strdup(node->name); + node_elem->base.ui_name = BLI_strdup(node->name); BLI_addtail(&r_dst.path, node_elem); } - NodeViewerPathElem *viewer_node_elem = BKE_viewer_path_elem_new_node(); + snode.edittree->ensure_topology_cache(); + const TreeZones *tree_zones = snode.edittree->zones(); + if (!tree_zones) { + return; + } + const Vector zone_stack = tree_zones->get_zone_stack_for_node(node.identifier); + for (const TreeZone *zone : zone_stack) { + SimulationZoneViewerPathElem *node_elem = BKE_viewer_path_elem_new_simulation_zone(); + node_elem->sim_output_node_id = zone->output_node->identifier; + BLI_addtail(&r_dst.path, node_elem); + } + + ViewerNodeViewerPathElem *viewer_node_elem = BKE_viewer_path_elem_new_viewer_node(); viewer_node_elem->node_id = node.identifier; - viewer_node_elem->node_name = BLI_strdup(node.name); + viewer_node_elem->base.ui_name = BLI_strdup(node.name); BLI_addtail(&r_dst.path, viewer_node_elem); } @@ -185,16 +217,21 @@ std::optional parse_geometry_nodes_viewer( return std::nullopt; } remaining_elems = remaining_elems.drop_front(1); - Vector node_ids; - for (const ViewerPathElem *elem : remaining_elems) { - if (elem->type != VIEWER_PATH_ELEM_TYPE_NODE) { + Vector node_path; + for (const ViewerPathElem *elem : remaining_elems.drop_back(1)) { + if (!ELEM(elem->type, VIEWER_PATH_ELEM_TYPE_GROUP_NODE, VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE)) + { return std::nullopt; } - const int32_t node_id = reinterpret_cast(elem)->node_id; - node_ids.append(node_id); + node_path.append(elem); + } + const ViewerPathElem *last_elem = remaining_elems.last(); + if (last_elem->type != VIEWER_PATH_ELEM_TYPE_VIEWER_NODE) { + return std::nullopt; } - const int32_t viewer_node_id = node_ids.pop_last(); - return ViewerPathForGeometryNodesViewer{root_ob, modifier_name, node_ids, viewer_node_id}; + const int32_t viewer_node_id = + reinterpret_cast(last_elem)->node_id; + return ViewerPathForGeometryNodesViewer{root_ob, modifier_name, node_path, viewer_node_id}; } bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed_viewer_path) @@ -217,81 +254,72 @@ bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed return false; } const bNodeTree *ngroup = modifier->node_group; - ngroup->ensure_topology_cache(); - for (const int32_t group_node_id : parsed_viewer_path.group_node_ids) { - const bNode *group_node = nullptr; - for (const bNode *node : ngroup->group_nodes()) { - if (node->identifier != group_node_id) { - continue; + const TreeZone *zone = nullptr; + for (const ViewerPathElem *path_elem : parsed_viewer_path.node_path) { + ngroup->ensure_topology_cache(); + const TreeZones *tree_zones = ngroup->zones(); + switch (path_elem->type) { + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: { + const auto &typed_elem = *reinterpret_cast( + path_elem); + const TreeZone *next_zone = tree_zones->get_zone_by_node(typed_elem.sim_output_node_id); + if (next_zone == nullptr) { + return false; + } + if (next_zone->parent_zone != zone) { + return false; + } + zone = next_zone; + break; + } + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: { + const auto &typed_elem = *reinterpret_cast(path_elem); + const bNode *group_node = ngroup->node_by_id(typed_elem.node_id); + if (group_node == nullptr) { + return false; + } + const TreeZone *parent_zone = tree_zones->get_zone_by_node(typed_elem.node_id); + if (parent_zone != zone) { + return false; + } + if (group_node->id == nullptr) { + return false; + } + ngroup = reinterpret_cast(group_node->id); + zone = nullptr; + break; + } + default: { + BLI_assert_unreachable(); } - group_node = node; - break; - } - if (group_node == nullptr) { - return false; - } - if (group_node->id == nullptr) { - return false; - } - ngroup = reinterpret_cast(group_node->id); - } - const bNode *viewer_node = nullptr; - for (const bNode *node : ngroup->nodes_by_type("GeometryNodeViewer")) { - if (node->identifier != parsed_viewer_path.viewer_node_id) { - continue; } - viewer_node = node; - break; } + + const bNode *viewer_node = ngroup->node_by_id(parsed_viewer_path.viewer_node_id); if (viewer_node == nullptr) { return false; } - return true; -} - -static bool viewer_path_matches_node_editor_path( - const SpaceNode &snode, const ViewerPathForGeometryNodesViewer &parsed_viewer_path) -{ - Vector tree_path = snode.treepath; - if (tree_path.size() != parsed_viewer_path.group_node_ids.size() + 1) { + const TreeZones *tree_zones = ngroup->zones(); + if (tree_zones == nullptr) { return false; } - for (const int i : parsed_viewer_path.group_node_ids.index_range()) { - const bNode *node = tree_path[i]->nodetree->node_by_id(parsed_viewer_path.group_node_ids[i]); - if (!node) { - return false; - } - if (!STREQ(node->name, tree_path[i + 1]->node_name)) { - return false; - } + if (tree_zones->get_zone_by_node(viewer_node->identifier) != zone) { + return false; } return true; } -bool is_active_geometry_nodes_viewer(const bContext &C, - const ViewerPathForGeometryNodesViewer &parsed_viewer_path) +bool is_active_geometry_nodes_viewer(const bContext &C, const ViewerPath &viewer_path) { - const NodesModifierData *modifier = nullptr; - LISTBASE_FOREACH (const ModifierData *, md, &parsed_viewer_path.object->modifiers) { - if (md->name != parsed_viewer_path.modifier_name) { - continue; - } - if (md->type != eModifierType_Nodes) { - return false; - } - if ((md->mode & eModifierMode_Realtime) == 0) { - return false; - } - modifier = reinterpret_cast(md); - break; - } - if (modifier == nullptr) { + if (BLI_listbase_is_empty(&viewer_path.path)) { return false; } - if (modifier->node_group == nullptr) { + const ViewerPathElem *last_elem = static_cast(viewer_path.path.last); + if (last_elem->type != VIEWER_PATH_ELEM_TYPE_VIEWER_NODE) { return false; } - const bool modifier_is_active = modifier->modifier.flag & eModifierFlag_Active; + const int32_t viewer_node_id = + reinterpret_cast(last_elem)->node_id; const Main *bmain = CTX_data_main(&C); const wmWindowManager *wm = static_cast(bmain->wm.first); @@ -315,30 +343,26 @@ bool is_active_geometry_nodes_viewer(const bContext &C, continue; } const SpaceNode &snode = *reinterpret_cast(sl); - if (!modifier_is_active) { - if (!(snode.flag & SNODE_PIN)) { - /* Node tree has to be pinned when the modifier is not active. */ - continue; - } - } - if (snode.id != &parsed_viewer_path.object->id) { + if (snode.edittree == nullptr) { continue; } - if (snode.nodetree != modifier->node_group) { + if (snode.edittree->type != NTREE_GEOMETRY) { continue; } - if (!viewer_path_matches_node_editor_path(snode, parsed_viewer_path)) { - continue; - } - const bNodeTree *ngroup = snode.edittree; - ngroup->ensure_topology_cache(); - const bNode *viewer_node = ngroup->node_by_id(parsed_viewer_path.viewer_node_id); + snode.edittree->ensure_topology_cache(); + const bNode *viewer_node = snode.edittree->node_by_id(viewer_node_id); if (viewer_node == nullptr) { continue; } if (!(viewer_node->flag & NODE_DO_OUTPUT)) { continue; } + ViewerPath tmp_viewer_path{}; + BLI_SCOPED_DEFER([&]() { BKE_viewer_path_clear(&tmp_viewer_path); }); + viewer_path_for_geometry_node(snode, *viewer_node, tmp_viewer_path); + if (!BKE_viewer_path_equal(&viewer_path, &tmp_viewer_path)) { + continue; + } return true; } } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index b5166042f73a..31e387ab43e0 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -31,6 +31,10 @@ class bNodeTreeRuntime; class bNodeRuntime; class bNodeSocketRuntime; } // namespace blender::bke +namespace blender::bke::node_tree_zones { +class TreeZones; +struct TreeZone; +} // namespace blender::bke::node_tree_zones using NodeDeclarationHandle = blender::nodes::NodeDeclaration; using SocketDeclarationHandle = blender::nodes::SocketDeclaration; using bNodeTreeRuntimeHandle = blender::bke::bNodeTreeRuntime; @@ -677,6 +681,8 @@ typedef struct bNodeTree { blender::Span panels() const; blender::MutableSpan panels_for_write(); + /** Zones in the node tree. Currently there are only simulation zones in geometry nodes. */ + const blender::bke::node_tree_zones::TreeZones *zones() const; #endif } bNodeTree; diff --git a/source/blender/makesdna/DNA_viewer_path_types.h b/source/blender/makesdna/DNA_viewer_path_types.h index c6d6a75be08d..e9593e896fac 100644 --- a/source/blender/makesdna/DNA_viewer_path_types.h +++ b/source/blender/makesdna/DNA_viewer_path_types.h @@ -12,13 +12,16 @@ struct ID; typedef enum ViewerPathElemType { VIEWER_PATH_ELEM_TYPE_ID = 0, VIEWER_PATH_ELEM_TYPE_MODIFIER = 1, - VIEWER_PATH_ELEM_TYPE_NODE = 2, + VIEWER_PATH_ELEM_TYPE_GROUP_NODE = 2, + VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE = 3, + VIEWER_PATH_ELEM_TYPE_VIEWER_NODE = 4, } ViewerPathElemType; typedef struct ViewerPathElem { struct ViewerPathElem *next, *prev; int type; char _pad[4]; + char *ui_name; } ViewerPathElem; typedef struct IDViewerPathElem { @@ -31,18 +34,26 @@ typedef struct ModifierViewerPathElem { char *modifier_name; } ModifierViewerPathElem; -typedef struct NodeViewerPathElem { +typedef struct GroupNodeViewerPathElem { ViewerPathElem base; int32_t node_id; char _pad1[4]; +} GroupNodeViewerPathElem; - /** - * The name of the node with the identifier. Not used to lookup nodes, only for display - * in the UI. Still stored here to avoid looking up the name for every redraw. - */ - char *node_name; -} NodeViewerPathElem; +typedef struct SimulationZoneViewerPathElem { + ViewerPathElem base; + + int32_t sim_output_node_id; + char _pad1[4]; +} SimulationZoneViewerPathElem; + +typedef struct ViewerNodeViewerPathElem { + ViewerPathElem base; + + int32_t node_id; + char _pad1[4]; +} ViewerNodeViewerPathElem; typedef struct ViewerPath { /** List of #ViewerPathElem. */ diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index 63683ba3dca1..fd1a268d349d 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -3331,8 +3331,12 @@ static StructRNA *rna_viewer_path_elem_refine(PointerRNA *ptr) return &RNA_IDViewerPathElem; case VIEWER_PATH_ELEM_TYPE_MODIFIER: return &RNA_ModifierViewerPathElem; - case VIEWER_PATH_ELEM_TYPE_NODE: - return &RNA_NodeViewerPathElem; + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: + return &RNA_GroupNodeViewerPathElem; + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: + return &RNA_SimulationZoneViewerPathElem; + case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: + return &RNA_ViewerNodeViewerPathElem; } BLI_assert_unreachable(); return nullptr; @@ -8049,7 +8053,9 @@ static void rna_def_spreadsheet_row_filter(BlenderRNA *brna) static const EnumPropertyItem viewer_path_elem_type_items[] = { {VIEWER_PATH_ELEM_TYPE_ID, "ID", ICON_NONE, "ID", ""}, {VIEWER_PATH_ELEM_TYPE_MODIFIER, "MODIFIER", ICON_NONE, "Modifier", ""}, - {VIEWER_PATH_ELEM_TYPE_NODE, "NODE", ICON_NONE, "Node", ""}, + {VIEWER_PATH_ELEM_TYPE_GROUP_NODE, "GROUP_NODE", ICON_NONE, "Group Node", ""}, + {VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE, "SIMULATION_ZONE", ICON_NONE, "Simulation Zone", ""}, + {VIEWER_PATH_ELEM_TYPE_VIEWER_NODE, "VIEWER_NODE", ICON_NONE, "Viewer Node", ""}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -8066,6 +8072,11 @@ static void rna_def_viewer_path_elem(BlenderRNA *brna) RNA_def_property_enum_items(prop, viewer_path_elem_type_items); RNA_def_property_ui_text(prop, "Type", "Type of the path element"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "ui_name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text( + prop, "UI Name", "Name that can be displayed in the UI for this element"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); } static void rna_def_id_viewer_path_elem(BlenderRNA *brna) @@ -8090,15 +8101,37 @@ static void rna_def_modifier_viewer_path_elem(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Modifier Name", ""); } -static void rna_def_node_viewer_path_elem(BlenderRNA *brna) +static void rna_def_group_node_viewer_path_elem(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GroupNodeViewerPathElem", "ViewerPathElem"); + + prop = RNA_def_property(srna, "node_id", PROP_INT, PROP_NONE); + RNA_def_property_ui_text(prop, "Node ID", ""); +} + +static void rna_def_simulation_zone_viewer_path_elem(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "SimulationZoneViewerPathElem", "ViewerPathElem"); + + prop = RNA_def_property(srna, "sim_output_node_id", PROP_INT, PROP_NONE); + RNA_def_property_ui_text(prop, "Simulation Output Node ID", ""); +} + +static void rna_def_viewer_node_viewer_path_elem(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; - srna = RNA_def_struct(brna, "NodeViewerPathElem", "ViewerPathElem"); + srna = RNA_def_struct(brna, "ViewerNodeViewerPathElem", "ViewerPathElem"); - prop = RNA_def_property(srna, "node_name", PROP_STRING, PROP_NONE); - RNA_def_property_ui_text(prop, "Node Name", ""); + prop = RNA_def_property(srna, "node_id", PROP_INT, PROP_NONE); + RNA_def_property_ui_text(prop, "Node ID", ""); } static void rna_def_viewer_path(BlenderRNA *brna) @@ -8109,7 +8142,9 @@ static void rna_def_viewer_path(BlenderRNA *brna) rna_def_viewer_path_elem(brna); rna_def_id_viewer_path_elem(brna); rna_def_modifier_viewer_path_elem(brna); - rna_def_node_viewer_path_elem(brna); + rna_def_group_node_viewer_path_elem(brna); + rna_def_simulation_zone_viewer_path_elem(brna); + rna_def_viewer_node_viewer_path_elem(brna); srna = RNA_def_struct(brna, "ViewerPath", nullptr); RNA_def_struct_ui_text(srna, "Viewer Path", "Path to data that is viewed"); diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 76ad845166a3..40ea1a1bc356 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -424,31 +424,14 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) namespace blender { -static const lf::FunctionNode *find_viewer_lf_node(const bNode &viewer_bnode) -{ - if (const nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info = - nodes::ensure_geometry_nodes_lazy_function_graph(viewer_bnode.owner_tree())) - { - return lf_graph_info->mapping.viewer_node_map.lookup_default(&viewer_bnode, nullptr); - } - return nullptr; -} -static const lf::FunctionNode *find_group_lf_node(const bNode &group_bnode) -{ - if (const nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info = - nodes::ensure_geometry_nodes_lazy_function_graph(group_bnode.owner_tree())) - { - return lf_graph_info->mapping.group_node_map.lookup_default(&group_bnode, nullptr); - } - return nullptr; -} - static void find_side_effect_nodes_for_viewer_path( const ViewerPath &viewer_path, const NodesModifierData &nmd, const ModifierEvalContext &ctx, MultiValueMap &r_side_effect_nodes) { + using namespace bke::node_tree_zones; + const std::optional parsed_path = ed::viewer_path::parse_geometry_nodes_viewer(viewer_path); if (!parsed_path.has_value()) { @@ -464,45 +447,100 @@ static void find_side_effect_nodes_for_viewer_path( ComputeContextBuilder compute_context_builder; compute_context_builder.push(parsed_path->modifier_name); + /* Write side effect nodes to a new map and only if everything succeeds, move the nodes to the + * caller. This is easier than changing r_side_effect_nodes directly and then undoing changes in + * case of errors. */ + MultiValueMap local_side_effect_nodes; + const bNodeTree *group = nmd.node_group; - Stack group_node_stack; - for (const int32_t group_node_id : parsed_path->group_node_ids) { - const bNode *found_node = group->node_by_id(group_node_id); - if (found_node == nullptr) { + const TreeZone *zone = nullptr; + for (const ViewerPathElem *elem : parsed_path->node_path) { + const TreeZones *tree_zones = group->zones(); + if (tree_zones == nullptr) { return; } - if (found_node->id == nullptr) { + const auto *lf_graph_info = nodes::ensure_geometry_nodes_lazy_function_graph(*group); + if (lf_graph_info == nullptr) { return; } - if (found_node->is_muted()) { - return; + switch (elem->type) { + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: { + const auto &typed_elem = *reinterpret_cast(elem); + const TreeZone *next_zone = tree_zones->get_zone_by_node(typed_elem.sim_output_node_id); + if (next_zone == nullptr) { + return; + } + if (next_zone->parent_zone != zone) { + return; + } + const lf::FunctionNode *lf_zone_node = lf_graph_info->mapping.zone_node_map.lookup_default( + next_zone, nullptr); + if (lf_zone_node == nullptr) { + return; + } + local_side_effect_nodes.add(compute_context_builder.hash(), lf_zone_node); + compute_context_builder.push(*next_zone->output_node); + zone = next_zone; + break; + } + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: { + const auto &typed_elem = *reinterpret_cast(elem); + const bNode *node = group->node_by_id(typed_elem.node_id); + if (node == nullptr) { + return; + } + if (node->id == nullptr) { + return; + } + if (node->is_muted()) { + return; + } + if (zone != tree_zones->get_zone_by_node(node->identifier)) { + return; + } + const lf::FunctionNode *lf_group_node = + lf_graph_info->mapping.group_node_map.lookup_default(node, nullptr); + if (lf_group_node == nullptr) { + return; + } + local_side_effect_nodes.add(compute_context_builder.hash(), lf_group_node); + compute_context_builder.push(*node); + group = reinterpret_cast(node->id); + zone = nullptr; + break; + } + default: { + BLI_assert_unreachable(); + return; + } } - group_node_stack.push(found_node); - group = reinterpret_cast(found_node->id); - compute_context_builder.push(*found_node); } const bNode *found_viewer_node = group->node_by_id(parsed_path->viewer_node_id); if (found_viewer_node == nullptr) { return; } - const lf::FunctionNode *lf_viewer_node = find_viewer_lf_node(*found_viewer_node); + const auto *lf_graph_info = nodes::ensure_geometry_nodes_lazy_function_graph(*group); + if (lf_graph_info == nullptr) { + return; + } + const TreeZones *tree_zones = group->zones(); + if (tree_zones == nullptr) { + return; + } + if (tree_zones->get_zone_by_node(found_viewer_node->identifier) != zone) { + return; + } + const lf::FunctionNode *lf_viewer_node = lf_graph_info->mapping.viewer_node_map.lookup_default( + found_viewer_node, nullptr); if (lf_viewer_node == nullptr) { return; } + local_side_effect_nodes.add(compute_context_builder.hash(), lf_viewer_node); - /* Not only mark the viewer node as having side effects, but also all group nodes it is contained - * in. */ - r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(), lf_viewer_node); - compute_context_builder.pop(); - while (!compute_context_builder.is_empty()) { - const lf::FunctionNode *lf_group_node = find_group_lf_node(*group_node_stack.pop()); - if (lf_group_node == nullptr) { - return; - } - - r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(), lf_group_node); - compute_context_builder.pop(); + /* Successfully found all compute contexts for the viewer. */ + for (const auto item : local_side_effect_nodes.items()) { + r_side_effect_nodes.add_multiple(item.key, item.value); } } @@ -550,11 +588,11 @@ static void find_socket_log_contexts(const NodesModifierData &nmd, const SpaceLink *sl = static_cast(area->spacedata.first); if (sl->spacetype == SPACE_NODE) { const SpaceNode &snode = *reinterpret_cast(sl); - if (const std::optional hash = - geo_log::GeoModifierLog::get_compute_context_hash_for_node_editor( - snode, nmd.modifier.name)) - { - r_socket_log_contexts.add(*hash); + const Map hash_by_zone = + geo_log::GeoModifierLog::get_context_hash_by_zone_for_node_editor(snode, + nmd.modifier.name); + for (const ComputeContextHash &hash : hash_by_zone.values()) { + r_socket_log_contexts.add(hash); } } } diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh index 3f05774d1cdd..7e08e1fff54f 100644 --- a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh +++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh @@ -28,6 +28,7 @@ #include "BLI_compute_context.hh" +#include "BKE_node_tree_zones.hh" #include "BKE_simulation_state.hh" struct Object; @@ -175,7 +176,7 @@ struct GeometryNodeLazyFunctionGraphMapping { */ Map group_node_map; Map viewer_node_map; - Map sim_output_node_map; + Map zone_node_map; /* Indexed by #bNodeSocket::index_in_all_outputs. */ Array lf_input_index_for_output_bsocket_usage; @@ -190,29 +191,9 @@ struct GeometryNodeLazyFunctionGraphMapping { */ struct GeometryNodesLazyFunctionGraphInfo { /** - * Allocator used for many things contained in this struct. + * Contains resources that need to be freed when the graph is not needed anymore. */ - LinearAllocator<> allocator; - /** - * Many nodes are implemented as multi-functions. So this contains a mapping from nodes to their - * corresponding multi-functions. - */ - std::unique_ptr node_multi_functions; - /** - * Many lazy-functions are build for the lazy-function graph. Since the graph does not own them, - * we have to keep track of them separately. - */ - Vector> functions; - /** - * Debug info that has to be destructed when the graph is not used anymore. - */ - Vector> dummy_debug_infos_; - /** - * Many sockets have default values. Since those are not owned by the lazy-function graph, we - * have to keep track of them separately. This only owns the values, the memory is owned by the - * allocator above. - */ - Vector values_to_destruct; + ResourceScope scope; /** * The actual lazy-function graph. */ @@ -226,9 +207,6 @@ struct GeometryNodesLazyFunctionGraphInfo { * This can be used as a simple heuristic for the complexity of the node group. */ int num_inline_nodes_approximate = 0; - - GeometryNodesLazyFunctionGraphInfo(); - ~GeometryNodesLazyFunctionGraphInfo(); }; /** diff --git a/source/blender/nodes/NOD_geometry_nodes_log.hh b/source/blender/nodes/NOD_geometry_nodes_log.hh index 7502faee6e25..0765b9506557 100644 --- a/source/blender/nodes/NOD_geometry_nodes_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_log.hh @@ -37,6 +37,7 @@ #include "BKE_attribute.h" #include "BKE_geometry_set.hh" +#include "BKE_node_tree_zones.hh" #include "BKE_viewer_path.h" #include "FN_field.hh" @@ -333,9 +334,11 @@ class GeoModifierLog { /** * Utility accessor to logged data. */ - static std::optional get_compute_context_hash_for_node_editor( - const SpaceNode &snode, StringRefNull modifier_name); - static GeoTreeLog *get_tree_log_for_node_editor(const SpaceNode &snode); + static Map + get_context_hash_by_zone_for_node_editor(const SpaceNode &snode, StringRefNull modifier_name); + + static Map + get_tree_log_by_zone_for_node_editor(const SpaceNode &snode); static const ViewerNodeLog *find_viewer_node_log_for_path(const ViewerPath &viewer_path); }; diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 9574777321b4..f19299750464 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -5,10 +5,15 @@ /** \file * \ingroup nodes * - * This file mainly converts a #bNodeTree into a lazy-function graph. This generally works by - * creating a lazy-function for every node, which is then put into the lazy-function graph. Then - * the nodes in the new graph are linked based on links in the original #bNodeTree. Some additional - * nodes are inserted for things like type conversions and multi-input sockets. + * This file mainly converts a #bNodeTree into a lazy-function graph, that can then be evaluated to + * execute geometry nodes. This generally works by creating a lazy-function for every node, which + * is then put into the lazy-function graph. Then the nodes in the new graph are linked based on + * links in the original #bNodeTree. Some additional nodes are inserted for things like type + * conversions and multi-input sockets. + * + * If the #bNodeTree contains zones, those are turned into separate lazy-functions first. + * Essentially, a separate lazy-function graph is created for every zone that is than called by the + * parent zone or by the root graph. * * Currently, lazy-functions are even created for nodes that don't strictly require it, like * reroutes or muted nodes. In the future we could avoid that at the cost of additional code @@ -34,6 +39,7 @@ #include "BKE_compute_contexts.hh" #include "BKE_geometry_set.hh" #include "BKE_node_tree_anonymous_attributes.hh" +#include "BKE_node_tree_zones.hh" #include "BKE_type_conversions.hh" #include "FN_field_cpp_type.hh" @@ -45,6 +51,9 @@ namespace blender::nodes { +namespace aai = bke::anonymous_attribute_inferencing; +using bke::node_tree_zones::TreeZone; +using bke::node_tree_zones::TreeZones; using fn::ValueOrField; using fn::ValueOrFieldCPPType; @@ -1166,13 +1175,24 @@ class LazyFunctionForAnonymousAttributeSetExtract : public lf::LazyFunction { LazyFunctionForAnonymousAttributeSetExtract(const ValueOrFieldCPPType &type) : type_(type) { debug_name_ = "Extract Attribute Set"; - inputs_.append_as("Field", type.self); + inputs_.append_as("Use", CPPType::get()); + inputs_.append_as("Field", type.self, lf::ValueUsage::Maybe); outputs_.append_as("Attributes", CPPType::get()); } void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override { - const void *value_or_field = params.try_get_input_data_ptr(0); + const bool use = params.get_input(0); + if (!use) { + params.set_output(0, {}); + return; + } + const void *value_or_field = params.try_get_input_data_ptr_or_request(1); + if (value_or_field == nullptr) { + /* Wait until the field is computed. */ + return; + } + bke::AnonymousAttributeSet attributes; if (type_.is_field(value_or_field)) { const GField &field = *type_.get_field_ptr(value_or_field); @@ -1263,8 +1283,8 @@ class LazyFunctionForAnonymousAttributeSetJoin : public lf::LazyFunction { /** * Cache for functions small amounts to avoid to avoid building them many times. */ - static const LazyFunctionForAnonymousAttributeSetJoin &get_cached( - const int amount, Vector> &r_functions) + static const LazyFunctionForAnonymousAttributeSetJoin &get_cached(const int amount, + ResourceScope &scope) { constexpr int cache_amount = 16; static std::array cached_functions = @@ -1273,10 +1293,7 @@ class LazyFunctionForAnonymousAttributeSetJoin : public lf::LazyFunction { return cached_functions[amount]; } - auto fn = std::make_unique(amount); - const auto &fn_ref = *fn; - r_functions.append(std::move(fn)); - return fn_ref; + return scope.construct(amount); } private: @@ -1288,6 +1305,122 @@ class LazyFunctionForAnonymousAttributeSetJoin : public lf::LazyFunction { } }; +class LazyFunctionForSimulationZone : public LazyFunction { + private: + const bNode &sim_output_bnode_; + const LazyFunction &fn_; + + public: + LazyFunctionForSimulationZone(const bNode &sim_output_bnode, const LazyFunction &fn) + : sim_output_bnode_(sim_output_bnode), fn_(fn) + { + debug_name_ = "Simulation Zone"; + inputs_ = fn.inputs(); + outputs_ = fn.outputs(); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &context) const override + { + GeoNodesLFUserData &user_data = *static_cast(context.user_data); + + bke::SimulationZoneComputeContext compute_context{user_data.compute_context, + sim_output_bnode_}; + + GeoNodesLFUserData zone_user_data = user_data; + zone_user_data.compute_context = &compute_context; + if (user_data.modifier_data->socket_log_contexts) { + zone_user_data.log_socket_values = user_data.modifier_data->socket_log_contexts->contains( + compute_context.hash()); + } + GeoNodesLFLocalUserData zone_local_user_data{zone_user_data}; + + lf::Context zone_context{context.storage, &zone_user_data, &zone_local_user_data}; + fn_.execute(params, zone_context); + } + + void *init_storage(LinearAllocator<> &allocator) const override + { + return fn_.init_storage(allocator); + } + + void destruct_storage(void *storage) const override + { + fn_.destruct_storage(storage); + } + + std::string input_name(const int i) const override + { + return fn_.input_name(i); + } + + std::string output_name(const int i) const override + { + return fn_.output_name(i); + } +}; + +using JoinAttributeSetsCache = Map, lf::OutputSocket *>; + +struct BuildGraphParams { + /** Lazy-function graph that nodes and links should be inserted into. */ + lf::Graph &lf_graph; + /** Map #bNodeSocket to newly generated sockets. Those maps are later used to insert links. */ + MultiValueMap lf_inputs_by_bsocket; + Map lf_output_by_bsocket; + /** + * Maps sockets to corresponding generated boolean sockets that indicate whether the socket is + * used or not. + */ + Map usage_by_bsocket; + /** + * Nodes that propagate anonymous attributes have to know which of those attributes to propagate. + * For that they have an attribute set input for each geometry output. + */ + Map lf_attribute_set_input_by_output_geometry_bsocket; + /** + * Multi-input sockets are split into a separate node that collects all the individual values and + * then passes them to the main node function as list. + */ + Map multi_input_socket_nodes; + /** + * This is similar to #lf_inputs_by_bsocket but contains more relevant information when border + * links are linked to multi-input sockets. + */ + Map lf_input_by_border_link; + /** + * Keeps track of all boolean inputs that indicate whether a socket is used. Links to those + * sockets may be replaced with a constant-true if necessary to break dependency cycles in + * #fix_link_cycles. + */ + Set socket_usage_inputs; + /** + * Collect input sockets that anonymous attribute sets based on fields or group inputs have to be + * linked to later. + */ + MultiValueMap lf_attribute_set_input_by_field_source_index; + MultiValueMap lf_attribute_set_input_by_caller_propagation_index; + /** */ + /** Cache to avoid building the same socket combinations multiple times. */ + Map, lf::OutputSocket *> socket_usages_combination_cache; +}; + +struct ZoneBuildInfo { + /** The lazy function that contains the zone. */ + const LazyFunction *lazy_function = nullptr; + + /** Information about what the various inputs and outputs of the lazy-function are. */ + IndexRange main_input_indices; + IndexRange main_output_indices; + IndexRange border_link_input_indices; + + IndexRange main_input_usage_indices; + IndexRange main_output_usage_indices; + IndexRange border_link_input_usage_indices; + + Map attribute_set_input_by_field_source_index; + Map attribute_set_input_by_caller_propagation_index; +}; + /** * Utility class to build a lazy-function graph based on a geometry nodes tree. * This is mainly a separate class because it makes it easier to have variables that can be @@ -1296,33 +1429,12 @@ class LazyFunctionForAnonymousAttributeSetJoin : public lf::LazyFunction { struct GeometryNodesLazyFunctionGraphBuilder { private: const bNodeTree &btree_; + const aai::AnonymousAttributeInferencingResult &attribute_inferencing_; + ResourceScope &scope_; + NodeMultiFunctions &node_multi_functions_; GeometryNodesLazyFunctionGraphInfo *lf_graph_info_; - lf::Graph *lf_graph_; GeometryNodeLazyFunctionGraphMapping *mapping_; - MultiValueMap input_socket_map_; - Map output_socket_map_; - Map multi_input_socket_nodes_; const bke::DataTypeConversions *conversions_; - /** - * Maps bsockets to boolean sockets in the graph whereby each boolean socket indicates whether - * the bsocket is used. Sockets not contained in this map are not used. - * This is indexed by `bNodeSocket::index_in_tree()`. - */ - Array socket_is_used_map_; - /** - * Some built-in nodes get additional boolean inputs that indicate whether certain outputs are - * used (field output sockets that contain new anonymous attribute references). - */ - Vector> output_used_sockets_for_builtin_nodes_; - /** - * Maps from output geometry sockets to corresponding attribute set inputs. - */ - Map attribute_set_propagation_map_; - /** - * Boolean inputs that tell a node if some socket (of the same or another node) is used. If this - * socket is in a link-cycle, its input can become a constant true. - */ - Set socket_usage_inputs_; /** * All group input nodes are combined into one dummy node in the lazy-function graph. @@ -1333,12 +1445,19 @@ struct GeometryNodesLazyFunctionGraphBuilder { */ Map simulation_inputs_usage_nodes_; + const TreeZones *tree_zones_; + Array zone_build_infos_; + friend class UsedSocketVisualizeOptions; public: GeometryNodesLazyFunctionGraphBuilder(const bNodeTree &btree, GeometryNodesLazyFunctionGraphInfo &lf_graph_info) - : btree_(btree), lf_graph_info_(&lf_graph_info) + : btree_(btree), + attribute_inferencing_(*btree.runtime->anonymous_attribute_inferencing), + scope_(lf_graph_info.scope), + node_multi_functions_(lf_graph_info.scope.construct(btree)), + lf_graph_info_(&lf_graph_info) { } @@ -1346,12 +1465,18 @@ struct GeometryNodesLazyFunctionGraphBuilder { { btree_.ensure_topology_cache(); - lf_graph_ = &lf_graph_info_->graph; mapping_ = &lf_graph_info_->mapping; conversions_ = &bke::get_implicit_type_conversions(); + tree_zones_ = btree_.zones(); + + this->initialize_mapping_arrays(); + this->build_zone_functions(); + this->build_root_graph(); + } - socket_is_used_map_.reinitialize(btree_.all_sockets().size()); - socket_is_used_map_.fill(nullptr); + private: + void initialize_mapping_arrays() + { mapping_->lf_input_index_for_output_bsocket_usage.reinitialize( btree_.all_output_sockets().size()); mapping_->lf_input_index_for_output_bsocket_usage.fill(-1); @@ -1360,262 +1485,967 @@ struct GeometryNodesLazyFunctionGraphBuilder { mapping_->lf_input_index_for_attribute_propagation_to_output.fill(-1); mapping_->lf_index_by_bsocket.reinitialize(btree_.all_sockets().size()); mapping_->lf_index_by_bsocket.fill(-1); + } - this->prepare_node_multi_functions(); - this->build_group_input_node(); - if (btree_.group_output_node() == nullptr) { - this->build_fallback_output_node(); - } - this->handle_nodes(); - this->handle_links(); - this->add_default_inputs(); - - this->build_attribute_propagation_input_node(); - this->build_output_usage_input_node(); - this->build_input_usage_output_node(); - this->build_socket_usages(); - - this->build_attribute_propagation_sets(); - this->fix_link_cycles(); + /** + * Builds lazy-functions for all zones in the node tree. + */ + void build_zone_functions() + { + zone_build_infos_.reinitialize(tree_zones_->zones.size()); - // this->print_graph(); + const Array zone_build_order = this->compute_zone_build_order(); - lf_graph_->update_node_indices(); - lf_graph_info_->num_inline_nodes_approximate += lf_graph_->nodes().size(); + for (const int zone_i : zone_build_order) { + const TreeZone &zone = *tree_zones_->zones[zone_i]; + BLI_assert(zone.output_node->type == GEO_NODE_SIMULATION_OUTPUT); + this->build_simulation_zone_function(zone); + } } - private: - void prepare_node_multi_functions() + Array compute_zone_build_order() { - lf_graph_info_->node_multi_functions = std::make_unique(btree_); + /* Build nested zones first. */ + Array zone_build_order(tree_zones_->zones.size()); + std::iota(zone_build_order.begin(), zone_build_order.end(), 0); + std::sort( + zone_build_order.begin(), zone_build_order.end(), [&](const int zone_a, const int zone_b) { + return tree_zones_->zones[zone_a]->depth > tree_zones_->zones[zone_b]->depth; + }); + return zone_build_order; } - void build_group_input_node() + /** + * Builds a lazy-function for a simulation zone. + * Internally, the generated lazy-function is just another graph. + */ + void build_simulation_zone_function(const TreeZone &zone) { - Vector input_cpp_types; - const Span interface_inputs = btree_.interface_inputs(); - for (const bNodeSocket *interface_input : interface_inputs) { - input_cpp_types.append(interface_input->typeinfo->geometry_nodes_cpp_type); + const int zone_i = zone.index; + ZoneBuildInfo &zone_info = zone_build_infos_[zone_i]; + lf::Graph &lf_graph = scope_.construct(); + + lf::Node *lf_zone_input_node = nullptr; + lf::Node *lf_main_input_usage_node = nullptr; + if (zone.input_node != nullptr) { + lf_zone_input_node = &this->build_simulation_zone_input_node(zone, lf_graph); + lf_main_input_usage_node = &this->build_simulation_zone_input_usage_node(zone, lf_graph); } + lf::Node &lf_border_link_input_node = this->build_zone_border_links_input_node(zone, lf_graph); + lf::Node &lf_zone_output_node = this->build_simulation_zone_output_node(zone, lf_graph); + lf::Node &lf_main_output_usage_node = this->build_simulation_zone_output_usage_node(zone, + lf_graph); + lf::Node &lf_border_link_usage_node = this->build_border_link_input_usage_node(zone, lf_graph); - /* Create a dummy node for the group inputs. */ - auto debug_info = std::make_unique(); - group_input_lf_node_ = &lf_graph_->add_dummy({}, input_cpp_types, debug_info.get()); + lf::Node &lf_simulation_usage_node = [&]() -> lf::Node & { + auto &lazy_function = scope_.construct(); + lf::Node &lf_node = lf_graph.add_function(lazy_function); - for (const int i : interface_inputs.index_range()) { - mapping_->group_input_sockets.append(&group_input_lf_node_->output(i)); - debug_info->socket_names.append(interface_inputs[i]->name); + if (lf_main_input_usage_node) { + for (const int i : lf_main_input_usage_node->inputs().index_range()) { + lf_graph.add_link(lf_node.output(0), lf_main_input_usage_node->input(i)); + } + } + return lf_node; + }(); + + BuildGraphParams graph_params{lf_graph}; + + lf::FunctionNode *lf_simulation_input = nullptr; + if (zone.input_node) { + lf_simulation_input = this->insert_simulation_input_node( + btree_, *zone.input_node, graph_params); } - lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); + lf::FunctionNode &lf_simulation_output = this->insert_simulation_output_node(*zone.output_node, + graph_params); + + for (const bNodeSocket *bsocket : zone.output_node->input_sockets().drop_back(1)) { + graph_params.usage_by_bsocket.add(bsocket, &lf_simulation_usage_node.output(1)); + } + + this->insert_nodes_and_zones(zone.child_nodes, zone.child_zones, graph_params); + + if (zone.input_node) { + this->build_output_socket_usages(*zone.input_node, graph_params); + } + for (const auto item : graph_params.lf_output_by_bsocket.items()) { + this->insert_links_from_socket(*item.key, *item.value, graph_params); + } + + this->link_border_link_inputs_and_usages( + zone, lf_border_link_input_node, lf_border_link_usage_node, graph_params); + + if (lf_zone_input_node != nullptr) { + for (const int i : lf_zone_input_node->outputs().index_range()) { + lf_graph.add_link(lf_zone_input_node->output(i), lf_simulation_input->input(i)); + } + } + for (const int i : lf_zone_output_node.inputs().index_range()) { + lf_graph.add_link(lf_simulation_output.output(i), lf_zone_output_node.input(i)); + } + + this->add_default_inputs(graph_params); + + Vector lf_zone_inputs; + if (lf_zone_input_node) { + lf_zone_inputs.extend(lf_zone_input_node->outputs()); + zone_info.main_input_indices = lf_zone_inputs.index_range(); + } + lf_zone_inputs.extend(lf_border_link_input_node.outputs()); + zone_info.border_link_input_indices = lf_zone_inputs.index_range().take_back( + lf_border_link_input_node.outputs().size()); + + lf_zone_inputs.extend(lf_main_output_usage_node.outputs()); + zone_info.main_output_usage_indices = lf_zone_inputs.index_range().take_back( + lf_main_output_usage_node.outputs().size()); + + Map lf_attribute_set_by_field_source_index; + Map lf_attribute_set_by_caller_propagation_index; + this->build_attribute_set_inputs_for_zone(graph_params, + zone_info, + lf_attribute_set_by_field_source_index, + lf_attribute_set_by_caller_propagation_index, + lf_zone_inputs); + this->link_attribute_set_inputs(lf_graph, + graph_params, + lf_attribute_set_by_field_source_index, + lf_attribute_set_by_caller_propagation_index); + this->fix_link_cycles(lf_graph, graph_params.socket_usage_inputs); + + Vector lf_zone_outputs; + lf_zone_outputs.extend(lf_zone_output_node.inputs()); + zone_info.main_output_indices = lf_zone_outputs.index_range(); + + if (lf_main_input_usage_node) { + lf_zone_outputs.extend(lf_main_input_usage_node->inputs()); + zone_info.main_input_usage_indices = lf_zone_outputs.index_range().take_back( + lf_main_input_usage_node->inputs().size()); + } + + lf_zone_outputs.extend(lf_border_link_usage_node.inputs()); + zone_info.border_link_input_usage_indices = lf_zone_outputs.index_range().take_back( + lf_border_link_usage_node.inputs().size()); + + lf_graph.update_node_indices(); + + auto &logger = scope_.construct(*lf_graph_info_); + auto &side_effect_provider = scope_.construct(); + + const auto &lf_graph_fn = scope_.construct( + lf_graph, lf_zone_inputs, lf_zone_outputs, &logger, &side_effect_provider); + const auto &zone_function = scope_.construct(*zone.output_node, + lf_graph_fn); + zone_info.lazy_function = &zone_function; + + // std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; } - /** - * Build an output node that just outputs default values in the case when there is no Group - * Output node in the tree. - */ - void build_fallback_output_node() + lf::DummyNode &build_simulation_zone_input_node(const TreeZone &zone, lf::Graph &lf_graph) { - Vector output_cpp_types; - auto debug_info = std::make_unique(); - for (const bNodeSocket *interface_output : btree_.interface_outputs()) { - output_cpp_types.append(interface_output->typeinfo->geometry_nodes_cpp_type); - debug_info->socket_names.append(interface_output->name); + Vector zone_input_types; + auto &debug_info = scope_.construct(); + debug_info.name = "Zone Input"; + for (const bNodeSocket *socket : zone.input_node->input_sockets().drop_back(1)) { + zone_input_types.append(socket->typeinfo->geometry_nodes_cpp_type); + debug_info.output_names.append(socket->identifier); + } + lf::DummyNode &node = lf_graph.add_dummy({}, zone_input_types, &debug_info); + return node; + } + + lf::DummyNode &build_simulation_zone_output_node(const TreeZone &zone, lf::Graph &lf_graph) + { + auto &debug_info = scope_.construct(); + debug_info.name = "Zone Output"; + Vector zone_output_types; + for (const bNodeSocket *socket : zone.output_node->output_sockets().drop_back(1)) { + zone_output_types.append(socket->typeinfo->geometry_nodes_cpp_type); + debug_info.input_names.append(socket->identifier); } + lf::DummyNode &node = lf_graph.add_dummy(zone_output_types, {}, &debug_info); + return node; + } - lf::Node &lf_node = lf_graph_->add_dummy(output_cpp_types, {}, debug_info.get()); - for (lf::InputSocket *lf_socket : lf_node.inputs()) { - const CPPType &type = lf_socket->type(); - lf_socket->set_default_value(type.default_value()); + lf::DummyNode &build_simulation_zone_input_usage_node(const TreeZone &zone, lf::Graph &lf_graph) + { + auto &debug_info = scope_.construct(); + debug_info.name = "Input Usages"; + Vector types; + types.append_n_times(&CPPType::get(), + zone.input_node->input_sockets().drop_back(1).size()); + debug_info.input_names.append_n_times("Usage", types.size()); + lf::DummyNode &node = lf_graph.add_dummy(types, {}, &debug_info); + return node; + } + + lf::DummyNode &build_simulation_zone_output_usage_node(const TreeZone &zone, lf::Graph &lf_graph) + { + auto &debug_info = scope_.construct(); + debug_info.name = "Output Usages"; + Vector types; + types.append_n_times(&CPPType::get(), + zone.output_node->output_sockets().drop_back(1).size()); + debug_info.output_names.append_n_times("Usage", types.size()); + lf::DummyNode &node = lf_graph.add_dummy({}, types, &debug_info); + return node; + } + + lf::DummyNode &build_zone_border_links_input_node(const TreeZone &zone, lf::Graph &lf_graph) + { + auto &debug_info = scope_.construct(); + debug_info.name = "Border Links"; + Vector border_link_types; + for (const bNodeLink *border_link : zone.border_links) { + border_link_types.append(border_link->tosock->typeinfo->geometry_nodes_cpp_type); + debug_info.output_names.append(StringRef("Link from ") + border_link->fromsock->identifier); } - mapping_->standard_group_output_sockets = lf_node.inputs(); + lf::DummyNode &node = lf_graph.add_dummy({}, border_link_types, &debug_info); + return node; + } - lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); + lf::DummyNode &build_border_link_input_usage_node(const TreeZone &zone, lf::Graph &lf_graph) + { + auto &debug_info = scope_.construct(); + debug_info.name = "Border Link Usages"; + Vector types; + types.append_n_times(&CPPType::get(), zone.border_links.size()); + debug_info.input_names.append_n_times("Usage", types.size()); + lf::DummyNode &node = lf_graph.add_dummy(types, {}, &debug_info); + return node; } - void handle_nodes() + void build_attribute_set_inputs_for_zone( + BuildGraphParams &graph_params, + ZoneBuildInfo &zone_info, + Map &lf_attribute_set_by_field_source_index, + Map &lf_attribute_set_by_caller_propagation_index, + Vector &lf_zone_inputs) { - /* Insert all nodes into the lazy function graph. */ - for (const bNode *bnode : btree_.all_nodes()) { - const bNodeType *node_type = bnode->typeinfo; - if (node_type == nullptr) { - continue; - } - if (bnode->is_muted()) { - this->handle_muted_node(*bnode); - continue; + const Vector all_required_field_sources = this->find_all_required_field_source_indices( + graph_params.lf_attribute_set_input_by_output_geometry_bsocket, + graph_params.lf_attribute_set_input_by_field_source_index); + const Vector all_required_caller_propagation_indices = + this->find_all_required_caller_propagation_indices( + graph_params.lf_attribute_set_input_by_output_geometry_bsocket, + graph_params.lf_attribute_set_input_by_caller_propagation_index); + + Map input_by_field_source_index; + + for (const int field_source_index : all_required_field_sources) { + const aai::FieldSource &field_source = + attribute_inferencing_.all_field_sources[field_source_index]; + if ([[maybe_unused]] const auto *input_field_source = std::get_if( + &field_source.data)) + { + input_by_field_source_index.add_new(field_source_index, + input_by_field_source_index.size()); } - switch (node_type->type) { - case NODE_FRAME: { - /* Ignored. */ - break; - } - case NODE_REROUTE: { - this->handle_reroute_node(*bnode); - break; - } - case NODE_GROUP_INPUT: { - this->handle_group_input_node(*bnode); - break; - } - case NODE_GROUP_OUTPUT: { - this->handle_group_output_node(*bnode); - break; - } - case NODE_CUSTOM_GROUP: - case NODE_GROUP: { - this->handle_group_node(*bnode); - break; - } - case GEO_NODE_VIEWER: { - this->handle_viewer_node(*bnode); - break; - } - case GEO_NODE_SIMULATION_INPUT: { - this->handle_simulation_input_node(btree_, *bnode); - break; - } - case GEO_NODE_SIMULATION_OUTPUT: { - this->handle_simulation_output_node(*bnode); - break; - } - case GEO_NODE_SWITCH: { - this->handle_switch_node(*bnode); - break; + else { + const auto &socket_field_source = std::get(field_source.data); + const bNodeSocket &bsocket = *socket_field_source.socket; + if (lf::OutputSocket *lf_field_socket = graph_params.lf_output_by_bsocket.lookup_default( + &bsocket, nullptr)) + { + lf::OutputSocket *lf_usage_socket = graph_params.usage_by_bsocket.lookup_default( + &bsocket, nullptr); + lf::OutputSocket &lf_attribute_set_socket = this->get_extracted_attributes( + *lf_field_socket, + lf_usage_socket, + graph_params.lf_graph, + graph_params.socket_usage_inputs); + lf_attribute_set_by_field_source_index.add(field_source_index, &lf_attribute_set_socket); } - default: { - if (node_type->geometry_node_execute) { - this->handle_geometry_node(*bnode); - break; - } - const NodeMultiFunctions::Item &fn_item = lf_graph_info_->node_multi_functions->try_get( - *bnode); - if (fn_item.fn != nullptr) { - this->handle_multi_function_node(*bnode, fn_item); - break; - } - if (node_type == &bke::NodeTypeUndefined) { - this->handle_undefined_node(*bnode); - break; - } - /* Nodes that don't match any of the criteria above are just ignored. */ - break; + else { + input_by_field_source_index.add_new(field_source_index, + input_by_field_source_index.size()); } } } + + { + auto &debug_info = scope_.construct(); + debug_info.name = "Attribute Sets"; + Vector types; + const int num = input_by_field_source_index.size() + + all_required_caller_propagation_indices.size(); + types.append_n_times(&CPPType::get(), num); + debug_info.output_names.append_n_times("Set", num); + lf::DummyNode &node = graph_params.lf_graph.add_dummy({}, types, &debug_info); + + for (const auto item : input_by_field_source_index.items()) { + const int field_source_index = item.key; + const int attribute_set_index = item.value; + lf::OutputSocket &lf_attribute_set_socket = node.output(attribute_set_index); + lf_attribute_set_by_field_source_index.add(field_source_index, &lf_attribute_set_socket); + + const int zone_input_index = lf_zone_inputs.append_and_get_index(&lf_attribute_set_socket); + zone_info.attribute_set_input_by_field_source_index.add(field_source_index, + zone_input_index); + } + for (const int i : all_required_caller_propagation_indices.index_range()) { + const int caller_propagation_index = all_required_caller_propagation_indices[i]; + lf::OutputSocket &lf_attribute_set_socket = node.output( + input_by_field_source_index.size() + i); + lf_attribute_set_by_caller_propagation_index.add_new(caller_propagation_index, + &lf_attribute_set_socket); + const int zone_input_index = lf_zone_inputs.append_and_get_index(&lf_attribute_set_socket); + zone_info.attribute_set_input_by_caller_propagation_index.add(caller_propagation_index, + zone_input_index); + } + } } - void handle_muted_node(const bNode &bnode) + /** + * Build the graph that contains all nodes that are not contained in any zone. This graph is + * called when this geometry nodes node group is evaluated. + */ + void build_root_graph() { - auto lazy_function = std::make_unique(bnode, - mapping_->lf_index_by_bsocket); - lf::Node &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); - for (const bNodeSocket *bsocket : bnode.input_sockets()) { - const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()]; - if (lf_index == -1) { - continue; - } - lf::InputSocket &lf_socket = lf_node.input(lf_index); - input_socket_map_.add(bsocket, &lf_socket); - mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); + lf::Graph &lf_graph = lf_graph_info_->graph; + + this->build_group_input_node(lf_graph); + if (btree_.group_output_node() == nullptr) { + this->build_fallback_output_node(lf_graph); } - for (const bNodeSocket *bsocket : bnode.output_sockets()) { - const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()]; - if (lf_index == -1) { - continue; + + lf::Node &lf_output_usage_node = this->build_output_usage_input_node(lf_graph); + this->build_input_usage_output_node(lf_graph); + + BuildGraphParams graph_params{lf_graph}; + if (const bNode *group_output_bnode = btree_.group_output_node()) { + for (const bNodeSocket *bsocket : group_output_bnode->input_sockets().drop_back(1)) { + graph_params.usage_by_bsocket.add(bsocket, &lf_output_usage_node.output(bsocket->index())); } - lf::OutputSocket &lf_socket = lf_node.output(lf_index); - output_socket_map_.add_new(bsocket, &lf_socket); - mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); } - } - void handle_reroute_node(const bNode &bnode) - { - const bNodeSocket &input_bsocket = bnode.input_socket(0); - const bNodeSocket &output_bsocket = bnode.output_socket(0); - const CPPType *type = get_socket_cpp_type(input_bsocket); - if (type == nullptr) { - return; + this->insert_nodes_and_zones( + tree_zones_->nodes_outside_zones, tree_zones_->root_zones, graph_params); + + for (const auto item : graph_params.lf_output_by_bsocket.items()) { + this->insert_links_from_socket(*item.key, *item.value, graph_params); } + this->build_group_input_usages(graph_params); + this->add_default_inputs(graph_params); - auto lazy_function = std::make_unique(*type); - lf::Node &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); + this->build_attribute_propagation_input_node(lf_graph); - lf::InputSocket &lf_input = lf_node.input(0); - lf::OutputSocket &lf_output = lf_node.output(0); - input_socket_map_.add(&input_bsocket, &lf_input); - output_socket_map_.add_new(&output_bsocket, &lf_output); - mapping_->bsockets_by_lf_socket_map.add(&lf_input, &input_bsocket); - mapping_->bsockets_by_lf_socket_map.add(&lf_output, &output_bsocket); + Map lf_attribute_set_by_field_source_index; + Map lf_attribute_set_by_caller_propagation_index; + this->build_attribute_set_inputs_outside_of_zones( + graph_params, + lf_attribute_set_by_field_source_index, + lf_attribute_set_by_caller_propagation_index); + this->link_attribute_set_inputs(lf_graph, + graph_params, + lf_attribute_set_by_field_source_index, + lf_attribute_set_by_caller_propagation_index); + + this->fix_link_cycles(lf_graph, graph_params.socket_usage_inputs); + + // std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; + + lf_graph.update_node_indices(); + lf_graph_info_->num_inline_nodes_approximate += lf_graph.nodes().size(); } - void handle_group_input_node(const bNode &bnode) + void build_attribute_set_inputs_outside_of_zones( + BuildGraphParams &graph_params, + Map &lf_attribute_set_by_field_source_index, + Map &lf_attribute_set_by_caller_propagation_index) { - for (const int i : btree_.interface_inputs().index_range()) { - const bNodeSocket &bsocket = bnode.output_socket(i); - lf::OutputSocket &lf_socket = group_input_lf_node_->output(i); - output_socket_map_.add_new(&bsocket, &lf_socket); - mapping_->dummy_socket_map.add_new(&bsocket, &lf_socket); - mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + const Vector all_required_field_sources = this->find_all_required_field_source_indices( + graph_params.lf_attribute_set_input_by_output_geometry_bsocket, + graph_params.lf_attribute_set_input_by_field_source_index); + + for (const int field_source_index : all_required_field_sources) { + const aai::FieldSource &field_source = + attribute_inferencing_.all_field_sources[field_source_index]; + lf::OutputSocket *lf_attribute_set_socket; + if (const auto *input_field_source = std::get_if(&field_source.data)) + { + const int input_index = input_field_source->input_index; + lf::OutputSocket &lf_field_socket = const_cast( + *mapping_->group_input_sockets[input_index]); + lf::OutputSocket *lf_usage_socket = const_cast( + mapping_->group_input_usage_sockets[input_index]->origin()); + lf_attribute_set_socket = &this->get_extracted_attributes( + lf_field_socket, + lf_usage_socket, + graph_params.lf_graph, + graph_params.socket_usage_inputs); + } + else { + const auto &socket_field_source = std::get(field_source.data); + const bNodeSocket &bsocket = *socket_field_source.socket; + lf::OutputSocket &lf_field_socket = *graph_params.lf_output_by_bsocket.lookup(&bsocket); + lf::OutputSocket *lf_usage_socket = graph_params.usage_by_bsocket.lookup_default(&bsocket, + nullptr); + lf_attribute_set_socket = &this->get_extracted_attributes( + lf_field_socket, + lf_usage_socket, + graph_params.lf_graph, + graph_params.socket_usage_inputs); + } + lf_attribute_set_by_field_source_index.add_new(field_source_index, lf_attribute_set_socket); + } + + for (const int caller_propagation_index : + attribute_inferencing_.propagated_output_geometry_indices) + { + const int group_output_index = + attribute_inferencing_.propagated_output_geometry_indices[caller_propagation_index]; + lf::OutputSocket &lf_attribute_set_socket = const_cast( + *mapping_->attribute_set_by_geometry_output.lookup(group_output_index)); + lf_attribute_set_by_caller_propagation_index.add(caller_propagation_index, + &lf_attribute_set_socket); } } - void handle_group_output_node(const bNode &bnode) + Vector find_all_required_field_source_indices( + const Map + &lf_attribute_set_input_by_output_geometry_bsocket, + const MultiValueMap &lf_attribute_set_input_by_field_source_index) { - Vector output_cpp_types; - auto debug_info = std::make_unique(); - for (const bNodeSocket *interface_input : btree_.interface_outputs()) { - output_cpp_types.append(interface_input->typeinfo->geometry_nodes_cpp_type); - debug_info->socket_names.append(interface_input->name); + BitVector<> all_required_field_sources(attribute_inferencing_.all_field_sources.size(), false); + for (const bNodeSocket *geometry_output_bsocket : + lf_attribute_set_input_by_output_geometry_bsocket.keys()) + { + all_required_field_sources |= + attribute_inferencing_ + .required_fields_by_geometry_socket[geometry_output_bsocket->index_in_tree()]; + } + for (const int field_source_index : lf_attribute_set_input_by_field_source_index.keys()) { + all_required_field_sources[field_source_index].set(); } - lf::DummyNode &group_output_lf_node = lf_graph_->add_dummy( - output_cpp_types, {}, debug_info.get()); + Vector indices; + bits::foreach_1_index(all_required_field_sources, [&](const int i) { indices.append(i); }); + return indices; + } - for (const int i : group_output_lf_node.inputs().index_range()) { - const bNodeSocket &bsocket = bnode.input_socket(i); - lf::InputSocket &lf_socket = group_output_lf_node.input(i); - input_socket_map_.add(&bsocket, &lf_socket); - mapping_->dummy_socket_map.add(&bsocket, &lf_socket); - mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + Vector find_all_required_caller_propagation_indices( + const Map + &lf_attribute_set_input_by_output_geometry_bsocket, + const MultiValueMap + &lf_attribute_set_input_by_caller_propagation_index) + { + BitVector<> all_required_caller_propagation_indices( + attribute_inferencing_.propagated_output_geometry_indices.size(), false); + for (const bNodeSocket *geometry_output_bs : + lf_attribute_set_input_by_output_geometry_bsocket.keys()) + { + all_required_caller_propagation_indices |= + attribute_inferencing_ + .propagate_to_output_by_geometry_socket[geometry_output_bs->index_in_tree()]; } - - if (&bnode == btree_.group_output_node()) { - mapping_->standard_group_output_sockets = group_output_lf_node.inputs(); + for (const int caller_propagation_index : + lf_attribute_set_input_by_caller_propagation_index.keys()) + { + all_required_caller_propagation_indices[caller_propagation_index].set(); } - lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); + Vector indices; + bits::foreach_1_index(all_required_caller_propagation_indices, + [&](const int i) { indices.append(i); }); + return indices; } - void handle_group_node(const bNode &bnode) + void link_attribute_set_inputs( + lf::Graph &lf_graph, + BuildGraphParams &graph_params, + const Map &lf_attribute_set_by_field_source_index, + const Map &lf_attribute_set_by_caller_propagation_index) { - const bNodeTree *group_btree = reinterpret_cast(bnode.id); - if (group_btree == nullptr) { - return; - } - const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info = - ensure_geometry_nodes_lazy_function_graph(*group_btree); - if (group_lf_graph_info == nullptr) { - return; - } + JoinAttributeSetsCache join_attribute_sets_cache; - auto lazy_function = std::make_unique( - bnode, *group_lf_graph_info, *lf_graph_info_); - lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); + for (const MapItem item : + graph_params.lf_attribute_set_input_by_output_geometry_bsocket.items()) + { + const bNodeSocket &geometry_output_bsocket = *item.key; + lf::InputSocket &lf_attribute_set_input = *item.value; + + Vector lf_attribute_set_sockets; + + const BoundedBitSpan required_fields = + attribute_inferencing_ + .required_fields_by_geometry_socket[geometry_output_bsocket.index_in_tree()]; + bits::foreach_1_index(required_fields, [&](const int field_source_index) { + const auto &field_source = attribute_inferencing_.all_field_sources[field_source_index]; + if (const auto *socket_field_source = std::get_if( + &field_source.data)) { + if (&socket_field_source->socket->owner_node() == &geometry_output_bsocket.owner_node()) + { + return; + } + } + lf_attribute_set_sockets.append( + lf_attribute_set_by_field_source_index.lookup(field_source_index)); + }); + + const BoundedBitSpan required_caller_propagations = + attribute_inferencing_ + .propagate_to_output_by_geometry_socket[geometry_output_bsocket.index_in_tree()]; + bits::foreach_1_index(required_caller_propagations, [&](const int caller_propagation_index) { + lf_attribute_set_sockets.append( + lf_attribute_set_by_caller_propagation_index.lookup(caller_propagation_index)); + }); + + if (lf::OutputSocket *lf_attribute_set = this->join_attribute_sets( + lf_attribute_set_sockets, + join_attribute_sets_cache, + lf_graph, + graph_params.socket_usage_inputs)) + { + lf_graph.add_link(*lf_attribute_set, lf_attribute_set_input); + } + else { + static const bke::AnonymousAttributeSet empty_set; + lf_attribute_set_input.set_default_value(&empty_set); + } + } + + for (const auto item : graph_params.lf_attribute_set_input_by_field_source_index.items()) { + const int field_source_index = item.key; + lf::OutputSocket &lf_attribute_set_socket = *lf_attribute_set_by_field_source_index.lookup( + field_source_index); + for (lf::InputSocket *lf_attribute_set_input : item.value) { + lf_graph.add_link(lf_attribute_set_socket, *lf_attribute_set_input); + } + } + for (const auto item : graph_params.lf_attribute_set_input_by_caller_propagation_index.items()) + { + const int caller_propagation_index = item.key; + lf::OutputSocket &lf_attribute_set_socket = + *lf_attribute_set_by_caller_propagation_index.lookup(caller_propagation_index); + for (lf::InputSocket *lf_attribute_set_input : item.value) { + lf_graph.add_link(lf_attribute_set_socket, *lf_attribute_set_input); + } + } + } + + void insert_nodes_and_zones(const Span bnodes, + const Span zones, + BuildGraphParams &graph_params) + { + Vector nodes_to_insert = bnodes; + Map zone_by_output; + for (const TreeZone *zone : zones) { + nodes_to_insert.append(zone->output_node); + zone_by_output.add(zone->output_node, zone); + } + /* Insert nodes from right to left so that usage sockets can be build in the same pass. */ + std::sort(nodes_to_insert.begin(), nodes_to_insert.end(), [](const bNode *a, const bNode *b) { + return a->runtime->toposort_right_to_left_index < b->runtime->toposort_right_to_left_index; + }); + + for (const bNode *bnode : nodes_to_insert) { + this->build_output_socket_usages(*bnode, graph_params); + if (const TreeZone *zone = zone_by_output.lookup_default(bnode, nullptr)) { + this->insert_child_zone_node(*zone, graph_params); + } + else { + this->insert_node_in_graph(*bnode, graph_params); + } + } + } + + void link_border_link_inputs_and_usages(const TreeZone &zone, + lf::Node &lf_border_link_input_node, + lf::Node &lf_border_link_usage_node, + BuildGraphParams &graph_params) + { + lf::Graph &lf_graph = graph_params.lf_graph; + for (const int border_link_i : zone.border_links.index_range()) { + const bNodeLink &border_link = *zone.border_links[border_link_i]; + lf::OutputSocket &lf_from = lf_border_link_input_node.output(border_link_i); + const Vector lf_link_targets = this->find_link_targets(border_link, + graph_params); + for (lf::InputSocket *lf_to : lf_link_targets) { + lf_graph.add_link(lf_from, *lf_to); + } + lf::InputSocket &lf_usage_output = lf_border_link_usage_node.input(border_link_i); + if (lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default( + border_link.tosock, nullptr)) + { + lf_graph.add_link(*lf_usage, lf_usage_output); + } + else { + static const bool static_false = false; + lf_usage_output.set_default_value(&static_false); + } + } + } + + lf::OutputSocket &get_extracted_attributes(lf::OutputSocket &lf_field_socket, + lf::OutputSocket *lf_usage_socket, + lf::Graph &lf_graph, + Set &socket_usage_inputs) + { + const ValueOrFieldCPPType &type = *ValueOrFieldCPPType::get_from_self(lf_field_socket.type()); + auto &lazy_function = scope_.construct(type); + lf::Node &lf_node = lf_graph.add_function(lazy_function); + lf::InputSocket &lf_use_input = lf_node.input(0); + lf::InputSocket &lf_field_input = lf_node.input(1); + socket_usage_inputs.add_new(&lf_use_input); + if (lf_usage_socket) { + lf_graph.add_link(*lf_usage_socket, lf_use_input); + } + else { + static const bool static_false = false; + lf_use_input.set_default_value(&static_false); + } + lf_graph.add_link(lf_field_socket, lf_field_input); + return lf_node.output(0); + } + + /** + * Join multiple attributes set into a single attribute set that can be passed into a node. + */ + lf::OutputSocket *join_attribute_sets(const Span lf_attribute_set_sockets, + JoinAttributeSetsCache &cache, + lf::Graph &lf_graph, + Set &socket_usage_inputs) + { + if (lf_attribute_set_sockets.is_empty()) { + return nullptr; + } + if (lf_attribute_set_sockets.size() == 1) { + return lf_attribute_set_sockets[0]; + } + + Vector key = lf_attribute_set_sockets; + std::sort(key.begin(), key.end()); + return cache.lookup_or_add_cb(key, [&]() { + const auto &lazy_function = LazyFunctionForAnonymousAttributeSetJoin::get_cached( + lf_attribute_set_sockets.size(), scope_); + lf::Node &lf_node = lf_graph.add_function(lazy_function); + for (const int i : lf_attribute_set_sockets.index_range()) { + lf::OutputSocket &lf_attribute_set_socket = *lf_attribute_set_sockets[i]; + lf::InputSocket &lf_use_input = lf_node.input(lazy_function.get_use_input(i)); + + /* Some attribute sets could potentially be set unused in the future based on more dynamic + * analysis of the node tree. */ + static const bool static_true = true; + lf_use_input.set_default_value(&static_true); + + socket_usage_inputs.add(&lf_use_input); + lf::InputSocket &lf_attribute_set_input = lf_node.input( + lazy_function.get_attribute_set_input(i)); + lf_graph.add_link(lf_attribute_set_socket, lf_attribute_set_input); + } + return &lf_node.output(0); + }); + } + + void insert_child_zone_node(const TreeZone &child_zone, BuildGraphParams &graph_params) + { + const int child_zone_i = child_zone.index; + ZoneBuildInfo &child_zone_info = zone_build_infos_[child_zone_i]; + lf::FunctionNode &child_zone_node = graph_params.lf_graph.add_function( + *child_zone_info.lazy_function); + mapping_->zone_node_map.add_new(&child_zone, &child_zone_node); + + for (const int i : child_zone_info.main_input_indices.index_range()) { + const bNodeSocket &bsocket = child_zone.input_node->input_socket(i); + lf::InputSocket &lf_input_socket = child_zone_node.input( + child_zone_info.main_input_indices[i]); + lf::OutputSocket &lf_usage_socket = child_zone_node.output( + child_zone_info.main_input_usage_indices[i]); + mapping_->bsockets_by_lf_socket_map.add(&lf_input_socket, &bsocket); + graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_input_socket); + graph_params.usage_by_bsocket.add(&bsocket, &lf_usage_socket); + } + for (const int i : child_zone_info.main_output_indices.index_range()) { + const bNodeSocket &bsocket = child_zone.output_node->output_socket(i); + lf::OutputSocket &lf_output_socket = child_zone_node.output( + child_zone_info.main_output_indices[i]); + lf::InputSocket &lf_usage_input = child_zone_node.input( + child_zone_info.main_output_usage_indices[i]); + mapping_->bsockets_by_lf_socket_map.add(&lf_output_socket, &bsocket); + graph_params.lf_output_by_bsocket.add(&bsocket, &lf_output_socket); + graph_params.socket_usage_inputs.add(&lf_usage_input); + if (lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default(&bsocket, + nullptr)) { + graph_params.lf_graph.add_link(*lf_usage, lf_usage_input); + } + else { + static const bool static_false = false; + lf_usage_input.set_default_value(&static_false); + } + } + + const Span child_border_links = child_zone.border_links; + for (const int child_border_link_i : child_border_links.index_range()) { + lf::InputSocket &child_border_link_input = child_zone_node.input( + child_zone_info.border_link_input_indices[child_border_link_i]); + const bNodeLink &link = *child_border_links[child_border_link_i]; + graph_params.lf_input_by_border_link.add(&link, &child_border_link_input); + lf::OutputSocket &lf_usage = child_zone_node.output( + child_zone_info.border_link_input_usage_indices[child_border_link_i]); + graph_params.lf_inputs_by_bsocket.add(link.tosock, &child_border_link_input); + graph_params.usage_by_bsocket.add(link.tosock, &lf_usage); + } + + for (const auto item : child_zone_info.attribute_set_input_by_field_source_index.items()) { + const int field_source_index = item.key; + const int child_zone_input_index = item.value; + lf::InputSocket &lf_attribute_set_input = child_zone_node.input(child_zone_input_index); + graph_params.lf_attribute_set_input_by_field_source_index.add(field_source_index, + &lf_attribute_set_input); + } + for (const auto item : child_zone_info.attribute_set_input_by_caller_propagation_index.items()) + { + const int caller_propagation_index = item.key; + const int child_zone_input_index = item.value; + lf::InputSocket &lf_attribute_set_input = child_zone_node.input(child_zone_input_index); + graph_params.lf_attribute_set_input_by_caller_propagation_index.add(caller_propagation_index, + &lf_attribute_set_input); + } + } + + void build_group_input_node(lf::Graph &lf_graph) + { + Vector input_cpp_types; + const Span interface_inputs = btree_.interface_inputs(); + for (const bNodeSocket *interface_input : interface_inputs) { + input_cpp_types.append(interface_input->typeinfo->geometry_nodes_cpp_type); + } + + /* Create a dummy node for the group inputs. */ + auto &debug_info = scope_.construct(); + group_input_lf_node_ = &lf_graph.add_dummy({}, input_cpp_types, &debug_info); + + for (const int i : interface_inputs.index_range()) { + mapping_->group_input_sockets.append(&group_input_lf_node_->output(i)); + debug_info.socket_names.append(interface_inputs[i]->name); + } + } + + /** + * Build an output node that just outputs default values in the case when there is no Group + * Output node in the tree. + */ + void build_fallback_output_node(lf::Graph &lf_graph) + { + Vector output_cpp_types; + auto &debug_info = scope_.construct(); + for (const bNodeSocket *interface_output : btree_.interface_outputs()) { + output_cpp_types.append(interface_output->typeinfo->geometry_nodes_cpp_type); + debug_info.socket_names.append(interface_output->name); + } + + lf::Node &lf_node = lf_graph.add_dummy(output_cpp_types, {}, &debug_info); + for (lf::InputSocket *lf_socket : lf_node.inputs()) { + const CPPType &type = lf_socket->type(); + lf_socket->set_default_value(type.default_value()); + } + mapping_->standard_group_output_sockets = lf_node.inputs(); + } + + void insert_node_in_graph(const bNode &bnode, BuildGraphParams &graph_params) + { + const bNodeType *node_type = bnode.typeinfo; + if (node_type == nullptr) { + return; + } + if (bnode.is_muted()) { + this->build_muted_node(bnode, graph_params); + return; + } + switch (node_type->type) { + case NODE_FRAME: { + /* Ignored. */ + break; + } + case NODE_REROUTE: { + this->build_reroute_node(bnode, graph_params); + break; + } + case NODE_GROUP_INPUT: { + this->handle_group_input_node(bnode, graph_params); + break; + } + case NODE_GROUP_OUTPUT: { + this->build_group_output_node(bnode, graph_params); + break; + } + case NODE_CUSTOM_GROUP: + case NODE_GROUP: { + this->build_group_node(bnode, graph_params); + break; + } + case GEO_NODE_VIEWER: { + this->build_viewer_node(bnode, graph_params); + break; + } + case GEO_NODE_SWITCH: { + this->build_switch_node(bnode, graph_params); + break; + } + default: { + if (node_type->geometry_node_execute) { + this->build_geometry_node(bnode, graph_params); + break; + } + const NodeMultiFunctions::Item &fn_item = node_multi_functions_.try_get(bnode); + if (fn_item.fn != nullptr) { + this->build_multi_function_node(bnode, fn_item, graph_params); + break; + } + if (node_type == &bke::NodeTypeUndefined) { + this->build_undefined_node(bnode, graph_params); + break; + } + /* Nodes that don't match any of the criteria above are just ignored. */ + break; + } + } + } + + void build_muted_node(const bNode &bnode, BuildGraphParams &graph_params) + { + auto &lazy_function = scope_.construct( + bnode, mapping_->lf_index_by_bsocket); + lf::Node &lf_node = graph_params.lf_graph.add_function(lazy_function); + for (const bNodeSocket *bsocket : bnode.input_sockets()) { + const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()]; + if (lf_index == -1) { + continue; + } + lf::InputSocket &lf_socket = lf_node.input(lf_index); + graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); + } + for (const bNodeSocket *bsocket : bnode.output_sockets()) { + const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()]; + if (lf_index == -1) { + continue; + } + lf::OutputSocket &lf_socket = lf_node.output(lf_index); + graph_params.lf_output_by_bsocket.add_new(bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); + } + + this->build_muted_node_usages(bnode, graph_params); + } + + /** + * An input of a muted node is used when any of its internally linked outputs is used. + */ + void build_muted_node_usages(const bNode &bnode, BuildGraphParams &graph_params) + { + /* Find all outputs that use a specific input. */ + MultiValueMap outputs_by_input; + for (const bNodeLink &blink : bnode.internal_links()) { + outputs_by_input.add(blink.fromsock, blink.tosock); + } + for (const auto item : outputs_by_input.items()) { + const bNodeSocket &input_bsocket = *item.key; + const Span output_bsockets = item.value; + + /* The input is used if any of the internally linked outputs is used. */ + Vector lf_socket_usages; + for (const bNodeSocket *output_bsocket : output_bsockets) { + if (lf::OutputSocket *lf_socket = graph_params.usage_by_bsocket.lookup_default( + output_bsocket, nullptr)) + { + lf_socket_usages.append(lf_socket); + } + } + graph_params.usage_by_bsocket.add(&input_bsocket, + this->or_socket_usages(lf_socket_usages, graph_params)); + } + } + + void build_reroute_node(const bNode &bnode, BuildGraphParams &graph_params) + { + const bNodeSocket &input_bsocket = bnode.input_socket(0); + const bNodeSocket &output_bsocket = bnode.output_socket(0); + const CPPType *type = get_socket_cpp_type(input_bsocket); + if (type == nullptr) { + return; + } + + auto &lazy_function = scope_.construct(*type); + lf::Node &lf_node = graph_params.lf_graph.add_function(lazy_function); + + lf::InputSocket &lf_input = lf_node.input(0); + lf::OutputSocket &lf_output = lf_node.output(0); + graph_params.lf_inputs_by_bsocket.add(&input_bsocket, &lf_input); + graph_params.lf_output_by_bsocket.add_new(&output_bsocket, &lf_output); + mapping_->bsockets_by_lf_socket_map.add(&lf_input, &input_bsocket); + mapping_->bsockets_by_lf_socket_map.add(&lf_output, &output_bsocket); + + if (lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default( + &bnode.output_socket(0), nullptr)) + { + graph_params.usage_by_bsocket.add(&bnode.input_socket(0), lf_usage); + } + } + + void handle_group_input_node(const bNode &bnode, BuildGraphParams &graph_params) + { + for (const int i : btree_.interface_inputs().index_range()) { + const bNodeSocket &bsocket = bnode.output_socket(i); + lf::OutputSocket &lf_socket = group_input_lf_node_->output(i); + graph_params.lf_output_by_bsocket.add_new(&bsocket, &lf_socket); + mapping_->dummy_socket_map.add_new(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + + void build_group_output_node(const bNode &bnode, BuildGraphParams &graph_params) + { + Vector output_cpp_types; + auto &debug_info = scope_.construct(); + for (const bNodeSocket *interface_input : btree_.interface_outputs()) { + output_cpp_types.append(interface_input->typeinfo->geometry_nodes_cpp_type); + debug_info.socket_names.append(interface_input->name); + } + + lf::DummyNode &group_output_lf_node = graph_params.lf_graph.add_dummy( + output_cpp_types, {}, &debug_info); + + for (const int i : group_output_lf_node.inputs().index_range()) { + const bNodeSocket &bsocket = bnode.input_socket(i); + lf::InputSocket &lf_socket = group_output_lf_node.input(i); + graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_socket); + mapping_->dummy_socket_map.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + + if (&bnode == btree_.group_output_node()) { + mapping_->standard_group_output_sockets = group_output_lf_node.inputs(); + } + } + + void build_group_node(const bNode &bnode, BuildGraphParams &graph_params) + { + const bNodeTree *group_btree = reinterpret_cast(bnode.id); + if (group_btree == nullptr) { + return; + } + const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info = + ensure_geometry_nodes_lazy_function_graph(*group_btree); + if (group_lf_graph_info == nullptr) { + return; + } + + auto &lazy_function = scope_.construct( + bnode, *group_lf_graph_info, *lf_graph_info_); + lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(lazy_function); for (const int i : bnode.input_sockets().index_range()) { const bNodeSocket &bsocket = bnode.input_socket(i); BLI_assert(!bsocket.is_multi_input()); lf::InputSocket &lf_socket = lf_node.input(i); - input_socket_map_.add(&bsocket, &lf_socket); + graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); } for (const int i : bnode.output_sockets().index_range()) { const bNodeSocket &bsocket = bnode.output_socket(i); lf::OutputSocket &lf_socket = lf_node.output(i); - output_socket_map_.add_new(&bsocket, &lf_socket); + graph_params.lf_output_by_bsocket.add_new(&bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); } mapping_->group_node_map.add(&bnode, &lf_node); @@ -1629,7 +2459,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { if (lf_input_index != -1) { lf::InputSocket &lf_input = lf_node.input(lf_input_index); lf_input.set_default_value(&static_false); - socket_usage_inputs_.add(&lf_input); + graph_params.socket_usage_inputs.add(&lf_input); } } { @@ -1638,17 +2468,82 @@ struct GeometryNodesLazyFunctionGraphBuilder { [bsocket->index_in_all_outputs()]; if (lf_input_index != -1) { lf::InputSocket &lf_input = lf_node.input(lf_input_index); - attribute_set_propagation_map_.add(bsocket, &lf_input); + graph_params.lf_attribute_set_input_by_output_geometry_bsocket.add(bsocket, &lf_input); + } + } + } + + this->build_group_node_socket_usage(bnode, lf_node, graph_params); + } + + void build_group_node_socket_usage(const bNode &bnode, + lf::FunctionNode &lf_group_node, + BuildGraphParams &graph_params) + { + const bNodeTree *bgroup = reinterpret_cast(bnode.id); + if (bgroup == nullptr) { + return; + } + const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info = + ensure_geometry_nodes_lazy_function_graph(*bgroup); + if (group_lf_graph_info == nullptr) { + return; + } + const auto &fn = static_cast(lf_group_node.function()); + + for (const bNodeSocket *input_bsocket : bnode.input_sockets()) { + const int input_index = input_bsocket->index(); + const InputUsageHint &input_usage_hint = + group_lf_graph_info->mapping.group_input_usage_hints[input_index]; + switch (input_usage_hint.type) { + case InputUsageHintType::Never: { + /* Nothing to do. */ + break; } + case InputUsageHintType::DependsOnOutput: { + Vector output_usages; + for (const int i : input_usage_hint.output_dependencies) { + if (lf::OutputSocket *lf_socket = graph_params.usage_by_bsocket.lookup_default( + &bnode.output_socket(i), nullptr)) + { + output_usages.append(lf_socket); + } + } + graph_params.usage_by_bsocket.add(input_bsocket, + this->or_socket_usages(output_usages, graph_params)); + break; + } + case InputUsageHintType::DynamicSocket: { + graph_params.usage_by_bsocket.add( + input_bsocket, + &lf_group_node.output(fn.lf_output_for_input_bsocket_usage_.lookup(input_index))); + break; + } + } + } + + for (const bNodeSocket *output_bsocket : bnode.output_sockets()) { + const int lf_input_index = + mapping_ + ->lf_input_index_for_output_bsocket_usage[output_bsocket->index_in_all_outputs()]; + BLI_assert(lf_input_index >= 0); + lf::InputSocket &lf_socket = lf_group_node.input(lf_input_index); + if (lf::OutputSocket *lf_output_is_used = graph_params.usage_by_bsocket.lookup_default( + output_bsocket, nullptr)) + { + graph_params.lf_graph.add_link(*lf_output_is_used, lf_socket); + } + else { + static const bool static_false = false; + lf_socket.set_default_value(&static_false); } } - lf_graph_info_->functions.append(std::move(lazy_function)); } - void handle_geometry_node(const bNode &bnode) + void build_geometry_node(const bNode &bnode, BuildGraphParams &graph_params) { - auto lazy_function = std::make_unique(bnode, *lf_graph_info_); - lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + auto &lazy_function = scope_.construct(bnode, *lf_graph_info_); + lf::Node &lf_node = graph_params.lf_graph.add_function(lazy_function); for (const bNodeSocket *bsocket : bnode.input_sockets()) { const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()]; @@ -1658,11 +2553,11 @@ struct GeometryNodesLazyFunctionGraphBuilder { lf::InputSocket &lf_socket = lf_node.input(lf_index); if (bsocket->is_multi_input()) { - auto multi_input_lazy_function = std::make_unique(*bsocket); - lf::Node &lf_multi_input_node = lf_graph_->add_function(*multi_input_lazy_function); - lf_graph_info_->functions.append(std::move(multi_input_lazy_function)); - lf_graph_->add_link(lf_multi_input_node.output(0), lf_socket); - multi_input_socket_nodes_.add_new(bsocket, &lf_multi_input_node); + auto &multi_input_lazy_function = scope_.construct(*bsocket); + lf::Node &lf_multi_input_node = graph_params.lf_graph.add_function( + multi_input_lazy_function); + graph_params.lf_graph.add_link(lf_multi_input_node.output(0), lf_socket); + graph_params.multi_input_socket_nodes.add_new(bsocket, &lf_multi_input_node); for (lf::InputSocket *lf_multi_input_socket : lf_multi_input_node.inputs()) { mapping_->bsockets_by_lf_socket_map.add(lf_multi_input_socket, bsocket); const void *default_value = lf_multi_input_socket->type().default_value(); @@ -1670,7 +2565,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { } } else { - input_socket_map_.add(bsocket, &lf_socket); + graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); } } @@ -1680,7 +2575,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { continue; } lf::OutputSocket &lf_socket = lf_node.output(lf_index); - output_socket_map_.add_new(bsocket, &lf_socket); + graph_params.lf_output_by_bsocket.add_new(bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); } @@ -1689,9 +2584,16 @@ struct GeometryNodesLazyFunctionGraphBuilder { const int lf_input_index = mapping_->lf_input_index_for_output_bsocket_usage[bsocket->index_in_all_outputs()]; if (lf_input_index != -1) { - output_used_sockets_for_builtin_nodes_.append_as(bsocket, - &lf_node.input(lf_input_index)); - socket_usage_inputs_.add_new(&lf_node.input(lf_input_index)); + lf::InputSocket &lf_input_socket = lf_node.input(lf_input_index); + if (lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default(bsocket, + nullptr)) { + graph_params.lf_graph.add_link(*lf_usage, lf_input_socket); + } + else { + static const bool static_false = false; + lf_input_socket.set_default_value(&static_false); + } + graph_params.socket_usage_inputs.add_new(&lf_node.input(lf_input_index)); } } { @@ -1699,20 +2601,53 @@ struct GeometryNodesLazyFunctionGraphBuilder { const int lf_input_index = mapping_->lf_input_index_for_attribute_propagation_to_output [bsocket->index_in_all_outputs()]; if (lf_input_index != -1) { - attribute_set_propagation_map_.add(bsocket, &lf_node.input(lf_input_index)); + graph_params.lf_attribute_set_input_by_output_geometry_bsocket.add( + bsocket, &lf_node.input(lf_input_index)); } } } - lf_graph_info_->functions.append(std::move(lazy_function)); + this->build_standard_node_input_socket_usage(bnode, graph_params); + } + + void build_standard_node_input_socket_usage(const bNode &bnode, BuildGraphParams &graph_params) + { + if (bnode.input_sockets().is_empty()) { + return; + } + + Vector output_usages; + for (const bNodeSocket *output_socket : bnode.output_sockets()) { + if (!output_socket->is_available()) { + continue; + } + if (lf::OutputSocket *is_used_socket = graph_params.usage_by_bsocket.lookup_default( + output_socket, nullptr)) + { + output_usages.append_non_duplicates(is_used_socket); + } + } + + /* Assume every input is used when any output is used. */ + lf::OutputSocket *lf_usage = this->or_socket_usages(output_usages, graph_params); + if (lf_usage == nullptr) { + return; + } + + for (const bNodeSocket *input_socket : bnode.input_sockets()) { + if (input_socket->is_available()) { + graph_params.usage_by_bsocket.add(input_socket, lf_usage); + } + } } - void handle_multi_function_node(const bNode &bnode, const NodeMultiFunctions::Item &fn_item) + void build_multi_function_node(const bNode &bnode, + const NodeMultiFunctions::Item &fn_item, + BuildGraphParams &graph_params) { - auto lazy_function = std::make_unique( + auto &lazy_function = scope_.construct( bnode, fn_item, mapping_->lf_index_by_bsocket); - lf::Node &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); + lf::Node &lf_node = graph_params.lf_graph.add_function(lazy_function); for (const bNodeSocket *bsocket : bnode.input_sockets()) { const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()]; @@ -1721,7 +2656,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { } BLI_assert(!bsocket->is_multi_input()); lf::InputSocket &lf_socket = lf_node.input(lf_index); - input_socket_map_.add(bsocket, &lf_socket); + graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); } for (const bNodeSocket *bsocket : bnode.output_sockets()) { @@ -1730,96 +2665,112 @@ struct GeometryNodesLazyFunctionGraphBuilder { continue; } lf::OutputSocket &lf_socket = lf_node.output(lf_index); - output_socket_map_.add(bsocket, &lf_socket); + graph_params.lf_output_by_bsocket.add(bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); } + + this->build_standard_node_input_socket_usage(bnode, graph_params); } - void handle_viewer_node(const bNode &bnode) + void build_viewer_node(const bNode &bnode, BuildGraphParams &graph_params) { - auto lazy_function = std::make_unique( + auto &lazy_function = scope_.construct( bnode, mapping_->lf_index_by_bsocket); - lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); + lf::FunctionNode &lf_viewer_node = graph_params.lf_graph.add_function(lazy_function); for (const bNodeSocket *bsocket : bnode.input_sockets()) { const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()]; if (lf_index == -1) { continue; } - lf::InputSocket &lf_socket = lf_node.input(lf_index); - input_socket_map_.add(bsocket, &lf_socket); + lf::InputSocket &lf_socket = lf_viewer_node.input(lf_index); + graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); } - mapping_->viewer_node_map.add(&bnode, &lf_node); + mapping_->viewer_node_map.add(&bnode, &lf_viewer_node); + + { + auto &usage_lazy_function = scope_.construct( + lf_viewer_node); + lf::FunctionNode &lf_usage_node = graph_params.lf_graph.add_function(usage_lazy_function); + + for (const bNodeSocket *bsocket : bnode.input_sockets()) { + if (bsocket->is_available()) { + graph_params.usage_by_bsocket.add(bsocket, &lf_usage_node.output(0)); + } + } + } } - void handle_simulation_input_node(const bNodeTree &node_tree, const bNode &bnode) + lf::FunctionNode *insert_simulation_input_node(const bNodeTree &node_tree, + const bNode &bnode, + BuildGraphParams &graph_params) { const NodeGeometrySimulationInput *storage = static_cast( bnode.storage); if (node_tree.node_by_id(storage->output_node_id) == nullptr) { - return; + return nullptr; } std::unique_ptr lazy_function = get_simulation_input_lazy_function( node_tree, bnode, *lf_graph_info_); - lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); + lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(*lazy_function); + scope_.add(std::move(lazy_function)); for (const int i : bnode.input_sockets().index_range().drop_back(1)) { const bNodeSocket &bsocket = bnode.input_socket(i); lf::InputSocket &lf_socket = lf_node.input( mapping_->lf_index_by_bsocket[bsocket.index_in_tree()]); - input_socket_map_.add(&bsocket, &lf_socket); + graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); } for (const int i : bnode.output_sockets().index_range().drop_back(1)) { const bNodeSocket &bsocket = bnode.output_socket(i); lf::OutputSocket &lf_socket = lf_node.output( mapping_->lf_index_by_bsocket[bsocket.index_in_tree()]); - output_socket_map_.add(&bsocket, &lf_socket); + graph_params.lf_output_by_bsocket.add(&bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); } + return &lf_node; } - void handle_simulation_output_node(const bNode &bnode) + lf::FunctionNode &insert_simulation_output_node(const bNode &bnode, + BuildGraphParams &graph_params) { std::unique_ptr lazy_function = get_simulation_output_lazy_function( bnode, *lf_graph_info_); - lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); + lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(*lazy_function); + scope_.add(std::move(lazy_function)); for (const int i : bnode.input_sockets().index_range().drop_back(1)) { const bNodeSocket &bsocket = bnode.input_socket(i); lf::InputSocket &lf_socket = lf_node.input( mapping_->lf_index_by_bsocket[bsocket.index_in_tree()]); - input_socket_map_.add(&bsocket, &lf_socket); + graph_params.lf_inputs_by_bsocket.add(&bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); } for (const int i : bnode.output_sockets().index_range().drop_back(1)) { const bNodeSocket &bsocket = bnode.output_socket(i); lf::OutputSocket &lf_socket = lf_node.output( mapping_->lf_index_by_bsocket[bsocket.index_in_tree()]); - output_socket_map_.add(&bsocket, &lf_socket); + graph_params.lf_output_by_bsocket.add(&bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); } - - mapping_->sim_output_node_map.add(&bnode, &lf_node); + return lf_node; } - void handle_switch_node(const bNode &bnode) + void build_switch_node(const bNode &bnode, BuildGraphParams &graph_params) { std::unique_ptr lazy_function = get_switch_node_lazy_function(bnode); - lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); + lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(*lazy_function); + scope_.add(std::move(lazy_function)); int input_index = 0; for (const bNodeSocket *bsocket : bnode.input_sockets()) { if (bsocket->is_available()) { lf::InputSocket &lf_socket = lf_node.input(input_index); - input_socket_map_.add(bsocket, &lf_socket); + graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); input_index++; } @@ -1827,19 +2778,70 @@ struct GeometryNodesLazyFunctionGraphBuilder { for (const bNodeSocket *bsocket : bnode.output_sockets()) { if (bsocket->is_available()) { lf::OutputSocket &lf_socket = lf_node.output(0); - output_socket_map_.add(bsocket, &lf_socket); + graph_params.lf_output_by_bsocket.add(bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); break; } } + + this->build_switch_node_socket_usage(bnode, graph_params); + } + + void build_switch_node_socket_usage(const bNode &bnode, BuildGraphParams &graph_params) + { + const bNodeSocket *switch_input_bsocket = nullptr; + const bNodeSocket *false_input_bsocket = nullptr; + const bNodeSocket *true_input_bsocket = nullptr; + const bNodeSocket *output_bsocket = nullptr; + for (const bNodeSocket *socket : bnode.input_sockets()) { + if (!socket->is_available()) { + continue; + } + if (socket->name == StringRef("Switch")) { + switch_input_bsocket = socket; + } + else if (socket->name == StringRef("False")) { + false_input_bsocket = socket; + } + else if (socket->name == StringRef("True")) { + true_input_bsocket = socket; + } + } + for (const bNodeSocket *socket : bnode.output_sockets()) { + if (socket->is_available()) { + output_bsocket = socket; + break; + } + } + lf::OutputSocket *output_is_used_socket = graph_params.usage_by_bsocket.lookup_default( + output_bsocket, nullptr); + if (output_is_used_socket == nullptr) { + return; + } + graph_params.usage_by_bsocket.add(switch_input_bsocket, output_is_used_socket); + if (switch_input_bsocket->is_directly_linked()) { + /* The condition input is dynamic, so the usage of the other inputs is as well. */ + static const LazyFunctionForSwitchSocketUsage switch_socket_usage_fn; + lf::Node &lf_node = graph_params.lf_graph.add_function(switch_socket_usage_fn); + graph_params.lf_inputs_by_bsocket.add(switch_input_bsocket, &lf_node.input(0)); + graph_params.usage_by_bsocket.add(false_input_bsocket, &lf_node.output(0)); + graph_params.usage_by_bsocket.add(true_input_bsocket, &lf_node.output(1)); + } + else { + if (switch_input_bsocket->default_value_typed()->value) { + graph_params.usage_by_bsocket.add(true_input_bsocket, output_is_used_socket); + } + else { + graph_params.usage_by_bsocket.add(false_input_bsocket, output_is_used_socket); + } + } } - void handle_undefined_node(const bNode &bnode) + void build_undefined_node(const bNode &bnode, BuildGraphParams &graph_params) { - auto lazy_function = std::make_unique( + auto &lazy_function = scope_.construct( bnode, mapping_->lf_index_by_bsocket); - lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); + lf::FunctionNode &lf_node = graph_params.lf_graph.add_function(lazy_function); for (const bNodeSocket *bsocket : bnode.output_sockets()) { const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()]; @@ -1847,32 +2849,55 @@ struct GeometryNodesLazyFunctionGraphBuilder { continue; } lf::OutputSocket &lf_socket = lf_node.output(lf_index); - output_socket_map_.add(bsocket, &lf_socket); + graph_params.lf_output_by_bsocket.add(bsocket, &lf_socket); mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket); } } - void handle_links() + struct TypeWithLinks { + const CPPType *type; + Vector links; + }; + + void insert_links_from_socket(const bNodeSocket &from_bsocket, + lf::OutputSocket &from_lf_socket, + BuildGraphParams &graph_params) { - for (const auto item : output_socket_map_.items()) { - this->insert_links_from_socket(*item.key, *item.value); + if (bke::nodeIsDanglingReroute(&btree_, &from_bsocket.owner_node())) { + return; + } + + /* Group available target sockets by type so that they can be handled together. */ + const Vector types_with_links = this->group_link_targets_by_type(from_bsocket); + + for (const TypeWithLinks &type_with_links : types_with_links) { + const CPPType &to_type = *type_with_links.type; + const Span links = type_with_links.links; + + lf::OutputSocket *converted_from_lf_socket = this->insert_type_conversion_if_necessary( + from_lf_socket, to_type, graph_params.lf_graph); + + for (const bNodeLink *link : links) { + const Vector lf_link_targets = this->find_link_targets(*link, + graph_params); + if (converted_from_lf_socket == nullptr) { + const void *default_value = to_type.default_value(); + for (lf::InputSocket *to_lf_socket : lf_link_targets) { + to_lf_socket->set_default_value(default_value); + } + } + else { + for (lf::InputSocket *to_lf_socket : lf_link_targets) { + graph_params.lf_graph.add_link(*converted_from_lf_socket, *to_lf_socket); + } + } + } } } - void insert_links_from_socket(const bNodeSocket &from_bsocket, lf::OutputSocket &from_lf_socket) + Vector group_link_targets_by_type(const bNodeSocket &from_bsocket) { - if (bke::nodeIsDanglingReroute(&btree_, &from_bsocket.owner_node())) { - return; - } - const Span links_from_bsocket = from_bsocket.directly_linked_links(); - - struct TypeWithLinks { - const CPPType *type; - Vector links; - }; - - /* Group available target sockets by type so that they can be handled together. */ Vector types_with_links; for (const bNodeLink *link : links_from_bsocket) { if (link->is_muted()) { @@ -1899,67 +2924,56 @@ struct GeometryNodesLazyFunctionGraphBuilder { } types_with_links.append({to_type, {link}}); } + return types_with_links; + } - for (const TypeWithLinks &type_with_links : types_with_links) { - const CPPType &to_type = *type_with_links.type; - const Span links = type_with_links.links; - - lf::OutputSocket *converted_from_lf_socket = this->insert_type_conversion_if_necessary( - from_lf_socket, to_type); + Vector find_link_targets(const bNodeLink &link, + const BuildGraphParams &graph_params) + { + if (lf::InputSocket *lf_input_socket = graph_params.lf_input_by_border_link.lookup_default( + &link, nullptr)) + { + return {lf_input_socket}; + } - auto make_input_link_or_set_default = [&](lf::InputSocket &to_lf_socket) { - if (converted_from_lf_socket == nullptr) { - const void *default_value = to_type.default_value(); - to_lf_socket.set_default_value(default_value); + const bNodeSocket &to_bsocket = *link.tosock; + if (to_bsocket.is_multi_input()) { + /* TODO: Cache this index on the link. */ + int link_index = 0; + for (const bNodeLink *multi_input_link : to_bsocket.directly_linked_links()) { + if (multi_input_link == &link) { + break; } - else { - lf_graph_->add_link(*converted_from_lf_socket, to_lf_socket); + if (multi_input_link->is_muted() || !multi_input_link->fromsock->is_available() || + bke::nodeIsDanglingReroute(&btree_, multi_input_link->fromnode)) + { + continue; } - }; - - for (const bNodeLink *link : links) { - const bNodeSocket &to_bsocket = *link->tosock; - if (to_bsocket.is_multi_input()) { - /* TODO: Cache this index on the link. */ - int link_index = 0; - for (const bNodeLink *multi_input_link : to_bsocket.directly_linked_links()) { - if (multi_input_link == link) { - break; - } - if (multi_input_link->is_muted() || !multi_input_link->fromsock->is_available() || - bke::nodeIsDanglingReroute(&btree_, multi_input_link->fromnode)) - { - continue; - } - link_index++; - } - if (to_bsocket.owner_node().is_muted()) { - if (link_index == 0) { - for (lf::InputSocket *to_lf_socket : input_socket_map_.lookup(&to_bsocket)) { - make_input_link_or_set_default(*to_lf_socket); - } - } - } - else { - lf::Node *multi_input_lf_node = multi_input_socket_nodes_.lookup_default(&to_bsocket, - nullptr); - if (multi_input_lf_node == nullptr) { - continue; - } - make_input_link_or_set_default(multi_input_lf_node->input(link_index)); - } + link_index++; + } + if (to_bsocket.owner_node().is_muted()) { + if (link_index == 0) { + return Vector(graph_params.lf_inputs_by_bsocket.lookup(&to_bsocket)); } - else { - for (lf::InputSocket *to_lf_socket : input_socket_map_.lookup(&to_bsocket)) { - make_input_link_or_set_default(*to_lf_socket); - } + } + else { + lf::Node *multi_input_lf_node = graph_params.multi_input_socket_nodes.lookup_default( + &to_bsocket, nullptr); + if (multi_input_lf_node == nullptr) { + return {}; } + return {&multi_input_lf_node->input(link_index)}; } } + else { + return Vector(graph_params.lf_inputs_by_bsocket.lookup(&to_bsocket)); + } + return {}; } lf::OutputSocket *insert_type_conversion_if_necessary(lf::OutputSocket &from_socket, - const CPPType &to_type) + const CPPType &to_type, + lf::Graph &lf_graph) { const CPPType &from_type = from_socket.type(); if (from_type == to_type) { @@ -1972,20 +2986,19 @@ struct GeometryNodesLazyFunctionGraphBuilder { const MultiFunction &multi_fn = *conversions_->get_conversion_multi_function( mf::DataType::ForSingle(from_field_type->value), mf::DataType::ForSingle(to_field_type->value)); - auto fn = std::make_unique( + auto &fn = scope_.construct( multi_fn, *from_field_type, *to_field_type); - lf::Node &conversion_node = lf_graph_->add_function(*fn); - lf_graph_info_->functions.append(std::move(fn)); - lf_graph_->add_link(from_socket, conversion_node.input(0)); + lf::Node &conversion_node = lf_graph.add_function(fn); + lf_graph.add_link(from_socket, conversion_node.input(0)); return &conversion_node.output(0); } } return nullptr; } - void add_default_inputs() + void add_default_inputs(BuildGraphParams &graph_params) { - for (auto item : input_socket_map_.items()) { + for (auto item : graph_params.lf_inputs_by_bsocket.items()) { const bNodeSocket &bsocket = *item.key; const Span lf_sockets = item.value; for (lf::InputSocket *lf_socket : lf_sockets) { @@ -1993,488 +3006,184 @@ struct GeometryNodesLazyFunctionGraphBuilder { /* Is linked already. */ continue; } - this->add_default_input(bsocket, *lf_socket); - } - } - } - - void add_default_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket) - { - if (this->try_add_implicit_input(input_bsocket, input_lf_socket)) { - return; - } - GMutablePointer value = get_socket_default_value(lf_graph_info_->allocator, input_bsocket); - if (value.get() == nullptr) { - /* Not possible to add a default value. */ - return; - } - input_lf_socket.set_default_value(value.get()); - if (!value.type()->is_trivially_destructible()) { - lf_graph_info_->values_to_destruct.append(value); - } - } - - bool try_add_implicit_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket) - { - const bNode &bnode = input_bsocket.owner_node(); - const SocketDeclaration *socket_decl = input_bsocket.runtime->declaration; - if (socket_decl == nullptr) { - return false; - } - if (socket_decl->input_field_type != InputSocketFieldType::Implicit) { - return false; - } - const ImplicitInputValueFn *implicit_input_fn = socket_decl->implicit_input_fn(); - if (implicit_input_fn == nullptr) { - return false; - } - std::function init_fn = [&bnode, implicit_input_fn](void *r_value) { - (*implicit_input_fn)(bnode, r_value); - }; - const CPPType &type = input_lf_socket.type(); - auto lazy_function = std::make_unique(type, std::move(init_fn)); - lf::Node &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); - lf_graph_->add_link(lf_node.output(0), input_lf_socket); - return true; - } - - /** - * Every output geometry socket that may propagate attributes has to know which attributes should - * be propagated. Therefore, every one of these outputs gets a corresponding attribute set input. - */ - void build_attribute_propagation_input_node() - { - const aal::RelationsInNode &tree_relations = - btree_.runtime->anonymous_attribute_inferencing->tree_relations; - Vector output_indices; - for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { - output_indices.append_non_duplicates(relation.to_geometry_output); - } - Vector cpp_types; - auto debug_info = std::make_unique(); - debug_info->name = "Attributes to Propagate to Output"; - cpp_types.append_n_times(&CPPType::get(), output_indices.size()); - lf::Node &lf_node = lf_graph_->add_dummy({}, cpp_types, debug_info.get()); - for (const int i : output_indices.index_range()) { - const int output_index = output_indices[i]; - mapping_->attribute_set_by_geometry_output.add(output_index, &lf_node.output(i)); - debug_info->output_names.append(btree_.interface_outputs()[output_index]->name); - } - lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); - } - - /** - * Build new boolean group inputs that indicate which group outputs are used. - */ - void build_output_usage_input_node() - { - const Span interface_outputs = btree_.interface_outputs(); - - Vector cpp_types; - cpp_types.append_n_times(&CPPType::get(), interface_outputs.size()); - auto debug_info = std::make_unique(); - debug_info->name = "Output Socket Usage"; - lf::Node &lf_node = lf_graph_->add_dummy({}, cpp_types, debug_info.get()); - for (const int i : interface_outputs.index_range()) { - mapping_->group_output_used_sockets.append(&lf_node.output(i)); - debug_info->output_names.append(interface_outputs[i]->name); - } - lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); - } - - /** - * Build new boolean group outputs that indicate which group inputs are used depending on other - * group inputs. - */ - void build_input_usage_output_node() - { - const Span interface_inputs = btree_.interface_inputs(); - - Vector cpp_types; - cpp_types.append_n_times(&CPPType::get(), interface_inputs.size()); - auto debug_info = std::make_unique(); - debug_info->name = "Input Socket Usage"; - lf::Node &lf_node = lf_graph_->add_dummy(cpp_types, {}, debug_info.get()); - for (const int i : interface_inputs.index_range()) { - mapping_->group_input_usage_sockets.append(&lf_node.input(i)); - debug_info->input_names.append(interface_inputs[i]->name); - } - lf_graph_info_->dummy_debug_infos_.append(std::move(debug_info)); - } - - /** - * For every socket we want to determine if it will be used depending on the inputs of the node - * group (just static analysis is not enough when there are e.g. Switch nodes). This function - * populates #socket_is_used_map_ with that information. - */ - void build_socket_usages() - { - OrSocketUsagesCache or_socket_usages_cache; - - if (const bNode *group_output_bnode = btree_.group_output_node()) { - /* Whether a group output is used is determined by a group input that has been created - * exactly for this purpose. */ - for (const bNodeSocket *bsocket : group_output_bnode->input_sockets().drop_back(1)) { - const int index = bsocket->index(); - socket_is_used_map_[bsocket->index_in_tree()] = const_cast( - mapping_->group_output_used_sockets[index]); - } - } - - /* Iterate over all nodes from right to left to determine when which sockets are used. */ - for (const bNode *bnode : btree_.toposort_right_to_left()) { - const bNodeType *node_type = bnode->typeinfo; - if (node_type == nullptr) { - /* Ignore. */ - continue; - } - - this->build_output_socket_usages(*bnode, or_socket_usages_cache); - - if (bnode->is_muted()) { - this->build_muted_node_usages(*bnode, or_socket_usages_cache); - continue; - } - - switch (node_type->type) { - case NODE_GROUP_OUTPUT: { - /* Handled before this loop already. */ - break; - } - case NODE_GROUP_INPUT: { - /* Handled after this loop. */ - break; - } - case NODE_FRAME: { - /* Ignored. */ - break; - } - case NODE_REROUTE: { - /* The input is used exactly when the output is used. */ - socket_is_used_map_[bnode->input_socket(0).index_in_tree()] = - socket_is_used_map_[bnode->output_socket(0).index_in_tree()]; - break; - } - case GEO_NODE_SWITCH: { - this->build_switch_node_socket_usage(*bnode); - break; - } - case GEO_NODE_VIEWER: { - this->build_viewer_node_socket_usage(*bnode); - break; - } - case GEO_NODE_SIMULATION_INPUT: { - this->build_simulation_input_socket_usage(*bnode); - break; - } - case GEO_NODE_SIMULATION_OUTPUT: { - this->build_simulation_output_socket_usage(*bnode); - break; - } - case NODE_GROUP: - case NODE_CUSTOM_GROUP: { - this->build_group_node_socket_usage(*bnode, or_socket_usages_cache); - break; - } - default: { - this->build_standard_node_input_socket_usage(*bnode, or_socket_usages_cache); - break; - } - } - } - - this->build_group_input_usages(or_socket_usages_cache); - this->link_output_used_sockets_for_builtin_nodes(); - } - - using OrSocketUsagesCache = Map, lf::OutputSocket *>; - - /** - * Combine multiple socket usages with a logical or. Inserts a new node for that purpose if - * necessary. - */ - lf::OutputSocket *or_socket_usages(MutableSpan usages, - OrSocketUsagesCache &cache) - { - if (usages.is_empty()) { - return nullptr; - } - if (usages.size() == 1) { - return usages[0]; - } - - std::sort(usages.begin(), usages.end()); - return cache.lookup_or_add_cb_as(usages, [&]() { - auto logical_or_fn = std::make_unique(usages.size()); - lf::Node &logical_or_node = lf_graph_->add_function(*logical_or_fn); - lf_graph_info_->functions.append(std::move(logical_or_fn)); - - for (const int i : usages.index_range()) { - lf_graph_->add_link(*usages[i], logical_or_node.input(i)); - } - return &logical_or_node.output(0); - }); - } - - void build_output_socket_usages(const bNode &bnode, OrSocketUsagesCache &or_socket_usages_cache) - { - /* Output sockets are used when any of their linked inputs are used. */ - for (const bNodeSocket *socket : bnode.output_sockets()) { - if (!socket->is_available()) { - continue; - } - /* Determine when linked target sockets are used. */ - Vector target_usages; - for (const bNodeLink *link : socket->directly_linked_links()) { - if (!link->is_used()) { - continue; - } - const bNodeSocket &target_socket = *link->tosock; - if (lf::OutputSocket *is_used_socket = socket_is_used_map_[target_socket.index_in_tree()]) - { - target_usages.append_non_duplicates(is_used_socket); - } - } - /* Combine target socket usages into the usage of the current socket. */ - socket_is_used_map_[socket->index_in_tree()] = this->or_socket_usages( - target_usages, or_socket_usages_cache); - } - } - - /** - * An input of a muted node is used when any of its internally linked outputs is used. - */ - void build_muted_node_usages(const bNode &bnode, OrSocketUsagesCache &or_socket_usages_cache) - { - /* Find all outputs that use a specific input. */ - MultiValueMap outputs_by_input; - for (const bNodeLink &blink : bnode.internal_links()) { - outputs_by_input.add(blink.fromsock, blink.tosock); - } - for (const auto item : outputs_by_input.items()) { - const bNodeSocket &input_bsocket = *item.key; - const Span output_bsockets = item.value; - - /* The input is used if any of the internally linked outputs is used. */ - Vector lf_socket_usages; - for (const bNodeSocket *output_bsocket : output_bsockets) { - if (lf::OutputSocket *lf_socket = socket_is_used_map_[output_bsocket->index_in_tree()]) { - lf_socket_usages.append(lf_socket); - } + this->add_default_input(bsocket, *lf_socket, graph_params); } - socket_is_used_map_[input_bsocket.index_in_tree()] = this->or_socket_usages( - lf_socket_usages, or_socket_usages_cache); } } - - void build_switch_node_socket_usage(const bNode &bnode) - { - const bNodeSocket *switch_input_bsocket = nullptr; - const bNodeSocket *false_input_bsocket = nullptr; - const bNodeSocket *true_input_bsocket = nullptr; - const bNodeSocket *output_bsocket = nullptr; - for (const bNodeSocket *socket : bnode.input_sockets()) { - if (!socket->is_available()) { - continue; - } - if (socket->name == StringRef("Switch")) { - switch_input_bsocket = socket; - } - else if (socket->name == StringRef("False")) { - false_input_bsocket = socket; - } - else if (socket->name == StringRef("True")) { - true_input_bsocket = socket; - } - } - for (const bNodeSocket *socket : bnode.output_sockets()) { - if (socket->is_available()) { - output_bsocket = socket; - break; - } - } - lf::OutputSocket *output_is_used_socket = socket_is_used_map_[output_bsocket->index_in_tree()]; - if (output_is_used_socket == nullptr) { + + void add_default_input(const bNodeSocket &input_bsocket, + lf::InputSocket &input_lf_socket, + BuildGraphParams &graph_params) + { + if (this->try_add_implicit_input(input_bsocket, input_lf_socket, graph_params)) { return; } - socket_is_used_map_[switch_input_bsocket->index_in_tree()] = output_is_used_socket; - lf::InputSocket *lf_switch_input = input_socket_map_.lookup(switch_input_bsocket)[0]; - if (lf::OutputSocket *lf_switch_origin = lf_switch_input->origin()) { - /* The condition input is dynamic, so the usage of the other inputs is as well. */ - static const LazyFunctionForSwitchSocketUsage switch_socket_usage_fn; - lf::Node &lf_node = lf_graph_->add_function(switch_socket_usage_fn); - lf_graph_->add_link(*lf_switch_origin, lf_node.input(0)); - socket_is_used_map_[false_input_bsocket->index_in_tree()] = &lf_node.output(0); - socket_is_used_map_[true_input_bsocket->index_in_tree()] = &lf_node.output(1); + GMutablePointer value = get_socket_default_value(scope_.linear_allocator(), input_bsocket); + if (value.get() == nullptr) { + /* Not possible to add a default value. */ + return; } - else { - if (switch_input_bsocket->default_value_typed()->value) { - socket_is_used_map_[true_input_bsocket->index_in_tree()] = output_is_used_socket; - } - else { - socket_is_used_map_[false_input_bsocket->index_in_tree()] = output_is_used_socket; - } + input_lf_socket.set_default_value(value.get()); + if (!value.type()->is_trivially_destructible()) { + scope_.add_destruct_call([value]() mutable { value.destruct(); }); } } - void build_viewer_node_socket_usage(const bNode &bnode) + bool try_add_implicit_input(const bNodeSocket &input_bsocket, + lf::InputSocket &input_lf_socket, + BuildGraphParams &graph_params) { - const lf::FunctionNode &lf_viewer_node = *mapping_->viewer_node_map.lookup(&bnode); - auto lazy_function = std::make_unique(lf_viewer_node); - lf::Node &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); - - for (const bNodeSocket *bsocket : bnode.input_sockets()) { - if (bsocket->is_available()) { - socket_is_used_map_[bsocket->index_in_tree()] = &lf_node.output(0); - } + const bNode &bnode = input_bsocket.owner_node(); + const SocketDeclaration *socket_decl = input_bsocket.runtime->declaration; + if (socket_decl == nullptr) { + return false; + } + if (socket_decl->input_field_type != InputSocketFieldType::Implicit) { + return false; + } + const ImplicitInputValueFn *implicit_input_fn = socket_decl->implicit_input_fn(); + if (implicit_input_fn == nullptr) { + return false; } + std::function init_fn = [&bnode, implicit_input_fn](void *r_value) { + (*implicit_input_fn)(bnode, r_value); + }; + const CPPType &type = input_lf_socket.type(); + auto &lazy_function = scope_.construct(type, std::move(init_fn)); + lf::Node &lf_node = graph_params.lf_graph.add_function(lazy_function); + graph_params.lf_graph.add_link(lf_node.output(0), input_lf_socket); + return true; } - void build_simulation_input_socket_usage(const bNode &bnode) + /** + * Every output geometry socket that may propagate attributes has to know which attributes + * should be propagated. Therefore, every one of these outputs gets a corresponding attribute + * set input. + */ + void build_attribute_propagation_input_node(lf::Graph &lf_graph) { - const NodeGeometrySimulationInput *storage = static_cast( - bnode.storage); - const bNode *sim_output_node = btree_.node_by_id(storage->output_node_id); - if (sim_output_node == nullptr) { - return; + const aal::RelationsInNode &tree_relations = + btree_.runtime->anonymous_attribute_inferencing->tree_relations; + Vector output_indices; + for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) { + output_indices.append_non_duplicates(relation.to_geometry_output); } - lf::Node &lf_node = this->get_simulation_inputs_usage_node(*sim_output_node); - for (const bNodeSocket *bsocket : bnode.input_sockets()) { - if (bsocket->is_available()) { - socket_is_used_map_[bsocket->index_in_tree()] = &lf_node.output(0); - } + Vector cpp_types; + auto &debug_info = scope_.construct(); + debug_info.name = "Attributes to Propagate to Output"; + cpp_types.append_n_times(&CPPType::get(), output_indices.size()); + lf::Node &lf_node = lf_graph.add_dummy({}, cpp_types, &debug_info); + for (const int i : output_indices.index_range()) { + const int output_index = output_indices[i]; + mapping_->attribute_set_by_geometry_output.add(output_index, &lf_node.output(i)); + debug_info.output_names.append(btree_.interface_outputs()[output_index]->name); } } - void build_simulation_output_socket_usage(const bNode &bnode) + /** + * Build new boolean group inputs that indicate which group outputs are used. + */ + lf::DummyNode &build_output_usage_input_node(lf::Graph &lf_graph) { - lf::Node &lf_node = this->get_simulation_inputs_usage_node(bnode); - for (const bNodeSocket *bsocket : bnode.input_sockets()) { - if (bsocket->is_available()) { - socket_is_used_map_[bsocket->index_in_tree()] = &lf_node.output(1); - } + const Span interface_outputs = btree_.interface_outputs(); + + Vector cpp_types; + cpp_types.append_n_times(&CPPType::get(), interface_outputs.size()); + auto &debug_info = scope_.construct(); + debug_info.name = "Output Socket Usage"; + lf::DummyNode &lf_node = lf_graph.add_dummy({}, cpp_types, &debug_info); + for (const int i : interface_outputs.index_range()) { + mapping_->group_output_used_sockets.append(&lf_node.output(i)); + debug_info.output_names.append(interface_outputs[i]->name); } + return lf_node; } - lf::Node &get_simulation_inputs_usage_node(const bNode &sim_output_bnode) + /** + * Build new boolean group outputs that indicate which group inputs are used depending on other + * group inputs. + */ + void build_input_usage_output_node(lf::Graph &lf_graph) { - BLI_assert(sim_output_bnode.type == GEO_NODE_SIMULATION_OUTPUT); - return *simulation_inputs_usage_nodes_.lookup_or_add_cb(&sim_output_bnode, [&]() { - auto lazy_function = std::make_unique(); - lf::Node &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_info_->functions.append(std::move(lazy_function)); - return &lf_node; - }); + const Span interface_inputs = btree_.interface_inputs(); + + Vector cpp_types; + cpp_types.append_n_times(&CPPType::get(), interface_inputs.size()); + auto &debug_info = scope_.construct(); + debug_info.name = "Input Socket Usage"; + lf::Node &lf_node = lf_graph.add_dummy(cpp_types, {}, &debug_info); + for (const int i : interface_inputs.index_range()) { + mapping_->group_input_usage_sockets.append(&lf_node.input(i)); + debug_info.input_names.append(interface_inputs[i]->name); + } } - void build_group_node_socket_usage(const bNode &bnode, - OrSocketUsagesCache &or_socket_usages_cache) + /** + * Combine multiple socket usages with a logical or. Inserts a new node for that purpose if + * necessary. + */ + lf::OutputSocket *or_socket_usages(MutableSpan usages, + BuildGraphParams &graph_params) { - const bNodeTree *bgroup = reinterpret_cast(bnode.id); - if (bgroup == nullptr) { - return; + if (usages.is_empty()) { + return nullptr; } - const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info = - ensure_geometry_nodes_lazy_function_graph(*bgroup); - if (group_lf_graph_info == nullptr) { - return; + if (usages.size() == 1) { + return usages[0]; } - lf::FunctionNode &lf_group_node = const_cast( - *mapping_->group_node_map.lookup(&bnode)); - const auto &fn = static_cast(lf_group_node.function()); - for (const bNodeSocket *input_bsocket : bnode.input_sockets()) { - const int input_index = input_bsocket->index(); - const InputUsageHint &input_usage_hint = - group_lf_graph_info->mapping.group_input_usage_hints[input_index]; - switch (input_usage_hint.type) { - case InputUsageHintType::Never: { - /* Nothing to do. */ - break; - } - case InputUsageHintType::DependsOnOutput: { - Vector output_usages; - for (const int i : input_usage_hint.output_dependencies) { - if (lf::OutputSocket *lf_socket = - socket_is_used_map_[bnode.output_socket(i).index_in_tree()]) { - output_usages.append(lf_socket); - } - } - socket_is_used_map_[input_bsocket->index_in_tree()] = this->or_socket_usages( - output_usages, or_socket_usages_cache); - break; - } - case InputUsageHintType::DynamicSocket: { - socket_is_used_map_[input_bsocket->index_in_tree()] = &const_cast( - lf_group_node.output(fn.lf_output_for_input_bsocket_usage_.lookup(input_index))); - break; - } - } - } + std::sort(usages.begin(), usages.end()); + return graph_params.socket_usages_combination_cache.lookup_or_add_cb_as(usages, [&]() { + auto &logical_or_fn = scope_.construct(usages.size()); + lf::Node &logical_or_node = graph_params.lf_graph.add_function(logical_or_fn); - for (const bNodeSocket *output_bsocket : bnode.output_sockets()) { - const int lf_input_index = - mapping_ - ->lf_input_index_for_output_bsocket_usage[output_bsocket->index_in_all_outputs()]; - BLI_assert(lf_input_index >= 0); - lf::InputSocket &lf_socket = lf_group_node.input(lf_input_index); - if (lf::OutputSocket *lf_output_is_used = - socket_is_used_map_[output_bsocket->index_in_tree()]) { - lf_graph_->add_link(*lf_output_is_used, lf_socket); - } - else { - static const bool static_false = false; - lf_socket.set_default_value(&static_false); + for (const int i : usages.index_range()) { + graph_params.lf_graph.add_link(*usages[i], logical_or_node.input(i)); } - } + return &logical_or_node.output(0); + }); } - void build_standard_node_input_socket_usage(const bNode &bnode, - OrSocketUsagesCache &or_socket_usages_cache) + void build_output_socket_usages(const bNode &bnode, BuildGraphParams &graph_params) { - if (bnode.input_sockets().is_empty()) { - return; - } - - Vector output_usages; - for (const bNodeSocket *output_socket : bnode.output_sockets()) { - if (!output_socket->is_available()) { + /* Output sockets are used when any of their linked inputs are used. */ + for (const bNodeSocket *socket : bnode.output_sockets()) { + if (!socket->is_available()) { continue; } - if (lf::OutputSocket *is_used_socket = socket_is_used_map_[output_socket->index_in_tree()]) { - output_usages.append_non_duplicates(is_used_socket); - } - } - - /* Assume every input is used when any output is used. */ - lf::OutputSocket *lf_usage = this->or_socket_usages(output_usages, or_socket_usages_cache); - if (lf_usage == nullptr) { - return; - } - - for (const bNodeSocket *input_socket : bnode.input_sockets()) { - if (input_socket->is_available()) { - socket_is_used_map_[input_socket->index_in_tree()] = lf_usage; + /* Determine when linked target sockets are used. */ + Vector target_usages; + for (const bNodeLink *link : socket->directly_linked_links()) { + if (!link->is_used()) { + continue; + } + const bNodeSocket &target_socket = *link->tosock; + if (lf::OutputSocket *is_used_socket = graph_params.usage_by_bsocket.lookup_default( + &target_socket, nullptr)) + { + target_usages.append_non_duplicates(is_used_socket); + } } + /* Combine target socket usages into the usage of the current socket. */ + graph_params.usage_by_bsocket.add(socket, + this->or_socket_usages(target_usages, graph_params)); } } - void build_group_input_usages(OrSocketUsagesCache &or_socket_usages_cache) + void build_group_input_usages(BuildGraphParams &graph_params) { const Span group_input_nodes = btree_.group_input_nodes(); for (const int i : btree_.interface_inputs().index_range()) { Vector target_usages; for (const bNode *group_input_node : group_input_nodes) { - if (lf::OutputSocket *lf_socket = - socket_is_used_map_[group_input_node->output_socket(i).index_in_tree()]) + if (lf::OutputSocket *lf_socket = graph_params.usage_by_bsocket.lookup_default( + &group_input_node->output_socket(i), nullptr)) { target_usages.append_non_duplicates(lf_socket); } } - lf::OutputSocket *lf_socket = this->or_socket_usages(target_usages, or_socket_usages_cache); + lf::OutputSocket *lf_socket = this->or_socket_usages(target_usages, graph_params); lf::InputSocket *lf_group_output = const_cast( mapping_->group_input_usage_sockets[i]); InputUsageHint input_usage_hint; @@ -2484,7 +3193,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { input_usage_hint.type = InputUsageHintType::Never; } else { - lf_graph_->add_link(*lf_socket, *lf_group_output); + graph_params.lf_graph.add_link(*lf_socket, *lf_group_output); if (lf_socket->node().is_dummy()) { /* Can support slightly more complex cases where it depends on more than one output in * the future. */ @@ -2500,154 +3209,21 @@ struct GeometryNodesLazyFunctionGraphBuilder { } } - void link_output_used_sockets_for_builtin_nodes() - { - for (const auto &[output_bsocket, lf_input] : output_used_sockets_for_builtin_nodes_) { - if (lf::OutputSocket *lf_is_used = socket_is_used_map_[output_bsocket->index_in_tree()]) { - lf_graph_->add_link(*lf_is_used, *lf_input); - } - else { - static const bool static_false = false; - lf_input->set_default_value(&static_false); - } - } - } - - using JoinAttibuteSetsCache = Map, lf::OutputSocket *>; - - void build_attribute_propagation_sets() - { - using namespace bke::anonymous_attribute_inferencing; - - const AnonymousAttributeInferencingResult &result = - *btree_.runtime->anonymous_attribute_inferencing; - - JoinAttibuteSetsCache join_attribute_sets_cache; - - Map get_attributes_node_cache; - - auto add_get_attributes_node = [&](lf::OutputSocket &lf_field_socket) -> lf::OutputSocket & { - return *get_attributes_node_cache.lookup_or_add_cb(&lf_field_socket, [&]() { - const ValueOrFieldCPPType &type = *ValueOrFieldCPPType::get_from_self( - lf_field_socket.type()); - auto lazy_function = std::make_unique(type); - lf::Node &lf_node = lf_graph_->add_function(*lazy_function); - lf_graph_->add_link(lf_field_socket, lf_node.input(0)); - lf_graph_info_->functions.append(std::move(lazy_function)); - return &lf_node.output(0); - }); - }; - - for (const MapItem item : - attribute_set_propagation_map_.items()) - { - const bNodeSocket &geometry_output_bsocket = *item.key; - lf::InputSocket &lf_attribute_set_input = *item.value; - - const BoundedBitSpan required_fields = - result.required_fields_by_geometry_socket[geometry_output_bsocket.index_in_tree()]; - const BoundedBitSpan required_output_propagations = - result.propagate_to_output_by_geometry_socket[geometry_output_bsocket.index_in_tree()]; - - Vector attribute_set_sockets; - Vector used_sockets; - - bits::foreach_1_index(required_fields, [&](const int field_source_index) { - const FieldSource &field_source = result.all_field_sources[field_source_index]; - lf::OutputSocket *lf_socket_usage = nullptr; - lf::OutputSocket *lf_attribute_set_socket = nullptr; - if (const auto *input_field = std::get_if(&field_source.data)) { - lf_socket_usage = const_cast( - mapping_->group_input_usage_sockets[input_field->input_index]) - ->origin(); - lf::OutputSocket *lf_field_socket = const_cast( - mapping_->group_input_sockets[input_field->input_index]); - lf_attribute_set_socket = &add_get_attributes_node(*lf_field_socket); - } - else { - const auto &socket_field = std::get(field_source.data); - if (&socket_field.socket->owner_node() == &geometry_output_bsocket.owner_node()) { - return; - } - lf::OutputSocket *lf_field_socket = output_socket_map_.lookup(socket_field.socket); - lf_socket_usage = socket_is_used_map_[socket_field.socket->index_in_tree()]; - lf_attribute_set_socket = &add_get_attributes_node(*lf_field_socket); - } - if (lf_socket_usage) { - attribute_set_sockets.append(lf_attribute_set_socket); - used_sockets.append(lf_socket_usage); - } - }); - bits::foreach_1_index(required_output_propagations, [&](const int i) { - const int output_geometry_index = result.propagated_output_geometry_indices[i]; - lf::OutputSocket *lf_socket_usage = const_cast( - mapping_->group_output_used_sockets[output_geometry_index]); - if (lf_socket_usage) { - lf::OutputSocket *lf_attribute_set_socket = const_cast( - mapping_->attribute_set_by_geometry_output.lookup(output_geometry_index)); - attribute_set_sockets.append(lf_attribute_set_socket); - used_sockets.append(lf_socket_usage); - } - }); - if (lf::OutputSocket *joined_attribute_set = this->join_attribute_sets( - attribute_set_sockets, used_sockets, join_attribute_sets_cache)) - { - lf_graph_->add_link(*joined_attribute_set, lf_attribute_set_input); - } - else { - static const bke::AnonymousAttributeSet empty_set; - lf_attribute_set_input.set_default_value(&empty_set); - } - } - } - - /** - * Join multiple attributes set into a single attribute set that can be passed into a node. - */ - lf::OutputSocket *join_attribute_sets(const Span attribute_set_sockets, - const Span used_sockets, - JoinAttibuteSetsCache &cache) - { - BLI_assert(attribute_set_sockets.size() == used_sockets.size()); - if (attribute_set_sockets.is_empty()) { - return nullptr; - } - - Vector key; - key.extend(attribute_set_sockets); - key.extend(used_sockets); - std::sort(key.begin(), key.end()); - return cache.lookup_or_add_cb(key, [&]() { - const auto &lazy_function = LazyFunctionForAnonymousAttributeSetJoin::get_cached( - attribute_set_sockets.size(), lf_graph_info_->functions); - lf::Node &lf_node = lf_graph_->add_function(lazy_function); - for (const int i : attribute_set_sockets.index_range()) { - lf::InputSocket &lf_use_input = lf_node.input(lazy_function.get_use_input(i)); - socket_usage_inputs_.add(&lf_use_input); - lf::InputSocket &lf_attributes_input = lf_node.input( - lazy_function.get_attribute_set_input(i)); - lf_graph_->add_link(*used_sockets[i], lf_use_input); - lf_graph_->add_link(*attribute_set_sockets[i], lf_attributes_input); - } - return &lf_node.output(0); - }); - } - /** - * By depending on "the future" (whether a specific socket is used in the future), it is possible - * to introduce cycles in the graph. This function finds those cycles and breaks them by removing - * specific links. + * By depending on "the future" (whether a specific socket is used in the future), it is + * possible to introduce cycles in the graph. This function finds those cycles and breaks them + * by removing specific links. * * Example for a cycle: There is a `Distribute Points on Faces` node and its `Normal` output is - * only used when the number of generated points is larger than 1000 because of some switch node - * later in the tree. In this case, to know whether the `Normal` output is needed, one first has - * to compute the points, but for that one has to know whether the normal information has to be - * added to the points. The fix is to always add the normal information in this case. + * only used when the number of generated points is larger than 1000 because of some switch + * node later in the tree. In this case, to know whether the `Normal` output is needed, one + * first has to compute the points, but for that one has to know whether the normal information + * has to be added to the points. The fix is to always add the normal information in this case. */ - void fix_link_cycles() + void fix_link_cycles(lf::Graph &lf_graph, const Set &socket_usage_inputs) { - lf_graph_->update_socket_indices(); - const int sockets_num = lf_graph_->socket_num(); + lf_graph.update_socket_indices(); + const int sockets_num = lf_graph.socket_num(); struct SocketState { bool done = false; @@ -2657,7 +3233,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { Array socket_states(sockets_num); Vector lf_sockets_to_check; - for (lf::Node *lf_node : lf_graph_->nodes()) { + for (lf::Node *lf_node : lf_graph.nodes()) { if (lf_node->is_function()) { for (lf::OutputSocket *lf_socket : lf_node->outputs()) { if (lf_socket->targets().is_empty()) { @@ -2713,8 +3289,8 @@ struct GeometryNodesLazyFunctionGraphBuilder { * computation later, but does not change correctness. * * After the cycle is broken, the cycle-detection is "rolled back" to the socket where - * the first socket of the cycle was found. This is necessary in case another cycle goes - * through this socket. */ + * the first socket of the cycle was found. This is necessary in case another cycle + * goes through this socket. */ detected_cycle = true; const int index_in_socket_stack = lf_socket_stack.first_index_of(lf_origin_socket); @@ -2726,9 +3302,9 @@ struct GeometryNodesLazyFunctionGraphBuilder { bool broke_cycle = false; for (lf::Socket *lf_cycle_socket : cycle) { if (lf_cycle_socket->is_input() && - socket_usage_inputs_.contains(&lf_cycle_socket->as_input())) { + socket_usage_inputs.contains(&lf_cycle_socket->as_input())) { lf::InputSocket &lf_cycle_input_socket = lf_cycle_socket->as_input(); - lf_graph_->clear_origin(lf_cycle_input_socket); + lf_graph.clear_origin(lf_cycle_input_socket); static const bool static_true = true; lf_cycle_input_socket.set_default_value(&static_true); broke_cycle = true; @@ -2765,82 +3341,8 @@ struct GeometryNodesLazyFunctionGraphBuilder { lf_socket_stack.pop_last(); } } - - void print_graph(); -}; - -class UsedSocketVisualizeOptions : public lf::Graph::ToDotOptions { - private: - const GeometryNodesLazyFunctionGraphBuilder &builder_; - Map socket_font_colors_; - Map socket_name_suffixes_; - - public: - UsedSocketVisualizeOptions(const GeometryNodesLazyFunctionGraphBuilder &builder) - : builder_(builder) - { - VectorSet found; - for (const int bsocket_index : builder_.socket_is_used_map_.index_range()) { - const bNodeSocket *bsocket = builder_.btree_.all_sockets()[bsocket_index]; - lf::OutputSocket *lf_used_socket = builder_.socket_is_used_map_[bsocket_index]; - if (lf_used_socket == nullptr) { - continue; - } - const float hue = BLI_hash_int_01(uintptr_t(lf_used_socket)); - std::stringstream ss; - ss.precision(3); - ss << hue << " 0.9 0.5"; - const std::string color_str = ss.str(); - const std::string suffix = " (" + std::to_string(found.index_of_or_add(lf_used_socket)) + - ")"; - socket_font_colors_.add(lf_used_socket, color_str); - socket_name_suffixes_.add(lf_used_socket, suffix); - - if (bsocket->is_input()) { - for (const lf::InputSocket *lf_socket : builder_.input_socket_map_.lookup(bsocket)) { - socket_font_colors_.add(lf_socket, color_str); - socket_name_suffixes_.add(lf_socket, suffix); - } - } - else if (lf::OutputSocket *lf_socket = builder_.output_socket_map_.lookup_default(bsocket, - nullptr)) - { - socket_font_colors_.add(lf_socket, color_str); - socket_name_suffixes_.add(lf_socket, suffix); - } - } - } - - std::optional socket_font_color(const lf::Socket &socket) const override - { - if (const std::string *color = socket_font_colors_.lookup_ptr(&socket)) { - return *color; - } - return std::nullopt; - } - - std::string socket_name(const lf::Socket &socket) const override - { - return socket.name() + socket_name_suffixes_.lookup_default(&socket, ""); - } - - void add_edge_attributes(const lf::OutputSocket & /*from*/, - const lf::InputSocket &to, - dot::DirectedEdge &dot_edge) const override - { - if (builder_.socket_usage_inputs_.contains_as(&to)) { - // dot_edge.attributes.set("constraint", "false"); - dot_edge.attributes.set("color", "#00000055"); - } - } }; -void GeometryNodesLazyFunctionGraphBuilder::print_graph() -{ - UsedSocketVisualizeOptions options{*this}; - std::cout << "\n\n" << lf_graph_->to_dot(options) << "\n\n"; -} - const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph( const bNodeTree &btree) { @@ -2967,14 +3469,6 @@ Vector GeometryNodesLazyFunctionSideEffectProvider:: return modifier_data.side_effect_nodes->lookup(context_hash); } -GeometryNodesLazyFunctionGraphInfo::GeometryNodesLazyFunctionGraphInfo() = default; -GeometryNodesLazyFunctionGraphInfo::~GeometryNodesLazyFunctionGraphInfo() -{ - for (GMutablePointer &p : this->values_to_destruct) { - p.destruct(); - } -} - [[maybe_unused]] static void add_thread_id_debug_message( const GeometryNodesLazyFunctionGraphInfo &lf_graph_info, const lf::FunctionNode &node, diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc index 80204233fa0a..34884939532e 100644 --- a/source/blender/nodes/intern/geometry_nodes_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -21,6 +21,7 @@ namespace blender::nodes::geo_eval_log { using fn::FieldInput; using fn::FieldInputs; +using namespace bke::node_tree_zones; GenericValueLog::~GenericValueLog() { @@ -518,46 +519,82 @@ static std::optional get_modifier_for_node_editor(const Space return ObjectAndModifier{object, used_modifier}; } -std::optional GeoModifierLog::get_compute_context_hash_for_node_editor( - const SpaceNode &snode, const StringRefNull modifier_name) +static void find_tree_zone_hash_recursive( + const TreeZone &zone, + ComputeContextBuilder &compute_context_builder, + Map &r_hash_by_zone) { - Vector tree_path = snode.treepath; + compute_context_builder.push(*zone.output_node); + r_hash_by_zone.add_new(&zone, compute_context_builder.hash()); + for (const TreeZone *child_zone : zone.child_zones) { + find_tree_zone_hash_recursive(*child_zone, compute_context_builder, r_hash_by_zone); + } + compute_context_builder.pop(); +} + +Map GeoModifierLog::get_context_hash_by_zone_for_node_editor( + const SpaceNode &snode, StringRefNull modifier_name) +{ + const Vector tree_path = snode.treepath; if (tree_path.is_empty()) { - return std::nullopt; + return {}; } + ComputeContextBuilder compute_context_builder; compute_context_builder.push(modifier_name); + for (const int i : tree_path.index_range().drop_back(1)) { - /* The tree path contains the name of the node but not its ID. */ - const bNode *node = nodeFindNodebyName(tree_path[i]->nodetree, tree_path[i + 1]->node_name); - if (node == nullptr) { - /* The current tree path is invalid, probably because some parent group node has been - * deleted. */ - return std::nullopt; + bNodeTree *tree = tree_path[i]->nodetree; + const char *group_node_name = tree_path[i + 1]->node_name; + const bNode *group_node = nodeFindNodebyName(tree, group_node_name); + if (group_node == nullptr) { + return {}; + } + const TreeZones *tree_zones = tree->zones(); + if (tree_zones == nullptr) { + return {}; + } + const Vector zone_stack = tree_zones->get_zone_stack_for_node( + group_node->identifier); + for (const TreeZone *zone : zone_stack) { + compute_context_builder.push(*zone->output_node); } - compute_context_builder.push(*node); + compute_context_builder.push(*group_node); + } + + const TreeZones *tree_zones = snode.edittree->zones(); + if (tree_zones == nullptr) { + return {}; + } + Map hash_by_zone; + hash_by_zone.add_new(nullptr, compute_context_builder.hash()); + for (const TreeZone *zone : tree_zones->root_zones) { + find_tree_zone_hash_recursive(*zone, compute_context_builder, hash_by_zone); } - return compute_context_builder.hash(); + return hash_by_zone; } -GeoTreeLog *GeoModifierLog::get_tree_log_for_node_editor(const SpaceNode &snode) +Map GeoModifierLog::get_tree_log_by_zone_for_node_editor( + const SpaceNode &snode) { std::optional object_and_modifier = get_modifier_for_node_editor(snode); if (!object_and_modifier) { - return nullptr; + return {}; } GeoModifierLog *modifier_log = static_cast( object_and_modifier->nmd->runtime_eval_log); if (modifier_log == nullptr) { - return nullptr; + return {}; } - if (const std::optional hash = - GeoModifierLog::get_compute_context_hash_for_node_editor( - snode, object_and_modifier->nmd->modifier.name)) - { - return &modifier_log->get_tree_log(*hash); + const Map hash_by_zone = + GeoModifierLog::get_context_hash_by_zone_for_node_editor( + snode, object_and_modifier->nmd->modifier.name); + Map log_by_zone; + for (const auto item : hash_by_zone.items()) { + GeoTreeLog &tree_log = modifier_log->get_tree_log(item.value); + log_by_zone.add(item.key, &tree_log); } - return nullptr; + return log_by_zone; } const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_path(const ViewerPath &viewer_path) @@ -587,8 +624,24 @@ const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_path(const ViewerP ComputeContextBuilder compute_context_builder; compute_context_builder.push(parsed_path->modifier_name); - for (const int32_t group_node_id : parsed_path->group_node_ids) { - compute_context_builder.push(group_node_id); + for (const ViewerPathElem *elem : parsed_path->node_path) { + switch (elem->type) { + case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: { + const auto &typed_elem = *reinterpret_cast(elem); + compute_context_builder.push(typed_elem.node_id); + break; + } + case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: { + const auto &typed_elem = *reinterpret_cast(elem); + compute_context_builder.push( + typed_elem.sim_output_node_id); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } } const ComputeContextHash context_hash = compute_context_builder.hash(); nodes::geo_eval_log::GeoTreeLog &tree_log = modifier_log->get_tree_log(context_hash); From 887faf83e52d887d17b48a2553d30c0c7eaa8052 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 20 Jun 2023 10:25:41 +0200 Subject: [PATCH 15/36] Cleanup: improve node tree zones naming --- source/blender/blenkernel/BKE_node_runtime.hh | 6 +- .../blender/blenkernel/BKE_node_tree_zones.hh | 35 ++++---- .../blenkernel/intern/node_tree_zones.cc | 86 ++++++++++--------- .../blender/editors/space_node/node_draw.cc | 36 ++++---- .../node_geometry_attribute_search.cc | 8 +- source/blender/editors/util/ed_viewer_path.cc | 27 +++--- source/blender/makesdna/DNA_node_types.h | 10 +-- source/blender/modifiers/intern/MOD_nodes.cc | 13 ++- .../nodes/NOD_geometry_nodes_lazy_function.hh | 2 +- .../blender/nodes/NOD_geometry_nodes_log.hh | 6 +- .../intern/geometry_nodes_lazy_function.cc | 36 ++++---- .../nodes/intern/geometry_nodes_log.cc | 31 +++---- 12 files changed, 152 insertions(+), 144 deletions(-) diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index 122a5ac819b0..aacd2ed4e919 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -33,8 +33,8 @@ struct RelationsInNode; } namespace aal = anonymous_attribute_lifetime; } // namespace blender::nodes -namespace blender::bke::node_tree_zones { -class TreeZones; +namespace blender::bke { +class bNodeTreeZones; } namespace blender::bke::anonymous_attribute_inferencing { struct AnonymousAttributeInferencingResult; @@ -149,7 +149,7 @@ class bNodeTreeRuntime : NonCopyable, NonMovable { mutable std::atomic allow_use_dirty_topology_cache = 0; CacheMutex tree_zones_cache_mutex; - std::unique_ptr tree_zones; + std::unique_ptr tree_zones; /** Only valid when #topology_cache_is_dirty is false. */ Vector links; diff --git a/source/blender/blenkernel/BKE_node_tree_zones.hh b/source/blender/blenkernel/BKE_node_tree_zones.hh index 2a5908ae57b7..c1915bfe74eb 100644 --- a/source/blender/blenkernel/BKE_node_tree_zones.hh +++ b/source/blender/blenkernel/BKE_node_tree_zones.hh @@ -12,12 +12,13 @@ #include "BLI_vector.hh" -namespace blender::bke::node_tree_zones { +namespace blender::bke { -class TreeZones; +class bNodeTreeZones; -struct TreeZone { - TreeZones *owner = nullptr; +class bNodeTreeZone { + public: + bNodeTreeZones *owner = nullptr; /** Index of the zone in the array of all zones in a node tree. */ int index = -1; /** Zero for top level zones, one for a nested zone, and so on. */ @@ -27,22 +28,22 @@ struct TreeZone { /** Output node of the zone. */ const bNode *output_node = nullptr; /** Direct parent of the zone. If this is null, this is a top level zone. */ - TreeZone *parent_zone = nullptr; + bNodeTreeZone *parent_zone = nullptr; /** Direct children zones. Does not contain recursively nested zones. */ - Vector child_zones; + Vector child_zones; /** Direct children nodes excluding nodes that belong to child zones. */ Vector child_nodes; /** Links that enter the zone through the zone border. */ Vector border_links; bool contains_node_recursively(const bNode &node) const; - bool contains_zone_recursively(const TreeZone &other_zone) const; + bool contains_zone_recursively(const bNodeTreeZone &other_zone) const; }; -class TreeZones { +class bNodeTreeZones { public: - Vector> zones; - Vector root_zones; + Vector> zones; + Vector root_zones; Vector nodes_outside_zones; /** * Zone index by node. Nodes that are in no zone, are not included. Nodes that are at the border @@ -54,26 +55,26 @@ class TreeZones { * Get the deepest zone that a socket is in. Note that the inputs of a Simulation Input node are * in a different zone than its output sockets. */ - const TreeZone *get_zone_by_socket(const bNodeSocket &socket) const; + const bNodeTreeZone *get_zone_by_socket(const bNodeSocket &socket) const; /** * Get the deepest zone that the node is in. Note that the e.g. Simulation Input and Output nodes * are considered to be inside of the zone they create. */ - const TreeZone *get_zone_by_node(const int32_t node_id) const; + const bNodeTreeZone *get_zone_by_node(const int32_t node_id) const; /** * Get a sorted list of zones that the node is in. First comes the root zone and last the most * nested zone. For nodes that are at the root level, the returned list is empty. */ - Vector get_zone_stack_for_node(const int32_t node_id) const; + Vector get_zone_stack_for_node(const int32_t node_id) const; }; -const TreeZones *get_tree_zones(const bNodeTree &tree); +const bNodeTreeZones *get_tree_zones(const bNodeTree &tree); -} // namespace blender::bke::node_tree_zones +} // namespace blender::bke -inline const blender::bke::node_tree_zones::TreeZones *bNodeTree::zones() const +inline const blender::bke::bNodeTreeZones *bNodeTree::zones() const { - return blender::bke::node_tree_zones::get_tree_zones(*this); + return blender::bke::get_tree_zones(*this); } diff --git a/source/blender/blenkernel/intern/node_tree_zones.cc b/source/blender/blenkernel/intern/node_tree_zones.cc index 7c9160d9695d..1240c0366f81 100644 --- a/source/blender/blenkernel/intern/node_tree_zones.cc +++ b/source/blender/blenkernel/intern/node_tree_zones.cc @@ -11,9 +11,9 @@ #include "BLI_task.hh" #include "BLI_timeit.hh" -namespace blender::bke::node_tree_zones { +namespace blender::bke { -static void update_zone_depths(TreeZone &zone) +static void update_zone_depths(bNodeTreeZone &zone) { if (zone.depth >= 0) { return; @@ -26,12 +26,14 @@ static void update_zone_depths(TreeZone &zone) zone.depth = zone.parent_zone->depth + 1; } -static Vector> find_zone_nodes( - const bNodeTree &tree, TreeZones &owner, Map &r_zone_by_inout_node) +static Vector> find_zone_nodes( + const bNodeTree &tree, + bNodeTreeZones &owner, + Map &r_zone_by_inout_node) { - Vector> zones; + Vector> zones; for (const bNode *node : tree.nodes_by_type("GeometryNodeSimulationOutput")) { - auto zone = std::make_unique(); + auto zone = std::make_unique(); zone->owner = &owner; zone->index = zones.size(); zone->output_node = node; @@ -41,7 +43,7 @@ static Vector> find_zone_nodes( for (const bNode *node : tree.nodes_by_type("GeometryNodeSimulationInput")) { const auto &storage = *static_cast(node->storage); if (const bNode *sim_output_node = tree.node_by_id(storage.output_node_id)) { - if (TreeZone *zone = r_zone_by_inout_node.lookup_default(sim_output_node, nullptr)) { + if (bNodeTreeZone *zone = r_zone_by_inout_node.lookup_default(sim_output_node, nullptr)) { zone->input_node = node; r_zone_by_inout_node.add(node, zone); } @@ -51,18 +53,18 @@ static Vector> find_zone_nodes( } struct ZoneRelation { - TreeZone *parent; - TreeZone *child; + bNodeTreeZone *parent; + bNodeTreeZone *child; }; static Vector get_direct_zone_relations( - const Span> all_zones, + const Span> all_zones, const BitGroupVector<> &depend_on_input_flag_array) { Vector zone_relations; /* Gather all relations, even the transitive once. */ - for (const std::unique_ptr &zone : all_zones) { + for (const std::unique_ptr &zone : all_zones) { const int zone_i = zone->index; for (const bNode *node : {zone->output_node}) { if (node == nullptr) { @@ -108,18 +110,18 @@ static Vector get_direct_zone_relations( } static void update_zone_per_node(const Span all_nodes, - const Span> all_zones, + const Span> all_zones, const BitGroupVector<> &depend_on_input_flag_array, - const Map &zone_by_inout_node, + const Map &zone_by_inout_node, Map &r_zone_by_node_id, Vector &r_node_outside_zones) { for (const int node_i : all_nodes.index_range()) { const bNode &node = *all_nodes[node_i]; const BoundedBitSpan depend_on_input_flags = depend_on_input_flag_array[node_i]; - TreeZone *parent_zone = nullptr; + bNodeTreeZone *parent_zone = nullptr; bits::foreach_1_index(depend_on_input_flags, [&](const int parent_zone_i) { - TreeZone *zone = all_zones[parent_zone_i].get(); + bNodeTreeZone *zone = all_zones[parent_zone_i].get(); if (ELEM(&node, zone->input_node, zone->output_node)) { return; } @@ -136,12 +138,12 @@ static void update_zone_per_node(const Span all_nodes, r_zone_by_node_id.add(node.identifier, parent_zone->index); } } - for (const MapItem item : zone_by_inout_node.items()) { + for (const MapItem item : zone_by_inout_node.items()) { r_zone_by_node_id.add_overwrite(item.key->identifier, item.value->index); } } -static void update_zone_border_links(const bNodeTree &tree, TreeZones &tree_zones) +static void update_zone_border_links(const bNodeTree &tree, bNodeTreeZones &tree_zones) { for (const bNodeLink *link : tree.all_links()) { if (!link->is_available()) { @@ -150,28 +152,30 @@ static void update_zone_border_links(const bNodeTree &tree, TreeZones &tree_zone if (link->is_muted()) { continue; } - TreeZone *from_zone = const_cast(tree_zones.get_zone_by_socket(*link->fromsock)); - TreeZone *to_zone = const_cast(tree_zones.get_zone_by_socket(*link->tosock)); + bNodeTreeZone *from_zone = const_cast( + tree_zones.get_zone_by_socket(*link->fromsock)); + bNodeTreeZone *to_zone = const_cast( + tree_zones.get_zone_by_socket(*link->tosock)); if (from_zone == to_zone) { continue; } BLI_assert(from_zone == nullptr || from_zone->contains_zone_recursively(*to_zone)); - for (TreeZone *zone = to_zone; zone != from_zone; zone = zone->parent_zone) { + for (bNodeTreeZone *zone = to_zone; zone != from_zone; zone = zone->parent_zone) { zone->border_links.append(link); } } } -static std::unique_ptr discover_tree_zones(const bNodeTree &tree) +static std::unique_ptr discover_tree_zones(const bNodeTree &tree) { if (tree.has_available_link_cycle()) { return {}; } - std::unique_ptr tree_zones = std::make_unique(); + std::unique_ptr tree_zones = std::make_unique(); const Span all_nodes = tree.all_nodes(); - Map zone_by_inout_node; + Map zone_by_inout_node; tree_zones->zones = find_zone_nodes(tree, *tree_zones, zone_by_inout_node); const int zones_num = tree_zones->zones.size(); @@ -203,13 +207,13 @@ static std::unique_ptr discover_tree_zones(const bNodeTree &tree) } } if (node->type == GEO_NODE_SIMULATION_INPUT) { - if (const TreeZone *zone = zone_by_inout_node.lookup_default(node, nullptr)) { + if (const bNodeTreeZone *zone = zone_by_inout_node.lookup_default(node, nullptr)) { /* Now entering a zone, so set the corresponding bit. */ depend_on_input_flags[zone->index].set(); } } else if (node->type == GEO_NODE_SIMULATION_OUTPUT) { - if (const TreeZone *zone = zone_by_inout_node.lookup_default(node, nullptr)) { + if (const bNodeTreeZone *zone = zone_by_inout_node.lookup_default(node, nullptr)) { /* The output is implicitly linked to the input, so also propagate the bits from there. */ if (const bNode *zone_input_node = zone->input_node) { const int input_node_i = zone_input_node->index(); @@ -239,11 +243,11 @@ static std::unique_ptr discover_tree_zones(const bNodeTree &tree) } /* Update depths. */ - for (std::unique_ptr &zone : tree_zones->zones) { + for (std::unique_ptr &zone : tree_zones->zones) { update_zone_depths(*zone); } - for (std::unique_ptr &zone : tree_zones->zones) { + for (std::unique_ptr &zone : tree_zones->zones) { if (zone->depth == 0) { tree_zones->root_zones.append(zone.get()); } @@ -262,7 +266,7 @@ static std::unique_ptr discover_tree_zones(const bNodeTree &tree) if (zone_i == -1) { continue; } - const TreeZone &zone = *tree_zones->zones[zone_i]; + const bNodeTreeZone &zone = *tree_zones->zones[zone_i]; if (ELEM(node, zone.input_node, zone.output_node)) { continue; } @@ -274,21 +278,21 @@ static std::unique_ptr discover_tree_zones(const bNodeTree &tree) return tree_zones; } -const TreeZones *get_tree_zones(const bNodeTree &tree) +const bNodeTreeZones *get_tree_zones(const bNodeTree &tree) { tree.runtime->tree_zones_cache_mutex.ensure( [&]() { tree.runtime->tree_zones = discover_tree_zones(tree); }); return tree.runtime->tree_zones.get(); } -bool TreeZone::contains_node_recursively(const bNode &node) const +bool bNodeTreeZone::contains_node_recursively(const bNode &node) const { - const TreeZones *zones = this->owner; + const bNodeTreeZones *zones = this->owner; const int zone_i = zones->zone_by_node_id.lookup_default(node.identifier, -1); if (zone_i == -1) { return false; } - for (const TreeZone *zone = zones->zones[zone_i].get(); zone; zone = zone->parent_zone) { + for (const bNodeTreeZone *zone = zones->zones[zone_i].get(); zone; zone = zone->parent_zone) { if (zone == this) { return true; } @@ -296,9 +300,9 @@ bool TreeZone::contains_node_recursively(const bNode &node) const return false; } -bool TreeZone::contains_zone_recursively(const TreeZone &other_zone) const +bool bNodeTreeZone::contains_zone_recursively(const bNodeTreeZone &other_zone) const { - for (const TreeZone *zone = other_zone.parent_zone; zone; zone = zone->parent_zone) { + for (const bNodeTreeZone *zone = other_zone.parent_zone; zone; zone = zone->parent_zone) { if (zone == this) { return true; } @@ -306,10 +310,10 @@ bool TreeZone::contains_zone_recursively(const TreeZone &other_zone) const return false; } -const TreeZone *TreeZones::get_zone_by_socket(const bNodeSocket &socket) const +const bNodeTreeZone *bNodeTreeZones::get_zone_by_socket(const bNodeSocket &socket) const { const bNode &node = socket.owner_node(); - const TreeZone *zone = this->get_zone_by_node(node.identifier); + const bNodeTreeZone *zone = this->get_zone_by_node(node.identifier); if (zone == nullptr) { return zone; } @@ -326,7 +330,7 @@ const TreeZone *TreeZones::get_zone_by_socket(const bNodeSocket &socket) const return zone; } -const TreeZone *TreeZones::get_zone_by_node(const int32_t node_id) const +const bNodeTreeZone *bNodeTreeZones::get_zone_by_node(const int32_t node_id) const { const int zone_i = this->zone_by_node_id.lookup_default(node_id, -1); if (zone_i == -1) { @@ -335,13 +339,13 @@ const TreeZone *TreeZones::get_zone_by_node(const int32_t node_id) const return this->zones[zone_i].get(); } -Vector TreeZones::get_zone_stack_for_node(const int node_id) const +Vector bNodeTreeZones::get_zone_stack_for_node(const int node_id) const { - const TreeZone *zone = this->get_zone_by_node(node_id); + const bNodeTreeZone *zone = this->get_zone_by_node(node_id); if (zone == nullptr) { return {}; } - Vector zone_stack; + Vector zone_stack; for (; zone; zone = zone->parent_zone) { zone_stack.append(zone); } @@ -349,4 +353,4 @@ Vector TreeZones::get_zone_stack_for_node(const int node_id) c return zone_stack; } -} // namespace blender::bke::node_tree_zones +} // namespace blender::bke diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index b06ad1276b14..6eea19a8f049 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -94,8 +94,8 @@ #include namespace geo_log = blender::nodes::geo_eval_log; -using blender::bke::node_tree_zones::TreeZone; -using blender::bke::node_tree_zones::TreeZones; +using blender::bke::bNodeTreeZone; +using blender::bke::bNodeTreeZones; /** * This is passed to many functions which draw the node editor. @@ -113,7 +113,7 @@ struct TreeDrawContext { * Geometry nodes logs various data during execution. The logged data that corresponds to the * currently drawn node tree can be retrieved from the log below. */ - blender::Map geo_log_by_zone; + blender::Map geo_log_by_zone; /** * True if there is an active realtime compositor using the node tree, false otherwise. */ @@ -1146,11 +1146,11 @@ static char *node_socket_get_tooltip(const SpaceNode *snode, } geo_log::GeoTreeLog *geo_tree_log = [&]() -> geo_log::GeoTreeLog * { - const TreeZones *zones = ntree.zones(); + const bNodeTreeZones *zones = ntree.zones(); if (!zones) { return nullptr; } - const TreeZone *zone = zones->get_zone_by_socket(socket); + const bNodeTreeZone *zone = zones->get_zone_by_socket(socket); return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr); }(); @@ -1749,11 +1749,11 @@ static void node_add_error_message_button(const TreeDrawContext &tree_draw_ctx, } geo_log::GeoTreeLog *geo_tree_log = [&]() -> geo_log::GeoTreeLog * { - const TreeZones *zones = node.owner_tree().zones(); + const bNodeTreeZones *zones = node.owner_tree().zones(); if (!zones) { return nullptr; } - const TreeZone *zone = zones->get_zone_by_node(node.identifier); + const bNodeTreeZone *zone = zones->get_zone_by_node(node.identifier); return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr); }(); @@ -1798,11 +1798,11 @@ static std::optional node_get_execution_time( const TreeDrawContext &tree_draw_ctx, const bNodeTree &ntree, const bNode &node) { geo_log::GeoTreeLog *tree_log = [&]() -> geo_log::GeoTreeLog * { - const TreeZones *zones = ntree.zones(); + const bNodeTreeZones *zones = ntree.zones(); if (!zones) { return nullptr; } - const TreeZone *zone = zones->get_zone_by_node(node.identifier); + const bNodeTreeZone *zone = zones->get_zone_by_node(node.identifier); return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr); }(); @@ -1964,11 +1964,11 @@ static std::optional node_get_accessed_attributes_row( TreeDrawContext &tree_draw_ctx, const bNode &node) { geo_log::GeoTreeLog *geo_tree_log = [&]() -> geo_log::GeoTreeLog * { - const TreeZones *zones = node.owner_tree().zones(); + const bNodeTreeZones *zones = node.owner_tree().zones(); if (!zones) { return nullptr; } - const TreeZone *zone = zones->get_zone_by_node(node.identifier); + const bNodeTreeZone *zone = zones->get_zone_by_node(node.identifier); return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr); }(); if (geo_tree_log == nullptr) { @@ -2034,11 +2034,11 @@ static Vector node_get_extra_info(TreeDrawContext &tree_draw_c if (snode.edittree->type == NTREE_GEOMETRY) { geo_log::GeoTreeLog *tree_log = [&]() -> geo_log::GeoTreeLog * { - const TreeZones *tree_zones = node.owner_tree().zones(); + const bNodeTreeZones *tree_zones = node.owner_tree().zones(); if (!tree_zones) { return nullptr; } - const TreeZone *zone = tree_zones->get_zone_by_node(node.identifier); + const bNodeTreeZone *zone = tree_zones->get_zone_by_node(node.identifier); return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr); }(); @@ -3097,8 +3097,8 @@ static void add_rect_corner_positions(Vector &positions, const rctf &rec } static void find_bounds_by_zone_recursive(const SpaceNode &snode, - const TreeZone &zone, - const Span> all_zones, + const bNodeTreeZone &zone, + const Span> all_zones, MutableSpan> r_bounds_by_zone) { const float node_padding = UI_UNIT_X; @@ -3110,7 +3110,7 @@ static void find_bounds_by_zone_recursive(const SpaceNode &snode, } Vector possible_bounds; - for (const TreeZone *child_zone : zone.child_zones) { + for (const bNodeTreeZone *child_zone : zone.child_zones) { find_bounds_by_zone_recursive(snode, *child_zone, all_zones, r_bounds_by_zone); const Span child_bounds = r_bounds_by_zone[child_zone->index]; for (const float2 &pos : child_bounds) { @@ -3170,7 +3170,7 @@ static void node_draw_zones(TreeDrawContext & /*tree_draw_ctx*/, const SpaceNode &snode, const bNodeTree &ntree) { - const TreeZones *zones = ntree.zones(); + const bNodeTreeZones *zones = ntree.zones(); if (zones == nullptr) { return; } @@ -3179,7 +3179,7 @@ static void node_draw_zones(TreeDrawContext & /*tree_draw_ctx*/, Array fillet_curve_by_zone(zones->zones.size()); for (const int zone_i : zones->zones.index_range()) { - const TreeZone &zone = *zones->zones[zone_i]; + const bNodeTreeZone &zone = *zones->zones[zone_i]; find_bounds_by_zone_recursive(snode, zone, zones->zones, bounds_by_zone); const Span boundary_positions = bounds_by_zone[zone_i]; diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index 2b6d63cdceef..eaf9d07ca95a 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -42,8 +42,6 @@ using blender::nodes::geo_eval_log::GeometryAttributeInfo; namespace blender::ed::space_node { -using namespace bke::node_tree_zones; - struct AttributeSearchData { int32_t node_id; char socket_identifier[MAX_NAME]; @@ -72,11 +70,11 @@ static Vector get_attribute_info_from_context( BLI_assert_unreachable(); return {}; } - const TreeZones *tree_zones = node_tree->zones(); + const bke::bNodeTreeZones *tree_zones = node_tree->zones(); if (!tree_zones) { return {}; } - const Map log_by_zone = + const Map log_by_zone = GeoModifierLog::get_tree_log_by_zone_for_node_editor(*snode); /* For the attribute input node, collect attribute information from all nodes in the group. */ @@ -93,7 +91,7 @@ static Vector get_attribute_info_from_context( } return attributes; } - const TreeZone *zone = tree_zones->get_zone_by_node(node->identifier); + const bke::bNodeTreeZone *zone = tree_zones->get_zone_by_node(node->identifier); GeoTreeLog *tree_log = log_by_zone.lookup_default(zone, nullptr); if (!tree_log) { return {}; diff --git a/source/blender/editors/util/ed_viewer_path.cc b/source/blender/editors/util/ed_viewer_path.cc index 31f125d31721..c2207dd93be3 100644 --- a/source/blender/editors/util/ed_viewer_path.cc +++ b/source/blender/editors/util/ed_viewer_path.cc @@ -23,7 +23,8 @@ namespace blender::ed::viewer_path { -using namespace bke::node_tree_zones; +using bke::bNodeTreeZone; +using bke::bNodeTreeZones; static void viewer_path_for_geometry_node(const SpaceNode &snode, const bNode &node, @@ -75,13 +76,13 @@ static void viewer_path_for_geometry_node(const SpaceNode &snode, BLI_assert(node != nullptr); tree->ensure_topology_cache(); - const TreeZones *tree_zones = tree->zones(); + const bNodeTreeZones *tree_zones = tree->zones(); if (!tree_zones) { return; } - const Vector zone_stack = tree_zones->get_zone_stack_for_node( + const Vector zone_stack = tree_zones->get_zone_stack_for_node( node->identifier); - for (const TreeZone *zone : zone_stack) { + for (const bNodeTreeZone *zone : zone_stack) { SimulationZoneViewerPathElem *node_elem = BKE_viewer_path_elem_new_simulation_zone(); node_elem->sim_output_node_id = zone->output_node->identifier; BLI_addtail(&r_dst.path, node_elem); @@ -94,12 +95,13 @@ static void viewer_path_for_geometry_node(const SpaceNode &snode, } snode.edittree->ensure_topology_cache(); - const TreeZones *tree_zones = snode.edittree->zones(); + const bNodeTreeZones *tree_zones = snode.edittree->zones(); if (!tree_zones) { return; } - const Vector zone_stack = tree_zones->get_zone_stack_for_node(node.identifier); - for (const TreeZone *zone : zone_stack) { + const Vector zone_stack = tree_zones->get_zone_stack_for_node( + node.identifier); + for (const bNodeTreeZone *zone : zone_stack) { SimulationZoneViewerPathElem *node_elem = BKE_viewer_path_elem_new_simulation_zone(); node_elem->sim_output_node_id = zone->output_node->identifier; BLI_addtail(&r_dst.path, node_elem); @@ -254,15 +256,16 @@ bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed return false; } const bNodeTree *ngroup = modifier->node_group; - const TreeZone *zone = nullptr; + const bNodeTreeZone *zone = nullptr; for (const ViewerPathElem *path_elem : parsed_viewer_path.node_path) { ngroup->ensure_topology_cache(); - const TreeZones *tree_zones = ngroup->zones(); + const bNodeTreeZones *tree_zones = ngroup->zones(); switch (path_elem->type) { case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: { const auto &typed_elem = *reinterpret_cast( path_elem); - const TreeZone *next_zone = tree_zones->get_zone_by_node(typed_elem.sim_output_node_id); + const bNodeTreeZone *next_zone = tree_zones->get_zone_by_node( + typed_elem.sim_output_node_id); if (next_zone == nullptr) { return false; } @@ -278,7 +281,7 @@ bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed if (group_node == nullptr) { return false; } - const TreeZone *parent_zone = tree_zones->get_zone_by_node(typed_elem.node_id); + const bNodeTreeZone *parent_zone = tree_zones->get_zone_by_node(typed_elem.node_id); if (parent_zone != zone) { return false; } @@ -299,7 +302,7 @@ bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed if (viewer_node == nullptr) { return false; } - const TreeZones *tree_zones = ngroup->zones(); + const bNodeTreeZones *tree_zones = ngroup->zones(); if (tree_zones == nullptr) { return false; } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 31e387ab43e0..076fe448e788 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -31,10 +31,10 @@ class bNodeTreeRuntime; class bNodeRuntime; class bNodeSocketRuntime; } // namespace blender::bke -namespace blender::bke::node_tree_zones { -class TreeZones; -struct TreeZone; -} // namespace blender::bke::node_tree_zones +namespace blender::bke { +class bNodeTreeZones; +class bNodeTreeZone; +} // namespace blender::bke using NodeDeclarationHandle = blender::nodes::NodeDeclaration; using SocketDeclarationHandle = blender::nodes::SocketDeclaration; using bNodeTreeRuntimeHandle = blender::bke::bNodeTreeRuntime; @@ -682,7 +682,7 @@ typedef struct bNodeTree { blender::Span panels() const; blender::MutableSpan panels_for_write(); /** Zones in the node tree. Currently there are only simulation zones in geometry nodes. */ - const blender::bke::node_tree_zones::TreeZones *zones() const; + const blender::bke::bNodeTreeZones *zones() const; #endif } bNodeTree; diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 40ea1a1bc356..dbee969bf8bd 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -430,8 +430,6 @@ static void find_side_effect_nodes_for_viewer_path( const ModifierEvalContext &ctx, MultiValueMap &r_side_effect_nodes) { - using namespace bke::node_tree_zones; - const std::optional parsed_path = ed::viewer_path::parse_geometry_nodes_viewer(viewer_path); if (!parsed_path.has_value()) { @@ -453,9 +451,9 @@ static void find_side_effect_nodes_for_viewer_path( MultiValueMap local_side_effect_nodes; const bNodeTree *group = nmd.node_group; - const TreeZone *zone = nullptr; + const bke::bNodeTreeZone *zone = nullptr; for (const ViewerPathElem *elem : parsed_path->node_path) { - const TreeZones *tree_zones = group->zones(); + const bke::bNodeTreeZones *tree_zones = group->zones(); if (tree_zones == nullptr) { return; } @@ -466,7 +464,8 @@ static void find_side_effect_nodes_for_viewer_path( switch (elem->type) { case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: { const auto &typed_elem = *reinterpret_cast(elem); - const TreeZone *next_zone = tree_zones->get_zone_by_node(typed_elem.sim_output_node_id); + const bke::bNodeTreeZone *next_zone = tree_zones->get_zone_by_node( + typed_elem.sim_output_node_id); if (next_zone == nullptr) { return; } @@ -524,7 +523,7 @@ static void find_side_effect_nodes_for_viewer_path( if (lf_graph_info == nullptr) { return; } - const TreeZones *tree_zones = group->zones(); + const bke::bNodeTreeZones *tree_zones = group->zones(); if (tree_zones == nullptr) { return; } @@ -588,7 +587,7 @@ static void find_socket_log_contexts(const NodesModifierData &nmd, const SpaceLink *sl = static_cast(area->spacedata.first); if (sl->spacetype == SPACE_NODE) { const SpaceNode &snode = *reinterpret_cast(sl); - const Map hash_by_zone = + const Map hash_by_zone = geo_log::GeoModifierLog::get_context_hash_by_zone_for_node_editor(snode, nmd.modifier.name); for (const ComputeContextHash &hash : hash_by_zone.values()) { diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh index 7e08e1fff54f..61f1e1394f38 100644 --- a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh +++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh @@ -176,7 +176,7 @@ struct GeometryNodeLazyFunctionGraphMapping { */ Map group_node_map; Map viewer_node_map; - Map zone_node_map; + Map zone_node_map; /* Indexed by #bNodeSocket::index_in_all_outputs. */ Array lf_input_index_for_output_bsocket_usage; diff --git a/source/blender/nodes/NOD_geometry_nodes_log.hh b/source/blender/nodes/NOD_geometry_nodes_log.hh index 0765b9506557..75d9825f96f8 100644 --- a/source/blender/nodes/NOD_geometry_nodes_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_log.hh @@ -334,11 +334,11 @@ class GeoModifierLog { /** * Utility accessor to logged data. */ - static Map + static Map get_context_hash_by_zone_for_node_editor(const SpaceNode &snode, StringRefNull modifier_name); - static Map - get_tree_log_by_zone_for_node_editor(const SpaceNode &snode); + static Map get_tree_log_by_zone_for_node_editor( + const SpaceNode &snode); static const ViewerNodeLog *find_viewer_node_log_for_path(const ViewerPath &viewer_path); }; diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index f19299750464..ec6131b393ea 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -52,8 +52,8 @@ namespace blender::nodes { namespace aai = bke::anonymous_attribute_inferencing; -using bke::node_tree_zones::TreeZone; -using bke::node_tree_zones::TreeZones; +using bke::bNodeTreeZone; +using bke::bNodeTreeZones; using fn::ValueOrField; using fn::ValueOrFieldCPPType; @@ -1445,7 +1445,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { */ Map simulation_inputs_usage_nodes_; - const TreeZones *tree_zones_; + const bNodeTreeZones *tree_zones_; Array zone_build_infos_; friend class UsedSocketVisualizeOptions; @@ -1497,7 +1497,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { const Array zone_build_order = this->compute_zone_build_order(); for (const int zone_i : zone_build_order) { - const TreeZone &zone = *tree_zones_->zones[zone_i]; + const bNodeTreeZone &zone = *tree_zones_->zones[zone_i]; BLI_assert(zone.output_node->type == GEO_NODE_SIMULATION_OUTPUT); this->build_simulation_zone_function(zone); } @@ -1519,7 +1519,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { * Builds a lazy-function for a simulation zone. * Internally, the generated lazy-function is just another graph. */ - void build_simulation_zone_function(const TreeZone &zone) + void build_simulation_zone_function(const bNodeTreeZone &zone) { const int zone_i = zone.index; ZoneBuildInfo &zone_info = zone_build_infos_[zone_i]; @@ -1640,7 +1640,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { // std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; } - lf::DummyNode &build_simulation_zone_input_node(const TreeZone &zone, lf::Graph &lf_graph) + lf::DummyNode &build_simulation_zone_input_node(const bNodeTreeZone &zone, lf::Graph &lf_graph) { Vector zone_input_types; auto &debug_info = scope_.construct(); @@ -1653,7 +1653,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { return node; } - lf::DummyNode &build_simulation_zone_output_node(const TreeZone &zone, lf::Graph &lf_graph) + lf::DummyNode &build_simulation_zone_output_node(const bNodeTreeZone &zone, lf::Graph &lf_graph) { auto &debug_info = scope_.construct(); debug_info.name = "Zone Output"; @@ -1666,7 +1666,8 @@ struct GeometryNodesLazyFunctionGraphBuilder { return node; } - lf::DummyNode &build_simulation_zone_input_usage_node(const TreeZone &zone, lf::Graph &lf_graph) + lf::DummyNode &build_simulation_zone_input_usage_node(const bNodeTreeZone &zone, + lf::Graph &lf_graph) { auto &debug_info = scope_.construct(); debug_info.name = "Input Usages"; @@ -1678,7 +1679,8 @@ struct GeometryNodesLazyFunctionGraphBuilder { return node; } - lf::DummyNode &build_simulation_zone_output_usage_node(const TreeZone &zone, lf::Graph &lf_graph) + lf::DummyNode &build_simulation_zone_output_usage_node(const bNodeTreeZone &zone, + lf::Graph &lf_graph) { auto &debug_info = scope_.construct(); debug_info.name = "Output Usages"; @@ -1690,7 +1692,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { return node; } - lf::DummyNode &build_zone_border_links_input_node(const TreeZone &zone, lf::Graph &lf_graph) + lf::DummyNode &build_zone_border_links_input_node(const bNodeTreeZone &zone, lf::Graph &lf_graph) { auto &debug_info = scope_.construct(); debug_info.name = "Border Links"; @@ -1703,7 +1705,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { return node; } - lf::DummyNode &build_border_link_input_usage_node(const TreeZone &zone, lf::Graph &lf_graph) + lf::DummyNode &build_border_link_input_usage_node(const bNodeTreeZone &zone, lf::Graph &lf_graph) { auto &debug_info = scope_.construct(); debug_info.name = "Border Link Usages"; @@ -2024,12 +2026,12 @@ struct GeometryNodesLazyFunctionGraphBuilder { } void insert_nodes_and_zones(const Span bnodes, - const Span zones, + const Span zones, BuildGraphParams &graph_params) { Vector nodes_to_insert = bnodes; - Map zone_by_output; - for (const TreeZone *zone : zones) { + Map zone_by_output; + for (const bNodeTreeZone *zone : zones) { nodes_to_insert.append(zone->output_node); zone_by_output.add(zone->output_node, zone); } @@ -2040,7 +2042,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { for (const bNode *bnode : nodes_to_insert) { this->build_output_socket_usages(*bnode, graph_params); - if (const TreeZone *zone = zone_by_output.lookup_default(bnode, nullptr)) { + if (const bNodeTreeZone *zone = zone_by_output.lookup_default(bnode, nullptr)) { this->insert_child_zone_node(*zone, graph_params); } else { @@ -2049,7 +2051,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { } } - void link_border_link_inputs_and_usages(const TreeZone &zone, + void link_border_link_inputs_and_usages(const bNodeTreeZone &zone, lf::Node &lf_border_link_input_node, lf::Node &lf_border_link_usage_node, BuildGraphParams &graph_params) @@ -2137,7 +2139,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { }); } - void insert_child_zone_node(const TreeZone &child_zone, BuildGraphParams &graph_params) + void insert_child_zone_node(const bNodeTreeZone &child_zone, BuildGraphParams &graph_params) { const int child_zone_i = child_zone.index; ZoneBuildInfo &child_zone_info = zone_build_infos_[child_zone_i]; diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc index 34884939532e..fe5efb7a1a72 100644 --- a/source/blender/nodes/intern/geometry_nodes_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -19,9 +19,10 @@ namespace blender::nodes::geo_eval_log { +using bke::bNodeTreeZone; +using bke::bNodeTreeZones; using fn::FieldInput; using fn::FieldInputs; -using namespace bke::node_tree_zones; GenericValueLog::~GenericValueLog() { @@ -520,20 +521,20 @@ static std::optional get_modifier_for_node_editor(const Space } static void find_tree_zone_hash_recursive( - const TreeZone &zone, + const bNodeTreeZone &zone, ComputeContextBuilder &compute_context_builder, - Map &r_hash_by_zone) + Map &r_hash_by_zone) { compute_context_builder.push(*zone.output_node); r_hash_by_zone.add_new(&zone, compute_context_builder.hash()); - for (const TreeZone *child_zone : zone.child_zones) { + for (const bNodeTreeZone *child_zone : zone.child_zones) { find_tree_zone_hash_recursive(*child_zone, compute_context_builder, r_hash_by_zone); } compute_context_builder.pop(); } -Map GeoModifierLog::get_context_hash_by_zone_for_node_editor( - const SpaceNode &snode, StringRefNull modifier_name) +Map GeoModifierLog:: + get_context_hash_by_zone_for_node_editor(const SpaceNode &snode, StringRefNull modifier_name) { const Vector tree_path = snode.treepath; if (tree_path.is_empty()) { @@ -550,31 +551,31 @@ Map GeoModifierLog::get_context_hash_by_zo if (group_node == nullptr) { return {}; } - const TreeZones *tree_zones = tree->zones(); + const bNodeTreeZones *tree_zones = tree->zones(); if (tree_zones == nullptr) { return {}; } - const Vector zone_stack = tree_zones->get_zone_stack_for_node( + const Vector zone_stack = tree_zones->get_zone_stack_for_node( group_node->identifier); - for (const TreeZone *zone : zone_stack) { + for (const bNodeTreeZone *zone : zone_stack) { compute_context_builder.push(*zone->output_node); } compute_context_builder.push(*group_node); } - const TreeZones *tree_zones = snode.edittree->zones(); + const bNodeTreeZones *tree_zones = snode.edittree->zones(); if (tree_zones == nullptr) { return {}; } - Map hash_by_zone; + Map hash_by_zone; hash_by_zone.add_new(nullptr, compute_context_builder.hash()); - for (const TreeZone *zone : tree_zones->root_zones) { + for (const bNodeTreeZone *zone : tree_zones->root_zones) { find_tree_zone_hash_recursive(*zone, compute_context_builder, hash_by_zone); } return hash_by_zone; } -Map GeoModifierLog::get_tree_log_by_zone_for_node_editor( +Map GeoModifierLog::get_tree_log_by_zone_for_node_editor( const SpaceNode &snode) { std::optional object_and_modifier = get_modifier_for_node_editor(snode); @@ -586,10 +587,10 @@ Map GeoModifierLog::get_tree_log_by_zone_for_nod if (modifier_log == nullptr) { return {}; } - const Map hash_by_zone = + const Map hash_by_zone = GeoModifierLog::get_context_hash_by_zone_for_node_editor( snode, object_and_modifier->nmd->modifier.name); - Map log_by_zone; + Map log_by_zone; for (const auto item : hash_by_zone.items()) { GeoTreeLog &tree_log = modifier_log->get_tree_log(item.value); log_by_zone.add(item.key, &tree_log); From e40f993a70dc33cca97c82e8a3043104c26217d3 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 20 Jun 2023 10:51:11 +0200 Subject: [PATCH 16/36] Fix #108704: scrubbing timeline does not to proper sampling like normal frame change This was broken in 037b3f87bd58e003d8fe8ca725dfa59969657e6c. This fix brings the original problem back a bit in that there will be an additional frame update now, but really only a redraw should be necessary without a depsgraph update. The depsgraph update is caused by the `NC_SCENE | ND_FRAME` notifier, which is checked for in `wm_event_do_notifiers`. Changing that in more depth is a bit risky for 3.6 now unfortunately. --- source/blender/editors/animation/anim_ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 5382988e3721..2649290848fb 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -265,7 +265,7 @@ static bool need_extra_redraw_after_scrubbing_ends(bContext *C) return true; } Scene *scene = CTX_data_scene(C); - if (scene->eevee.flag & SCE_EEVEE_TAA_REPROJECTION) { + if (scene->eevee.taa_samples != 1) { return true; } wmWindowManager *wm = CTX_wm_manager(C); From fff98eb59f177b3169dc373ac63045d4bc8770aa Mon Sep 17 00:00:00 2001 From: casey bianco-davis Date: Tue, 20 Jun 2023 11:02:17 +0200 Subject: [PATCH 17/36] Fix: Grease pencil conversion to new type not in menu When legacy grease pencil objects were selected the conversion operator was not visible. Pull Request: https://projects.blender.org/blender/blender/pulls/109094 --- scripts/startup/bl_ui/space_view3d.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index ef9a1dfd71d1..6aeac9d94355 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -3065,8 +3065,8 @@ class VIEW3D_MT_object_convert(Menu): def draw(self, context): layout = self.layout ob = context.active_object - - if ob and ob.type == 'GPENCIL' and context.gpencil_data: + + if ob and ob.type == 'GPENCIL' and context.gpencil_data and not context.preferences.experimental.use_grease_pencil_version3: layout.operator_enum("gpencil.convert", "type") else: layout.operator_enum("object.convert", "target") From fa59084025041cee338dd0aa3adc5b89f5d38681 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 20 Jun 2023 11:30:26 +0200 Subject: [PATCH 18/36] RNA: Fix compile error when assigning to a typed enum in C++ When a RNA file was compiled in C++, building would fail when defining a RNA enum property that assigns to a enum value with a defined (i.e. non-integer) type. Pull Request: https://projects.blender.org/blender/blender/pulls/109136 --- source/blender/makesrna/intern/makesrna.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 7a9601e43fe9..c9b45e5ac55f 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1422,8 +1422,20 @@ static char *rna_def_property_set_func( } else { rna_clamp_value_range(f, prop); + /* C++ may require casting to an enum type. */ + fprintf(f, "#ifdef __cplusplus\n"); + fprintf(f, + /* If #rna_clamp_value() adds an expression like `CLAMPIS(...)` (instead of an + lvalue), #decltype() yields a reference, so that has to be removed.*/ + " data->%s = %s(std::remove_reference_t%s)>)", + dp->dnaname, + (dp->booleannegative) ? "!" : "", + dp->dnaname); + rna_clamp_value(f, prop, 0); + fprintf(f, "#else\n"); fprintf(f, " data->%s = %s", dp->dnaname, (dp->booleannegative) ? "!" : ""); rna_clamp_value(f, prop, 0); + fprintf(f, "#endif\n"); } } From 954c262a960d19d977923dd41520cd0893aea214 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 9 Jun 2023 18:42:01 +0200 Subject: [PATCH 19/36] Fix #108778: Crash when rendering multiple view layers The rendering pipeline will re-use dependency graph and request for re-building its relations for every new view layer, and try to re-use as much evaluation as possible. This could potentially run into situation when a content of collection is changed: due to the difference in the per-view layer visibility. If the evaluated collection has an object cache this will make the cache to get out-of-sync with the actual content. The cache on the evaluated collection might be created when instancing system iterates over the collection. This change makes it so the cache is freed when the dependency graph relations are updated. This might be a bit too intrusive. There might be ways to somehow ensure the content of the collection is still the same as it was before the relations update, but this is much more complicated task. Perhaps the performance is already good enough. This is a collaboration with Jacques Lucke, who was looking into the same report, bouncing some ideas back and forth, and helped testing the patch. Pull Request: https://projects.blender.org/blender/blender/pulls/108816 --- source/blender/depsgraph/intern/builder/deg_builder.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index 328ddd00ab95..748461bdd28b 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -20,6 +20,7 @@ #include "BLI_utildefines.h" #include "BKE_action.h" +#include "BKE_collection.h" #include "RNA_prototypes.h" @@ -182,6 +183,14 @@ void deg_graph_build_finalize(Main *bmain, Depsgraph *graph) flag |= ID_RECALC_NTREE_OUTPUT; } } + else { + /* Collection content might have changed (children collection might have been added or + * removed from the graph based on their inclusion and visibility flags). */ + const ID_Type id_type = GS(id_node->id_cow->name); + if (id_type == ID_GR) { + BKE_collection_object_cache_free(reinterpret_cast(id_node->id_cow)); + } + } /* Restore recalc flags from original ID, which could possibly contain recalc flags set by * an operator and then were carried on by the undo system. */ flag |= id_orig->recalc; From 79cd8d6682479c62dd500585f79c9d75fcff7f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Tue, 20 Jun 2023 11:30:57 +0200 Subject: [PATCH 20/36] Alembic: add missing topology check for subd meshes No functional changes. --- source/blender/io/alembic/intern/abc_reader_mesh.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 8067be5a62ff..836f7d18f319 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -467,8 +467,8 @@ static void read_velocity(const V3fArraySamplePtr &velocities, } } -static bool samples_have_same_topology(const IPolyMeshSchema::Sample &sample, - const IPolyMeshSchema::Sample &ceil_sample) +template +static bool samples_have_same_topology(const SampleType &sample, const SampleType &ceil_sample) { const P3fArraySamplePtr &positions = sample.getPositions(); const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); @@ -926,7 +926,10 @@ static void read_subd_sample(const std::string &iobject_full_name, if (config.weight != 0.0f) { Alembic::AbcGeom::ISubDSchema::Sample ceil_sample; schema.get(ceil_sample, Alembic::Abc::ISampleSelector(config.ceil_index)); - abc_mesh_data.ceil_positions = ceil_sample.getPositions(); + if (samples_have_same_topology(sample, ceil_sample)) { + /* Only set interpolation data if the samples are compatible. */ + abc_mesh_data.ceil_positions = ceil_sample.getPositions(); + } } if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) { From c49af381c049679105c817f616d936aeae74b7e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Tue, 20 Jun 2023 11:35:26 +0200 Subject: [PATCH 21/36] Alembic: set output error string for subdivision meshes This will ensure that errors will be propagated to the modifier when reading subdivision meshes. --- source/blender/io/alembic/intern/abc_reader_mesh.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 836f7d18f319..90af7db308da 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -1164,6 +1164,7 @@ Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh, const bool use_vertex_interpolation = read_flag & MOD_MESHSEQ_INTERPOLATE_VERTICES; CDStreamConfig config = get_config(mesh_to_export, use_vertex_interpolation); config.time = sample_sel.getRequestedTime(); + config.modifier_error_message = err_str; read_subd_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config); return mesh_to_export; From a4d792a3adef2e91af440798fccf11fa91204642 Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Tue, 20 Jun 2023 12:23:05 +0200 Subject: [PATCH 22/36] Cycles/EEVEE: change point light to double-sided sphere light for energy preservation and better compatibility with other renderes. Ref: #108505 Point light now behaves the same as a spherical mesh light with the same overall energy (scaling from emission strength to power is \(4\pi^2R^2\)). # Cycles ## Comparison | Mesh Light | This patch | Previous behavior | | -------- | -------- | -------- | | ![mesh_1024](attachments/2900954c-57f8-49c2-b6f3-8fb559b820ac) | ![sphere_1024](attachments/148241ca-9350-48b6-be04-3933e015424c) | ![point_1024](attachments/d9b19d54-2b00-4986-ba8c-c4b28f687f09) | The behavior stays the same when `radius = 0`. | This patch | Previous behavior | | -------- | -------- | | ![sphere_64](attachments/aa05d59a-146a-4f69-b257-5d09a7f41d4e) | ![point_64](attachments/69a743be-bc15-454b-92d8-af02f4e8ab07) | No obvious performance change observed. ## Sampling When shading point lies outside the sphere, sample the spanned solid angle uniformly. When shading point lies inside the sphere, sample spherical direction uniformly when inside volume or the surface is transmissive, otherwise sample cosine-weighted upper hemisphere. ## Light Tree When shading point lies outside the sphere, treat as a disk light spanning the same solid angle. When shading point lies inside the sphere, it behaves like a background light, with estimated outgoing radiance \[L_o=\int f_aL_i\cos\theta_i\mathrm{d}\omega_i=\int f_a\frac{E}{\pi r^2}\cos\theta_i\mathrm{d}\omega_i\approx f_a \frac{E}{r^2}\], with \(f_a\) being the BSDF and \(E\) `measure.energy` in `light_tree.cpp`. The importance calculation for `LIGHT_POINT` is \[L_o=f_a E\cos\theta_i\frac{\cos\theta}{d^2}\]. Consider `min_importance = 0` because maximal incidence angle is \(\pi\), we could substitute \(d^2\) with \(\frac{r^2}{2}\) so the averaged outgoing radiance is \(f_a \frac{E}{r^2}\). This only holds for non-transmissive surface, but should be fine to use in volume. # EEVEE When shading point lies outside the sphere, the sphere light is equivalent to a disk light spanning the same solid angle. The sine of the new half-angle is the tangent of the previous half-angle. When shading point lies inside the sphere, integrating over the cosine-weighted hemisphere gives 1.0. ## Comparison with Cycles The plane is diffuse, the blue sphere has specular component. | Before | |After || |---|--|--|--| |Cycles|EEVEE|Cycles|EEVEE| |![](attachments/5824c494-0645-461a-b193-d74e02f353b8)|![](attachments/d2e85b53-3c2a-4a9f-a3b2-6e11c6083ce0)|![](attachments/a8dcdd8b-c13c-4fdc-808c-2563624549be)|![](attachments/8c3618ef-1ab4-4210-9535-c85e873f1e45)| Pull Request: https://projects.blender.org/blender/blender/pulls/108506 --- intern/cycles/kernel/integrator/mnee.h | 4 +- .../kernel/integrator/shade_dedicated_light.h | 10 +- intern/cycles/kernel/integrator/shade_light.h | 6 +- intern/cycles/kernel/light/distribution.h | 17 +- intern/cycles/kernel/light/light.h | 12 +- intern/cycles/kernel/light/point.h | 163 ++++++++++++------ intern/cycles/kernel/light/sample.h | 10 +- intern/cycles/kernel/light/tree.h | 2 + intern/cycles/kernel/sample/mapping.h | 88 +++++++--- intern/cycles/scene/light.cpp | 3 + intern/cycles/util/math.h | 6 + intern/cycles/util/math_intersect.h | 47 ++--- .../blender/draw/engines/eevee/eevee_lights.c | 10 +- .../engines/eevee/shaders/lights_lib.glsl | 25 +++ .../draw/engines/eevee_next/eevee_light.cc | 10 +- .../eevee_next/shaders/eevee_light_lib.glsl | 35 +++- 16 files changed, 322 insertions(+), 126 deletions(-) diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index f6c8d20247dc..7af104f6096c 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -775,7 +775,9 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, surface_shader_bsdf_eval(kg, state, sd, wo, throughput, ls->shader); /* Update light sample with new position / direction and keep pdf in vertex area measure. */ - light_sample_update(kg, ls, vertices[vertex_count - 1].p); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + light_sample_update( + kg, ls, vertices[vertex_count - 1].p, vertices[vertex_count - 1].n, path_flag); /* Save state path bounce info in case a light path node is used in the refractive interface or * light shader graph. */ diff --git a/intern/cycles/kernel/integrator/shade_dedicated_light.h b/intern/cycles/kernel/integrator/shade_dedicated_light.h index 9c6b2e3045b7..3e91d6e29963 100644 --- a/intern/cycles/kernel/integrator/shade_dedicated_light.h +++ b/intern/cycles/kernel/integrator/shade_dedicated_light.h @@ -20,6 +20,8 @@ ccl_device_inline bool shadow_linking_light_sample_from_intersection( KernelGlobals kg, ccl_private const Intersection &ccl_restrict isect, ccl_private const Ray &ccl_restrict ray, + const float3 N, + const uint32_t path_flag, ccl_private LightSample *ccl_restrict ls) { const int lamp = isect.prim; @@ -31,7 +33,7 @@ ccl_device_inline bool shadow_linking_light_sample_from_intersection( return distant_light_sample_from_intersection(kg, ray.D, lamp, ls); } - return light_sample_from_intersection(kg, &isect, ray.P, ray.D, ls); + return light_sample_from_intersection(kg, &isect, ray.P, ray.D, N, path_flag, ls); } ccl_device_inline float shadow_linking_light_sample_mis_weight(KernelGlobals kg, @@ -88,8 +90,11 @@ ccl_device bool shadow_linking_shade_light(KernelGlobals kg, ccl_private float &mis_weight, ccl_private int &ccl_restrict light_group) { + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n); LightSample ls ccl_optional_struct_init; - const bool use_light_sample = shadow_linking_light_sample_from_intersection(kg, isect, ray, &ls); + const bool use_light_sample = shadow_linking_light_sample_from_intersection( + kg, isect, ray, N, path_flag, &ls); if (!use_light_sample) { /* No light to be sampled, so no direct light contribution either. */ return false; @@ -100,7 +105,6 @@ ccl_device bool shadow_linking_shade_light(KernelGlobals kg, return false; } - const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); if (!is_light_shader_visible_to_path(ls.shader, path_flag)) { return false; } diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h index fb9c53ffdd1a..8bcf55a96ffe 100644 --- a/intern/cycles/kernel/integrator/shade_light.h +++ b/intern/cycles/kernel/integrator/shade_light.h @@ -24,12 +24,15 @@ ccl_device_inline void integrate_light(KernelGlobals kg, float3 ray_P = INTEGRATOR_STATE(state, ray, P); const float3 ray_D = INTEGRATOR_STATE(state, ray, D); const float ray_time = INTEGRATOR_STATE(state, ray, time); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n); /* Advance ray to new start distance. */ INTEGRATOR_STATE_WRITE(state, ray, tmin) = intersection_t_offset(isect.t); LightSample ls ccl_optional_struct_init; - const bool use_light_sample = light_sample_from_intersection(kg, &isect, ray_P, ray_D, &ls); + const bool use_light_sample = light_sample_from_intersection( + kg, &isect, ray_P, ray_D, N, path_flag, &ls); if (!use_light_sample) { return; @@ -37,7 +40,6 @@ ccl_device_inline void integrate_light(KernelGlobals kg, /* Use visibility flag to skip lights. */ #ifdef __PASSES__ - const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); if (!is_light_shader_visible_to_path(ls.shader, path_flag)) { return; } diff --git a/intern/cycles/kernel/light/distribution.h b/intern/cycles/kernel/light/distribution.h index 9ca6856d94dd..23cdaa6dff5b 100644 --- a/intern/cycles/kernel/light/distribution.h +++ b/intern/cycles/kernel/light/distribution.h @@ -46,7 +46,9 @@ ccl_device_noinline bool light_distribution_sample(KernelGlobals kg, const float3 rand, const float time, const float3 P, + const float3 N, const int object_receiver, + const int shader_flags, const int bounce, const uint32_t path_flag, ccl_private LightSample *ls) @@ -56,8 +58,19 @@ ccl_device_noinline bool light_distribution_sample(KernelGlobals kg, const int index = light_distribution_sample(kg, rand.z); const float pdf_selection = kernel_data.integrator.distribution_pdf_lights; const float2 rand_uv = float3_to_float2(rand); - return light_sample( - kg, rand_uv, time, P, object_receiver, bounce, path_flag, index, 0, pdf_selection, ls); + return light_sample(kg, + rand_uv, + time, + P, + N, + object_receiver, + shader_flags, + bounce, + path_flag, + index, + 0, + pdf_selection, + ls); } ccl_device_inline float light_distribution_pdf_lamp(KernelGlobals kg) diff --git a/intern/cycles/kernel/light/light.h b/intern/cycles/kernel/light/light.h index 22dbdb3a318e..87596a0abef2 100644 --- a/intern/cycles/kernel/light/light.h +++ b/intern/cycles/kernel/light/light.h @@ -96,6 +96,8 @@ ccl_device_inline bool light_sample(KernelGlobals kg, const int lamp, const float2 rand, const float3 P, + const float3 N, + const int shader_flags, const uint32_t path_flag, ccl_private LightSample *ls) { @@ -150,7 +152,7 @@ ccl_device_inline bool light_sample(KernelGlobals kg, } } else if (type == LIGHT_POINT) { - if (!point_light_sample(klight, rand, P, ls)) { + if (!point_light_sample(klight, rand, P, N, shader_flags, ls)) { return false; } } @@ -171,7 +173,9 @@ ccl_device_noinline bool light_sample(KernelGlobals kg, const float2 rand, const float time, const float3 P, + const float3 N, const int object_receiver, + const int shader_flags, const int bounce, const uint32_t path_flag, const int emitter_index, @@ -233,7 +237,7 @@ ccl_device_noinline bool light_sample(KernelGlobals kg, return false; } - if (!light_sample(kg, light, rand, P, path_flag, ls)) { + if (!light_sample(kg, light, rand, P, N, shader_flags, path_flag, ls)) { return false; } } @@ -446,6 +450,8 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg, ccl_private const Intersection *ccl_restrict isect, const float3 ray_P, const float3 ray_D, + const float3 N, + const uint32_t path_flag, ccl_private LightSample *ccl_restrict ls) { const int lamp = isect->prim; @@ -468,7 +474,7 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg, } } else if (type == LIGHT_POINT) { - if (!point_light_sample_from_intersection(klight, isect, ray_P, ray_D, ls)) { + if (!point_light_sample_from_intersection(klight, isect, ray_P, ray_D, N, path_flag, ls)) { return false; } } diff --git a/intern/cycles/kernel/light/point.h b/intern/cycles/kernel/light/point.h index bac53ff58ccf..a4dbd1850fe5 100644 --- a/intern/cycles/kernel/light/point.h +++ b/intern/cycles/kernel/light/point.h @@ -8,68 +8,123 @@ CCL_NAMESPACE_BEGIN -template ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, const float2 rand, const float3 P, + const float3 N, + const int shader_flags, ccl_private LightSample *ls) { - float3 center = klight->co; - float radius = klight->spot.radius; - /* disk oriented normal */ - const float3 lightN = normalize(P - center); - ls->P = center; - - if (radius > 0.0f) { - ls->P += disk_light_sample(lightN, rand) * radius; + float3 lightN = P - klight->co; + const float d_sq = len_squared(lightN); + const float d = sqrtf(d_sq); + lightN /= d; + + const float r_sq = sqr(klight->spot.radius); + + float cos_theta; + if (d_sq > r_sq) { + const float one_minus_cos = sin_sqr_to_one_minus_cos(r_sq / d_sq); + sample_uniform_cone_concentric(-lightN, one_minus_cos, rand, &cos_theta, &ls->D, &ls->pdf); + } + else { + const bool has_transmission = (shader_flags & SD_BSDF_HAS_TRANSMISSION); + if (has_transmission) { + ls->D = sample_uniform_sphere(rand); + ls->pdf = M_1_2PI_F * 0.5f; + } + else { + sample_cos_hemisphere(N, rand, &ls->D, &ls->pdf); + } + cos_theta = -dot(ls->D, lightN); } - ls->pdf = klight->spot.invarea; - ls->D = normalize_len(ls->P - P, &ls->t); - /* we set the light normal to the outgoing direction to support texturing */ - ls->Ng = -ls->D; + /* Law of cosines. */ + ls->t = d * cos_theta - copysignf(safe_sqrtf(r_sq - d_sq + d_sq * sqr(cos_theta)), d_sq - r_sq); + + ls->P = P + ls->D * ls->t; ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea; + if (r_sq == 0) { + /* Use intensity instead of radiance for point light. */ + ls->eval_fac /= sqr(ls->t); + /* `ls->Ng` is not well-defined for point light, so use the incoming direction instead. */ + ls->Ng = -ls->D; + } + else { + ls->Ng = normalize(ls->P - klight->co); + /* Remap sampled point onto the sphere to prevent precision issues with small radius. */ + ls->P = ls->Ng * klight->spot.radius + klight->co; + } + + const Transform itfm = klight->itfm; + const float2 uv = map_to_sphere(transform_direction(&itfm, ls->Ng)); + /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */ + ls->u = uv.y; + ls->v = 1.0f - uv.x - uv.y; - float2 uv = map_to_sphere(ls->Ng); - ls->u = uv.x; - ls->v = uv.y; - ls->pdf *= lamp_light_pdf(lightN, -ls->D, ls->t); return true; } +ccl_device_forceinline float point_light_pdf( + const float d_sq, const float r_sq, const float3 N, const float3 D, const uint32_t path_flag) +{ + if (d_sq > r_sq) { + return M_1_2PI_F / sin_sqr_to_one_minus_cos(r_sq / d_sq); + } + + const bool has_transmission = (path_flag & PATH_RAY_MIS_HAD_TRANSMISSION); + return has_transmission ? M_1_2PI_F * 0.5f : pdf_cos_hemisphere(N, D); +} + ccl_device_forceinline void point_light_mnee_sample_update(const ccl_global KernelLight *klight, ccl_private LightSample *ls, - const float3 P) + const float3 P, + const float3 N, + const uint32_t path_flag) { ls->D = normalize_len(ls->P - P, &ls->t); - ls->Ng = -ls->D; - float2 uv = map_to_sphere(ls->Ng); - ls->u = uv.x; - ls->v = uv.y; + const float radius = klight->spot.radius; - float invarea = klight->spot.invarea; - ls->eval_fac = (0.25f * M_1_PI_F) * invarea; - /* NOTE : preserve pdf in area measure. */ - ls->pdf = invarea; + if (radius > 0) { + const float d_sq = len_squared(P - klight->co); + const float r_sq = sqr(radius); + const float t_sq = sqr(ls->t); + + ls->pdf = point_light_pdf(d_sq, r_sq, N, ls->D, path_flag); + + /* NOTE : preserve pdf in area measure. */ + ls->pdf *= 0.5f * fabsf(d_sq - r_sq - t_sq) / (radius * ls->t * t_sq); + + ls->Ng = normalize(ls->P - klight->co); + } + else { + ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea; + + ls->Ng = -ls->D; + + /* PDF does not change. */ + } + + const Transform itfm = klight->itfm; + const float2 uv = map_to_sphere(transform_direction(&itfm, ls->Ng)); + /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */ + ls->u = uv.y; + ls->v = 1.0f - uv.x - uv.y; } ccl_device_inline bool point_light_intersect(const ccl_global KernelLight *klight, const ccl_private Ray *ccl_restrict ray, ccl_private float *t) { - /* Sphere light (aka, aligned disk light). */ - const float3 lightP = klight->co; const float radius = klight->spot.radius; if (radius == 0.0f) { return false; } - /* disk oriented normal */ - const float3 lightN = normalize(ray->P - lightP); float3 P; - return ray_disk_intersect(ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, t); + return ray_sphere_intersect(ray->P, ray->D, ray->tmin, ray->tmax, klight->co, radius, &P, t); } ccl_device_inline bool point_light_sample_from_intersection( @@ -77,27 +132,27 @@ ccl_device_inline bool point_light_sample_from_intersection( ccl_private const Intersection *ccl_restrict isect, const float3 ray_P, const float3 ray_D, + const float3 N, + const uint32_t path_flag, ccl_private LightSample *ccl_restrict ls) { - const float3 lighN = normalize(ray_P - klight->co); + ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea; - /* We set the light normal to the outgoing direction to support texturing. */ - ls->Ng = -ls->D; + const float radius = klight->spot.radius; - float invarea = klight->spot.invarea; - ls->eval_fac = (0.25f * M_1_PI_F) * invarea; - ls->pdf = invarea; + ls->Ng = radius > 0 ? normalize(ls->P - klight->co) : -ray_D; - float2 uv = map_to_sphere(ls->Ng); - ls->u = uv.x; - ls->v = uv.y; + const Transform itfm = klight->itfm; + const float2 uv = map_to_sphere(transform_direction(&itfm, ls->Ng)); + /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */ + ls->u = uv.y; + ls->v = 1.0f - uv.x - uv.y; - /* compute pdf */ - if (ls->t != FLT_MAX) { - ls->pdf *= lamp_light_pdf(lighN, -ls->D, ls->t); + if (ls->t == FLT_MAX) { + ls->pdf = 0.0f; } else { - ls->pdf = 0.f; + ls->pdf = point_light_pdf(len_squared(ray_P - klight->co), sqr(radius), N, ray_D, path_flag); } return true; @@ -115,14 +170,22 @@ ccl_device_forceinline bool point_light_tree_parameters(const ccl_global KernelL cos_theta_u = 1.0f; /* Any value in [-1, 1], irrelevant since theta = 0 */ return true; } - float min_distance; - point_to_centroid = safe_normalize_len(centroid - P, &min_distance); - const float radius = klight->spot.radius; - const float hypotenus = sqrtf(sqr(radius) + sqr(min_distance)); - cos_theta_u = min_distance / hypotenus; + float dist_point_to_centroid; + point_to_centroid = safe_normalize_len(centroid - P, &dist_point_to_centroid); - distance = make_float2(hypotenus, min_distance); + const float radius = klight->spot.radius; + if (dist_point_to_centroid > radius) { + /* Equivalent to a disk light with the same angular span. */ + cos_theta_u = cos_from_sin(radius / dist_point_to_centroid); + distance = dist_point_to_centroid * make_float2(1.0f / cos_theta_u, 1.0f); + } + else { + /* Similar to background light. */ + cos_theta_u = -1.0f; + /* HACK: pack radiance scaling in the distance. */ + distance = one_float2() * radius / M_SQRT2_F; + } return true; } diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h index 2a2fce850344..96d6474e7af0 100644 --- a/intern/cycles/kernel/light/sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -338,7 +338,7 @@ ccl_device_inline bool light_sample_from_volume_segment(KernelGlobals kg, #endif { return light_distribution_sample( - kg, rand, time, P, object_receiver, bounce, path_flag, ls); + kg, rand, time, P, D, object_receiver, SD_BSDF_HAS_TRANSMISSION, bounce, path_flag, ls); } } @@ -363,7 +363,7 @@ ccl_device bool light_sample_from_position(KernelGlobals kg, #endif { return light_distribution_sample( - kg, rand, time, P, object_receiver, bounce, path_flag, ls); + kg, rand, time, P, N, object_receiver, shader_flags, bounce, path_flag, ls); } } @@ -371,12 +371,14 @@ ccl_device bool light_sample_from_position(KernelGlobals kg, * except for directional light. */ ccl_device_forceinline void light_sample_update(KernelGlobals kg, ccl_private LightSample *ls, - const float3 P) + const float3 P, + const float3 N, + const uint32_t path_flag) { const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp); if (ls->type == LIGHT_POINT) { - point_light_mnee_sample_update(klight, ls, P); + point_light_mnee_sample_update(klight, ls, P, N, path_flag); } else if (ls->type == LIGHT_SPOT) { spot_light_mnee_sample_update(klight, ls, P); diff --git a/intern/cycles/kernel/light/tree.h b/intern/cycles/kernel/light/tree.h index 52ff88a35213..55788f064cd2 100644 --- a/intern/cycles/kernel/light/tree.h +++ b/intern/cycles/kernel/light/tree.h @@ -761,7 +761,9 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg, float3_to_float2(rand), time, P, + N_or_D, object_receiver, + shader_flags, bounce, path_flag, selected_emitter, diff --git a/intern/cycles/kernel/sample/mapping.h b/intern/cycles/kernel/sample/mapping.h index 43c84a409458..6ae8fb9f95ae 100644 --- a/intern/cycles/kernel/sample/mapping.h +++ b/intern/cycles/kernel/sample/mapping.h @@ -19,6 +19,29 @@ ccl_device void to_unit_disk(ccl_private float2 *rand) rand->y = r * sinf(phi); } +/* Distribute 2D uniform random samples on [0, 1] over unit disk [-1, 1], with concentric mapping + * to better preserve stratification for some RNG sequences. */ +ccl_device float2 concentric_sample_disk(const float2 rand) +{ + float phi, r; + float a = 2.0f * rand.x - 1.0f; + float b = 2.0f * rand.y - 1.0f; + + if (a == 0.0f && b == 0.0f) { + return zero_float2(); + } + else if (a * a > b * b) { + r = a; + phi = M_PI_4_F * (b / a); + } + else { + r = b; + phi = M_PI_2_F - M_PI_4_F * (a / b); + } + + return make_float2(r * cosf(phi), r * sinf(phi)); +} + /* return an orthogonal tangent and bitangent given a normal and tangent that * may not be exactly orthogonal */ ccl_device void make_orthonormals_tangent(const float3 N, @@ -45,6 +68,12 @@ ccl_device_inline void sample_cos_hemisphere(const float3 N, *pdf = costheta * M_1_PI_F; } +ccl_device_inline float pdf_cos_hemisphere(const float3 N, const float3 D) +{ + const float cos_theta = dot(N, D); + return cos_theta > 0 ? cos_theta * M_1_PI_F : 0.0f; +} + /* sample direction uniformly distributed in hemisphere */ ccl_device_inline void sample_uniform_hemisphere(const float3 N, const float2 rand, @@ -91,6 +120,42 @@ ccl_device_inline float pdf_uniform_cone(const float3 N, float3 D, float angle) return 0.0f; } +/* Uniformly sample a direction in a cone of given angle around `N`. Use concentric mapping to + * better preserve stratification. Return the angle between `N` and the sampled direction as + * `cos_theta`. + * Pass `1 - cos(angle)` as argument instead of `angle` to alleviate precision issues at small + * angles (see sphere light for reference). */ +ccl_device_inline void sample_uniform_cone_concentric(const float3 N, + const float one_minus_cos_angle, + const float2 rand, + ccl_private float *cos_theta, + ccl_private float3 *wo, + ccl_private float *pdf) +{ + if (one_minus_cos_angle > 0) { + /* Map random number from 2D to 1D. */ + float2 xy = concentric_sample_disk(rand); + const float r2 = len_squared(xy); + + /* Equivalent to `mix(cos_angle, 1.0f, 1.0f - r2)` */ + *cos_theta = 1.0f - r2 * one_minus_cos_angle; + + /* Equivalent to `xy *= sin_theta / sqrt(r2); */ + xy *= safe_sqrtf(one_minus_cos_angle * (2.0f - one_minus_cos_angle * r2)); + + float3 T, B; + make_orthonormals(N, &T, &B); + + *wo = xy.x * T + xy.y * B + *cos_theta * N; + *pdf = M_1_2PI_F / one_minus_cos_angle; + } + else { + *cos_theta = 1.0f; + *wo = N; + *pdf = 1.0f; + } +} + /* sample uniform point on the surface of a sphere */ ccl_device float3 sample_uniform_sphere(const float2 rand) { @@ -103,29 +168,6 @@ ccl_device float3 sample_uniform_sphere(const float2 rand) return make_float3(x, y, z); } -/* distribute uniform xy on [0,1] over unit disk [-1,1], with concentric mapping - * to better preserve stratification for some RNG sequences */ -ccl_device float2 concentric_sample_disk(const float2 rand) -{ - float phi, r; - float a = 2.0f * rand.x - 1.0f; - float b = 2.0f * rand.y - 1.0f; - - if (a == 0.0f && b == 0.0f) { - return zero_float2(); - } - else if (a * a > b * b) { - r = a; - phi = M_PI_4_F * (b / a); - } - else { - r = b; - phi = M_PI_2_F - M_PI_4_F * (a / b); - } - - return make_float2(r * cosf(phi), r * sinf(phi)); -} - /* sample point in unit polygon with given number of corners and rotation */ ccl_device float2 regular_polygon_sample(float corners, float rotation, const float2 rand) { diff --git a/intern/cycles/scene/light.cpp b/intern/cycles/scene/light.cpp index bc7a41a5824d..f6728f023668 100644 --- a/intern/cycles/scene/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -1214,6 +1214,9 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce shader_id &= ~SHADER_AREA_LIGHT; float radius = light->size; + /* TODO: `invarea` was used for disk sampling, with the current solid angle sampling this + * becomes unnecessary. We could store `eval_fac` instead, but currently it shares the same + * #KernelSpotLight type with #LIGHT_SPOT, so keep it know until refactor for spot light. */ float invarea = (light->normalize && radius > 0.0f) ? 1.0f / (M_PI_F * radius * radius) : 1.0f; diff --git a/intern/cycles/util/math.h b/intern/cycles/util/math.h index a29abe51e7a2..394e56ea9268 100644 --- a/intern/cycles/util/math.h +++ b/intern/cycles/util/math.h @@ -748,6 +748,12 @@ ccl_device_inline float cos_from_sin(const float s) return safe_sqrtf(1.0f - sqr(s)); } +ccl_device_inline float sin_sqr_to_one_minus_cos(const float s_sq) +{ + /* Using second-order Taylor expansion at small angles for better accuracy. */ + return s_sq > 0.0004f ? 1.0f - safe_sqrtf(1.0f - s_sq) : 0.5f * s_sq; +} + ccl_device_inline float pow20(float a) { return sqr(sqr(sqr(sqr(a)) * a)); diff --git a/intern/cycles/util/math_intersect.h b/intern/cycles/util/math_intersect.h index 0e1ed3de6083..c7277f38f25b 100644 --- a/intern/cycles/util/math_intersect.h +++ b/intern/cycles/util/math_intersect.h @@ -18,29 +18,32 @@ ccl_device bool ray_sphere_intersect(float3 ray_P, ccl_private float3 *isect_P, ccl_private float *isect_t) { - const float3 d = sphere_P - ray_P; - const float radiussq = sphere_radius * sphere_radius; - const float tsq = dot(d, d); - - if (tsq > radiussq) { - /* Ray origin outside sphere. */ - const float tp = dot(d, ray_D); - if (tp < 0.0f) { - /* Ray points away from sphere. */ - return false; - } - const float dsq = tsq - tp * tp; /* Pythagoras. */ - if (dsq > radiussq) { - /* Closest point on ray outside sphere. */ - return false; - } - const float t = tp - sqrtf(radiussq - dsq); /* pythagoras */ - if (t > ray_tmin && t < ray_tmax) { - *isect_t = t; - *isect_P = ray_P + ray_D * t; - return true; - } + const float3 d_vec = sphere_P - ray_P; + const float r_sq = sphere_radius * sphere_radius; + const float d_sq = dot(d_vec, d_vec); + const float d_cos_theta = dot(d_vec, ray_D); + + if (d_sq > r_sq && d_cos_theta < 0.0f) { + /* Ray origin outside sphere and points away from sphere. */ + return false; } + + const float d_sin_theta_sq = d_sq - d_cos_theta * d_cos_theta; + + if (d_sin_theta_sq > r_sq) { + /* Closest point on ray outside sphere. */ + return false; + } + + /* Law of cosines. */ + const float t = d_cos_theta - copysignf(sqrtf(r_sq - d_sin_theta_sq), d_sq - r_sq); + + if (t > ray_tmin && t < ray_tmax) { + *isect_t = t; + *isect_P = ray_P + ray_D * t; + return true; + } + return false; } diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c index d05bb85f6271..15f194927a54 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.c +++ b/source/blender/draw/engines/eevee/eevee_lights.c @@ -86,8 +86,7 @@ static float light_shape_radiance_get(const Light *la, const EEVEE_Light *evli) case LA_LOCAL: { /* Sphere area. */ float area = 4.0f * (float)M_PI * square_f(evli->radius); - /* NOTE: Presence of a factor of PI here to match Cycles. But it should be missing to be - * consistent with the other cases. */ + /* Convert radiant flux to radiance. */ return 1.0f / (area * (float)M_PI); } default: @@ -128,10 +127,9 @@ static float light_volume_radiance_factor_get(const Light *la, } case LA_SPOT: case LA_LOCAL: { - /* Sphere solid angle. */ - float area = 4.0f * (float)M_PI; - /* NOTE: Missing a factor of PI here to match Cycles. */ - power *= 1.0f / area; + /* Convert radiant flux to intensity. */ + /* Inverse of sphere solid angle. */ + power *= 0.25f * (float)M_1_PI; break; } default: diff --git a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl index d6b4d9b02467..174aceae7e63 100644 --- a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl @@ -325,6 +325,20 @@ float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector) return ltc_evaluate_disk(N, V, mat3(1.0), points); } + else if (ld.l_type == POINT) { + if (l_vector.w < ld.l_radius) { + /* Inside, treat as hemispherical light. */ + return 1.0; + } + else { + /* Outside, treat as disk light spanning the same solid angle. */ + /* The result is the same as passing the scaled radius to #ltc_evaluate_disk_simple (see + * #light_specular), using simplified math here. */ + float r_sq = sqr(ld.l_radius / l_vector.w); + vec3 L = l_vector.xyz / l_vector.w; + return r_sq * diffuse_sphere_integral(dot(N, L), r_sq); + } + } else { float radius = ld.l_radius; radius /= (ld.l_type == SUN) ? 1.0 : l_vector.w; @@ -347,11 +361,22 @@ float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector) return ltc_evaluate_quad(corners, vec3(0.0, 0.0, 1.0)); } + else if (ld.l_type == POINT && l_vector.w < ld.l_radius) { + /* Inside the sphere light, integrate over the hemisphere. */ + return 1.0; + } else { bool is_ellipse = (ld.l_type == AREA_ELLIPSE); float radius_x = is_ellipse ? ld.l_sizex : ld.l_radius; float radius_y = is_ellipse ? ld.l_sizey : ld.l_radius; + if (ld.l_type == POINT) { + /* The sine of the half-angle spanned by a sphere light is equal to the tangent of the + * half-angle spanned by a disk light with the same radius. */ + radius_x *= inversesqrt(1.0 - sqr(radius_x / l_vector.w)); + radius_y *= inversesqrt(1.0 - sqr(radius_y / l_vector.w)); + } + vec3 L = (ld.l_type == SUN) ? -ld.l_forward : l_vector.xyz; vec3 Px = ld.l_right; vec3 Py = ld.l_up; diff --git a/source/blender/draw/engines/eevee_next/eevee_light.cc b/source/blender/draw/engines/eevee_next/eevee_light.cc index 239e0e0d3879..b85e35a2171c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_light.cc +++ b/source/blender/draw/engines/eevee_next/eevee_light.cc @@ -186,8 +186,7 @@ float Light::shape_radiance_get(const ::Light *la) case LA_LOCAL: { /* Sphere area. */ float area = 4.0f * float(M_PI) * square_f(_radius); - /* NOTE: Presence of a factor of PI here to match Cycles. But it should be missing to be - * consistent with the other cases. */ + /* Convert radiant flux to radiance. */ return 1.0f / (area * float(M_PI)); } default: @@ -221,10 +220,9 @@ float Light::point_radiance_get(const ::Light *la) } case LA_SPOT: case LA_LOCAL: { - /* Sphere solid angle. */ - float area = 4.0f * float(M_PI); - /* NOTE: Missing a factor of PI here to match Cycles. */ - return 1.0f / area; + /* Convert radiant flux to intensity. */ + /* Inverse of sphere solid angle. */ + return 0.25f * float(M_1_PI); } default: case LA_SUN: { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl index 761e403b1389..8894f6327519 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl @@ -106,7 +106,20 @@ float light_diffuse(sampler2DArray utility_tx, vec3 L, float dist) { - if (is_directional || !is_area_light(ld.type)) { + if (ld.type == LIGHT_POINT) { + if (dist < ld._radius) { + /* Inside, treat as hemispherical light. */ + return 1.0; + } + else { + /* Outside, treat as disk light spanning the same solid angle. */ + /* The result is the same as passing the scaled radius to #ltc_evaluate_disk_simple (see + * #light_ltc), using simplified math here. */ + float r_sq = sqr(ld._radius / dist); + return r_sq * ltc_diffuse_sphere_integral(utility_tx, dot(N, L), r_sq); + } + } + else if (is_directional || ld.type == LIGHT_SPOT) { float radius = ld._radius / dist; return ltc_evaluate_disk_simple(utility_tx, radius, dot(N, L)); } @@ -147,7 +160,11 @@ float light_ltc(sampler2DArray utility_tx, float dist, vec4 ltc_mat) { - if (is_directional || ld.type != LIGHT_RECT) { + if (ld.type == LIGHT_POINT && dist < ld._radius) { + /* Inside the sphere light, integrate over the hemisphere. */ + return 1.0; + } + else if (is_directional || ld.type != LIGHT_RECT) { vec3 Px = ld._right; vec3 Py = ld._up; @@ -156,8 +173,18 @@ float light_ltc(sampler2DArray utility_tx, } vec3 points[3]; - points[0] = Px * -ld._area_size_x + Py * -ld._area_size_y; - points[1] = Px * ld._area_size_x + Py * -ld._area_size_y; + if (ld.type == LIGHT_POINT) { + /* The sine of the half-angle spanned by a sphere light is equal to the tangent of the + * half-angle spanned by a disk light with the same radius. */ + float radius = ld._radius * inversesqrt(1.0 - sqr(ld._radius / dist)); + + points[0] = Px * -radius + Py * -radius; + points[1] = Px * radius + Py * -radius; + } + else { + points[0] = Px * -ld._area_size_x + Py * -ld._area_size_y; + points[1] = Px * ld._area_size_x + Py * -ld._area_size_y; + } points[2] = -points[0]; points[0] += L * dist; From a6f3afe99505e405b8d998924610f62f33f7c587 Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Mon, 19 Jun 2023 14:37:22 -0400 Subject: [PATCH 23/36] Fix: USD export: wrong emissive color in viewport material When a Blender material has no nodes, its viewport color, roughness and metallic values are saved as inputs to a simple USD Preview Surface. This pull request fixes a bug where the Blender material's viewport color is also saved as the USD Preview Surface emissiveColor attribute. This bug was accidentally introduced in #107947. To reproduce the issue, open the default Blender scene, turn off nodes on the material and set the material's viewport color. Export to USDA and notice that the color is incorrectly set as the emissiveColor input in the USD shader. Pull Request: https://projects.blender.org/blender/blender/pulls/109138 --- source/blender/io/usd/intern/usd_writer_material.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc index 3a1abc9005eb..e1172fee92da 100644 --- a/source/blender/io/usd/intern/usd_writer_material.cc +++ b/source/blender/io/usd/intern/usd_writer_material.cc @@ -278,8 +278,6 @@ void create_usd_viewport_material(const USDExporterContext &usd_export_context, shader.CreateIdAttr(pxr::VtValue(usdtokens::preview_surface)); shader.CreateInput(usdtokens::diffuse_color, pxr::SdfValueTypeNames->Color3f) .Set(pxr::GfVec3f(material->r, material->g, material->b)); - shader.CreateInput(usdtokens::emissive_color, pxr::SdfValueTypeNames->Color3f) - .Set(pxr::GfVec3f(material->r, material->g, material->b)); shader.CreateInput(usdtokens::roughness, pxr::SdfValueTypeNames->Float).Set(material->roughness); shader.CreateInput(usdtokens::metallic, pxr::SdfValueTypeNames->Float).Set(material->metallic); From e2dbc4779eac0a3f5d29d2c31a0502d65cc6c84a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 20 Jun 2023 21:08:22 +1000 Subject: [PATCH 24/36] UI: change the UV packing pin option into a toggle & drop-down Exposing both the option not to use pinned islands and to skip pinned islands in the same drop-down was confusing. Now there is a checkbox "Pin", when disabled, pinned UV's don't have any impact on the packed result. When enabled, the pin-method selects how pinned islands are handled. Also remove the "ignore" option from the UI as this didn't fit well with other methods of handling pinned islands. Instead, islands to be ignored can be de-selected by the user. Ref !108733. --- .../editors/uvedit/uvedit_unwrap_ops.cc | 57 ++++++++++++++----- source/blender/geometry/GEO_uv_pack.hh | 12 +++- source/blender/geometry/intern/uv_pack.cc | 2 +- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.cc b/source/blender/editors/uvedit/uvedit_unwrap_ops.cc index 785e62b9927d..224a9b34389b 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.cc +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.cc @@ -1511,7 +1511,13 @@ static int pack_islands_exec(bContext *C, wmOperator *op) } pack_island_params.scale_to_fit = RNA_boolean_get(op->ptr, "scale"); pack_island_params.merge_overlap = RNA_boolean_get(op->ptr, "merge_overlap"); - pack_island_params.pin_method = eUVPackIsland_PinMethod(RNA_enum_get(op->ptr, "pin")); + + if (RNA_boolean_get(op->ptr, "pin")) { + pack_island_params.pin_method = eUVPackIsland_PinMethod(RNA_enum_get(op->ptr, "pin_method")); + } + else { + pack_island_params.pin_method = ED_UVPACK_PIN_NONE; + } pack_island_params.margin_method = eUVPackIsland_MarginMethod( RNA_enum_get(op->ptr, "margin_method")); @@ -1591,17 +1597,21 @@ static const EnumPropertyItem pack_shape_method_items[] = { {0, nullptr, 0, nullptr, nullptr}, }; +/** + * \note #ED_UVPACK_PIN_NONE is exposed as a boolean "pin". + * \note #ED_UVPACK_PIN_IGNORE is intentionally not exposed as it is confusing from the UI level + * (users can simply not select these islands). + * The option is kept kept internally because it's used for live unwrap. + */ static const EnumPropertyItem pinned_islands_method_items[] = { - {ED_UVPACK_PIN_PACK, "PACK", 0, "Pack", "Pinned islands are packed normally"}, - {ED_UVPACK_PIN_LOCK_SCALE, "SCALE", 0, "Lock Scale", "Pinned islands won't rescale"}, - {ED_UVPACK_PIN_LOCK_ROTATION, "ROTATION", 0, "Lock Rotation", "Pinned islands won't rotate"}, + {ED_UVPACK_PIN_LOCK_SCALE, "SCALE", 0, "Scale", "Pinned islands won't rescale"}, + {ED_UVPACK_PIN_LOCK_ROTATION, "ROTATION", 0, "Rotation", "Pinned islands won't rotate"}, {ED_UVPACK_PIN_LOCK_ROTATION_SCALE, "ROTATION_SCALE", 0, - "Lock Rotation and Scale", + "Rotation and Scale", "Pinned islands will translate only"}, - {ED_UVPACK_PIN_LOCK_ALL, "LOCKED", 0, "Lock in Place", "Pinned islands are locked in place"}, - {ED_UVPACK_PIN_IGNORE, "IGNORE", 0, "Ignore", "Pinned islands are not packed"}, + {ED_UVPACK_PIN_LOCK_ALL, "LOCKED", 0, "All", "Pinned islands are locked in place"}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -1612,15 +1622,23 @@ static void uv_pack_islands_ui(bContext * /*C*/, wmOperator *op) uiLayoutSetPropDecorate(layout, false); uiItemR(layout, op->ptr, "shape_method", 0, nullptr, ICON_NONE); uiItemR(layout, op->ptr, "scale", 0, nullptr, ICON_NONE); - uiItemR(layout, op->ptr, "rotate", 0, nullptr, ICON_NONE); - uiLayout *sub = uiLayoutRow(layout, true); - uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "rotate")); - uiItemR(sub, op->ptr, "rotate_method", 0, nullptr, ICON_NONE); - uiItemS(layout); + { + uiItemR(layout, op->ptr, "rotate", 0, nullptr, ICON_NONE); + uiLayout *sub = uiLayoutRow(layout, true); + uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "rotate")); + uiItemR(sub, op->ptr, "rotate_method", 0, nullptr, ICON_NONE); + uiItemS(layout); + } uiItemR(layout, op->ptr, "margin_method", 0, nullptr, ICON_NONE); uiItemR(layout, op->ptr, "margin", 0, nullptr, ICON_NONE); uiItemS(layout); - uiItemR(layout, op->ptr, "pin", 0, nullptr, ICON_NONE); + { + uiItemR(layout, op->ptr, "pin", 0, nullptr, ICON_NONE); + uiLayout *sub = uiLayoutRow(layout, true); + uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "pin")); + uiItemR(sub, op->ptr, "pin_method", 0, IFACE_("Lock Method"), ICON_NONE); + uiItemS(layout); + } uiItemR(layout, op->ptr, "merge_overlap", 0, nullptr, ICON_NONE); uiItemR(layout, op->ptr, "udim_source", 0, nullptr, ICON_NONE); uiItemS(layout); @@ -1686,8 +1704,17 @@ void UV_OT_pack_islands(wmOperatorType *ot) ""); RNA_def_float_factor( ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f); - RNA_def_enum( - ot->srna, "pin", pinned_islands_method_items, ED_UVPACK_PIN_PACK, "Pinned Islands", ""); + RNA_def_boolean(ot->srna, + "pin", + false, + "Lock Pinned Islands", + "Constrain islands containing any pinned UV's"); + RNA_def_enum(ot->srna, + "pin_method", + pinned_islands_method_items, + ED_UVPACK_PIN_LOCK_ALL, + "Pin Method", + ""); RNA_def_enum(ot->srna, "shape_method", pack_shape_method_items, diff --git a/source/blender/geometry/GEO_uv_pack.hh b/source/blender/geometry/GEO_uv_pack.hh index 90af1b968efa..0b386b90d9cb 100644 --- a/source/blender/geometry/GEO_uv_pack.hh +++ b/source/blender/geometry/GEO_uv_pack.hh @@ -47,12 +47,18 @@ enum eUVPackIsland_ShapeMethod { }; enum eUVPackIsland_PinMethod { - ED_UVPACK_PIN_IGNORE = 0, - ED_UVPACK_PIN_PACK, + /** Pin has no impact on packing. */ + ED_UVPACK_PIN_NONE = 0, + /** + * Ignore islands containing any pinned UV's. + * \note Not exposed in the UI, used only for live-unwrap. + */ + ED_UVPACK_PIN_IGNORE, ED_UVPACK_PIN_LOCK_ROTATION, ED_UVPACK_PIN_LOCK_ROTATION_SCALE, ED_UVPACK_PIN_LOCK_SCALE, - ED_UVPACK_PIN_LOCK_ALL, /* Lock translation, rotation and scale. */ + /** Lock the island in-place (translation, rotation and scale). */ + ED_UVPACK_PIN_LOCK_ALL, }; namespace blender::geometry { diff --git a/source/blender/geometry/intern/uv_pack.cc b/source/blender/geometry/intern/uv_pack.cc index 745c775f048b..46a2947d5ca6 100644 --- a/source/blender/geometry/intern/uv_pack.cc +++ b/source/blender/geometry/intern/uv_pack.cc @@ -337,7 +337,7 @@ UVPackIsland_Params::UVPackIsland_Params() only_selected_faces = false; use_seams = false; correct_aspect = false; - pin_method = ED_UVPACK_PIN_PACK; + pin_method = ED_UVPACK_PIN_NONE; pin_unselected = false; merge_overlap = false; margin = 0.001f; From 77c37fb01d8674aa8146122abd14653dfa26ce3f Mon Sep 17 00:00:00 2001 From: Martijn Versteegh Date: Tue, 20 Jun 2023 16:24:33 +0200 Subject: [PATCH 25/36] Fix #108053: Make the Collada UV sets enumerated from zero The UV sets incorrectly used the CustomData layer index instead of the count within the FLOAT2 layers. Pull Request: https://projects.blender.org/blender/blender/pulls/109056 --- source/blender/io/collada/GeometryExporter.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/blender/io/collada/GeometryExporter.cpp b/source/blender/io/collada/GeometryExporter.cpp index 41fc987aa076..695746ed92d7 100644 --- a/source/blender/io/collada/GeometryExporter.cpp +++ b/source/blender/io/collada/GeometryExporter.cpp @@ -368,17 +368,16 @@ void GeometryExporter::create_mesh_primitive_list(short material_index, /* if mesh has uv coords writes for TEXCOORD */ int num_layers = CustomData_number_of_layers(&me->ldata, CD_PROP_FLOAT2); - int active_uv_index = CustomData_get_active_layer_index(&me->ldata, CD_PROP_FLOAT2); + int active_uv = CustomData_get_active_layer(&me->ldata, CD_PROP_FLOAT2); for (int i = 0; i < num_layers; i++) { - int layer_index = CustomData_get_layer_index_n(&me->ldata, CD_PROP_FLOAT2, i); - if (!this->export_settings.get_active_uv_only() || layer_index == active_uv_index) { + if (!this->export_settings.get_active_uv_only() || i == active_uv) { // char *name = CustomData_get_layer_name(&me->ldata, CD_PROP_FLOAT2, i); COLLADASW::Input texcoord_input( COLLADASW::InputSemantic::TEXCOORD, makeUrl(makeTexcoordSourceId(geom_id, i, this->export_settings.get_active_uv_only())), 2, /* this is only until we have optimized UV sets */ - (this->export_settings.get_active_uv_only()) ? 0 : layer_index - 1 /* set (0,1,2,...) */ + (this->export_settings.get_active_uv_only()) ? 0 : i /* set (0,1,2,...) */ ); til.push_back(texcoord_input); } From c8395e3bbc0be69bab6caee0a358c7f5d2973e5b Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 20 Jun 2023 16:54:45 +0200 Subject: [PATCH 26/36] Cleanup: Remove unused header includes in outliner_tree.cc --- .../blender/editors/space_outliner/outliner_tree.cc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index 3b793eba34e8..2970fa0929d7 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -18,20 +18,15 @@ #include "DNA_collection_types.h" #include "DNA_constraint_types.h" #include "DNA_curves_types.h" -#include "DNA_gpencil_legacy_types.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_key_types.h" #include "DNA_light_types.h" #include "DNA_lightprobe_types.h" -#include "DNA_linestyle_types.h" #include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meta_types.h" #include "DNA_object_types.h" #include "DNA_particle_types.h" #include "DNA_pointcloud_types.h" #include "DNA_scene_types.h" -#include "DNA_sequence_types.h" #include "DNA_shader_fx_types.h" #include "DNA_simulation_types.h" #include "DNA_speaker_types.h" @@ -42,25 +37,19 @@ #include "BLI_fnmatch.h" #include "BLI_listbase.h" #include "BLI_mempool.h" -#include "BLI_timeit.hh" #include "BLI_utildefines.h" #include "BLT_translation.h" -#include "BKE_armature.h" #include "BKE_deform.h" #include "BKE_layer.h" -#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_outliner_treehash.hh" #include "ED_screen.h" -#include "RNA_access.h" - #include "UI_interface.h" -#include "UI_resources.h" #include "outliner_intern.hh" #include "tree/common.hh" From 4b0dc9eb2956b7257bc88f6fffce7ac533525c8b Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Tue, 20 Jun 2023 17:00:11 +0200 Subject: [PATCH 27/36] License: Update THIRD-PARTY-LICENSES doc for 3.6. --- release/license/THIRD-PARTY-LICENSES.txt | 72 +++++++++++++++++------- 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/release/license/THIRD-PARTY-LICENSES.txt b/release/license/THIRD-PARTY-LICENSES.txt index 268399287f80..cd43f71f2a9b 100644 --- a/release/license/THIRD-PARTY-LICENSES.txt +++ b/release/license/THIRD-PARTY-LICENSES.txt @@ -22,11 +22,10 @@ PERFORMANCE OF THIS SOFTWARE. ** Audaspace; version 1.3.0 -- https://audaspace.github.io/ ** Cuda Wrangler; version cbf465b -- https://github.com/CudaWrangler/cuew ** Draco; version 1.3.6 -- https://google.github.io/draco/ -** Embree; version 3.13.4 -- https://github.com/embree/embree -** Intel(R) oneAPI DPC++ compiler; version 20221019 -- +** Embree; version 4.1.0 -- https://github.com/embree/embree +** Intel(R) oneAPI DPC++ compiler; version 2022-12 -- https://github.com/intel/llvm#oneapi-dpc-compiler -** Intel® Open Path Guiding Library; version v0.4.1-beta -- -http://www.openpgl.org/ +** Intel® Open Path Guiding Library; version 0.5.0 -- http://www.openpgl.org/ ** Mantaflow; version 0.13 -- http://mantaflow.com/ ** materialX; version 1.38.6 -- https://github.com/AcademySoftwareFoundation/MaterialX @@ -40,6 +39,8 @@ https://software.intel.com/en-us/oneapi/onetbb ** SDL Extension Wrangler; version 15edf8e -- https://github.com/SDLWrangler/sdlew ** ShaderC; version 2022.3 -- https://github.com/google/shaderc +** SYCL Unified Runtime ; version fd711c920acc4434cb52ff18b078c082d9d7f44d -- +https://github.com/oneapi-src/unified-runtime ** Vulkan Loader; version 1.2.198 -- https://github.com/KhronosGroup/Vulkan-Loader @@ -279,6 +280,8 @@ limitations under the License. Copyright 2014 Blender Foundation * For ShaderC see also this required NOTICE: Copyright 2015 The Shaderc Authors. All rights reserved. +* For SYCL Unified Runtime see also this required NOTICE: + Copyright (C) 2022-2023 Intel Corporation * For Vulkan Loader see also this required NOTICE: Copyright (c) 2019 The Khronos Group Inc. Copyright (c) 2019 Valve Corporation @@ -378,7 +381,7 @@ All rights reserved. ** Google Logging; version 4.4.0 -- https://github.com/google/glog Copyright (c) 2006, Google Inc. All rights reserved. -** Imath; version 3.1.5 -- https://github.com/AcademySoftwareFoundation/Imath +** Imath; version 3.1.7 -- https://github.com/AcademySoftwareFoundation/Imath Copyright Contributors to the OpenEXR Project. All rights reserved. ** ISPC; version 1.17.0 -- https://github.com/ispc/ispc Copyright Intel Corporation @@ -395,10 +398,10 @@ Copyright Contributors to the Open Shading Language project. ** OpenColorIO; version 2.2.0 -- https://github.com/AcademySoftwareFoundation/OpenColorIO Copyright Contributors to the OpenColorIO Project. -** OpenEXR; version 3.1.5 -- +** OpenEXR; version 3.1.7 -- https://github.com/AcademySoftwareFoundation/openexr Copyright Contributors to the OpenEXR Project. All rights reserved. -** OpenImageIO; version 2.4.9.0 -- http://www.openimageio.org +** OpenImageIO; version 2.4.11.0 -- http://www.openimageio.org Copyright (c) 2008-present by Contributors to the OpenImageIO project. All Rights Reserved. ** Pystring; version 1.1.3 -- https://github.com/imageworks/pystring @@ -571,7 +574,7 @@ effect of CC0 on those rights. ------ -** FLAC; version 1.3.4 -- https://xiph.org/flac/ +** FLAC; version 1.4.2 -- https://xiph.org/flac/ Copyright (C) 2001-2009 Josh Coalson Copyright (C) 2011-2016 Xiph.Org Foundation ** Potrace; version 1.16 -- http://potrace.sourceforge.net/ @@ -1242,7 +1245,7 @@ Copyright (C) 2003-2021 x264 project ** miniLZO; version 2.08 -- http://www.oberhumer.com/opensource/lzo/ LZO and miniLZO are Copyright (C) 1996-2014 Markus Franz Xaver Oberhumer All Rights Reserved. -** The FreeType Project; version 2.12.1 -- +** The FreeType Project; version 2.13.0 -- https://sourceforge.net/projects/freetype Copyright (C) 1996-2020 by David Turner, Robert Wilhelm, and Werner Lemberg. ** X Drag and Drop; version 2000-08-08 -- @@ -2796,7 +2799,7 @@ That's all there is to it! ------ -** FFmpeg; version 5.1.2 -- http://ffmpeg.org/ +** FFmpeg; version 6.0 -- http://ffmpeg.org/ Copyright: The FFmpeg contributors https://github.com/FFmpeg/FFmpeg/blob/master/CREDITS ** Libsndfile; version 1.1.0 -- http://libsndfile.github.io/libsndfile/ @@ -3425,7 +3428,35 @@ December 9, 2010 ------ -** {fmt}; version 8.0.0 -- https://github.com/fmtlib/fmt +** vcintrinsics; version 782fbf7301dc73acaa049a4324c976ad94f587f7 -- +https://github.com/intel/vc-intrinsics +Copyright (c) 2019 Intel Corporation + +MIT License + +Copyright (c) 2019 Intel Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------ + +** {fmt}; version 9.1.0 -- https://github.com/fmtlib/fmt Copyright (c) 2012 - present, Victor Zverovich ** Brotli; version 1.0.9 -- https://github.com/google/brotli Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. @@ -3454,11 +3485,11 @@ Copyright (c) 2006, 2008 Junio C Hamano Copyright © 2017-2018 Red Hat Inc. Copyright © 2012 Collabora, Ltd. Copyright © 2008 Kristian Høgsberg -** Libxml2; version 2.10.3 -- http://xmlsoft.org/ +** Libxml2; version 2.10.4 -- http://xmlsoft.org/ Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved. ** Mesa 3D; version 21.1.5 -- https://www.mesa3d.org/ Copyright (C) 1999-2007 Brian Paul All Rights Reserved. -** oneAPI Level Zero; version v1.8.5 -- +** oneAPI Level Zero; version v1.8.8 -- https://github.com/oneapi-src/level-zero Copyright (C) 2019-2021 Intel Corporation ** OPENCollada; version 1.6.68 -- https://github.com/KhronosGroup/OpenCOLLADA @@ -3470,12 +3501,11 @@ Copyright (c) 2018 Jingwei Huang, Yichao Zhou, Matthias Niessner, Jonathan Shewchuk and Leonidas Guibas. All rights reserved. ** robin-map; version 0.6.2 -- https://github.com/Tessil/robin-map Copyright (c) 2017 Thibaut Goetghebuer-Planchon -** sse2neon; version fe5ff00bb8d19b327714a3c290f3e2ce81ba3525 -- -https://github.com/DLTcollab/sse2neon +** sse2neon; version 1.6.0 -- https://github.com/DLTcollab/sse2neon Copyright sse2neon contributors ** TinyGLTF; version 2.5.0 -- https://github.com/syoyo/tinygltf Copyright (c) 2017 Syoyo Fujita, Aurélien Chatelain and many contributors -** Wayland protocols; version 1.21 -- +** Wayland protocols; version 1.31 -- https://gitlab.freedesktop.org/wayland/wayland-protocols Copyright © 2008-2013 Kristian Høgsberg Copyright © 2010-2013 Intel Corporation @@ -3973,7 +4003,7 @@ the following restrictions: ------ -** LibTIFF; version 4.4.0 -- http://www.libtiff.org/ +** LibTIFF; version 4.5.1 -- http://www.libtiff.org/ Copyright (c) 1988-1997 Sam Leffler Copyright (c) 1991-1997 Silicon Graphics, Inc. @@ -4029,7 +4059,7 @@ Software. ** OpenSubdiv; version 3.5.0 -- http://graphics.pixar.com/opensubdiv Copyright 2013 Pixar -** Universal Scene Description; version 22.11 -- http://www.openusd.org/ +** Universal Scene Description; version 23.05 -- http://www.openusd.org/ Copyright 2016 Pixar Licensed under the Apache License, Version 2.0 (the "Apache License") with the @@ -4215,7 +4245,7 @@ disclaims all warranties with regard to this software. ------ -** Wayland; version 1.21.0 -- https://gitlab.freedesktop.org/wayland/wayland +** Wayland; version 1.22.0 -- https://gitlab.freedesktop.org/wayland/wayland Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. Copyright © 2011 Kristian Høgsberg Copyright © 2011 Benjamin Franzke @@ -4240,7 +4270,7 @@ MIT Expat ------ -** OpenSSL; version 1.1.1q -- https://www.openssl.org/ +** OpenSSL; version 3.1.1 -- https://www.openssl.org/ Copyright (c) 1998-2021 The OpenSSL Project Copyright (c) 1995-1998 Eric A. Young, Tim J. Hudson @@ -4363,7 +4393,7 @@ OpenSSL License ------ -** Python; version 3.10.9 -- https://www.python.org +** Python; version 3.10.12 -- https://www.python.org Copyright (c) 2001-2021 Python Software Foundation. All rights reserved. A. HISTORY OF THE SOFTWARE From 2014084d29933f6017da00f87a761d2ac2787d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 20 Jun 2023 16:06:13 +0200 Subject: [PATCH 28/36] Animation: Convert armature RNA from C to C++ Transform `rna_armature.c` and `rna_armature.cc` to C++. - `UNUSED(x)` -> `/*x*/` - Explicit casts for ID and void pointers - Explicit casts for 0 enum values No functional changes. --- source/blender/makesrna/intern/CMakeLists.txt | 4 +- source/blender/makesrna/intern/makesrna.c | 2 +- .../{rna_armature.c => rna_armature.cc} | 307 +++++++++--------- ...rna_armature_api.c => rna_armature_api.cc} | 16 +- 4 files changed, 167 insertions(+), 162 deletions(-) rename source/blender/makesrna/intern/{rna_armature.c => rna_armature.cc} (84%) rename source/blender/makesrna/intern/{rna_armature_api.c => rna_armature_api.cc} (93%) diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 48eb894fa189..c05262ab1508 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -18,7 +18,7 @@ set(DEFSRC rna_action.c rna_animation.c rna_animviz.c - rna_armature.c + rna_armature.cc rna_asset.c rna_attribute.c rna_boid.c @@ -101,7 +101,7 @@ endif() set(APISRC rna_action_api.c rna_animation_api.c - rna_armature_api.c + rna_armature_api.cc rna_camera_api.c rna_curve_api.cc rna_fcurve_api.c diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index c9b45e5ac55f..e68062571bba 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -4551,7 +4551,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_action.c", "rna_action_api.c", RNA_def_action}, {"rna_animation.c", "rna_animation_api.c", RNA_def_animation}, {"rna_animviz.c", NULL, RNA_def_animviz}, - {"rna_armature.c", "rna_armature_api.c", RNA_def_armature}, + {"rna_armature.cc", "rna_armature_api.cc", RNA_def_armature}, {"rna_attribute.c", NULL, RNA_def_attribute}, {"rna_asset.c", NULL, RNA_def_asset}, {"rna_boid.c", NULL, RNA_def_boid}, diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.cc similarity index 84% rename from source/blender/makesrna/intern/rna_armature.c rename to source/blender/makesrna/intern/rna_armature.cc index f256f60ac9d5..08b975600655 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.cc @@ -40,23 +40,23 @@ # include "DEG_depsgraph.h" # include "DEG_depsgraph_build.h" -static void rna_Armature_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_Armature_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { ID *id = ptr->owner_id; DEG_id_tag_update(id, ID_RECALC_COPY_ON_WRITE); } -static void rna_Armature_update_data(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_Armature_update_data(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { ID *id = ptr->owner_id; DEG_id_tag_update(id, 0); WM_main_add_notifier(NC_GEOM | ND_DATA, id); - // WM_main_add_notifier(NC_OBJECT|ND_POSE, NULL); + // WM_main_add_notifier(NC_OBJECT|ND_POSE, nullptr); } -static void rna_Armature_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_Armature_dependency_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr) { ID *id = ptr->owner_id; @@ -68,12 +68,12 @@ static void rna_Armature_dependency_update(Main *bmain, Scene *UNUSED(scene), Po static void rna_Armature_act_bone_set(PointerRNA *ptr, PointerRNA value, - struct ReportList *UNUSED(reports)) + struct ReportList * /*reports*/) { bArmature *arm = (bArmature *)ptr->data; - if (value.owner_id == NULL && value.data == NULL) { - arm->act_bone = NULL; + if (value.owner_id == nullptr && value.data == nullptr) { + arm->act_bone = nullptr; } else { if (value.owner_id != &arm->id) { @@ -85,26 +85,26 @@ static void rna_Armature_act_bone_set(PointerRNA *ptr, } } - arm->act_bone = value.data; + arm->act_bone = static_cast(value.data); arm->act_bone->flag |= BONE_SELECTED; } } static void rna_Armature_act_edit_bone_set(PointerRNA *ptr, PointerRNA value, - struct ReportList *UNUSED(reports)) + struct ReportList * /*reports*/) { bArmature *arm = (bArmature *)ptr->data; - if (value.owner_id == NULL && value.data == NULL) { - arm->act_edbone = NULL; + if (value.owner_id == nullptr && value.data == nullptr) { + arm->act_edbone = nullptr; } else { if (value.owner_id != &arm->id) { /* raise an error! */ } else { - arm->act_edbone = value.data; + arm->act_edbone = static_cast(value.data); ((EditBone *)arm->act_edbone)->flag |= BONE_SELECTED; } } @@ -112,12 +112,12 @@ static void rna_Armature_act_edit_bone_set(PointerRNA *ptr, static EditBone *rna_Armature_edit_bone_new(bArmature *arm, ReportList *reports, const char *name) { - if (arm->edbo == NULL) { + if (arm->edbo == nullptr) { BKE_reportf(reports, RPT_ERROR, "Armature '%s' not in edit mode, cannot add an editbone", arm->id.name + 2); - return NULL; + return nullptr; } return ED_armature_ebone_add(arm, name); } @@ -126,8 +126,8 @@ static void rna_Armature_edit_bone_remove(bArmature *arm, ReportList *reports, PointerRNA *ebone_ptr) { - EditBone *ebone = ebone_ptr->data; - if (arm->edbo == NULL) { + EditBone *ebone = static_cast(ebone_ptr->data); + if (arm->edbo == nullptr) { BKE_reportf(reports, RPT_ERROR, "Armature '%s' not in edit mode, cannot remove an editbone", @@ -148,7 +148,7 @@ static void rna_Armature_edit_bone_remove(bArmature *arm, RNA_POINTER_INVALIDATE(ebone_ptr); } -static void rna_Armature_update_layers(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_Armature_update_layers(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { bArmature *arm = (bArmature *)ptr->owner_id; @@ -156,7 +156,7 @@ static void rna_Armature_update_layers(Main *UNUSED(bmain), Scene *UNUSED(scene) WM_main_add_notifier(NC_GEOM | ND_DATA, arm); } -static void rna_Armature_redraw_data(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_Armature_redraw_data(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { ID *id = ptr->owner_id; @@ -165,7 +165,7 @@ static void rna_Armature_redraw_data(Main *UNUSED(bmain), Scene *UNUSED(scene), } /* Unselect bones when hidden */ -static void rna_Bone_hide_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_Bone_hide_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { bArmature *arm = (bArmature *)ptr->owner_id; Bone *bone = (Bone *)ptr->data; @@ -179,7 +179,7 @@ static void rna_Bone_hide_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poin } /* called whenever a bone is renamed */ -static void rna_Bone_update_renamed(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_Bone_update_renamed(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { ID *id = ptr->owner_id; @@ -190,7 +190,7 @@ static void rna_Bone_update_renamed(Main *UNUSED(bmain), Scene *UNUSED(scene), P WM_main_add_notifier(NC_ANIMATION | ND_ANIMCHAN, id); } -static void rna_Bone_select_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_Bone_select_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) { ID *id = ptr->owner_id; @@ -250,13 +250,13 @@ static char *rna_Bone_path(const PointerRNA *ptr) static IDProperty **rna_Bone_idprops(PointerRNA *ptr) { - Bone *bone = ptr->data; + Bone *bone = static_cast(ptr->data); return &bone->prop; } static IDProperty **rna_EditBone_idprops(PointerRNA *ptr) { - EditBone *ebone = ptr->data; + EditBone *ebone = static_cast(ptr->data); return &ebone->prop; } @@ -291,7 +291,7 @@ static void rna_Bone_layer_set(PointerRNA *ptr, const bool *values) Bone *bone = (Bone *)ptr->data; rna_bone_layer_set(&bone->layer, values); - BKE_armature_refresh_layer_used(NULL, arm); + BKE_armature_refresh_layer_used(nullptr, arm); } /* TODO: remove the deprecation stubs. */ @@ -427,17 +427,17 @@ static PointerRNA rna_EditBone_parent_get(PointerRNA *ptr) static void rna_EditBone_parent_set(PointerRNA *ptr, PointerRNA value, - struct ReportList *UNUSED(reports)) + struct ReportList * /*reports*/) { EditBone *ebone = (EditBone *)(ptr->data); EditBone *pbone, *parbone = (EditBone *)value.data; - if (parbone == NULL) { + if (parbone == nullptr) { if (ebone->parent && !(ebone->parent->flag & BONE_ROOTSEL)) { ebone->parent->flag &= ~BONE_TIPSEL; } - ebone->parent = NULL; + ebone->parent = nullptr; ebone->flag &= ~BONE_CONNECTED; } else { @@ -501,7 +501,9 @@ static void rna_Bone_bbone_handle_update(Main *bmain, Scene *scene, PointerRNA * Bone *bone = (Bone *)ptr->data; /* Update all users of this armature after changing B-Bone handles. */ - for (Object *obt = bmain->objects.first; obt; obt = obt->id.next) { + for (Object *obt = static_cast(bmain->objects.first); obt; + obt = static_cast(obt->id.next)) + { if (obt->data == arm && obt->pose) { bPoseChannel *pchan = BKE_pose_channel_find_name(obt->pose, bone->name); @@ -523,26 +525,26 @@ static PointerRNA rna_EditBone_bbone_prev_get(PointerRNA *ptr) static void rna_EditBone_bbone_prev_set(PointerRNA *ptr, PointerRNA value, - struct ReportList *UNUSED(reports)) + struct ReportList * /*reports*/) { EditBone *ebone = (EditBone *)(ptr->data); EditBone *hbone = (EditBone *)value.data; /* Within the same armature? */ - if (hbone == NULL || value.owner_id == ptr->owner_id) { + if (hbone == nullptr || value.owner_id == ptr->owner_id) { ebone->bbone_prev = hbone; } } static void rna_Bone_bbone_prev_set(PointerRNA *ptr, PointerRNA value, - struct ReportList *UNUSED(reports)) + struct ReportList * /*reports*/) { Bone *bone = (Bone *)ptr->data; Bone *hbone = (Bone *)value.data; /* Within the same armature? */ - if (hbone == NULL || value.owner_id == ptr->owner_id) { + if (hbone == nullptr || value.owner_id == ptr->owner_id) { bone->bbone_prev = hbone; } } @@ -555,26 +557,26 @@ static PointerRNA rna_EditBone_bbone_next_get(PointerRNA *ptr) static void rna_EditBone_bbone_next_set(PointerRNA *ptr, PointerRNA value, - struct ReportList *UNUSED(reports)) + struct ReportList * /*reports*/) { EditBone *ebone = (EditBone *)(ptr->data); EditBone *hbone = (EditBone *)value.data; /* Within the same armature? */ - if (hbone == NULL || value.owner_id == ptr->owner_id) { + if (hbone == nullptr || value.owner_id == ptr->owner_id) { ebone->bbone_next = hbone; } } static void rna_Bone_bbone_next_set(PointerRNA *ptr, PointerRNA value, - struct ReportList *UNUSED(reports)) + struct ReportList * /*reports*/) { Bone *bone = (Bone *)ptr->data; Bone *hbone = (Bone *)value.data; /* Within the same armature? */ - if (hbone == NULL || value.owner_id == ptr->owner_id) { + if (hbone == nullptr || value.owner_id == ptr->owner_id) { bone->bbone_next = hbone; } } @@ -591,7 +593,7 @@ static void rna_Armature_editbone_transform_update(Main *bmain, Scene *scene, Po } /* update our children if necessary */ - for (child = arm->edbo->first; child; child = child->next) { + for (child = static_cast(arm->edbo->first); child; child = child->next) { if (child->parent == ebone && (child->flag & BONE_CONNECTED)) { copy_v3_v3(child->head, ebone->tail); } @@ -616,7 +618,7 @@ static void rna_Armature_bones_next(CollectionPropertyIterator *iter) internal->link = (Link *)bone->next; } else { - internal->link = NULL; + internal->link = nullptr; do { bone = bone->parent; @@ -627,7 +629,7 @@ static void rna_Armature_bones_next(CollectionPropertyIterator *iter) } while (bone); } - iter->valid = (internal->link != NULL); + iter->valid = (internal->link != nullptr); } /* not essential, but much faster than the default lookup function */ @@ -647,7 +649,7 @@ static int rna_Armature_bones_lookup_string(PointerRNA *ptr, const char *key, Po static bool rna_Armature_is_editmode_get(PointerRNA *ptr) { bArmature *arm = (bArmature *)ptr->owner_id; - return (arm->edbo != NULL); + return (arm->edbo != nullptr); } static void rna_Armature_transform(bArmature *arm, float mat[16]) @@ -703,14 +705,14 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone, bool is_editb /* Roll In/Out */ prop = RNA_def_property(srna, "bbone_rollin", PROP_FLOAT, PROP_ANGLE); - RNA_def_property_float_sdna(prop, NULL, "roll1"); + RNA_def_property_float_sdna(prop, nullptr, "roll1"); RNA_def_property_ui_range(prop, -M_PI * 2, M_PI * 2, 10, 2); RNA_def_property_ui_text( prop, "Roll In", "Roll offset for the start of the B-Bone, adjusts twist"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); prop = RNA_def_property(srna, "bbone_rollout", PROP_FLOAT, PROP_ANGLE); - RNA_def_property_float_sdna(prop, NULL, "roll2"); + RNA_def_property_float_sdna(prop, nullptr, "roll2"); RNA_def_property_ui_range(prop, -M_PI * 2, M_PI * 2, 10, 2); RNA_def_property_ui_text( prop, "Roll Out", "Roll offset for the end of the B-Bone, adjusts twist"); @@ -720,35 +722,35 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone, bool is_editb prop = RNA_def_property(srna, "use_endroll_as_inroll", PROP_BOOLEAN, PROP_NONE); RNA_def_property_ui_text( prop, "Inherit End Roll", "Add Roll Out of the Start Handle bone to the Roll In value"); - RNA_def_property_boolean_sdna(prop, NULL, "bbone_flag", BBONE_ADD_PARENT_END_ROLL); + RNA_def_property_boolean_sdna(prop, nullptr, "bbone_flag", BBONE_ADD_PARENT_END_ROLL); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_Armature_dependency_update"); } /* Curve X/Y Offsets */ prop = RNA_def_property(srna, "bbone_curveinx", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "curve_in_x"); + RNA_def_property_float_sdna(prop, nullptr, "curve_in_x"); RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text( prop, "In X", "X-axis handle offset for start of the B-Bone's curve, adjusts curvature"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); prop = RNA_def_property(srna, "bbone_curveinz", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "curve_in_z"); + RNA_def_property_float_sdna(prop, nullptr, "curve_in_z"); RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text( prop, "In Z", "Z-axis handle offset for start of the B-Bone's curve, adjusts curvature"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); prop = RNA_def_property(srna, "bbone_curveoutx", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "curve_out_x"); + RNA_def_property_float_sdna(prop, nullptr, "curve_out_x"); RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text( prop, "Out X", "X-axis handle offset for end of the B-Bone's curve, adjusts curvature"); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); prop = RNA_def_property(srna, "bbone_curveoutz", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "curve_out_z"); + RNA_def_property_float_sdna(prop, nullptr, "curve_out_z"); RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text( prop, "Out Z", "Z-axis handle offset for end of the B-Bone's curve, adjusts curvature"); @@ -756,7 +758,7 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone, bool is_editb /* Ease In/Out */ prop = RNA_def_property(srna, "bbone_easein", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "ease1"); + RNA_def_property_float_sdna(prop, nullptr, "ease1"); RNA_def_property_ui_range(prop, -5.0f, 5.0f, 1, 3); RNA_def_property_float_default(prop, 1.0f); RNA_def_property_ui_text(prop, "Ease In", "Length of first Bezier Handle (for B-Bones only)"); @@ -764,7 +766,7 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone, bool is_editb RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); prop = RNA_def_property(srna, "bbone_easeout", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "ease2"); + RNA_def_property_float_sdna(prop, nullptr, "ease2"); RNA_def_property_ui_range(prop, -5.0f, 5.0f, 1, 3); RNA_def_property_float_default(prop, 1.0f); RNA_def_property_ui_text(prop, "Ease Out", "Length of second Bezier Handle (for B-Bones only)"); @@ -775,13 +777,13 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone, bool is_editb prop = RNA_def_property(srna, "use_scale_easing", PROP_BOOLEAN, PROP_NONE); RNA_def_property_ui_text( prop, "Scale Easing", "Multiply the final easing values by the Scale In/Out Y factors"); - RNA_def_property_boolean_sdna(prop, NULL, "bbone_flag", BBONE_SCALE_EASING); + RNA_def_property_boolean_sdna(prop, nullptr, "bbone_flag", BBONE_SCALE_EASING); RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); } /* Scale In/Out */ prop = RNA_def_property(srna, "bbone_scalein", PROP_FLOAT, PROP_XYZ); - RNA_def_property_float_sdna(prop, NULL, "scale_in"); + RNA_def_property_float_sdna(prop, nullptr, "scale_in"); RNA_def_property_array(prop, 3); RNA_def_property_flag(prop, PROP_PROPORTIONAL); RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, 3); @@ -793,7 +795,7 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone, bool is_editb RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone); prop = RNA_def_property(srna, "bbone_scaleout", PROP_FLOAT, PROP_XYZ); - RNA_def_property_float_sdna(prop, NULL, "scale_out"); + RNA_def_property_float_sdna(prop, nullptr, "scale_out"); RNA_def_property_array(prop, 3); RNA_def_property_flag(prop, PROP_PROPORTIONAL); RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, 3); @@ -830,7 +832,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) 0, "Tangent", "Use the orientation of the specified bone to compute the handle, ignoring the location"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static const EnumPropertyItem prop_inherit_scale_mode[] = { @@ -858,21 +860,21 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) "None (Legacy)", "Ignore parent scaling without compensating for parent shear. " "Replicates the effect of disabling the original Inherit Scale checkbox"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; PropertyRNA *prop; /* strings */ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "name"); + RNA_def_property_string_sdna(prop, nullptr, "name"); RNA_def_property_ui_text(prop, "Name", ""); RNA_def_struct_name_property(srna, prop); if (editbone) { - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_EditBone_name_set"); + RNA_def_property_string_funcs(prop, nullptr, nullptr, "rna_EditBone_name_set"); } else { - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Bone_name_set"); + RNA_def_property_string_funcs(prop, nullptr, nullptr, "rna_Bone_name_set"); } RNA_def_property_update(prop, 0, "rna_Bone_update_renamed"); @@ -880,21 +882,21 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) /* flags */ prop = RNA_def_property(srna, "layers", PROP_BOOLEAN, PROP_LAYER_MEMBER); - RNA_def_property_boolean_sdna(prop, NULL, "layer", 1); + RNA_def_property_boolean_sdna(prop, nullptr, "layer", 1); RNA_def_property_array(prop, 32); if (editbone) { - RNA_def_property_boolean_funcs(prop, NULL, "rna_EditBone_layer_set"); + RNA_def_property_boolean_funcs(prop, nullptr, "rna_EditBone_layer_set"); } else { - RNA_def_property_boolean_funcs(prop, NULL, "rna_Bone_layer_set"); + RNA_def_property_boolean_funcs(prop, nullptr, "rna_Bone_layer_set"); } RNA_def_property_ui_text(prop, "Layers", "Layers bone exists in"); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); prop = RNA_def_property(srna, "use_connect", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_CONNECTED); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_CONNECTED); if (editbone) { - RNA_def_property_boolean_funcs(prop, NULL, "rna_EditBone_connected_set"); + RNA_def_property_boolean_funcs(prop, nullptr, "rna_EditBone_connected_set"); } else { RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -904,13 +906,13 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_update(prop, 0, "rna_Armature_update_data"); prop = RNA_def_property(srna, "use_inherit_rotation", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", BONE_HINGE); + RNA_def_property_boolean_negative_sdna(prop, nullptr, "flag", BONE_HINGE); RNA_def_property_ui_text( prop, "Inherit Rotation", "Bone inherits rotation or scale from parent bone"); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); prop = RNA_def_property(srna, "use_envelope_multiply", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_MULT_VG_ENV); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_MULT_VG_ENV); RNA_def_property_ui_text( prop, "Multiply Vertex Group with Envelope", @@ -918,14 +920,14 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_update(prop, 0, "rna_Armature_update_data"); prop = RNA_def_property(srna, "use_deform", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", BONE_NO_DEFORM); + RNA_def_property_boolean_negative_sdna(prop, nullptr, "flag", BONE_NO_DEFORM); RNA_def_property_ui_text(prop, "Deform", "Enable Bone to deform geometry"); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); prop = RNA_def_property(srna, "inherit_scale", PROP_ENUM, PROP_NONE); RNA_def_property_ui_text( prop, "Inherit Scale", "Specifies how the bone inherits scaling from the parent bone"); - RNA_def_property_enum_sdna(prop, NULL, "inherit_scale_mode"); + RNA_def_property_enum_sdna(prop, nullptr, "inherit_scale_mode"); RNA_def_property_enum_items(prop, prop_inherit_scale_mode); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); @@ -945,17 +947,17 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) prop = RNA_def_property(srna, "use_local_location", PROP_BOOLEAN, PROP_NONE); RNA_def_property_ui_text(prop, "Local Location", "Bone location is set in local space"); - RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", BONE_NO_LOCAL_LOCATION); + RNA_def_property_boolean_negative_sdna(prop, nullptr, "flag", BONE_NO_LOCAL_LOCATION); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); prop = RNA_def_property(srna, "use_relative_parent", PROP_BOOLEAN, PROP_NONE); RNA_def_property_ui_text( prop, "Relative Parenting", "Object children will use relative transform, like deform"); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_RELATIVE_PARENTING); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_RELATIVE_PARENTING); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); prop = RNA_def_property(srna, "show_wire", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_DRAWWIRE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_DRAWWIRE); RNA_def_property_ui_text( prop, "Display Wire", @@ -965,7 +967,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) /* XXX: use_cyclic_offset is deprecated in 2.5. May/may not return */ prop = RNA_def_property(srna, "use_cyclic_offset", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", BONE_NO_CYCLICOFFSET); + RNA_def_property_boolean_negative_sdna(prop, nullptr, "flag", BONE_NO_CYCLICOFFSET); RNA_def_property_ui_text( prop, "Cyclic Offset", @@ -974,7 +976,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_update(prop, 0, "rna_Armature_update_data"); prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_UNSELECTABLE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_UNSELECTABLE); RNA_def_property_ui_text(prop, "Selectable", "Bone is able to be selected"); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); @@ -987,13 +989,13 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) else { RNA_def_property_update(prop, 0, "rna_Armature_update_data"); } - RNA_def_property_float_sdna(prop, NULL, "dist"); + RNA_def_property_float_sdna(prop, nullptr, "dist"); RNA_def_property_range(prop, 0.0f, 1000.0f); RNA_def_property_ui_text( prop, "Envelope Deform Distance", "Bone deformation distance (for Envelope deform only)"); prop = RNA_def_property(srna, "envelope_weight", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "weight"); + RNA_def_property_float_sdna(prop, nullptr, "weight"); RNA_def_property_range(prop, 0.0f, 1000.0f); RNA_def_property_ui_text( prop, "Envelope Deform Weight", "Bone deformation weight (for Envelope deform only)"); @@ -1006,7 +1008,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) else { RNA_def_property_update(prop, 0, "rna_Armature_update_data"); } - RNA_def_property_float_sdna(prop, NULL, "rad_head"); + RNA_def_property_float_sdna(prop, nullptr, "rad_head"); /* XXX range is 0 to limit, where limit = 10000.0f * MAX2(1.0, view3d->grid); */ // RNA_def_property_range(prop, 0, 1000); RNA_def_property_ui_range(prop, 0.01, 100, 0.1, 3); @@ -1020,7 +1022,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) else { RNA_def_property_update(prop, 0, "rna_Armature_update_data"); } - RNA_def_property_float_sdna(prop, NULL, "rad_tail"); + RNA_def_property_float_sdna(prop, nullptr, "rad_tail"); /* XXX range is 0 to limit, where limit = 10000.0f * MAX2(1.0, view3d->grid); */ // RNA_def_property_range(prop, 0, 1000); RNA_def_property_ui_range(prop, 0.01, 100, 0.1, 3); @@ -1035,7 +1037,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) else { RNA_def_property_update(prop, 0, "rna_Armature_dependency_update"); } - RNA_def_property_int_sdna(prop, NULL, "segments"); + RNA_def_property_int_sdna(prop, nullptr, "segments"); RNA_def_property_range(prop, 1, 32); RNA_def_property_ui_text( prop, "B-Bone Segments", "Number of subdivisions of bone (for B-Bones only)"); @@ -1047,7 +1049,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) else { RNA_def_property_update(prop, 0, "rna_Armature_update_data"); } - RNA_def_property_float_sdna(prop, NULL, "xwidth"); + RNA_def_property_float_sdna(prop, nullptr, "xwidth"); RNA_def_property_ui_range(prop, 0.0f, 1000.0f, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text(prop, "B-Bone Display X Width", "B-Bone X size"); @@ -1058,13 +1060,13 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) else { RNA_def_property_update(prop, 0, "rna_Armature_update_data"); } - RNA_def_property_float_sdna(prop, NULL, "zwidth"); + RNA_def_property_float_sdna(prop, nullptr, "zwidth"); RNA_def_property_ui_range(prop, 0.0f, 1000.0f, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text(prop, "B-Bone Display Z Width", "B-Bone Z size"); /* B-Bone Start Handle settings. */ prop = RNA_def_property(srna, "bbone_handle_type_start", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "bbone_prev_type"); + RNA_def_property_enum_sdna(prop, nullptr, "bbone_prev_type"); RNA_def_property_enum_items(prop, prop_bbone_handle_type); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text( @@ -1072,15 +1074,15 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_update(prop, 0, "rna_Armature_dependency_update"); prop = RNA_def_property(srna, "bbone_custom_handle_start", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "bbone_prev"); + RNA_def_property_pointer_sdna(prop, nullptr, "bbone_prev"); RNA_def_property_struct_type(prop, editbone ? "EditBone" : "Bone"); if (editbone) { RNA_def_property_pointer_funcs( - prop, "rna_EditBone_bbone_prev_get", "rna_EditBone_bbone_prev_set", NULL, NULL); + prop, "rna_EditBone_bbone_prev_get", "rna_EditBone_bbone_prev_set", nullptr, nullptr); RNA_def_property_update(prop, 0, "rna_Armature_dependency_update"); } else { - RNA_def_property_pointer_funcs(prop, NULL, "rna_Bone_bbone_prev_set", NULL, NULL); + RNA_def_property_pointer_funcs(prop, nullptr, "rna_Bone_bbone_prev_set", nullptr, nullptr); RNA_def_property_update(prop, 0, "rna_Bone_bbone_handle_update"); } RNA_def_property_flag(prop, PROP_EDITABLE | PROP_PTR_NO_OWNERSHIP); @@ -1094,7 +1096,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) "Start Handle Scale", "Multiply B-Bone Scale In channels by the local scale values of the start handle. " "This is done after the Scale Easing option and isn't affected by it"); - RNA_def_property_boolean_sdna(prop, NULL, "bbone_prev_flag", BBONE_HANDLE_SCALE_X); + RNA_def_property_boolean_sdna(prop, nullptr, "bbone_prev_flag", BBONE_HANDLE_SCALE_X); RNA_def_property_array(prop, 3); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); @@ -1104,12 +1106,12 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) "Start Handle Ease", "Multiply the B-Bone Ease In channel by the local Y scale value of the start handle. " "This is done after the Scale Easing option and isn't affected by it"); - RNA_def_property_boolean_sdna(prop, NULL, "bbone_prev_flag", BBONE_HANDLE_SCALE_EASE); + RNA_def_property_boolean_sdna(prop, nullptr, "bbone_prev_flag", BBONE_HANDLE_SCALE_EASE); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); /* B-Bone End Handle settings. */ prop = RNA_def_property(srna, "bbone_handle_type_end", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "bbone_next_type"); + RNA_def_property_enum_sdna(prop, nullptr, "bbone_next_type"); RNA_def_property_enum_items(prop, prop_bbone_handle_type); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text( @@ -1117,15 +1119,15 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_update(prop, 0, "rna_Armature_dependency_update"); prop = RNA_def_property(srna, "bbone_custom_handle_end", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "bbone_next"); + RNA_def_property_pointer_sdna(prop, nullptr, "bbone_next"); RNA_def_property_struct_type(prop, editbone ? "EditBone" : "Bone"); if (editbone) { RNA_def_property_pointer_funcs( - prop, "rna_EditBone_bbone_next_get", "rna_EditBone_bbone_next_set", NULL, NULL); + prop, "rna_EditBone_bbone_next_get", "rna_EditBone_bbone_next_set", nullptr, nullptr); RNA_def_property_update(prop, 0, "rna_Armature_dependency_update"); } else { - RNA_def_property_pointer_funcs(prop, NULL, "rna_Bone_bbone_next_set", NULL, NULL); + RNA_def_property_pointer_funcs(prop, nullptr, "rna_Bone_bbone_next_set", nullptr, nullptr); RNA_def_property_update(prop, 0, "rna_Bone_bbone_handle_update"); } RNA_def_property_flag(prop, PROP_EDITABLE | PROP_PTR_NO_OWNERSHIP); @@ -1139,7 +1141,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) "End Handle Scale", "Multiply B-Bone Scale Out channels by the local scale values of the end handle. " "This is done after the Scale Easing option and isn't affected by it"); - RNA_def_property_boolean_sdna(prop, NULL, "bbone_next_flag", BBONE_HANDLE_SCALE_X); + RNA_def_property_boolean_sdna(prop, nullptr, "bbone_next_flag", BBONE_HANDLE_SCALE_X); RNA_def_property_array(prop, 3); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); @@ -1149,7 +1151,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) "End Handle Ease", "Multiply the B-Bone Ease Out channel by the local Y scale value of the end handle. " "This is done after the Scale Easing option and isn't affected by it"); - RNA_def_property_boolean_sdna(prop, NULL, "bbone_next_flag", BBONE_HANDLE_SCALE_EASE); + RNA_def_property_boolean_sdna(prop, nullptr, "bbone_next_flag", BBONE_HANDLE_SCALE_EASE); RNA_def_property_update(prop, 0, "rna_Armature_update_data"); RNA_define_lib_overridable(false); @@ -1161,7 +1163,7 @@ static void rna_def_bone(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - srna = RNA_def_struct(brna, "Bone", NULL); + srna = RNA_def_struct(brna, "Bone", nullptr); RNA_def_struct_ui_text(srna, "Bone", "Bone in an Armature data-block"); RNA_def_struct_ui_icon(srna, ICON_BONE_DATA); RNA_def_struct_path_func(srna, "rna_Bone_path"); @@ -1171,14 +1173,14 @@ static void rna_def_bone(BlenderRNA *brna) /* parent (pointer) */ prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Bone"); - RNA_def_property_pointer_sdna(prop, NULL, "parent"); + RNA_def_property_pointer_sdna(prop, nullptr, "parent"); RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP); RNA_def_property_ui_text(prop, "Parent", "Parent bone (in same Armature)"); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); /* children (collection) */ prop = RNA_def_property(srna, "children", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "childbase", NULL); + RNA_def_property_collection_sdna(prop, nullptr, "childbase", nullptr); RNA_def_property_struct_type(prop, "Bone"); RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP); RNA_def_property_ui_text(prop, "Children", "Bones which are children of this bone"); @@ -1191,7 +1193,7 @@ static void rna_def_bone(BlenderRNA *brna) /* XXX should we define this in PoseChannel wrapping code instead? * But PoseChannels directly get some of their flags from here... */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_HIDDEN_P); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_HIDDEN_P); RNA_def_property_ui_text( prop, "Hide", @@ -1200,7 +1202,7 @@ static void rna_def_bone(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Bone_hide_update"); prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_SELECTED); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_SELECTED); RNA_def_property_ui_text(prop, "Select", ""); RNA_def_property_clear_flag( prop, @@ -1208,33 +1210,33 @@ static void rna_def_bone(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Bone_select_update"); prop = RNA_def_property(srna, "select_head", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_ROOTSEL); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_ROOTSEL); RNA_def_property_ui_text(prop, "Select Head", ""); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); prop = RNA_def_property(srna, "select_tail", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_TIPSEL); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_TIPSEL); RNA_def_property_ui_text(prop, "Select Tail", ""); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); /* XXX better matrix descriptions possible (Arystan) */ prop = RNA_def_property(srna, "matrix", PROP_FLOAT, PROP_MATRIX); - RNA_def_property_float_sdna(prop, NULL, "bone_mat"); + RNA_def_property_float_sdna(prop, nullptr, "bone_mat"); RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_3x3); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Bone Matrix", "3x3 bone matrix"); prop = RNA_def_property(srna, "matrix_local", PROP_FLOAT, PROP_MATRIX); - RNA_def_property_float_sdna(prop, NULL, "arm_mat"); + RNA_def_property_float_sdna(prop, nullptr, "arm_mat"); RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( prop, "Bone Armature-Relative Matrix", "4x4 bone matrix relative to armature"); prop = RNA_def_property(srna, "tail", PROP_FLOAT, PROP_TRANSLATION); - RNA_def_property_float_sdna(prop, NULL, "tail"); + RNA_def_property_float_sdna(prop, nullptr, "tail"); RNA_def_property_array(prop, 3); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( @@ -1242,7 +1244,7 @@ static void rna_def_bone(BlenderRNA *brna) RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); prop = RNA_def_property(srna, "tail_local", PROP_FLOAT, PROP_TRANSLATION); - RNA_def_property_float_sdna(prop, NULL, "arm_tail"); + RNA_def_property_float_sdna(prop, nullptr, "arm_tail"); RNA_def_property_array(prop, 3); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( @@ -1250,7 +1252,7 @@ static void rna_def_bone(BlenderRNA *brna) RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); prop = RNA_def_property(srna, "head", PROP_FLOAT, PROP_TRANSLATION); - RNA_def_property_float_sdna(prop, NULL, "head"); + RNA_def_property_float_sdna(prop, nullptr, "head"); RNA_def_property_array(prop, 3); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( @@ -1258,7 +1260,7 @@ static void rna_def_bone(BlenderRNA *brna) RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); prop = RNA_def_property(srna, "head_local", PROP_FLOAT, PROP_TRANSLATION); - RNA_def_property_float_sdna(prop, NULL, "arm_head"); + RNA_def_property_float_sdna(prop, nullptr, "arm_head"); RNA_def_property_array(prop, 3); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( @@ -1266,7 +1268,7 @@ static void rna_def_bone(BlenderRNA *brna) RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); prop = RNA_def_property(srna, "length", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_float_sdna(prop, NULL, "length"); + RNA_def_property_float_sdna(prop, nullptr, "length"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Length", "Length of the bone"); @@ -1280,7 +1282,7 @@ static void rna_def_edit_bone(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - srna = RNA_def_struct(brna, "EditBone", NULL); + srna = RNA_def_struct(brna, "EditBone", nullptr); RNA_def_struct_sdna(srna, "EditBone"); RNA_def_struct_idprops_func(srna, "rna_EditBone_idprops"); RNA_def_struct_ui_text(srna, "Edit Bone", "Edit mode bone in an armature data-block"); @@ -1291,20 +1293,20 @@ static void rna_def_edit_bone(BlenderRNA *brna) prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "EditBone"); RNA_def_property_pointer_funcs( - prop, "rna_EditBone_parent_get", "rna_EditBone_parent_set", NULL, NULL); + prop, "rna_EditBone_parent_get", "rna_EditBone_parent_set", nullptr, nullptr); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Parent", "Parent edit bone (in same Armature)"); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); prop = RNA_def_property(srna, "roll", PROP_FLOAT, PROP_ANGLE); - RNA_def_property_float_sdna(prop, NULL, "roll"); + RNA_def_property_float_sdna(prop, nullptr, "roll"); RNA_def_property_ui_range(prop, -M_PI * 2, M_PI * 2, 10, 2); RNA_def_property_ui_text(prop, "Roll", "Bone rotation around head-tail axis"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_Armature_editbone_transform_update"); prop = RNA_def_property(srna, "head", PROP_FLOAT, PROP_TRANSLATION); - RNA_def_property_float_sdna(prop, NULL, "head"); + RNA_def_property_float_sdna(prop, nullptr, "head"); RNA_def_property_ui_range(prop, 0, FLT_MAX, 10, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Head", "Location of head end of the bone"); @@ -1312,7 +1314,7 @@ static void rna_def_edit_bone(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Armature_editbone_transform_update"); prop = RNA_def_property(srna, "tail", PROP_FLOAT, PROP_TRANSLATION); - RNA_def_property_float_sdna(prop, NULL, "tail"); + RNA_def_property_float_sdna(prop, nullptr, "tail"); RNA_def_property_ui_range(prop, 0, FLT_MAX, 10, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Tail", "Location of tail end of the bone"); @@ -1320,7 +1322,8 @@ static void rna_def_edit_bone(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Armature_editbone_transform_update"); prop = RNA_def_property(srna, "length", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_float_funcs(prop, "rna_EditBone_length_get", "rna_EditBone_length_set", NULL); + RNA_def_property_float_funcs( + prop, "rna_EditBone_length_get", "rna_EditBone_length_set", nullptr); RNA_def_property_range(prop, 0, FLT_MAX); RNA_def_property_ui_range(prop, 0, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text(prop, "Length", "Length of the bone. Changing moves the tail end"); @@ -1331,38 +1334,38 @@ static void rna_def_edit_bone(BlenderRNA *brna) rna_def_bone_curved_common(srna, false, true); prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_HIDDEN_A); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_HIDDEN_A); RNA_def_property_ui_text(prop, "Hide", "Bone is not visible when in Edit Mode"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); prop = RNA_def_property(srna, "lock", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_EDITMODE_LOCKED); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_EDITMODE_LOCKED); RNA_def_property_ui_text(prop, "Lock", "Bone is not able to be transformed when in Edit Mode"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_SELECTED); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_SELECTED); RNA_def_property_ui_text(prop, "Select", ""); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); prop = RNA_def_property(srna, "select_head", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_ROOTSEL); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_ROOTSEL); RNA_def_property_ui_text(prop, "Head Select", ""); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); prop = RNA_def_property(srna, "select_tail", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_TIPSEL); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_TIPSEL); RNA_def_property_ui_text(prop, "Tail Select", ""); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); /* calculated and read only, not actual data access */ prop = RNA_def_property(srna, "matrix", PROP_FLOAT, PROP_MATRIX); - // RNA_def_property_float_sdna(prop, NULL, ""); /* Doesn't access any real data. */ + // RNA_def_property_float_sdna(prop, nullptr, ""); /* Doesn't access any real data. */ RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4); // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_THICK_WRAP); /* no reference to original data */ @@ -1371,7 +1374,8 @@ static void rna_def_edit_bone(BlenderRNA *brna) "Edit Bone Matrix", "Matrix combining location and rotation of the bone (head position, direction and roll), " "in armature space (does not include/support bone's length/size)"); - RNA_def_property_float_funcs(prop, "rna_EditBone_matrix_get", "rna_EditBone_matrix_set", NULL); + RNA_def_property_float_funcs( + prop, "rna_EditBone_matrix_get", "rna_EditBone_matrix_set", nullptr); RNA_api_armature_edit_bone(srna); @@ -1388,16 +1392,16 @@ static void rna_def_armature_bones(BlenderRNA *brna, PropertyRNA *cprop) /* PropertyRNA *parm; */ RNA_def_property_srna(cprop, "ArmatureBones"); - srna = RNA_def_struct(brna, "ArmatureBones", NULL); + srna = RNA_def_struct(brna, "ArmatureBones", nullptr); RNA_def_struct_sdna(srna, "bArmature"); RNA_def_struct_ui_text(srna, "Armature Bones", "Collection of armature bones"); prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Bone"); - RNA_def_property_pointer_sdna(prop, NULL, "act_bone"); + RNA_def_property_pointer_sdna(prop, nullptr, "act_bone"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Active Bone", "Armature's active bone"); - RNA_def_property_pointer_funcs(prop, NULL, "rna_Armature_act_bone_set", NULL, NULL); + RNA_def_property_pointer_funcs(prop, nullptr, "rna_Armature_act_bone_set", nullptr, nullptr); RNA_def_property_update(prop, 0, "rna_Armature_update"); /* TODO: redraw. */ @@ -1414,17 +1418,18 @@ static void rna_def_armature_edit_bones(BlenderRNA *brna, PropertyRNA *cprop) PropertyRNA *parm; RNA_def_property_srna(cprop, "ArmatureEditBones"); - srna = RNA_def_struct(brna, "ArmatureEditBones", NULL); + srna = RNA_def_struct(brna, "ArmatureEditBones", nullptr); RNA_def_struct_sdna(srna, "bArmature"); RNA_def_struct_ui_text(srna, "Armature EditBones", "Collection of armature edit bones"); prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "EditBone"); - RNA_def_property_pointer_sdna(prop, NULL, "act_edbone"); + RNA_def_property_pointer_sdna(prop, nullptr, "act_edbone"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Active EditBone", "Armatures active edit bone"); RNA_def_property_update(prop, 0, "rna_Armature_update"); - RNA_def_property_pointer_funcs(prop, NULL, "rna_Armature_act_edit_bone_set", NULL, NULL); + RNA_def_property_pointer_funcs( + prop, nullptr, "rna_Armature_act_edit_bone_set", nullptr, nullptr); /* TODO: redraw. */ /* RNA_def_property_collection_active(prop, prop_act); */ @@ -1434,7 +1439,7 @@ static void rna_def_armature_edit_bones(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_flag(func, FUNC_USE_REPORTS); RNA_def_function_ui_description(func, "Add a new bone"); parm = RNA_def_string(func, "name", "Object", 0, "", "New name for the bone"); - RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); /* return type */ parm = RNA_def_pointer(func, "bone", "EditBone", "", "Newly created edit bone"); RNA_def_function_return(func, parm); @@ -1446,7 +1451,7 @@ static void rna_def_armature_edit_bones(BlenderRNA *brna, PropertyRNA *cprop) /* Target to remove. */ parm = RNA_def_pointer(func, "bone", "EditBone", "", "EditBone to remove"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, ParameterFlag(0)); } static void rna_def_armature(BlenderRNA *brna) @@ -1475,7 +1480,7 @@ static void rna_def_armature(BlenderRNA *brna) 0, "Wire", "Display bones as thin wires, showing subdivision and B-Splines"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static const EnumPropertyItem prop_pose_position_items[] = { {0, "POSE", 0, "Pose Position", "Show armature in posed state"}, @@ -1484,12 +1489,12 @@ static void rna_def_armature(BlenderRNA *brna) 0, "Rest Position", "Show Armature in binding pose state (no posing possible)"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static const EnumPropertyItem prop_relation_lines_items[] = { {0, "TAIL", 0, "Tail", "Draw the relationship line from the parent tail to the child head"}, {1, "HEAD", 0, "Head", "Draw the relationship line from the parent head to the child head"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; srna = RNA_def_struct(brna, "Armature", "ID"); @@ -1502,8 +1507,8 @@ static void rna_def_armature(BlenderRNA *brna) func = RNA_def_function(srna, "transform", "rna_Armature_transform"); RNA_def_function_ui_description(func, "Transform armature bones by a matrix"); - parm = RNA_def_float_matrix(func, "matrix", 4, 4, NULL, 0.0f, 0.0f, "", "Matrix", 0.0f, 0.0f); - RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float_matrix(func, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "", "Matrix", 0.0f, 0.0f); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); /* Animation Data */ rna_def_animdata_common(srna); @@ -1512,29 +1517,29 @@ static void rna_def_armature(BlenderRNA *brna) /* Collections */ prop = RNA_def_property(srna, "bones", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "bonebase", NULL); + RNA_def_property_collection_sdna(prop, nullptr, "bonebase", nullptr); RNA_def_property_collection_funcs(prop, - NULL, + nullptr, "rna_Armature_bones_next", - NULL, - NULL, - NULL, - NULL, + nullptr, + nullptr, + nullptr, + nullptr, "rna_Armature_bones_lookup_string", - NULL); + nullptr); RNA_def_property_struct_type(prop, "Bone"); RNA_def_property_ui_text(prop, "Bones", ""); rna_def_armature_bones(brna, prop); prop = RNA_def_property(srna, "edit_bones", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "edbo", NULL); + RNA_def_property_collection_sdna(prop, nullptr, "edbo", nullptr); RNA_def_property_struct_type(prop, "EditBone"); RNA_def_property_ui_text(prop, "Edit Bones", ""); rna_def_armature_edit_bones(brna, prop); /* Enum values */ prop = RNA_def_property(srna, "pose_position", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_bitflag_sdna(prop, nullptr, "flag"); RNA_def_property_enum_items(prop, prop_pose_position_items); RNA_def_property_ui_text( prop, "Pose Position", "Show armature in binding pose or final posed state"); @@ -1542,7 +1547,7 @@ static void rna_def_armature(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); prop = RNA_def_property(srna, "display_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "drawtype"); + RNA_def_property_enum_sdna(prop, nullptr, "drawtype"); RNA_def_property_enum_items(prop, prop_drawtype_items); RNA_def_property_ui_text(prop, "Display Type", ""); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); @@ -1551,16 +1556,16 @@ static void rna_def_armature(BlenderRNA *brna) /* Boolean values */ /* layer */ prop = RNA_def_property(srna, "layers", PROP_BOOLEAN, PROP_LAYER_MEMBER); - RNA_def_property_boolean_sdna(prop, NULL, "layer", 1); + RNA_def_property_boolean_sdna(prop, nullptr, "layer", 1); RNA_def_property_array(prop, 32); RNA_def_property_ui_text(prop, "Visible Layers", "Armature layer visibility"); - RNA_def_property_boolean_funcs(prop, NULL, "rna_Armature_layer_set"); + RNA_def_property_boolean_funcs(prop, nullptr, "rna_Armature_layer_set"); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Armature_update_layers"); RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); /* layer protection */ prop = RNA_def_property(srna, "layers_protected", PROP_BOOLEAN, PROP_LAYER); - RNA_def_property_boolean_sdna(prop, NULL, "layer_protected", 1); + RNA_def_property_boolean_sdna(prop, nullptr, "layer_protected", 1); RNA_def_property_array(prop, 32); RNA_def_property_ui_text(prop, "Layer Override Protection", @@ -1570,13 +1575,13 @@ static void rna_def_armature(BlenderRNA *brna) /* flag */ prop = RNA_def_property(srna, "show_axes", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ARM_DRAWAXES); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", ARM_DRAWAXES); RNA_def_property_ui_text(prop, "Display Axes", "Display bone axes"); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); prop = RNA_def_property(srna, "axes_position", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "axes_position"); + RNA_def_property_float_sdna(prop, nullptr, "axes_position"); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_ui_range(prop, 0.0, 1.0, 10, 1); RNA_def_property_ui_text(prop, @@ -1596,35 +1601,35 @@ static void rna_def_armature(BlenderRNA *brna) RNA_def_property_enum_funcs(prop, "rna_Armature_relation_line_position_get", "rna_Armature_relation_line_position_set", - /*item function*/ NULL); + /*item function*/ nullptr); RNA_define_verify_sdna(true); /* Restore default. */ prop = RNA_def_property(srna, "show_names", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ARM_DRAWNAMES); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", ARM_DRAWNAMES); RNA_def_property_ui_text(prop, "Display Names", "Display bone names"); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); prop = RNA_def_property(srna, "use_mirror_x", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ARM_MIRROR_EDIT); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", ARM_MIRROR_EDIT); RNA_def_property_ui_text( prop, "X-Axis Mirror", "Apply changes to matching bone on opposite side of X-Axis"); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); prop = RNA_def_property(srna, "show_bone_custom_shapes", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", ARM_NO_CUSTOM); + RNA_def_property_boolean_negative_sdna(prop, nullptr, "flag", ARM_NO_CUSTOM); RNA_def_property_ui_text( prop, "Display Custom Bone Shapes", "Display bones with their custom shapes"); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); prop = RNA_def_property(srna, "show_group_colors", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ARM_COL_CUSTOM); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", ARM_COL_CUSTOM); RNA_def_property_ui_text(prop, "Display Bone Group Colors", "Display bone group colors"); RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); prop = RNA_def_property(srna, "is_editmode", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs(prop, "rna_Armature_is_editmode_get", NULL); + RNA_def_property_boolean_funcs(prop, "rna_Armature_is_editmode_get", nullptr); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Is Editmode", "True when used in editmode"); diff --git a/source/blender/makesrna/intern/rna_armature_api.c b/source/blender/makesrna/intern/rna_armature_api.cc similarity index 93% rename from source/blender/makesrna/intern/rna_armature_api.c rename to source/blender/makesrna/intern/rna_armature_api.cc index 21448491fea4..9bf07221d7c7 100644 --- a/source/blender/makesrna/intern/rna_armature_api.c +++ b/source/blender/makesrna/intern/rna_armature_api.cc @@ -57,7 +57,7 @@ static void rna_Bone_convert_local_to_pose(Bone *bone, if (is_zero_m4(parent_pose_mat) || is_zero_m4(parent_arm_mat)) { /* No parent case. */ BKE_bone_parent_transform_calc_from_matrices( - bone->flag, bone->inherit_scale_mode, bone_arm_mat, NULL, NULL, &bpt); + bone->flag, bone->inherit_scale_mode, bone_arm_mat, nullptr, nullptr, &bpt); } else { invert_m4_m4(offs_bone, parent_arm_mat); @@ -107,8 +107,8 @@ void RNA_api_armature_edit_bone(StructRNA *srna) "Align the bone to a local-space roll so the Z axis " "points in the direction of the vector given"); parm = RNA_def_float_vector( - func, "vector", 3, NULL, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); - RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + func, "vector", 3, nullptr, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); } void RNA_api_bone(StructRNA *srna) @@ -121,14 +121,14 @@ void RNA_api_bone(StructRNA *srna) parm = RNA_def_float_vector_xyz(func, "point", 3, - NULL, + nullptr, -FLT_MAX, FLT_MAX, "Point", "Position in 3d space to evaluate", -FLT_MAX, FLT_MAX); - RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); /* return value */ parm = RNA_def_float( func, "factor", 0, -FLT_MAX, FLT_MAX, "Factor", "Envelope factor", -FLT_MAX, FLT_MAX); @@ -148,11 +148,11 @@ void RNA_api_bone(StructRNA *srna) parm = RNA_def_property(func, "matrix", PROP_FLOAT, PROP_MATRIX); RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4); RNA_def_property_ui_text(parm, "", "The matrix to transform"); - RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); parm = RNA_def_property(func, "matrix_local", PROP_FLOAT, PROP_MATRIX); RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4); RNA_def_property_ui_text(parm, "", "The custom rest matrix of this bone (Bone.matrix_local)"); - RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); parm = RNA_def_property(func, "parent_matrix", PROP_FLOAT, PROP_MATRIX); RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4); RNA_def_property_ui_text( @@ -173,7 +173,7 @@ void RNA_api_bone(StructRNA *srna) RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_property(func, "roll", PROP_FLOAT, PROP_NONE); RNA_def_property_ui_text(parm, "", "The roll of the bone"); - RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); parm = RNA_def_property(func, "result_matrix", PROP_FLOAT, PROP_MATRIX); RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_3x3); RNA_def_property_ui_text(parm, "", "The resulting orientation matrix"); From 4268ac0ed96bfacf0ad093be2e593208d64c8fcb Mon Sep 17 00:00:00 2001 From: Nate Rupsis Date: Tue, 20 Jun 2023 17:10:09 +0200 Subject: [PATCH 29/36] Animation: NLA Vertical Reorder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow NLA strips to be vertically re-ordered. NLA strips can now be dragged through other strips, and locked tracks. Co-authored-by: Sybren A. Stüvel Pull Request: https://projects.blender.org/blender/blender/pulls/107990 --- .../editors/transform/transform_convert_nla.c | 298 +++++++++++++++--- source/blender/makesdna/DNA_anim_types.h | 4 + 2 files changed, 257 insertions(+), 45 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index c02d1484cf19..250ed29d03f2 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -55,6 +55,11 @@ typedef struct TransDataNla { /** index of track that strip is currently in. */ int trackIndex; + + /** NOTE: This index is relative to the initial first track at the start of transforming and + * thus can be negative when the tracks list grows downward. */ + int signed_track_index; + /** handle-index: 0 for dummy entry, -1 for start, 1 for end, 2 for both ends. */ int handle; } TransDataNla; @@ -143,6 +148,146 @@ static float transdata_get_time_shuffle_offset(ListBase *trans_datas) return -offset_left < offset_right ? offset_left : offset_right; } +/** Assumes all of given trans_datas are part of the same ID. + * + * + * \param shuffle_direction: the direction the strip is traveling. 1 is towards the bottom + * of the stack, -1 is away from it. + * + * \param r_total_offset: The minimal total signed offset that results in valid strip track-moves + * for all strips from \a trans_datas. + * + * \returns true if \a r_total_offset results in a valid offset, false if no solution exists in the + * desired direction. + */ +static bool transdata_get_track_shuffle_offset_side(ListBase *trans_datas, + const int shuffle_direction, + int *r_total_offset) +{ + *r_total_offset = 0; + if (BLI_listbase_is_empty(trans_datas)) { + return false; + } + + LinkData *first_link = trans_datas->first; + TransDataNla *first_transdata = first_link->data; + AnimData *adt = BKE_animdata_from_id(first_transdata->id); + ListBase *tracks = &adt->nla_tracks; + + int offset; + do { + offset = 0; + + LISTBASE_FOREACH (LinkData *, link, trans_datas) { + TransDataNla *trans_data = (TransDataNla *)link->data; + + NlaTrack *dst_track = BLI_findlink(tracks, trans_data->trackIndex + *r_total_offset); + + /* Cannot keep moving strip in given track direction. No solution. */ + if (dst_track == NULL) { + return false; + } + + /* Shuffle only if track is locked or library override. */ + if (((dst_track->flag & NLATRACK_PROTECTED) == 0) && + !BKE_nlatrack_is_nonlocal_in_liboverride(trans_data->id, dst_track)) + { + continue; + } + + offset = shuffle_direction; + break; + } + + *r_total_offset += offset; + } while (offset != 0); + + return true; +} + +/** Assumes all of given trans_datas are part of the same ID. + * + * \param r_track_offset: The minimal total signed offset that results in valid strip track-moves + * for all strips from \a trans_datas. + * + * \returns true if \a r_track_offset results in a valid offset, false if no solution exists in + * either direction. + */ +static bool transdata_get_track_shuffle_offset(ListBase *trans_datas, int *r_track_offset) +{ + int offset_down = 0; + const bool down_valid = transdata_get_track_shuffle_offset_side(trans_datas, 1, &offset_down); + + int offset_up = 0; + const bool up_valid = transdata_get_track_shuffle_offset_side(trans_datas, -1, &offset_up); + + if (down_valid && up_valid) { + if (offset_down < abs(offset_up)) { + *r_track_offset = offset_down; + } + else { + *r_track_offset = offset_up; + } + } + else if (down_valid) { + *r_track_offset = offset_down; + } + else if (up_valid) { + *r_track_offset = offset_up; + } + + return down_valid || up_valid; +} + +/* -------------------------------------------------------------------- */ +/** \name Transform application to NLA strips + * \{ */ + +/** \} */ +static void nlatrack_truncate_temporary_tracks(bAnimContext *ac) +{ + short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA); + ListBase anim_data = {NULL, NULL}; + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + ListBase *nla_tracks = &ale->adt->nla_tracks; + + /** Remove top tracks that weren't necessary. */ + LISTBASE_FOREACH_BACKWARD_MUTABLE (NlaTrack *, track, nla_tracks) { + if (!(track->flag & NLATRACK_TEMPORARILY_ADDED)) { + break; + } + if (track->strips.first != NULL) { + break; + } + BKE_nlatrack_remove_and_free(nla_tracks, track, true); + } + + /** Remove bottom tracks that weren't necessary. */ + LISTBASE_FOREACH_MUTABLE (NlaTrack *, track, nla_tracks) { + /** Library override tracks are the first N tracks. They're never temporary and determine + * where we start removing temporaries.*/ + if ((track->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) { + continue; + } + if (!(track->flag & NLATRACK_TEMPORARILY_ADDED)) { + break; + } + if (track->strips.first != NULL) { + break; + } + BKE_nlatrack_remove_and_free(nla_tracks, track, true); + } + + /** Clear temporary flag. */ + LISTBASE_FOREACH_MUTABLE (NlaTrack *, track, nla_tracks) { + track->flag &= ~NLATRACK_TEMPORARILY_ADDED; + } + } + + ANIM_animdata_freelist(&anim_data); +} /* -------------------------------------------------------------------- */ /** \name Transform application to NLA strips * \{ */ @@ -400,6 +545,7 @@ static void createTransNlaData(bContext *C, TransInfo *t) tdn->oldTrack = tdn->nlt = nlt; tdn->strip = strip; tdn->trackIndex = BLI_findindex(&adt->nla_tracks, nlt); + tdn->signed_track_index = tdn->trackIndex; yval = (float)(tdn->trackIndex * NLACHANNEL_STEP(snla)); @@ -581,56 +727,83 @@ static void recalcData_nla(TransInfo *t) continue; } - delta_y1 = ((int)tdn->h1[1] / NLACHANNEL_STEP(snla) - tdn->trackIndex); - delta_y2 = ((int)tdn->h2[1] / NLACHANNEL_STEP(snla) - tdn->trackIndex); + delta_y1 = ((int)tdn->h1[1] / NLACHANNEL_STEP(snla) - tdn->signed_track_index); + delta_y2 = ((int)tdn->h2[1] / NLACHANNEL_STEP(snla) - tdn->signed_track_index); + /* Move strip into track in the requested direction. */ if (delta_y1 || delta_y2) { - NlaTrack *track; int delta = (delta_y2) ? delta_y2 : delta_y1; - int n; - /* Move in the requested direction, - * checking at each layer if there's space for strip to pass through, - * stopping on the last track available or that we're able to fit in. + AnimData *anim_data = BKE_animdata_from_id(tdn->id); + ListBase *nla_tracks = &anim_data->nla_tracks; + + NlaTrack *old_track = tdn->nlt; + NlaTrack *dst_track = NULL; + + /* Calculate the total new tracks needed + * + * Determine dst_track, which will end up being NULL, the last library override + * track, or a normal local track. The first two cases lead to delta_new_tracks!=0. + * The last case leads to delta_new_tracks==0. */ - if (delta > 0) { - for (track = tdn->nlt->next, n = 0; (track) && (n < delta); track = track->next, n++) { - /* check if space in this track for the strip */ - if (BKE_nlatrack_has_space(track, strip->start, strip->end) && - !BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) - { - /* move strip to this track */ - BKE_nlatrack_remove_strip(tdn->nlt, strip); - BKE_nlatrack_add_strip(track, strip, is_liboverride); - - tdn->nlt = track; - tdn->trackIndex++; - } - else { /* can't move any further */ - break; - } - } + int delta_new_tracks = delta; + + /* it's possible to drag a strip fast enough to make delta > |1|. We only want to process + * 1 track shift at a time. + */ + CLAMP(delta_new_tracks, -1, 1); + dst_track = old_track; + + while (delta_new_tracks < 0) { + dst_track = dst_track->prev; + delta_new_tracks++; } - else { - /* make delta 'positive' before using it, since we now know to go backwards */ - delta = -delta; - - for (track = tdn->nlt->prev, n = 0; (track) && (n < delta); track = track->prev, n++) { - /* check if space in this track for the strip */ - if (BKE_nlatrack_has_space(track, strip->start, strip->end) && - !BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) - { - /* move strip to this track */ - BKE_nlatrack_remove_strip(tdn->nlt, strip); - BKE_nlatrack_add_strip(track, strip, is_liboverride); - - tdn->nlt = track; - tdn->trackIndex--; - } - else { /* can't move any further */ - break; - } - } + + /* We assume all library tracks are grouped at the bottom of the nla stack. + * Thus, no need to check for them when moving tracks upward. + */ + while (delta_new_tracks > 0) { + dst_track = dst_track->next; + delta_new_tracks--; + } + + for (int i = 0; i < -delta_new_tracks; i++) { + NlaTrack *new_track = BKE_nlatrack_new(); + new_track->flag |= NLATRACK_TEMPORARILY_ADDED; + BKE_nlatrack_insert_before( + nla_tracks, (NlaTrack *)nla_tracks->first, new_track, is_liboverride); + dst_track = new_track; + } + + for (int i = 0; i < delta_new_tracks; i++) { + NlaTrack *new_track = BKE_nlatrack_new(); + new_track->flag |= NLATRACK_TEMPORARILY_ADDED; + + BKE_nlatrack_insert_after( + nla_tracks, (NlaTrack *)nla_tracks->last, new_track, is_liboverride); + dst_track = new_track; + } + + /* If the destination track is null, then we need to go to the last track. */ + if (dst_track == NULL) { + dst_track = old_track; + } + + /* Move strip from old_track to dst_track. */ + if (dst_track != old_track) { + BKE_nlatrack_remove_strip(old_track, strip); + BKE_nlastrips_add_strip_unsafe(&dst_track->strips, strip); + + tdn->nlt = dst_track; + tdn->signed_track_index += delta; + tdn->trackIndex = BLI_findindex(nla_tracks, dst_track); + } + + /* Ensure we set the target track as active. */ + BKE_nlatrack_set_active(nla_tracks, dst_track); + + if (tdn->nlt->flag & NLATRACK_PROTECTED) { + strip->flag |= NLASTRIP_FLAG_INVALID_LOCATION; } } @@ -699,6 +872,38 @@ static void nlastrip_shuffle_transformed(TransDataContainer *tc, TransDataNla *f LISTBASE_FOREACH (IDGroupedTransData *, group, &grouped_trans_datas) { ListBase *trans_datas = &group->trans_datas; + /* Apply vertical shuffle. */ + int minimum_track_offset = 0; + transdata_get_track_shuffle_offset(trans_datas, &minimum_track_offset); + if (minimum_track_offset != 0) { + ListBase *tracks = &BKE_animdata_from_id(group->id)->nla_tracks; + + LISTBASE_FOREACH (LinkData *, link, trans_datas) { + TransDataNla *trans_data = (TransDataNla *)link->data; + NlaTrack *dst_track = BLI_findlink(tracks, trans_data->trackIndex + minimum_track_offset); + + NlaStrip *strip = trans_data->strip; + if ((dst_track->flag & NLATRACK_PROTECTED) != 0) { + + BKE_nlatrack_remove_strip(trans_data->nlt, strip); + BKE_nlatrack_add_strip(dst_track, strip, false); + + trans_data->nlt = dst_track; + } + else { + /* if destination track is locked, we need revert strip to source track. */ + printf("Cannot moved. Target track '%s' is locked. \n", trans_data->nlt->name); + int old_track_index = BLI_findindex(tracks, trans_data->oldTrack); + NlaTrack *old_track = BLI_findlink(tracks, old_track_index); + + BKE_nlatrack_remove_strip(trans_data->nlt, strip); + BKE_nlastrips_add_strip_unsafe(&old_track->strips, strip); + + trans_data->nlt = old_track; + } + } + } + /* Apply horizontal shuffle. */ const float minimum_time_offset = transdata_get_time_shuffle_offset(trans_datas); LISTBASE_FOREACH (LinkData *, link, trans_datas) { @@ -735,7 +940,7 @@ static void special_aftertrans_update__nla(bContext *C, TransInfo *t) TransDataNla *first_trans_data = tc->custom.type.data; /* Shuffle transformed strips. */ - if (ELEM(t->mode, TFM_TRANSLATION)) { + if (ELEM(t->mode, TFM_TRANSLATION) && t->state != TRANS_CANCEL) { nlastrip_shuffle_transformed(tc, first_trans_data); } @@ -774,6 +979,9 @@ static void special_aftertrans_update__nla(bContext *C, TransInfo *t) /* free temp memory */ ANIM_animdata_freelist(&anim_data); + /* Truncate temporarily added tracks. */ + nlatrack_truncate_temporary_tracks(&ac); + /* Perform after-transform validation. */ ED_nla_postop_refresh(&ac); } diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index e83cfcf47e07..ff6346c70092 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -906,6 +906,10 @@ typedef enum eNlaTrack_Flag { * usually as result of tweaking being enabled (internal flag) */ NLATRACK_DISABLED = (1 << 10), + /** Marks tracks automatically added for space while dragging strips vertically. + * Internal flag that's only set during transform operator. */ + NLATRACK_TEMPORARILY_ADDED = (1 << 11), + /** This NLA track is added to an override ID, which means it is fully editable. * Irrelevant in case the owner ID is not an override. */ NLATRACK_OVERRIDELIBRARY_LOCAL = 1 << 16, From a6d2bb92ed3dfa47081934418032995bc7b14d07 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 20 Jun 2023 17:07:50 +0200 Subject: [PATCH 30/36] Cleanup: deduplicate code to create dummy nodes in simulation graph The same new utility functions can also be used for serial loop zones later on. --- .../intern/geometry_nodes_lazy_function.cc | 107 ++++++++---------- 1 file changed, 50 insertions(+), 57 deletions(-) diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index ec6131b393ea..c54145733590 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1528,13 +1528,16 @@ struct GeometryNodesLazyFunctionGraphBuilder { lf::Node *lf_zone_input_node = nullptr; lf::Node *lf_main_input_usage_node = nullptr; if (zone.input_node != nullptr) { - lf_zone_input_node = &this->build_simulation_zone_input_node(zone, lf_graph); - lf_main_input_usage_node = &this->build_simulation_zone_input_usage_node(zone, lf_graph); + lf_zone_input_node = &this->build_dummy_node_for_sockets( + "Zone Input", {}, zone.input_node->input_sockets().drop_back(1), lf_graph); + lf_main_input_usage_node = &this->build_dummy_node_for_socket_usages( + "Input Usages", zone.input_node->input_sockets().drop_back(1), {}, lf_graph); } lf::Node &lf_border_link_input_node = this->build_zone_border_links_input_node(zone, lf_graph); - lf::Node &lf_zone_output_node = this->build_simulation_zone_output_node(zone, lf_graph); - lf::Node &lf_main_output_usage_node = this->build_simulation_zone_output_usage_node(zone, - lf_graph); + lf::Node &lf_zone_output_node = this->build_dummy_node_for_sockets( + "Zone Output", zone.output_node->output_sockets().drop_back(1), {}, lf_graph); + lf::Node &lf_main_output_usage_node = this->build_dummy_node_for_socket_usages( + "Output Usages", {}, zone.output_node->output_sockets().drop_back(1), lf_graph); lf::Node &lf_border_link_usage_node = this->build_border_link_input_usage_node(zone, lf_graph); lf::Node &lf_simulation_usage_node = [&]() -> lf::Node & { @@ -1640,58 +1643,6 @@ struct GeometryNodesLazyFunctionGraphBuilder { // std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; } - lf::DummyNode &build_simulation_zone_input_node(const bNodeTreeZone &zone, lf::Graph &lf_graph) - { - Vector zone_input_types; - auto &debug_info = scope_.construct(); - debug_info.name = "Zone Input"; - for (const bNodeSocket *socket : zone.input_node->input_sockets().drop_back(1)) { - zone_input_types.append(socket->typeinfo->geometry_nodes_cpp_type); - debug_info.output_names.append(socket->identifier); - } - lf::DummyNode &node = lf_graph.add_dummy({}, zone_input_types, &debug_info); - return node; - } - - lf::DummyNode &build_simulation_zone_output_node(const bNodeTreeZone &zone, lf::Graph &lf_graph) - { - auto &debug_info = scope_.construct(); - debug_info.name = "Zone Output"; - Vector zone_output_types; - for (const bNodeSocket *socket : zone.output_node->output_sockets().drop_back(1)) { - zone_output_types.append(socket->typeinfo->geometry_nodes_cpp_type); - debug_info.input_names.append(socket->identifier); - } - lf::DummyNode &node = lf_graph.add_dummy(zone_output_types, {}, &debug_info); - return node; - } - - lf::DummyNode &build_simulation_zone_input_usage_node(const bNodeTreeZone &zone, - lf::Graph &lf_graph) - { - auto &debug_info = scope_.construct(); - debug_info.name = "Input Usages"; - Vector types; - types.append_n_times(&CPPType::get(), - zone.input_node->input_sockets().drop_back(1).size()); - debug_info.input_names.append_n_times("Usage", types.size()); - lf::DummyNode &node = lf_graph.add_dummy(types, {}, &debug_info); - return node; - } - - lf::DummyNode &build_simulation_zone_output_usage_node(const bNodeTreeZone &zone, - lf::Graph &lf_graph) - { - auto &debug_info = scope_.construct(); - debug_info.name = "Output Usages"; - Vector types; - types.append_n_times(&CPPType::get(), - zone.output_node->output_sockets().drop_back(1).size()); - debug_info.output_names.append_n_times("Usage", types.size()); - lf::DummyNode &node = lf_graph.add_dummy({}, types, &debug_info); - return node; - } - lf::DummyNode &build_zone_border_links_input_node(const bNodeTreeZone &zone, lf::Graph &lf_graph) { auto &debug_info = scope_.construct(); @@ -2856,6 +2807,48 @@ struct GeometryNodesLazyFunctionGraphBuilder { } } + lf::DummyNode &build_dummy_node_for_sockets(const StringRef name, + const Span input_bsockets, + const Span output_bsockets, + lf::Graph &lf_graph) + { + auto &debug_info = scope_.construct(); + debug_info.name = name; + Vector input_types; + Vector output_types; + for (const bNodeSocket *bsocket : input_bsockets) { + input_types.append(bsocket->typeinfo->geometry_nodes_cpp_type); + debug_info.input_names.append(bsocket->identifier); + } + for (const bNodeSocket *bsocket : output_bsockets) { + output_types.append(bsocket->typeinfo->geometry_nodes_cpp_type); + debug_info.output_names.append(bsocket->identifier); + } + lf::DummyNode &node = lf_graph.add_dummy(input_types, output_types, &debug_info); + return node; + } + + lf::DummyNode &build_dummy_node_for_socket_usages( + const StringRef name, + const Span input_bsockets, + const Span output_bsockets, + lf::Graph &lf_graph) + { + auto &debug_info = scope_.construct(); + debug_info.name = name; + const CPPType &bool_cpp_type = CPPType::get(); + Vector input_types(input_bsockets.size(), &bool_cpp_type); + Vector output_types(output_bsockets.size(), &bool_cpp_type); + for (const bNodeSocket *bsocket : input_bsockets) { + debug_info.input_names.append(bsocket->identifier); + } + for (const bNodeSocket *bsocket : output_bsockets) { + debug_info.output_names.append(bsocket->identifier); + } + lf::DummyNode &node = lf_graph.add_dummy(input_types, output_types, &debug_info); + return node; + } + struct TypeWithLinks { const CPPType *type; Vector links; From 167c5c6b5318f495ea96321a258022236d6eb0b6 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 20 Jun 2023 17:16:18 +0200 Subject: [PATCH 31/36] Fix UI views not refreshing properly in popovers No UI views are used inside popups in the main branch yet, #104831 does this so this issue became apparent. For example tree-view items would not change their collapsed state. UI views require the block to be attached to a region when drawing, so that the matching to previous versions of the tree works. Before this commit the block was attached just after. The `BLI_findindex()` check before attaching the block was already done for other kinds of popups, so this is more in sync now. --- .../interface/interface_region_popover.cc | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/interface/interface_region_popover.cc b/source/blender/editors/interface/interface_region_popover.cc index 9a2967a5cc20..46d0154fd706 100644 --- a/source/blender/editors/interface/interface_region_popover.cc +++ b/source/blender/editors/interface/interface_region_popover.cc @@ -75,13 +75,21 @@ struct uiPopover { #endif }; -static void ui_popover_create_block(bContext *C, uiPopover *pup, wmOperatorCallContext opcontext) +/** + * \param region: Optional, the region the block will be placed in. Must be set if the popover is + * supposed to support refreshing. + */ +static void ui_popover_create_block(bContext *C, + ARegion *region, + uiPopover *pup, + wmOperatorCallContext opcontext) { BLI_assert(pup->ui_size_x != 0); const uiStyle *style = UI_style_get_dpi(); - pup->block = UI_block_begin(C, nullptr, __func__, UI_EMBOSS); + pup->block = UI_block_begin(C, region, __func__, UI_EMBOSS); + UI_block_flag_enable(pup->block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER); #ifdef USE_UI_POPOVER_ONCE if (pup->is_once) { @@ -109,7 +117,7 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v /* Create UI block and layout now if it wasn't done between begin/end. */ if (!pup->layout) { - ui_popover_create_block(C, pup, WM_OP_INVOKE_REGION_WIN); + ui_popover_create_block(C, nullptr, pup, WM_OP_INVOKE_REGION_WIN); if (pup->menu_func) { pup->block->handle = handle; @@ -124,7 +132,12 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v uiBlock *block = pup->block; int width, height; - UI_block_region_set(block, handle->region); + /* in some cases we create the block before the region, + * so we set it delayed here if necessary */ + if (BLI_findindex(&handle->region->uiblocks, block) == -1) { + UI_block_region_set(block, handle->region); + } + UI_block_layout_resolve(block, &width, &height); UI_block_direction_set(block, UI_DIR_DOWN | UI_DIR_CENTER_X); @@ -348,7 +361,7 @@ uiPopover *UI_popover_begin(bContext *C, int ui_menu_width, bool from_active_but pup->butregion = butregion; /* Operator context default same as menus, change if needed. */ - ui_popover_create_block(C, pup, WM_OP_EXEC_REGION_WIN); + ui_popover_create_block(C, nullptr, pup, WM_OP_EXEC_REGION_WIN); /* create in advance so we can let buttons point to retval already */ pup->block->handle = MEM_cnew(__func__); From 5b086fdc747111a9a07a118e874f1bbcd5c8587f Mon Sep 17 00:00:00 2001 From: Iliya Katueshenock Date: Tue, 20 Jun 2023 18:38:28 +0200 Subject: [PATCH 32/36] Cleanup: Preparation to move versioning file 2.6 to C++ Changes is a using `LISTBASE_FOREACH` instead of manual for-loops. Also decrease variable scope in some places. See #103343 Pull Request: https://projects.blender.org/blender/blender/pulls/108995 --- .../blenloader/intern/versioning_260.c | 2794 ++++++++--------- 1 file changed, 1259 insertions(+), 1535 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index fa12bb4c5b8d..5180fa04cd4f 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -79,22 +79,18 @@ static void do_versions_nodetree_image_default_alpha_output(bNodeTree *ntree) { - bNode *node; - bNodeSocket *sock; - - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS)) { /* default Image output value should have 0 alpha */ - sock = node->outputs.first; - ((bNodeSocketValueRGBA *)(sock->default_value))->value[3] = 0.0f; + bNodeSocket *sock = node->outputs.first; + ((bNodeSocketValueRGBA *)sock->default_value)->value[3] = 0.0f; } } } static void do_versions_nodetree_convert_angle(bNodeTree *ntree) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == CMP_NODE_ROTATE) { /* Convert degrees to radians. */ bNodeSocket *sock = ((bNodeSocket *)node->inputs.first)->next; @@ -221,26 +217,22 @@ static void do_versions_image_settings_2_60(Scene *sce) /* socket use flags were only temporary before */ static void do_versions_nodetree_socket_use_flags_2_62(bNodeTree *ntree) { - bNode *node; - bNodeSocket *sock; - bNodeLink *link; - - for (node = ntree->nodes.first; node; node = node->next) { - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { sock->flag &= ~SOCK_IS_LINKED; } - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { sock->flag &= ~SOCK_IS_LINKED; } } - for (sock = ntree->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { sock->flag &= ~SOCK_IS_LINKED; } - for (sock = ntree->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { sock->flag &= ~SOCK_IS_LINKED; } - for (link = ntree->links.first; link; link = link->next) { + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { link->fromsock->flag |= SOCK_IS_LINKED; link->tosock->flag |= SOCK_IS_LINKED; } @@ -248,16 +240,14 @@ static void do_versions_nodetree_socket_use_flags_2_62(bNodeTree *ntree) static void do_versions_nodetree_multi_file_output_format_2_62_1(Scene *sce, bNodeTree *ntree) { - bNode *node; - - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == CMP_NODE_OUTPUT_FILE) { /* previous CMP_NODE_OUTPUT_FILE nodes get converted to multi-file outputs */ NodeImageFile *old_data = node->storage; NodeImageMultiFile *nimf = MEM_callocN(sizeof(NodeImageMultiFile), "node image multi file"); bNodeSocket *old_image = BLI_findlink(&node->inputs, 0); bNodeSocket *old_z = BLI_findlink(&node->inputs, 1); - bNodeSocket *sock; + char filename[FILE_MAXFILE]; /* ugly, need to remove the old inputs list to avoid bad pointer @@ -292,7 +282,7 @@ static void do_versions_nodetree_multi_file_output_format_2_62_1(Scene *sce, bNo nimf->format.imtype = R_IMF_IMTYPE_MULTILAYER; SNPRINTF(sockpath, "%s_Image", filename); - sock = ntreeCompositOutputFileAddSocket(ntree, node, sockpath, &nimf->format); + bNodeSocket *sock = ntreeCompositOutputFileAddSocket(ntree, node, sockpath, &nimf->format); /* XXX later do_versions copies path from socket name, need to set this explicitly */ STRNCPY(sock->name, sockpath); if (old_image->link) { @@ -310,7 +300,7 @@ static void do_versions_nodetree_multi_file_output_format_2_62_1(Scene *sce, bNo } } else { - sock = ntreeCompositOutputFileAddSocket(ntree, node, filename, &nimf->format); + bNodeSocket *sock = ntreeCompositOutputFileAddSocket(ntree, node, filename, &nimf->format); /* XXX later do_versions copies path from socket name, need to set this explicitly */ STRNCPY(sock->name, filename); if (old_image->link) { @@ -327,7 +317,6 @@ static void do_versions_nodetree_multi_file_output_format_2_62_1(Scene *sce, bNo } else if (node->type == CMP_NODE_OUTPUT_MULTI_FILE__DEPRECATED) { NodeImageMultiFile *nimf = node->storage; - bNodeSocket *sock; /* CMP_NODE_OUTPUT_MULTI_FILE has been re-declared as CMP_NODE_OUTPUT_FILE */ node->type = CMP_NODE_OUTPUT_FILE; @@ -338,7 +327,7 @@ static void do_versions_nodetree_multi_file_output_format_2_62_1(Scene *sce, bNo } /* transfer render format toggle to node format toggle */ - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { NodeImageMultiFileSocket *simf = sock->storage; simf->use_node_format = simf->use_render_format; } @@ -352,17 +341,12 @@ static void do_versions_nodetree_multi_file_output_format_2_62_1(Scene *sce, bNo /* blue and red are swapped pre 2.62.1, be sane (red == red) now! */ static void do_versions_mesh_mloopcol_swap_2_62_1(Mesh *me) { - CustomDataLayer *layer; - MLoopCol *mloopcol; - int a; - int i; - - for (a = 0; a < me->ldata.totlayer; a++) { - layer = &me->ldata.layers[a]; + for (int a = 0; a < me->ldata.totlayer; a++) { + CustomDataLayer *layer = &me->ldata.layers[a]; if (layer->type == CD_PROP_BYTE_COLOR) { - mloopcol = (MLoopCol *)layer->data; - for (i = 0; i < me->totloop; i++, mloopcol++) { + MLoopCol *mloopcol = layer->data; + for (int i = 0; i < me->totloop; i++, mloopcol++) { SWAP(uchar, mloopcol->r, mloopcol->b); } } @@ -371,12 +355,9 @@ static void do_versions_mesh_mloopcol_swap_2_62_1(Mesh *me) static void do_versions_nodetree_multi_file_output_path_2_63_1(bNodeTree *ntree) { - bNode *node; - - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == CMP_NODE_OUTPUT_FILE) { - bNodeSocket *sock; - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { NodeImageMultiFileSocket *input = sock->storage; /* input file path is stored in dedicated struct now instead socket name */ STRNCPY(input->path, sock->name); @@ -387,12 +368,9 @@ static void do_versions_nodetree_multi_file_output_path_2_63_1(bNodeTree *ntree) static void do_versions_nodetree_file_output_layers_2_64_5(bNodeTree *ntree) { - bNode *node; - - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == CMP_NODE_OUTPUT_FILE) { - bNodeSocket *sock; - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { NodeImageMultiFileSocket *input = sock->storage; /* Multi-layer names are stored as separate strings now, @@ -409,12 +387,9 @@ static void do_versions_nodetree_file_output_layers_2_64_5(bNodeTree *ntree) static void do_versions_nodetree_image_layer_2_64_5(bNodeTree *ntree) { - bNode *node; - - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == CMP_NODE_IMAGE) { - bNodeSocket *sock; - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { NodeImageLayer *output = MEM_callocN(sizeof(NodeImageLayer), "node image layer"); /* take pass index both from current storage ptr (actually an int) */ @@ -429,13 +404,11 @@ static void do_versions_nodetree_image_layer_2_64_5(bNodeTree *ntree) static void do_versions_nodetree_frame_2_64_6(bNodeTree *ntree) { - bNode *node; - - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == NODE_FRAME) { /* initialize frame node storage data */ if (node->storage == NULL) { - NodeFrame *data = (NodeFrame *)MEM_callocN(sizeof(NodeFrame), "frame node storage"); + NodeFrame *data = MEM_callocN(sizeof(NodeFrame), "frame node storage"); node->storage = data; /* copy current flags */ @@ -452,9 +425,7 @@ static void do_versions_nodetree_frame_2_64_6(bNodeTree *ntree) static void do_versions_affine_tracker_track(MovieTrackingTrack *track) { - int i; - - for (i = 0; i < track->markersnr; i++) { + for (int i = 0; i < track->markersnr; i++) { MovieTrackingMarker *marker = &track->markers[i]; if (is_zero_v2(marker->pattern_corners[0]) && is_zero_v2(marker->pattern_corners[1]) && @@ -543,26 +514,23 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int UNUSED(is_gro { /* initialize node tree type idname */ { - bNode *node; - bNodeSocket *sock; - ntree->typeinfo = NULL; /* tree type idname */ switch (ntree->type) { case NTREE_COMPOSIT: - STRNCPY(ntree->idname, "CompositorNodeTree"); + strcpy(ntree->idname, "CompositorNodeTree"); break; case NTREE_SHADER: - STRNCPY(ntree->idname, "ShaderNodeTree"); + strcpy(ntree->idname, "ShaderNodeTree"); break; case NTREE_TEXTURE: - STRNCPY(ntree->idname, "TextureNodeTree"); + strcpy(ntree->idname, "TextureNodeTree"); break; } /* node type idname */ - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { BLI_strncpy( node->idname, node_get_static_idname(node->type, ntree->type), sizeof(node->idname)); @@ -570,80 +538,53 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int UNUSED(is_gro node->flag |= NODE_INIT; /* sockets idname */ - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { STRNCPY(sock->idname, node_socket_get_static_idname(sock)); } - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { STRNCPY(sock->idname, node_socket_get_static_idname(sock)); } } /* tree sockets idname */ - for (sock = ntree->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { STRNCPY(sock->idname, node_socket_get_static_idname(sock)); } - for (sock = ntree->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { STRNCPY(sock->idname, node_socket_get_static_idname(sock)); } } /* initialize socket in_out values */ - { - bNode *node; - bNodeSocket *sock; - - for (node = ntree->nodes.first; node; node = node->next) { - for (sock = node->inputs.first; sock; sock = sock->next) { - sock->in_out = SOCK_IN; - } - for (sock = node->outputs.first; sock; sock = sock->next) { - sock->in_out = SOCK_OUT; - } - } - for (sock = ntree->inputs.first; sock; sock = sock->next) { - sock->in_out = SOCK_IN; - } - for (sock = ntree->outputs.first; sock; sock = sock->next) { - sock->in_out = SOCK_OUT; - } - } + {LISTBASE_FOREACH (bNode *, node, &ntree->nodes){ + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs){sock->in_out = SOCK_IN; +} +LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { + sock->in_out = SOCK_OUT; +} +} +LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + sock->in_out = SOCK_IN; +} +LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + sock->in_out = SOCK_OUT; +} +} - /* initialize socket identifier strings */ - { - bNode *node; - bNodeSocket *sock; - - for (node = ntree->nodes.first; node; node = node->next) { - for (sock = node->inputs.first; sock; sock = sock->next) { - STRNCPY(sock->identifier, sock->name); - BLI_uniquename(&node->inputs, - sock, - "socket", - '.', - offsetof(bNodeSocket, identifier), - sizeof(sock->identifier)); - } - for (sock = node->outputs.first; sock; sock = sock->next) { - STRNCPY(sock->identifier, sock->name); - BLI_uniquename(&node->outputs, - sock, - "socket", - '.', - offsetof(bNodeSocket, identifier), - sizeof(sock->identifier)); - } - } - for (sock = ntree->inputs.first; sock; sock = sock->next) { +/* initialize socket identifier strings */ +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { STRNCPY(sock->identifier, sock->name); - BLI_uniquename(&ntree->inputs, + BLI_uniquename(&node->inputs, sock, "socket", '.', offsetof(bNodeSocket, identifier), sizeof(sock->identifier)); } - for (sock = ntree->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { STRNCPY(sock->identifier, sock->name); - BLI_uniquename(&ntree->outputs, + BLI_uniquename(&node->outputs, sock, "socket", '.', @@ -651,6 +592,25 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int UNUSED(is_gro sizeof(sock->identifier)); } } + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + STRNCPY(sock->identifier, sock->name); + BLI_uniquename(&ntree->inputs, + sock, + "socket", + '.', + offsetof(bNodeSocket, identifier), + sizeof(sock->identifier)); + } + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + STRNCPY(sock->identifier, sock->name); + BLI_uniquename(&ntree->outputs, + sock, + "socket", + '.', + offsetof(bNodeSocket, identifier), + sizeof(sock->identifier)); + } +} } static bool seq_colorbalance_update_cb(Sequence *seq, void *UNUSED(user_data)) @@ -658,11 +618,8 @@ static bool seq_colorbalance_update_cb(Sequence *seq, void *UNUSED(user_data)) Strip *strip = seq->strip; if (strip && strip->color_balance) { - SequenceModifierData *smd; - ColorBalanceModifierData *cbmd; - - smd = SEQ_modifier_new(seq, NULL, seqModifierType_ColorBalance); - cbmd = (ColorBalanceModifierData *)smd; + SequenceModifierData *smd = SEQ_modifier_new(seq, NULL, seqModifierType_ColorBalance); + ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd; cbmd->color_balance = *strip->color_balance; @@ -704,676 +661,603 @@ static bool seq_set_wipe_angle_cb(Sequence *seq, void *UNUSED(user_data)) void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) { if (bmain->versionfile < 260) { - { - /* set default alpha value of Image outputs in image and render layer nodes to 0 */ - Scene *sce; - bNodeTree *ntree; - - for (sce = bmain->scenes.first; sce; sce = sce->id.next) { - /* there are files with invalid audio_channels value, the real cause - * is unknown, but we fix it here anyway to avoid crashes */ - if (sce->r.ffcodecdata.audio_channels == 0) { - sce->r.ffcodecdata.audio_channels = 2; - } - - if (sce->nodetree) { - do_versions_nodetree_image_default_alpha_output(sce->nodetree); - } - } - - for (ntree = bmain->nodetrees.first; ntree; ntree = ntree->id.next) { - do_versions_nodetree_image_default_alpha_output(ntree); - } - } - - { - /* support old particle dupliobject rotation settings */ - ParticleSettings *part; - - for (part = bmain->particles.first; part; part = part->id.next) { - if (ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) { - part->draw |= PART_DRAW_ROTATE_OB; - - if (part->rotmode == 0) { - part->rotmode = PART_ROT_VEL; - } - } - } - } + {/* set default alpha value of Image outputs in image and render layer nodes to 0 */ + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes){ + /* there are files with invalid audio_channels value, the real cause + * is unknown, but we fix it here anyway to avoid crashes */ + if (sce->r.ffcodecdata.audio_channels == 0){sce->r.ffcodecdata.audio_channels = 2; } - if (!MAIN_VERSION_ATLEAST(bmain, 260, 1)) { - Object *ob; + if (sce->nodetree) { + do_versions_nodetree_image_default_alpha_output(sce->nodetree); + } +} - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ob->collision_boundtype = ob->boundtype; - } +LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + do_versions_nodetree_image_default_alpha_output(ntree); +} +} - { - Camera *cam; - for (cam = bmain->cameras.first; cam; cam = cam->id.next) { - if (cam->sensor_x < 0.01f) { - cam->sensor_x = DEFAULT_SENSOR_WIDTH; - } +{ + /* support old particle dupliobject rotation settings */ + LISTBASE_FOREACH (ParticleSettings *, part, &bmain->particles) { + if (ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) { + part->draw |= PART_DRAW_ROTATE_OB; - if (cam->sensor_y < 0.01f) { - cam->sensor_y = DEFAULT_SENSOR_HEIGHT; - } + if (part->rotmode == 0) { + part->rotmode = PART_ROT_VEL; } } } +} +} - if (!MAIN_VERSION_ATLEAST(bmain, 260, 2)) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_SHADER) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == SH_NODE_MAPPING) { - TexMapping *tex_mapping; - - tex_mapping = node->storage; - tex_mapping->projx = PROJ_X; - tex_mapping->projy = PROJ_Y; - tex_mapping->projz = PROJ_Z; - } - } - } - } - FOREACH_NODETREE_END; +if (!MAIN_VERSION_ATLEAST(bmain, 260, 1)) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + ob->collision_boundtype = ob->boundtype; } - if (!MAIN_VERSION_ATLEAST(bmain, 260, 4)) { - { - /* Convert node angles to radians! */ - Scene *sce; - Material *mat; - bNodeTree *ntree; - - for (sce = bmain->scenes.first; sce; sce = sce->id.next) { - if (sce->nodetree) { - do_versions_nodetree_convert_angle(sce->nodetree); - } - } - - for (mat = bmain->materials.first; mat; mat = mat->id.next) { - if (mat->nodetree) { - do_versions_nodetree_convert_angle(mat->nodetree); - } + { + LISTBASE_FOREACH (Camera *, cam, &bmain->cameras) { + if (cam->sensor_x < 0.01f) { + cam->sensor_x = DEFAULT_SENSOR_WIDTH; } - for (ntree = bmain->nodetrees.first; ntree; ntree = ntree->id.next) { - do_versions_nodetree_convert_angle(ntree); + if (cam->sensor_y < 0.01f) { + cam->sensor_y = DEFAULT_SENSOR_HEIGHT; } } + } +} - { - /* Tomato compatibility code. */ - bScreen *screen; - MovieClip *clip; - - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - ScrArea *area; - for (area = screen->areabase.first; area; area = area->next) { - SpaceLink *sl; - for (sl = area->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *)sl; - if (v3d->bundle_size == 0.0f) { - v3d->bundle_size = 0.2f; - v3d->flag2 |= V3D_SHOW_RECONSTRUCTION; - } +if (!MAIN_VERSION_ATLEAST(bmain, 260, 2)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_SHADER) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == SH_NODE_MAPPING) { + TexMapping *tex_mapping; - if (v3d->bundle_drawtype == 0) { - v3d->bundle_drawtype = OB_PLAINAXES; - } - } - else if (sl->spacetype == SPACE_CLIP) { - SpaceClip *sclip = (SpaceClip *)sl; - if (sclip->scopes.track_preview_height == 0) { - sclip->scopes.track_preview_height = 120; - } - } - } + tex_mapping = node->storage; + tex_mapping->projx = PROJ_X; + tex_mapping->projy = PROJ_Y; + tex_mapping->projz = PROJ_Z; } } + } + } + FOREACH_NODETREE_END; +} - for (clip = bmain->movieclips.first; clip; clip = clip->id.next) { - MovieTrackingTrack *track; - - if (clip->aspx < 1.0f) { - clip->aspx = 1.0f; - clip->aspy = 1.0f; - } +if (!MAIN_VERSION_ATLEAST(bmain, 260, 4)) { + {/* Convert node angles to radians! */ + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes){ + if (sce->nodetree){do_versions_nodetree_convert_angle(sce->nodetree); +} +} - clip->proxy.build_tc_flag = IMB_TC_RECORD_RUN | IMB_TC_FREE_RUN | - IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN; +LISTBASE_FOREACH (Material *, mat, &bmain->materials) { + if (mat->nodetree) { + do_versions_nodetree_convert_angle(mat->nodetree); + } +} - if (clip->proxy.build_size_flag == 0) { - clip->proxy.build_size_flag = IMB_PROXY_25; - } +LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + do_versions_nodetree_convert_angle(ntree); +} +} - if (clip->proxy.quality == 0) { - clip->proxy.quality = 90; - } +{ + /* Tomato compatibility code. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + if (v3d->bundle_size == 0.0f) { + v3d->bundle_size = 0.2f; + v3d->flag2 |= V3D_SHOW_RECONSTRUCTION; + } - if (clip->tracking.camera.pixel_aspect < 0.01f) { - clip->tracking.camera.pixel_aspect = 1.0f; + if (v3d->bundle_drawtype == 0) { + v3d->bundle_drawtype = OB_PLAINAXES; + } } - - track = clip->tracking.tracks_legacy.first; - while (track) { - if (track->minimum_correlation == 0.0f) { - track->minimum_correlation = 0.75f; + else if (sl->spacetype == SPACE_CLIP) { + SpaceClip *sclip = (SpaceClip *)sl; + if (sclip->scopes.track_preview_height == 0) { + sclip->scopes.track_preview_height = 120; } - - track = track->next; } } } } - if (!MAIN_VERSION_ATLEAST(bmain, 260, 6)) { - Scene *sce; - MovieClip *clip; + LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) { + if (clip->aspx < 1.0f) { + clip->aspx = 1.0f; + clip->aspy = 1.0f; + } + + clip->proxy.build_tc_flag = IMB_TC_RECORD_RUN | IMB_TC_FREE_RUN | + IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN; - for (sce = bmain->scenes.first; sce; sce = sce->id.next) { - do_versions_image_settings_2_60(sce); + if (clip->proxy.build_size_flag == 0) { + clip->proxy.build_size_flag = IMB_PROXY_25; } - for (clip = bmain->movieclips.first; clip; clip = clip->id.next) { - MovieTrackingSettings *settings = &clip->tracking.settings; + if (clip->proxy.quality == 0) { + clip->proxy.quality = 90; + } - if (settings->default_pattern_size == 0.0f) { - settings->default_motion_model = TRACK_MOTION_MODEL_TRANSLATION; - settings->default_minimum_correlation = 0.75; - settings->default_pattern_size = 11; - settings->default_search_size = 51; - } + if (clip->tracking.camera.pixel_aspect < 0.01f) { + clip->tracking.camera.pixel_aspect = 1.0f; } - { - Object *ob; - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - /* convert delta addition into delta scale */ - int i; - for (i = 0; i < 3; i++) { - if ((ob->dsize[i] == 0.0f) || /* simple case, user never touched dsize */ - (ob->scale[i] == 0.0f)) /* can't scale the dsize to give a non zero result, - * so fallback to 1.0f */ - { - ob->dscale[i] = 1.0f; - } - else { - ob->dscale[i] = (ob->scale[i] + ob->dsize[i]) / ob->scale[i]; - } - } + MovieTrackingTrack *track = clip->tracking.tracks_legacy.first; + while (track) { + if (track->minimum_correlation == 0.0f) { + track->minimum_correlation = 0.75f; } + + track = track->next; } } - /* sigh, this dscale vs dsize version patching was not done right, fix for fix, - * this intentionally checks an exact subversion, also note this was never in a release, - * at some point this could be removed. */ - else if (bmain->versionfile == 260 && bmain->subversionfile == 6) { - Object *ob; - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - if (is_zero_v3(ob->dscale)) { - copy_vn_fl(ob->dscale, 3, 1.0f); - } - } +} +} + +if (!MAIN_VERSION_ATLEAST(bmain, 260, 6)) { + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { + do_versions_image_settings_2_60(sce); } - if (!MAIN_VERSION_ATLEAST(bmain, 260, 8)) { - Brush *brush; + LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) { + MovieTrackingSettings *settings = &clip->tracking.settings; - for (brush = bmain->brushes.first; brush; brush = brush->id.next) { - if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) { - brush->alpha = 1.0f; - } + if (settings->default_pattern_size == 0.0f) { + settings->default_motion_model = TRACK_MOTION_MODEL_TRANSLATION; + settings->default_minimum_correlation = 0.75; + settings->default_pattern_size = 11; + settings->default_search_size = 51; } } - if (!MAIN_VERSION_ATLEAST(bmain, 261, 1)) { - { - /* update use flags for node sockets (was only temporary before) */ - Scene *sce; - Material *mat; - Tex *tex; - World *world; - bNodeTree *ntree; - - for (sce = bmain->scenes.first; sce; sce = sce->id.next) { - if (sce->nodetree) { - do_versions_nodetree_socket_use_flags_2_62(sce->nodetree); + { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + /* convert delta addition into delta scale */ + int i; + for (i = 0; i < 3; i++) { + if ((ob->dsize[i] == 0.0f) || /* simple case, user never touched dsize */ + (ob->scale[i] == 0.0f)) /* can't scale the dsize to give a non zero result, + * so fallback to 1.0f */ + { + ob->dscale[i] = 1.0f; } - } - - for (mat = bmain->materials.first; mat; mat = mat->id.next) { - if (mat->nodetree) { - do_versions_nodetree_socket_use_flags_2_62(mat->nodetree); + else { + ob->dscale[i] = (ob->scale[i] + ob->dsize[i]) / ob->scale[i]; } } + } + } +} +/* sigh, this dscale vs dsize version patching was not done right, fix for fix, + * this intentionally checks an exact subversion, also note this was never in a release, + * at some point this could be removed. */ +else if (bmain->versionfile == 260 && bmain->subversionfile == 6) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (is_zero_v3(ob->dscale)) { + copy_vn_fl(ob->dscale, 3, 1.0f); + } + } +} - for (tex = bmain->textures.first; tex; tex = tex->id.next) { - if (tex->nodetree) { - do_versions_nodetree_socket_use_flags_2_62(tex->nodetree); - } - } +if (!MAIN_VERSION_ATLEAST(bmain, 260, 8)) { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) { + brush->alpha = 1.0f; + } + } +} - for (Light *la = bmain->lights.first; la; la = la->id.next) { - if (la->nodetree) { - do_versions_nodetree_socket_use_flags_2_62(la->nodetree); - } - } +if (!MAIN_VERSION_ATLEAST(bmain, 261, 1)) { + {/* update use flags for node sockets (was only temporary before) */ + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes){ + if (sce->nodetree){do_versions_nodetree_socket_use_flags_2_62(sce->nodetree); +} +} - for (world = bmain->worlds.first; world; world = world->id.next) { - if (world->nodetree) { - do_versions_nodetree_socket_use_flags_2_62(world->nodetree); - } - } +LISTBASE_FOREACH (Material *, mat, &bmain->materials) { + if (mat->nodetree) { + do_versions_nodetree_socket_use_flags_2_62(mat->nodetree); + } +} - for (ntree = bmain->nodetrees.first; ntree; ntree = ntree->id.next) { - do_versions_nodetree_socket_use_flags_2_62(ntree); - } - } - { - MovieClip *clip; - Object *ob; +LISTBASE_FOREACH (Tex *, tex, &bmain->textures) { + if (tex->nodetree) { + do_versions_nodetree_socket_use_flags_2_62(tex->nodetree); + } +} + +LISTBASE_FOREACH (Light *, la, &bmain->lights) { + if (la->nodetree) { + do_versions_nodetree_socket_use_flags_2_62(la->nodetree); + } +} - for (clip = bmain->movieclips.first; clip; clip = clip->id.next) { - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *tracking_object = tracking->objects.first; +LISTBASE_FOREACH (World *, world, &bmain->worlds) { + if (world->nodetree) { + do_versions_nodetree_socket_use_flags_2_62(world->nodetree); + } +} - clip->proxy.build_tc_flag |= IMB_TC_RECORD_RUN_NO_GAPS; +LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + do_versions_nodetree_socket_use_flags_2_62(ntree); +} +} +{ + LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) { + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *tracking_object = tracking->objects.first; - if (!tracking->settings.object_distance) { - tracking->settings.object_distance = 1.0f; - } + clip->proxy.build_tc_flag |= IMB_TC_RECORD_RUN_NO_GAPS; - if (BLI_listbase_is_empty(&tracking->objects)) { - BKE_tracking_object_add(tracking, "Camera"); - } + if (!tracking->settings.object_distance) { + tracking->settings.object_distance = 1.0f; + } - while (tracking_object) { - if (!tracking_object->scale) { - tracking_object->scale = 1.0f; - } + if (BLI_listbase_is_empty(&tracking->objects)) { + BKE_tracking_object_add(tracking, "Camera"); + } - tracking_object = tracking_object->next; - } + while (tracking_object) { + if (!tracking_object->scale) { + tracking_object->scale = 1.0f; } - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - bConstraint *con; - for (con = ob->constraints.first; con; con = con->next) { - if (con->type == CONSTRAINT_TYPE_OBJECTSOLVER) { - bObjectSolverConstraint *data = (bObjectSolverConstraint *)con->data; + tracking_object = tracking_object->next; + } + } - if (data->invmat[3][3] == 0.0f) { - unit_m4(data->invmat); - } - } + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (bConstraint *, con, &ob->constraints) { + if (con->type == CONSTRAINT_TYPE_OBJECTSOLVER) { + bObjectSolverConstraint *data = con->data; + + if (data->invmat[3][3] == 0.0f) { + unit_m4(data->invmat); } } } } +} +} - if (!MAIN_VERSION_ATLEAST(bmain, 261, 2)) { - { - /* convert deprecated sculpt_paint_unified_* fields to - * UnifiedPaintSettings */ - Scene *scene; - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - ToolSettings *ts = scene->toolsettings; - UnifiedPaintSettings *ups = &ts->unified_paint_settings; - ups->size = ts->sculpt_paint_unified_size; - ups->unprojected_radius = ts->sculpt_paint_unified_unprojected_radius; - ups->alpha = ts->sculpt_paint_unified_alpha; - ups->flag = ts->sculpt_paint_settings; - } +if (!MAIN_VERSION_ATLEAST(bmain, 261, 2)) { + { + /* convert deprecated sculpt_paint_unified_* fields to + * UnifiedPaintSettings */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + ToolSettings *ts = scene->toolsettings; + UnifiedPaintSettings *ups = &ts->unified_paint_settings; + ups->size = ts->sculpt_paint_unified_size; + ups->unprojected_radius = ts->sculpt_paint_unified_unprojected_radius; + ups->alpha = ts->sculpt_paint_unified_alpha; + ups->flag = ts->sculpt_paint_settings; } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 261, 3)) { - { - /* convert extended ascii to utf-8 for text editor */ - Text *text; - for (text = bmain->texts.first; text; text = text->id.next) { - if (!(text->flags & TXT_ISEXT)) { - TextLine *tl; - - for (tl = text->lines.first; tl; tl = tl->next) { - int added = txt_extended_ascii_as_utf8(&tl->line); - tl->len += added; - - /* reset cursor position if line was changed */ - if (added && tl == text->curl) { - text->curc = 0; - } +if (!MAIN_VERSION_ATLEAST(bmain, 261, 3)) { + {/* convert extended ascii to utf-8 for text editor */ + LISTBASE_FOREACH (Text *, text, &bmain->texts){ + if (!(text->flags & TXT_ISEXT)){LISTBASE_FOREACH (TextLine *, tl, &text->lines){ + int added = txt_extended_ascii_as_utf8(&tl->line); + tl->len += added; + + /* reset cursor position if line was changed */ + if (added && tl == text->curl) { + text->curc = 0; + } +} +} +} +} +{ + /* set new dynamic paint values */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_DynamicPaint) { + DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; + if (pmd->canvas) { + DynamicPaintSurface *surface = pmd->canvas->surfaces.first; + for (; surface; surface = surface->next) { + surface->color_dry_threshold = 1.0f; + surface->influence_scale = 1.0f; + surface->radius_scale = 1.0f; + surface->flags |= MOD_DPAINT_USE_DRYING; } } } } - { - /* set new dynamic paint values */ - Object *ob; - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ModifierData *md; - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_DynamicPaint) { - DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; - if (pmd->canvas) { - DynamicPaintSurface *surface = pmd->canvas->surfaces.first; - for (; surface; surface = surface->next) { - surface->color_dry_threshold = 1.0f; - surface->influence_scale = 1.0f; - surface->radius_scale = 1.0f; - surface->flags |= MOD_DPAINT_USE_DRYING; - } - } - } + } +} +} + +if (bmain->versionfile < 262) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Cloth) { + ClothModifierData *clmd = (ClothModifierData *)md; + if (clmd->sim_parms) { + clmd->sim_parms->vel_damping = 1.0f; } } } } +} - if (bmain->versionfile < 262) { - Object *ob; - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ModifierData *md; - - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Cloth) { - ClothModifierData *clmd = (ClothModifierData *)md; - if (clmd->sim_parms) { - clmd->sim_parms->vel_damping = 1.0f; - } +if (bmain->versionfile < 263) { + /* set fluidsim rate. the version patch for this in 2.62 was wrong, so + * try to correct it, if rate is 0.0 that's likely not intentional */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Fluidsim) { + FluidsimModifierData *fmd = (FluidsimModifierData *)md; + if (fmd->fss->animRate == 0.0f) { + fmd->fss->animRate = 1.0f; } } } } +} - if (bmain->versionfile < 263) { - /* set fluidsim rate. the version patch for this in 2.62 was wrong, so - * try to correct it, if rate is 0.0 that's likely not intentional */ - Object *ob; - - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ModifierData *md; - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Fluidsim) { - FluidsimModifierData *fmd = (FluidsimModifierData *)md; - if (fmd->fss->animRate == 0.0f) { - fmd->fss->animRate = 1.0f; - } - } - } +if (!MAIN_VERSION_ATLEAST(bmain, 262, 1)) { + /* update use flags for node sockets (was only temporary before) */ + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { + if (sce->nodetree) { + do_versions_nodetree_multi_file_output_format_2_62_1(sce, sce->nodetree); } } - if (!MAIN_VERSION_ATLEAST(bmain, 262, 1)) { - /* update use flags for node sockets (was only temporary before) */ - Scene *sce; - bNodeTree *ntree; + /* XXX can't associate with scene for group nodes, image format will stay uninitialized */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + do_versions_nodetree_multi_file_output_format_2_62_1(NULL, ntree); + } +} - for (sce = bmain->scenes.first; sce; sce = sce->id.next) { - if (sce->nodetree) { - do_versions_nodetree_multi_file_output_format_2_62_1(sce, sce->nodetree); - } +/* only swap for pre-release bmesh merge which had MLoopCol red/blue swap */ +if (bmain->versionfile == 262 && bmain->subversionfile == 1) { + { + LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) { + do_versions_mesh_mloopcol_swap_2_62_1(me); } + } +} - /* XXX can't associate with scene for group nodes, image format will stay uninitialized */ - for (ntree = bmain->nodetrees.first; ntree; ntree = ntree->id.next) { - do_versions_nodetree_multi_file_output_format_2_62_1(NULL, ntree); +if (!MAIN_VERSION_ATLEAST(bmain, 262, 2)) { + /* Set new idname of keyingsets from their now "label-only" name. */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + LISTBASE_FOREACH (KeyingSet *, ks, &scene->keyingsets) { + if (!ks->idname[0]) { + STRNCPY(ks->idname, ks->name); + } } } +} - /* only swap for pre-release bmesh merge which had MLoopCol red/blue swap */ - if (bmain->versionfile == 262 && bmain->subversionfile == 1) { - { - Mesh *me; - for (me = bmain->meshes.first; me; me = me->id.next) { - do_versions_mesh_mloopcol_swap_2_62_1(me); +if (!MAIN_VERSION_ATLEAST(bmain, 262, 3)) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Lattice) { + LatticeModifierData *lmd = (LatticeModifierData *)md; + lmd->strength = 1.0f; } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 262, 2)) { - /* Set new idname of keyingsets from their now "label-only" name. */ - Scene *scene; - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - KeyingSet *ks; - for (ks = scene->keyingsets.first; ks; ks = ks->next) { - if (!ks->idname[0]) { - STRNCPY(ks->idname, ks->name); +if (!MAIN_VERSION_ATLEAST(bmain, 262, 4)) { + /* Read Viscosity presets from older files */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Fluidsim) { + FluidsimModifierData *fmd = (FluidsimModifierData *)md; + if (fmd->fss->viscosityMode == 3) { + fmd->fss->viscosityValue = 5.0; + fmd->fss->viscosityExponent = 5; + } + else if (fmd->fss->viscosityMode == 4) { + fmd->fss->viscosityValue = 2.0; + fmd->fss->viscosityExponent = 3; } } } } +} + +if (bmain->versionfile < 263) { + /* Default for old files is to save particle rotations to pointcache */ + LISTBASE_FOREACH (ParticleSettings *, part, &bmain->particles) { + part->flag |= PART_ROTATIONS; + } +} - if (!MAIN_VERSION_ATLEAST(bmain, 262, 3)) { - Object *ob; - ModifierData *md; +if (!MAIN_VERSION_ATLEAST(bmain, 263, 1)) { + /* file output node paths are now stored in the file info struct instead socket name */ + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { + if (sce->nodetree) { + do_versions_nodetree_multi_file_output_path_2_63_1(sce->nodetree); + } + } + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + do_versions_nodetree_multi_file_output_path_2_63_1(ntree); + } +} - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Lattice) { - LatticeModifierData *lmd = (LatticeModifierData *)md; - lmd->strength = 1.0f; - } - } +if (!MAIN_VERSION_ATLEAST(bmain, 263, 3)) { + /* For weight paint, each brush now gets its own weight; + * unified paint settings also have weight. Update unified + * paint settings and brushes with a default weight value. */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + ToolSettings *ts = scene->toolsettings; + if (ts) { + ts->unified_paint_settings.weight = ts->vgroup_weight; + ts->unified_paint_settings.flag |= UNIFIED_PAINT_WEIGHT; } } - if (!MAIN_VERSION_ATLEAST(bmain, 262, 4)) { - /* Read Viscosity presets from older files */ - Object *ob; + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + brush->weight = 0.5; + } +} - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ModifierData *md; - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Fluidsim) { - FluidsimModifierData *fmd = (FluidsimModifierData *)md; - if (fmd->fss->viscosityMode == 3) { - fmd->fss->viscosityValue = 5.0; - fmd->fss->viscosityExponent = 5; +if (!MAIN_VERSION_ATLEAST(bmain, 263, 2)) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_CLIP) { + SpaceClip *sclip = (SpaceClip *)sl; + bool hide = false; + + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + if (region->regiontype == RGN_TYPE_PREVIEW) { + if (region->alignment != RGN_ALIGN_NONE) { + region->flag |= RGN_FLAG_HIDDEN; + region->v2d.flag &= ~V2D_IS_INIT; + region->alignment = RGN_ALIGN_NONE; + + hide = true; + } + } } - else if (fmd->fss->viscosityMode == 4) { - fmd->fss->viscosityValue = 2.0; - fmd->fss->viscosityExponent = 3; + + if (hide) { + sclip->view = SC_VIEW_CLIP; } } } } } +} - if (bmain->versionfile < 263) { - /* Default for old files is to save particle rotations to pointcache */ - ParticleSettings *part; - for (part = bmain->particles.first; part; part = part->id.next) { - part->flag |= PART_ROTATIONS; +if (!MAIN_VERSION_ATLEAST(bmain, 263, 4)) { + LISTBASE_FOREACH (Camera *, cam, &bmain->cameras) { + if (cam->flag & CAM_PANORAMA) { + cam->type = CAM_PANO; + cam->flag &= ~CAM_PANORAMA; } } - if (!MAIN_VERSION_ATLEAST(bmain, 263, 1)) { - /* file output node paths are now stored in the file info struct instead socket name */ - Scene *sce; - bNodeTree *ntree; + LISTBASE_FOREACH (Curve *, cu, &bmain->curves) { + if (cu->bevfac2 == 0.0f) { + cu->bevfac1 = 0.0f; + cu->bevfac2 = 1.0f; + } + } +} - for (sce = bmain->scenes.first; sce; sce = sce->id.next) { +if (!MAIN_VERSION_ATLEAST(bmain, 263, 5)) { + { + /* file output node paths are now stored in the file info struct instead socket name */ + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { if (sce->nodetree) { - do_versions_nodetree_multi_file_output_path_2_63_1(sce->nodetree); + do_versions_nodetree_file_output_layers_2_64_5(sce->nodetree); + do_versions_nodetree_image_layer_2_64_5(sce->nodetree); } } - for (ntree = bmain->nodetrees.first; ntree; ntree = ntree->id.next) { - do_versions_nodetree_multi_file_output_path_2_63_1(ntree); + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + do_versions_nodetree_file_output_layers_2_64_5(ntree); + do_versions_nodetree_image_layer_2_64_5(ntree); } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 263, 3)) { - Scene *scene; - Brush *brush; - - /* For weight paint, each brush now gets its own weight; - * unified paint settings also have weight. Update unified - * paint settings and brushes with a default weight value. */ +if (!MAIN_VERSION_ATLEAST(bmain, 263, 6)) { + /* update use flags for node sockets (was only temporary before) */ + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { + if (sce->nodetree) { + do_versions_nodetree_frame_2_64_6(sce->nodetree); + } + } - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - ToolSettings *ts = scene->toolsettings; - if (ts) { - ts->unified_paint_settings.weight = ts->vgroup_weight; - ts->unified_paint_settings.flag |= UNIFIED_PAINT_WEIGHT; - } + LISTBASE_FOREACH (Material *, mat, &bmain->materials) { + if (mat->nodetree) { + do_versions_nodetree_frame_2_64_6(mat->nodetree); } + } - for (brush = bmain->brushes.first; brush; brush = brush->id.next) { - brush->weight = 0.5; + LISTBASE_FOREACH (Tex *, tex, &bmain->textures) { + if (tex->nodetree) { + do_versions_nodetree_frame_2_64_6(tex->nodetree); } } - if (!MAIN_VERSION_ATLEAST(bmain, 263, 2)) { - bScreen *screen; + LISTBASE_FOREACH (Light *, la, &bmain->lights) { + if (la->nodetree) { + do_versions_nodetree_frame_2_64_6(la->nodetree); + } + } - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - ScrArea *area; - for (area = screen->areabase.first; area; area = area->next) { - SpaceLink *sl; + LISTBASE_FOREACH (World *, world, &bmain->worlds) { + if (world->nodetree) { + do_versions_nodetree_frame_2_64_6(world->nodetree); + } + } - for (sl = area->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_CLIP) { - SpaceClip *sclip = (SpaceClip *)sl; - ARegion *region; - bool hide = false; - - for (region = area->regionbase.first; region; region = region->next) { - if (region->regiontype == RGN_TYPE_PREVIEW) { - if (region->alignment != RGN_ALIGN_NONE) { - region->flag |= RGN_FLAG_HIDDEN; - region->v2d.flag &= ~V2D_IS_INIT; - region->alignment = RGN_ALIGN_NONE; - - hide = true; - } - } - } + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + do_versions_nodetree_frame_2_64_6(ntree); + } +} - if (hide) { - sclip->view = SC_VIEW_CLIP; - } - } +if (!MAIN_VERSION_ATLEAST(bmain, 263, 7)) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Fluid) { + FluidModifierData *fmd = (FluidModifierData *)md; + if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { + int maxres = max_iii(fmd->domain->res[0], fmd->domain->res[1], fmd->domain->res[2]); + fmd->domain->scale = fmd->domain->dx * maxres; + fmd->domain->dx = 1.0f / fmd->domain->scale; } } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 263, 4)) { - Camera *cam; - Curve *cu; - - for (cam = bmain->cameras.first; cam; cam = cam->id.next) { - if (cam->flag & CAM_PANORAMA) { - cam->type = CAM_PANO; - cam->flag &= ~CAM_PANORAMA; - } - } +if (!MAIN_VERSION_ATLEAST(bmain, 263, 9)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_SHADER) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (ELEM(node->type, SH_NODE_TEX_IMAGE, SH_NODE_TEX_ENVIRONMENT)) { + NodeTexImage *tex = node->storage; - for (cu = bmain->curves.first; cu; cu = cu->id.next) { - if (cu->bevfac2 == 0.0f) { - cu->bevfac1 = 0.0f; - cu->bevfac2 = 1.0f; + tex->iuser.frames = 1; + tex->iuser.sfra = 1; + } } } } + FOREACH_NODETREE_END; +} - if (!MAIN_VERSION_ATLEAST(bmain, 263, 5)) { - { - /* file output node paths are now stored in the file info struct instead socket name */ - Scene *sce; - bNodeTree *ntree; - - for (sce = bmain->scenes.first; sce; sce = sce->id.next) { - if (sce->nodetree) { - do_versions_nodetree_file_output_layers_2_64_5(sce->nodetree); - do_versions_nodetree_image_layer_2_64_5(sce->nodetree); +if (!MAIN_VERSION_ATLEAST(bmain, 263, 10)) { + { + /* composite redesign */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->nodetree) { + if (scene->nodetree->chunksize == 0) { + scene->nodetree->chunksize = 256; } } - for (ntree = bmain->nodetrees.first; ntree; ntree = ntree->id.next) { - do_versions_nodetree_file_output_layers_2_64_5(ntree); - do_versions_nodetree_image_layer_2_64_5(ntree); - } } - } - - if (!MAIN_VERSION_ATLEAST(bmain, 263, 6)) { - /* update use flags for node sockets (was only temporary before) */ - Scene *sce; - Material *mat; - Tex *tex; - World *world; - bNodeTree *ntree; - - for (sce = bmain->scenes.first; sce; sce = sce->id.next) { - if (sce->nodetree) { - do_versions_nodetree_frame_2_64_6(sce->nodetree); - } - } - - for (mat = bmain->materials.first; mat; mat = mat->id.next) { - if (mat->nodetree) { - do_versions_nodetree_frame_2_64_6(mat->nodetree); - } - } - - for (tex = bmain->textures.first; tex; tex = tex->id.next) { - if (tex->nodetree) { - do_versions_nodetree_frame_2_64_6(tex->nodetree); - } - } - - for (Light *la = bmain->lights.first; la; la = la->id.next) { - if (la->nodetree) { - do_versions_nodetree_frame_2_64_6(la->nodetree); - } - } - - for (world = bmain->worlds.first; world; world = world->id.next) { - if (world->nodetree) { - do_versions_nodetree_frame_2_64_6(world->nodetree); - } - } - - for (ntree = bmain->nodetrees.first; ntree; ntree = ntree->id.next) { - do_versions_nodetree_frame_2_64_6(ntree); - } - } - - if (!MAIN_VERSION_ATLEAST(bmain, 263, 7)) { - Object *ob; - - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ModifierData *md; - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Fluid) { - FluidModifierData *fmd = (FluidModifierData *)md; - if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { - int maxres = max_iii(fmd->domain->res[0], fmd->domain->res[1], fmd->domain->res[2]); - fmd->domain->scale = fmd->domain->dx * maxres; - fmd->domain->dx = 1.0f / fmd->domain->scale; - } - } - } - } - } - if (!MAIN_VERSION_ATLEAST(bmain, 263, 9)) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_SHADER) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (ELEM(node->type, SH_NODE_TEX_IMAGE, SH_NODE_TEX_ENVIRONMENT)) { - NodeTexImage *tex = node->storage; - - tex->iuser.frames = 1; - tex->iuser.sfra = 1; + if (ntree->type == NTREE_COMPOSIT) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == CMP_NODE_DEFOCUS) { + NodeDefocus *data = node->storage; + if (data->maxblur == 0.0f) { + data->maxblur = 16.0f; + } } } } @@ -1381,746 +1265,630 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_END; } - if (!MAIN_VERSION_ATLEAST(bmain, 263, 10)) { - { - Scene *scene; - /* composite redesign */ - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (scene->nodetree) { - if (scene->nodetree->chunksize == 0) { - scene->nodetree->chunksize = 256; - } - } - } - - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_COMPOSIT) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == CMP_NODE_DEFOCUS) { - NodeDefocus *data = node->storage; - if (data->maxblur == 0.0f) { - data->maxblur = 16.0f; - } - } - } - } - } - FOREACH_NODETREE_END; - } - - { - bScreen *screen; - - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - ScrArea *area; - - for (area = screen->areabase.first; area; area = area->next) { - SpaceLink *sl; - - for (sl = area->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_CLIP) { - SpaceClip *sclip = (SpaceClip *)sl; - - if (sclip->around == 0) { - sclip->around = V3D_AROUND_CENTER_MEDIAN; - } - } - } - } - } - } - - { - MovieClip *clip; + {LISTBASE_FOREACH (bScreen *, screen, &bmain->screens){LISTBASE_FOREACH ( + ScrArea *, area, &screen->areabase){LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata){ + if (sl->spacetype == SPACE_CLIP){SpaceClip *sclip = (SpaceClip *)sl; - for (clip = bmain->movieclips.first; clip; clip = clip->id.next) { - clip->start_frame = 1; - } - } + if (sclip->around == 0) { + sclip->around = V3D_AROUND_CENTER_MEDIAN; } +} +} +} +} +} - if (!MAIN_VERSION_ATLEAST(bmain, 263, 11)) { - MovieClip *clip; - - for (clip = bmain->movieclips.first; clip; clip = clip->id.next) { - MovieTrackingTrack *track; +{ + LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) { + clip->start_frame = 1; + } +} +} - track = clip->tracking.tracks_legacy.first; - while (track) { - do_versions_affine_tracker_track(track); +if (!MAIN_VERSION_ATLEAST(bmain, 263, 11)) { + LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) { + MovieTrackingTrack *track = clip->tracking.tracks_legacy.first; + while (track) { + do_versions_affine_tracker_track(track); - track = track->next; - } + track = track->next; } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 263, 13)) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_COMPOSIT) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == CMP_NODE_DILATEERODE) { - if (node->storage == NULL) { - NodeDilateErode *data = MEM_callocN(sizeof(NodeDilateErode), __func__); - data->falloff = PROP_SMOOTH; - node->storage = data; - } +if (!MAIN_VERSION_ATLEAST(bmain, 263, 13)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_COMPOSIT) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == CMP_NODE_DILATEERODE) { + if (node->storage == NULL) { + NodeDilateErode *data = MEM_callocN(sizeof(NodeDilateErode), __func__); + data->falloff = PROP_SMOOTH; + node->storage = data; } } } } - FOREACH_NODETREE_END; } + FOREACH_NODETREE_END; +} - if (!MAIN_VERSION_ATLEAST(bmain, 263, 14)) { - ParticleSettings *part; - - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_COMPOSIT) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == CMP_NODE_KEYING) { - NodeKeyingData *data = node->storage; +if (!MAIN_VERSION_ATLEAST(bmain, 263, 14)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_COMPOSIT) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == CMP_NODE_KEYING) { + NodeKeyingData *data = node->storage; - if (data->despill_balance == 0.0f) { - data->despill_balance = 0.5f; - } + if (data->despill_balance == 0.0f) { + data->despill_balance = 0.5f; } } } } - FOREACH_NODETREE_END; + } + FOREACH_NODETREE_END; - /* keep compatibility for dupliobject particle size */ - for (part = bmain->particles.first; part; part = part->id.next) { - if (ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) { - if ((part->draw & PART_DRAW_ROTATE_OB) == 0) { - part->draw |= PART_DRAW_NO_SCALE_OB; - } + /* keep compatibility for dupliobject particle size */ + LISTBASE_FOREACH (ParticleSettings *, part, &bmain->particles) { + if (ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) { + if ((part->draw & PART_DRAW_ROTATE_OB) == 0) { + part->draw |= PART_DRAW_NO_SCALE_OB; } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 263, 17)) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_COMPOSIT) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == CMP_NODE_MASK) { - if (node->storage == NULL) { - NodeMask *data = MEM_callocN(sizeof(NodeMask), __func__); - /* move settings into own struct */ - data->size_x = (int)node->custom3; - data->size_y = (int)node->custom4; - node->custom3 = 0.5f; /* default shutter */ - node->storage = data; - } +if (!MAIN_VERSION_ATLEAST(bmain, 263, 17)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_COMPOSIT) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == CMP_NODE_MASK) { + if (node->storage == NULL) { + NodeMask *data = MEM_callocN(sizeof(NodeMask), __func__); + /* move settings into own struct */ + data->size_x = (int)node->custom3; + data->size_y = (int)node->custom4; + node->custom3 = 0.5f; /* default shutter */ + node->storage = data; } } } } - FOREACH_NODETREE_END; } + FOREACH_NODETREE_END; +} - if (!MAIN_VERSION_ATLEAST(bmain, 263, 18)) { - Scene *scene; - - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (scene->ed) { - SEQ_for_each_callback(&scene->ed->seqbase, seq_colorbalance_update_cb, NULL); - } +if (!MAIN_VERSION_ATLEAST(bmain, 263, 18)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed) { + SEQ_for_each_callback(&scene->ed->seqbase, seq_colorbalance_update_cb, NULL); } } +} - /* color management pipeline changes compatibility code */ - if (!MAIN_VERSION_ATLEAST(bmain, 263, 19)) { - Scene *scene; - Image *ima; - bool colormanagement_disabled = false; - - /* make scenes which are not using color management have got None as display device, - * so they wouldn't perform linear-to-sRGB conversion on display - */ - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - if ((scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) == 0) { - ColorManagedDisplaySettings *display_settings = &scene->display_settings; +/* color management pipeline changes compatibility code */ +if (!MAIN_VERSION_ATLEAST(bmain, 263, 19)) { + bool colormanagement_disabled = false; - if (display_settings->display_device[0] == 0) { - BKE_scene_disable_color_management(scene); - } + /* make scenes which are not using color management have got None as display device, + * so they wouldn't perform linear-to-sRGB conversion on display + */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if ((scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) == 0) { + ColorManagedDisplaySettings *display_settings = &scene->display_settings; - colormanagement_disabled = true; + if (display_settings->display_device[0] == 0) { + BKE_scene_disable_color_management(scene); } + + colormanagement_disabled = true; } + } - for (ima = bmain->images.first; ima; ima = ima->id.next) { - if (ima->source == IMA_SRC_VIEWER) { - ima->flag |= IMA_VIEW_AS_RENDER; - } - else if (colormanagement_disabled) { - /* if color-management not used, set image's color space to raw, so no sRGB->linear - * conversion would happen on display and render there's no clear way to check whether - * color management is enabled or not in render engine so set all images to raw if there's - * at least one scene with color management disabled this would still behave incorrect in - * cases when color management was used for only some of scenes, but such a setup is - * crazy anyway and think it's fair enough to break compatibility in that cases. - */ + LISTBASE_FOREACH (Image *, ima, &bmain->images) { + if (ima->source == IMA_SRC_VIEWER) { + ima->flag |= IMA_VIEW_AS_RENDER; + } + else if (colormanagement_disabled) { + /* if color-management not used, set image's color space to raw, so no sRGB->linear + * conversion would happen on display and render there's no clear way to check whether + * color management is enabled or not in render engine so set all images to raw if there's + * at least one scene with color management disabled this would still behave incorrect in + * cases when color management was used for only some of scenes, but such a setup is + * crazy anyway and think it's fair enough to break compatibility in that cases. + */ - STRNCPY(ima->colorspace_settings.name, "Raw"); - } + STRNCPY(ima->colorspace_settings.name, "Raw"); } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 263, 20)) { - Key *key; - for (key = bmain->shapekeys.first; key; key = key->id.next) { - blo_do_versions_key_uidgen(key); - } +if (!MAIN_VERSION_ATLEAST(bmain, 263, 20)) { + LISTBASE_FOREACH (Key *, key, &bmain->shapekeys) { + blo_do_versions_key_uidgen(key); } +} - if (!MAIN_VERSION_ATLEAST(bmain, 263, 21)) { - { - Mesh *me; - for (me = bmain->meshes.first; me; me = me->id.next) { - CustomData_update_typemap(&me->vdata); - CustomData_free_layers(&me->vdata, CD_MSTICKY, me->totvert); - } +if (!MAIN_VERSION_ATLEAST(bmain, 263, 21)) { + { + LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) { + CustomData_update_typemap(&me->vdata); + CustomData_free_layers(&me->vdata, CD_MSTICKY, me->totvert); } } +} - /* correction for files saved in blender version when BKE_pose_copy_data - * didn't copy animation visualization, which lead to deadlocks on motion - * path calculation for proxied armatures, see #32742. - */ - if (bmain->versionfile < 264) { - Object *ob; - - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - if (ob->pose) { - if (ob->pose->avs.path_step == 0) { - animviz_settings_init(&ob->pose->avs); - } +/* correction for files saved in blender version when BKE_pose_copy_data + * didn't copy animation visualization, which lead to deadlocks on motion + * path calculation for proxied armatures, see #32742. + */ +if (bmain->versionfile < 264) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->pose) { + if (ob->pose->avs.path_step == 0) { + animviz_settings_init(&ob->pose->avs); } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 264, 1)) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_SHADER) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == SH_NODE_TEX_COORD) { - node->flag |= NODE_OPTIONS; - } +if (!MAIN_VERSION_ATLEAST(bmain, 264, 1)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_SHADER) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == SH_NODE_TEX_COORD) { + node->flag |= NODE_OPTIONS; } } } - FOREACH_NODETREE_END; } + FOREACH_NODETREE_END; +} - if (!MAIN_VERSION_ATLEAST(bmain, 264, 2)) { - MovieClip *clip; - - for (clip = bmain->movieclips.first; clip; clip = clip->id.next) { - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *tracking_object; - - for (tracking_object = tracking->objects.first; tracking_object; - tracking_object = tracking_object->next) - { - if (tracking_object->keyframe1 == 0 && tracking_object->keyframe2 == 0) { - tracking_object->keyframe1 = tracking->settings.keyframe1_legacy; - tracking_object->keyframe2 = tracking->settings.keyframe2_legacy; - } +if (!MAIN_VERSION_ATLEAST(bmain, 264, 2)) { + LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) { + MovieTracking *tracking = &clip->tracking; + LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &tracking->objects) { + if (tracking_object->keyframe1 == 0 && tracking_object->keyframe2 == 0) { + tracking_object->keyframe1 = tracking->settings.keyframe1_legacy; + tracking_object->keyframe2 = tracking->settings.keyframe2_legacy; } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 264, 3)) { - /* smoke branch */ - { - Object *ob; - - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ModifierData *md; - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Fluid) { - FluidModifierData *fmd = (FluidModifierData *)md; - if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { - /* keep branch saves if possible */ - if (!fmd->domain->flame_max_temp) { - fmd->domain->burning_rate = 0.75f; - fmd->domain->flame_smoke = 1.0f; - fmd->domain->flame_vorticity = 0.5f; - fmd->domain->flame_ignition = 1.25f; - fmd->domain->flame_max_temp = 1.75f; - fmd->domain->adapt_threshold = 0.02f; - fmd->domain->adapt_margin = 4; - fmd->domain->flame_smoke_color[0] = 0.7f; - fmd->domain->flame_smoke_color[1] = 0.7f; - fmd->domain->flame_smoke_color[2] = 0.7f; - } - } - else if ((fmd->type & MOD_FLUID_TYPE_FLOW) && fmd->flow) { - if (!fmd->flow->texture_size) { - fmd->flow->fuel_amount = 1.0; - fmd->flow->surface_distance = 1.5; - fmd->flow->color[0] = 0.7f; - fmd->flow->color[1] = 0.7f; - fmd->flow->color[2] = 0.7f; - fmd->flow->texture_size = 1.0f; - } - } - } - } - } +if (!MAIN_VERSION_ATLEAST(bmain, 264, 3)) { + /* smoke branch */ + {LISTBASE_FOREACH (Object *, ob, &bmain->objects){ + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers){ + if (md->type == eModifierType_Fluid){FluidModifierData *fmd = (FluidModifierData *)md; + if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { + /* keep branch saves if possible */ + if (!fmd->domain->flame_max_temp) { + fmd->domain->burning_rate = 0.75f; + fmd->domain->flame_smoke = 1.0f; + fmd->domain->flame_vorticity = 0.5f; + fmd->domain->flame_ignition = 1.25f; + fmd->domain->flame_max_temp = 1.75f; + fmd->domain->adapt_threshold = 0.02f; + fmd->domain->adapt_margin = 4; + fmd->domain->flame_smoke_color[0] = 0.7f; + fmd->domain->flame_smoke_color[1] = 0.7f; + fmd->domain->flame_smoke_color[2] = 0.7f; + } + } + else if ((fmd->type & MOD_FLUID_TYPE_FLOW) && fmd->flow) { + if (!fmd->flow->texture_size) { + fmd->flow->fuel_amount = 1.0; + fmd->flow->surface_distance = 1.5; + fmd->flow->color[0] = 0.7f; + fmd->flow->color[1] = 0.7f; + fmd->flow->color[2] = 0.7f; + fmd->flow->texture_size = 1.0f; } + } +} +} +} +} - /* render border for viewport */ - { - bScreen *screen; - - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - ScrArea *area; - for (area = screen->areabase.first; area; area = area->next) { - SpaceLink *sl; - for (sl = area->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *)sl; - if (v3d->render_border.xmin == 0.0f && v3d->render_border.ymin == 0.0f && - v3d->render_border.xmax == 0.0f && v3d->render_border.ymax == 0.0f) - { - v3d->render_border.xmax = 1.0f; - v3d->render_border.ymax = 1.0f; - } - } +/* render border for viewport */ +{ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + if (v3d->render_border.xmin == 0.0f && v3d->render_border.ymin == 0.0f && + v3d->render_border.xmax == 0.0f && v3d->render_border.ymax == 0.0f) + { + v3d->render_border.xmax = 1.0f; + v3d->render_border.ymax = 1.0f; } } } } } +} +} - if (!MAIN_VERSION_ATLEAST(bmain, 264, 5)) { - /* set a unwrapping margin and ABF by default */ - Scene *scene; - - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (scene->toolsettings->uvcalc_margin == 0.0f) { - scene->toolsettings->uvcalc_margin = 0.001f; - scene->toolsettings->unwrapper = 0; - } +if (!MAIN_VERSION_ATLEAST(bmain, 264, 5)) { + /* set a unwrapping margin and ABF by default */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->toolsettings->uvcalc_margin == 0.0f) { + scene->toolsettings->uvcalc_margin = 0.001f; + scene->toolsettings->unwrapper = 0; } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 264, 7)) { - /* convert tiles size from resolution and number of tiles */ - { - Scene *scene; +if (!MAIN_VERSION_ATLEAST(bmain, 264, 7)) { + /* convert tiles size from resolution and number of tiles */ + {LISTBASE_FOREACH (Scene *, scene, &bmain->scenes){ + if (scene->r.tilex == 0 || scene->r.tiley == 1){scene->r.tilex = scene->r.tiley = 64; +} +} +} - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (scene->r.tilex == 0 || scene->r.tiley == 1) { - scene->r.tilex = scene->r.tiley = 64; - } - } +/* collision masks */ +{ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->col_group == 0) { + ob->col_group = 0x01; + ob->col_mask = 0xff; } + } +} +} - /* collision masks */ - { - Object *ob; - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - if (ob->col_group == 0) { - ob->col_group = 0x01; - ob->col_mask = 0xff; - } - } +if (!MAIN_VERSION_ATLEAST(bmain, 264, 7)) { + LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) { + LISTBASE_FOREACH (MovieTrackingTrack *, track, &clip->tracking.tracks_legacy) { + do_versions_affine_tracker_track(track); } - } - if (!MAIN_VERSION_ATLEAST(bmain, 264, 7)) { - LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) { - LISTBASE_FOREACH (MovieTrackingTrack *, track, &clip->tracking.tracks_legacy) { + LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &clip->tracking.objects) { + LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) { do_versions_affine_tracker_track(track); } - - LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &clip->tracking.objects) { - LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) { - do_versions_affine_tracker_track(track); - } - } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 265, 3)) { - bScreen *screen; - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - ScrArea *area; - for (area = screen->areabase.first; area; area = area->next) { - SpaceLink *sl; - for (sl = area->spacedata.first; sl; sl = sl->next) { - switch (sl->spacetype) { - case SPACE_VIEW3D: { - View3D *v3d = (View3D *)sl; - v3d->flag2 |= V3D_SHOW_ANNOTATION; - break; - } - case SPACE_SEQ: { - SpaceSeq *sseq = (SpaceSeq *)sl; - sseq->flag |= SEQ_PREVIEW_SHOW_GPENCIL; - break; - } - case SPACE_IMAGE: { - SpaceImage *sima = (SpaceImage *)sl; - sima->flag |= SI_SHOW_GPENCIL; - break; - } - case SPACE_NODE: { - SpaceNode *snode = (SpaceNode *)sl; - snode->flag |= SNODE_SHOW_GPENCIL; - break; - } - case SPACE_CLIP: { - SpaceClip *sclip = (SpaceClip *)sl; - sclip->flag |= SC_SHOW_ANNOTATION; - break; - } +if (!MAIN_VERSION_ATLEAST(bmain, 265, 3)) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + switch (sl->spacetype) { + case SPACE_VIEW3D: { + View3D *v3d = (View3D *)sl; + v3d->flag2 |= V3D_SHOW_ANNOTATION; + break; + } + case SPACE_SEQ: { + SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->flag |= SEQ_PREVIEW_SHOW_GPENCIL; + break; + } + case SPACE_IMAGE: { + SpaceImage *sima = (SpaceImage *)sl; + sima->flag |= SI_SHOW_GPENCIL; + break; + } + case SPACE_NODE: { + SpaceNode *snode = (SpaceNode *)sl; + snode->flag |= SNODE_SHOW_GPENCIL; + break; + } + case SPACE_CLIP: { + SpaceClip *sclip = (SpaceClip *)sl; + sclip->flag |= SC_SHOW_ANNOTATION; + break; } } } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 265, 5)) { - Scene *scene; - Tex *tex; - - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (scene->ed) { - SEQ_for_each_callback(&scene->ed->seqbase, seq_set_alpha_mode_cb, NULL); - } +if (!MAIN_VERSION_ATLEAST(bmain, 265, 5)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed) { + SEQ_for_each_callback(&scene->ed->seqbase, seq_set_alpha_mode_cb, NULL); + } - if (scene->r.bake_samples == 0) { - scene->r.bake_samples = 256; - } + if (scene->r.bake_samples == 0) { + scene->r.bake_samples = 256; } + } - for (Image *image = bmain->images.first; image; image = image->id.next) { - if (image->flag & IMA_DO_PREMUL) { - image->alpha_mode = IMA_ALPHA_STRAIGHT; - } - else { - BKE_image_alpha_mode_from_extension(image); - } + LISTBASE_FOREACH (Image *, image, &bmain->images) { + if (image->flag & IMA_DO_PREMUL) { + image->alpha_mode = IMA_ALPHA_STRAIGHT; + } + else { + BKE_image_alpha_mode_from_extension(image); } + } - for (tex = bmain->textures.first; tex; tex = tex->id.next) { - if (tex->type == TEX_IMAGE && (tex->imaflag & TEX_USEALPHA) == 0) { - Image *image = blo_do_versions_newlibadr(fd, &tex->id, ID_IS_LINKED(tex), tex->ima); + LISTBASE_FOREACH (Tex *, tex, &bmain->textures) { + if (tex->type == TEX_IMAGE && (tex->imaflag & TEX_USEALPHA) == 0) { + Image *image = blo_do_versions_newlibadr(fd, &tex->id, ID_IS_LINKED(tex), tex->ima); - if (image && (image->flag & IMA_DO_PREMUL) == 0) { - enum { IMA_IGNORE_ALPHA = (1 << 12) }; - image->flag |= IMA_IGNORE_ALPHA; - } + if (image && (image->flag & IMA_DO_PREMUL) == 0) { + enum { IMA_IGNORE_ALPHA = (1 << 12) }; + image->flag |= IMA_IGNORE_ALPHA; } } + } - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_COMPOSIT) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == CMP_NODE_IMAGE) { - Image *image = blo_do_versions_newlibadr( - fd, &ntree->id, ID_IS_LINKED(ntree), node->id); - - if (image) { - if ((image->flag & IMA_DO_PREMUL) == 0 && image->alpha_mode == IMA_ALPHA_STRAIGHT) { - node->custom1 |= CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT; - } + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_COMPOSIT) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == CMP_NODE_IMAGE) { + Image *image = blo_do_versions_newlibadr(fd, &ntree->id, ID_IS_LINKED(ntree), node->id); + + if (image) { + if ((image->flag & IMA_DO_PREMUL) == 0 && image->alpha_mode == IMA_ALPHA_STRAIGHT) { + node->custom1 |= CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT; } } } } } - FOREACH_NODETREE_END; } - else if (!MAIN_VERSION_ATLEAST(bmain, 266, 1)) { - /* texture use alpha was removed for 2.66 but added back again for 2.66a, - * for compatibility all textures assumed it to be enabled */ - Tex *tex; - - for (tex = bmain->textures.first; tex; tex = tex->id.next) { - if (tex->type == TEX_IMAGE) { - tex->imaflag |= TEX_USEALPHA; - } + FOREACH_NODETREE_END; +} +else if (!MAIN_VERSION_ATLEAST(bmain, 266, 1)) { + /* texture use alpha was removed for 2.66 but added back again for 2.66a, + * for compatibility all textures assumed it to be enabled */ + LISTBASE_FOREACH (Tex *, tex, &bmain->textures) { + if (tex->type == TEX_IMAGE) { + tex->imaflag |= TEX_USEALPHA; } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 265, 7)) { - Curve *cu; - - for (cu = bmain->curves.first; cu; cu = cu->id.next) { - if (cu->flag & (CU_FRONT | CU_BACK)) { - if (cu->extrude != 0.0f || cu->bevel_radius != 0.0f) { - Nurb *nu; - - for (nu = cu->nurb.first; nu; nu = nu->next) { - int a; +if (!MAIN_VERSION_ATLEAST(bmain, 265, 7)) { + LISTBASE_FOREACH (Curve *, cu, &bmain->curves) { + if (cu->flag & (CU_FRONT | CU_BACK)) { + if (cu->extrude != 0.0f || cu->bevel_radius != 0.0f) { + LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { + int a; - if (nu->bezt) { - BezTriple *bezt = nu->bezt; - a = nu->pntsu; + if (nu->bezt) { + BezTriple *bezt = nu->bezt; + a = nu->pntsu; - while (a--) { - bezt->radius = 1.0f; - bezt++; - } + while (a--) { + bezt->radius = 1.0f; + bezt++; } - else if (nu->bp) { - BPoint *bp = nu->bp; - a = nu->pntsu * nu->pntsv; + } + else if (nu->bp) { + BPoint *bp = nu->bp; + a = nu->pntsu * nu->pntsv; - while (a--) { - bp->radius = 1.0f; - bp++; - } + while (a--) { + bp->radius = 1.0f; + bp++; } } } } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 265, 9)) { - Mesh *me; - for (me = bmain->meshes.first; me; me = me->id.next) { - BKE_mesh_do_versions_cd_flag_init(me); - } +if (!MAIN_VERSION_ATLEAST(bmain, 265, 9)) { + LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) { + BKE_mesh_do_versions_cd_flag_init(me); } +} - if (!MAIN_VERSION_ATLEAST(bmain, 265, 10)) { - Brush *br; - for (br = bmain->brushes.first; br; br = br->id.next) { - if (br->ob_mode & OB_MODE_TEXTURE_PAINT) { - br->mtex.brush_map_mode = MTEX_MAP_MODE_TILED; - } +if (!MAIN_VERSION_ATLEAST(bmain, 265, 10)) { + LISTBASE_FOREACH (Brush *, br, &bmain->brushes) { + if (br->ob_mode & OB_MODE_TEXTURE_PAINT) { + br->mtex.brush_map_mode = MTEX_MAP_MODE_TILED; } } +} - /* add storage for compositor translate nodes when not existing */ - if (!MAIN_VERSION_ATLEAST(bmain, 265, 11)) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_COMPOSIT) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == CMP_NODE_TRANSLATE && node->storage == NULL) { - node->storage = MEM_callocN(sizeof(NodeTranslateData), "node translate data"); - } +/* add storage for compositor translate nodes when not existing */ +if (!MAIN_VERSION_ATLEAST(bmain, 265, 11)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_COMPOSIT) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == CMP_NODE_TRANSLATE && node->storage == NULL) { + node->storage = MEM_callocN(sizeof(NodeTranslateData), "node translate data"); } } } - FOREACH_NODETREE_END; } + FOREACH_NODETREE_END; +} - if (!MAIN_VERSION_ATLEAST(bmain, 266, 2)) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - do_versions_nodetree_customnodes(ntree, ((ID *)ntree == id)); - } - FOREACH_NODETREE_END; +if (!MAIN_VERSION_ATLEAST(bmain, 266, 2)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + do_versions_nodetree_customnodes(ntree, ((ID *)ntree == id)); } + FOREACH_NODETREE_END; +} - if (!MAIN_VERSION_ATLEAST(bmain, 266, 2)) { - bScreen *screen; - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - ScrArea *area; - for (area = screen->areabase.first; area; area = area->next) { - SpaceLink *sl; - for (sl = area->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_NODE) { - SpaceNode *snode = (SpaceNode *)sl; - - /* reset pointers to force tree path update from context */ - snode->nodetree = NULL; - snode->edittree = NULL; - snode->id = NULL; - snode->from = NULL; - - /* convert deprecated treetype setting to tree_idname */ - switch (snode->treetype) { - case NTREE_COMPOSIT: - STRNCPY(snode->tree_idname, "CompositorNodeTree"); - break; - case NTREE_SHADER: - STRNCPY(snode->tree_idname, "ShaderNodeTree"); - break; - case NTREE_TEXTURE: - STRNCPY(snode->tree_idname, "TextureNodeTree"); - break; - } +if (!MAIN_VERSION_ATLEAST(bmain, 266, 2)) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_NODE) { + SpaceNode *snode = (SpaceNode *)sl; + + /* reset pointers to force tree path update from context */ + snode->nodetree = NULL; + snode->edittree = NULL; + snode->id = NULL; + snode->from = NULL; + + /* convert deprecated treetype setting to tree_idname */ + switch (snode->treetype) { + case NTREE_COMPOSIT: + strcpy(snode->tree_idname, "CompositorNodeTree"); + break; + case NTREE_SHADER: + strcpy(snode->tree_idname, "ShaderNodeTree"); + break; + case NTREE_TEXTURE: + strcpy(snode->tree_idname, "TextureNodeTree"); + break; } } } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 266, 3)) { - { - /* Fix for a very old issue: - * Node names were nominally made unique in r24478 (2.50.8), but the do_versions check - * to update existing node names only applied to bmain->nodetree (i.e. group nodes). - * Uniqueness is now required for proper preview mapping, - * so do this now to ensure old files don't break. - */ - bNode *node; - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (id == &ntree->id) { - continue; /* already fixed for node groups */ - } +if (!MAIN_VERSION_ATLEAST(bmain, 266, 3)) { + { + /* Fix for a very old issue: + * Node names were nominally made unique in r24478 (2.50.8), but the do_versions check + * to update existing node names only applied to bmain->nodetree (i.e. group nodes). + * Uniqueness is now required for proper preview mapping, + * so do this now to ensure old files don't break. + */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (id == &ntree->id) { + continue; /* already fixed for node groups */ + } - for (node = ntree->nodes.first; node; node = node->next) { - nodeUniqueName(ntree, node); - } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + nodeUniqueName(ntree, node); } - FOREACH_NODETREE_END; } + FOREACH_NODETREE_END; } +} - if (!MAIN_VERSION_ATLEAST(bmain, 266, 4)) { - Brush *brush; - for (brush = bmain->brushes.first; brush; brush = brush->id.next) { - BKE_texture_mtex_default(&brush->mask_mtex); +if (!MAIN_VERSION_ATLEAST(bmain, 266, 4)) { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + BKE_texture_mtex_default(&brush->mask_mtex); - if (brush->ob_mode & OB_MODE_TEXTURE_PAINT) { - brush->spacing /= 2; - } + if (brush->ob_mode & OB_MODE_TEXTURE_PAINT) { + brush->spacing /= 2; } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 266, 6)) { - Brush *brush; +if (!MAIN_VERSION_ATLEAST(bmain, 266, 6)) { #define BRUSH_TEXTURE_OVERLAY (1 << 21) - for (brush = bmain->brushes.first; brush; brush = brush->id.next) { - brush->overlay_flags = 0; - if (brush->flag & BRUSH_TEXTURE_OVERLAY) { - brush->overlay_flags |= (BRUSH_OVERLAY_PRIMARY | BRUSH_OVERLAY_CURSOR); - } + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + brush->overlay_flags = 0; + if (brush->flag & BRUSH_TEXTURE_OVERLAY) { + brush->overlay_flags |= (BRUSH_OVERLAY_PRIMARY | BRUSH_OVERLAY_CURSOR); } -#undef BRUSH_TEXTURE_OVERLAY } +#undef BRUSH_TEXTURE_OVERLAY +} - if (bmain->versionfile < 267) { - // if (!DNA_struct_elem_find(fd->filesdna, "Brush", "int", "stencil_pos")) { - Brush *brush; - - for (brush = bmain->brushes.first; brush; brush = brush->id.next) { - if (brush->stencil_dimension[0] == 0) { - brush->stencil_dimension[0] = 256; - brush->stencil_dimension[1] = 256; - brush->stencil_pos[0] = 256; - brush->stencil_pos[1] = 256; - } - if (brush->mask_stencil_dimension[0] == 0) { - brush->mask_stencil_dimension[0] = 256; - brush->mask_stencil_dimension[1] = 256; - brush->mask_stencil_pos[0] = 256; - brush->mask_stencil_pos[1] = 256; - } +if (bmain->versionfile < 267) { + // if (!DNA_struct_elem_find(fd->filesdna, "Brush", "int", "stencil_pos")) { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (brush->stencil_dimension[0] == 0) { + brush->stencil_dimension[0] = 256; + brush->stencil_dimension[1] = 256; + brush->stencil_pos[0] = 256; + brush->stencil_pos[1] = 256; + } + if (brush->mask_stencil_dimension[0] == 0) { + brush->mask_stencil_dimension[0] = 256; + brush->mask_stencil_dimension[1] = 256; + brush->mask_stencil_pos[0] = 256; + brush->mask_stencil_pos[1] = 256; } - - /* TIP: to initialize new variables added, use the new function - * DNA_struct_elem_find(fd->filesdna, "structname", "typename", "varname") - * example: - * if (!DNA_struct_elem_find(fd->filesdna, "UserDef", "short", "image_gpubuffer_limit")) - * user->image_gpubuffer_limit = 10; - */ } - /* default values in Freestyle settings */ - if (bmain->versionfile < 267) { - Scene *sce; - SceneRenderLayer *srl; - FreestyleLineStyle *linestyle; + /* TIP: to initialize new variables added, use the new function + * DNA_struct_elem_find(fd->filesdna, "structname", "typename", "varname") + * example: + * if (!DNA_struct_elem_find(fd->filesdna, "UserDef", "short", "image_gpubuffer_limit")) + * user->image_gpubuffer_limit = 10; + */ +} - for (sce = bmain->scenes.first; sce; sce = sce->id.next) { - if (sce->r.line_thickness_mode == 0) { - sce->r.line_thickness_mode = R_LINE_THICKNESS_ABSOLUTE; - sce->r.unit_line_thickness = 1.0f; +/* default values in Freestyle settings */ +if (bmain->versionfile < 267) { + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { + if (sce->r.line_thickness_mode == 0) { + sce->r.line_thickness_mode = R_LINE_THICKNESS_ABSOLUTE; + sce->r.unit_line_thickness = 1.0f; + } + LISTBASE_FOREACH (SceneRenderLayer *, srl, &sce->r.layers) { + if (srl->freestyleConfig.mode == 0) { + srl->freestyleConfig.mode = FREESTYLE_CONTROL_EDITOR_MODE; } - for (srl = sce->r.layers.first; srl; srl = srl->next) { - if (srl->freestyleConfig.mode == 0) { - srl->freestyleConfig.mode = FREESTYLE_CONTROL_EDITOR_MODE; - } - if (ELEM(srl->freestyleConfig.raycasting_algorithm, - FREESTYLE_ALGO_CULLED_ADAPTIVE_CUMULATIVE, - FREESTYLE_ALGO_CULLED_ADAPTIVE_TRADITIONAL)) - { - srl->freestyleConfig.raycasting_algorithm = 0; /* deprecated */ - srl->freestyleConfig.flags |= FREESTYLE_CULLING; - } + if (ELEM(srl->freestyleConfig.raycasting_algorithm, + FREESTYLE_ALGO_CULLED_ADAPTIVE_CUMULATIVE, + FREESTYLE_ALGO_CULLED_ADAPTIVE_TRADITIONAL)) + { + srl->freestyleConfig.raycasting_algorithm = 0; /* deprecated */ + srl->freestyleConfig.flags |= FREESTYLE_CULLING; } + } - /* not freestyle */ - { - MeshStatVis *statvis = &sce->toolsettings->statvis; - if (statvis->thickness_samples == 0) { - statvis->overhang_axis = OB_NEGZ; - statvis->overhang_min = 0; - statvis->overhang_max = DEG2RADF(45.0f); + /* not freestyle */ + { + MeshStatVis *statvis = &sce->toolsettings->statvis; + if (statvis->thickness_samples == 0) { + statvis->overhang_axis = OB_NEGZ; + statvis->overhang_min = 0; + statvis->overhang_max = DEG2RADF(45.0f); - statvis->thickness_max = 0.1f; - statvis->thickness_samples = 1; + statvis->thickness_max = 0.1f; + statvis->thickness_samples = 1; - statvis->distort_min = DEG2RADF(5.0f); - statvis->distort_max = DEG2RADF(45.0f); + statvis->distort_min = DEG2RADF(5.0f); + statvis->distort_max = DEG2RADF(45.0f); - statvis->sharp_min = DEG2RADF(90.0f); - statvis->sharp_max = DEG2RADF(180.0f); - } + statvis->sharp_min = DEG2RADF(90.0f); + statvis->sharp_max = DEG2RADF(180.0f); } } - for (linestyle = bmain->linestyles.first; linestyle; linestyle = linestyle->id.next) { + } + LISTBASE_FOREACH (FreestyleLineStyle *, linestyle, &bmain->linestyles) { #if 1 - /* disable the Misc panel for now */ - if (linestyle->panel == LS_PANEL_MISC) { - linestyle->panel = LS_PANEL_STROKES; - } + /* disable the Misc panel for now */ + if (linestyle->panel == LS_PANEL_MISC) { + linestyle->panel = LS_PANEL_STROKES; + } #endif - if (linestyle->thickness_position == 0) { - linestyle->thickness_position = LS_THICKNESS_CENTER; - linestyle->thickness_ratio = 0.5f; - } - if (linestyle->chaining == 0) { - linestyle->chaining = LS_CHAINING_PLAIN; - } - if (linestyle->rounds == 0) { - linestyle->rounds = 3; - } + if (linestyle->thickness_position == 0) { + linestyle->thickness_position = LS_THICKNESS_CENTER; + linestyle->thickness_ratio = 0.5f; + } + if (linestyle->chaining == 0) { + linestyle->chaining = LS_CHAINING_PLAIN; + } + if (linestyle->rounds == 0) { + linestyle->rounds = 3; } } +} - if (bmain->versionfile < 267) { - /* Initialize the active_viewer_key for compositing */ - bScreen *screen; - Scene *scene; - bNodeInstanceKey active_viewer_key = {0}; - /* simply pick the first node space and use that for the active viewer key */ - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - ScrArea *area; - for (area = screen->areabase.first; area; area = area->next) { - SpaceLink *sl; - for (sl = area->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_NODE) { - SpaceNode *snode = (SpaceNode *)sl; - bNodeTreePath *path = snode->treepath.last; - if (!path) { - continue; - } - - active_viewer_key = path->parent_key; - break; +if (bmain->versionfile < 267) { + /* Initialize the active_viewer_key for compositing */ + bNodeInstanceKey active_viewer_key = {0}; + /* simply pick the first node space and use that for the active viewer key */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_NODE) { + SpaceNode *snode = (SpaceNode *)sl; + bNodeTreePath *path = snode->treepath.last; + if (!path) { + continue; } - } - if (active_viewer_key.value != 0) { + + active_viewer_key = path->parent_key; break; } } @@ -2128,429 +1896,384 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) break; } } - - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - /* NOTE: `scene->nodetree` is a local ID block, has been direct_link'ed. */ - if (scene->nodetree) { - scene->nodetree->active_viewer_key = active_viewer_key; - } + if (active_viewer_key.value != 0) { + break; } } - if (!MAIN_VERSION_ATLEAST(bmain, 267, 1)) { - Object *ob; + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + /* NOTE: `scene->nodetree` is a local ID block, has been direct_link'ed. */ + if (scene->nodetree) { + scene->nodetree->active_viewer_key = active_viewer_key; + } + } +} - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ModifierData *md; - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Fluid) { - FluidModifierData *fmd = (FluidModifierData *)md; - if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { - if (fmd->domain->flags & FLUID_DOMAIN_USE_HIGH_SMOOTH) { - fmd->domain->highres_sampling = SM_HRES_LINEAR; - } - else { - fmd->domain->highres_sampling = SM_HRES_NEAREST; - } +if (!MAIN_VERSION_ATLEAST(bmain, 267, 1)) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Fluid) { + FluidModifierData *fmd = (FluidModifierData *)md; + if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { + if (fmd->domain->flags & FLUID_DOMAIN_USE_HIGH_SMOOTH) { + fmd->domain->highres_sampling = SM_HRES_LINEAR; + } + else { + fmd->domain->highres_sampling = SM_HRES_NEAREST; } } } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 268, 1)) { - Brush *brush; - for (brush = bmain->brushes.first; brush; brush = brush->id.next) { - brush->spacing = MAX2(1, brush->spacing); - } +if (!MAIN_VERSION_ATLEAST(bmain, 268, 1)) { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + brush->spacing = MAX2(1, brush->spacing); } +} - if (!MAIN_VERSION_ATLEAST(bmain, 268, 2)) { - Brush *brush; +if (!MAIN_VERSION_ATLEAST(bmain, 268, 2)) { #define BRUSH_FIXED (1 << 6) - for (brush = bmain->brushes.first; brush; brush = brush->id.next) { - brush->flag &= ~BRUSH_FIXED; + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + brush->flag &= ~BRUSH_FIXED; - if (brush->cursor_overlay_alpha < 2) { - brush->cursor_overlay_alpha = 33; - } - if (brush->texture_overlay_alpha < 2) { - brush->texture_overlay_alpha = 33; - } - if (brush->mask_overlay_alpha < 2) { - brush->mask_overlay_alpha = 33; - } + if (brush->cursor_overlay_alpha < 2) { + brush->cursor_overlay_alpha = 33; + } + if (brush->texture_overlay_alpha < 2) { + brush->texture_overlay_alpha = 33; + } + if (brush->mask_overlay_alpha < 2) { + brush->mask_overlay_alpha = 33; } -#undef BRUSH_FIXED } +#undef BRUSH_FIXED +} - if (!MAIN_VERSION_ATLEAST(bmain, 268, 4)) { - bScreen *screen; - Object *ob; - - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - bConstraint *con; - for (con = ob->constraints.first; con; con = con->next) { - if (con->type == CONSTRAINT_TYPE_SHRINKWRAP) { - bShrinkwrapConstraint *data = (bShrinkwrapConstraint *)con->data; - if (data->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) { - data->projAxis = OB_POSX; - } - else if (data->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) { - data->projAxis = OB_POSY; - } - else { - data->projAxis = OB_POSZ; - } - data->projAxisSpace = CONSTRAINT_SPACE_LOCAL; +if (!MAIN_VERSION_ATLEAST(bmain, 268, 4)) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (bConstraint *, con, &ob->constraints) { + if (con->type == CONSTRAINT_TYPE_SHRINKWRAP) { + bShrinkwrapConstraint *data = con->data; + if (data->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) { + data->projAxis = OB_POSX; + } + else if (data->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) { + data->projAxis = OB_POSY; } + else { + data->projAxis = OB_POSZ; + } + data->projAxisSpace = CONSTRAINT_SPACE_LOCAL; } } + } - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ModifierData *md; - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Fluid) { - FluidModifierData *fmd = (FluidModifierData *)md; - if ((fmd->type & MOD_FLUID_TYPE_FLOW) && fmd->flow) { - if (!fmd->flow->particle_size) { - fmd->flow->particle_size = 1.0f; - } + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Fluid) { + FluidModifierData *fmd = (FluidModifierData *)md; + if ((fmd->type & MOD_FLUID_TYPE_FLOW) && fmd->flow) { + if (!fmd->flow->particle_size) { + fmd->flow->particle_size = 1.0f; } } } } + } - /* - * FIX some files have a zoom level of 0, and was checked during the drawing of the node space - * - * We moved this check to the do versions to be sure the value makes any sense. - */ - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - ScrArea *area; - for (area = screen->areabase.first; area; area = area->next) { - SpaceLink *sl; - for (sl = area->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_NODE) { - SpaceNode *snode = (SpaceNode *)sl; - if (snode->zoom < 0.02f) { - snode->zoom = 1.0; - } + /* + * FIX some files have a zoom level of 0, and was checked during the drawing of the node space + * + * We moved this check to the do versions to be sure the value makes any sense. + */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_NODE) { + SpaceNode *snode = (SpaceNode *)sl; + if (snode->zoom < 0.02f) { + snode->zoom = 1.0; } } } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 268, 5)) { - bScreen *screen; - ScrArea *area; - - /* add missing (+) expander in node editor */ - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - for (area = screen->areabase.first; area; area = area->next) { - ARegion *region, *arnew; - - if (area->spacetype == SPACE_NODE) { - region = BKE_area_find_region_type(area, RGN_TYPE_TOOLS); +if (!MAIN_VERSION_ATLEAST(bmain, 268, 5)) { + /* add missing (+) expander in node editor */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + if (area->spacetype == SPACE_NODE) { + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_TOOLS); - if (region) { - continue; - } + if (region) { + continue; + } - /* add subdiv level; after header */ - region = BKE_area_find_region_type(area, RGN_TYPE_HEADER); + /* add subdiv level; after header */ + region = BKE_area_find_region_type(area, RGN_TYPE_HEADER); - /* is error! */ - if (region == NULL) { - continue; - } + /* is error! */ + if (region == NULL) { + continue; + } - arnew = MEM_callocN(sizeof(ARegion), "node tools"); + ARegion *arnew = MEM_callocN(sizeof(ARegion), "node tools"); - BLI_insertlinkafter(&area->regionbase, region, arnew); - arnew->regiontype = RGN_TYPE_TOOLS; - arnew->alignment = RGN_ALIGN_LEFT; + BLI_insertlinkafter(&area->regionbase, region, arnew); + arnew->regiontype = RGN_TYPE_TOOLS; + arnew->alignment = RGN_ALIGN_LEFT; - arnew->flag = RGN_FLAG_HIDDEN; - } + arnew->flag = RGN_FLAG_HIDDEN; } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 269, 1)) { - /* Removal of Cycles SSS Compatible falloff */ - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_SHADER) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == SH_NODE_SUBSURFACE_SCATTERING) { - if (node->custom1 == SHD_SUBSURFACE_COMPATIBLE) { - node->custom1 = SHD_SUBSURFACE_CUBIC; - } +if (!MAIN_VERSION_ATLEAST(bmain, 269, 1)) { + /* Removal of Cycles SSS Compatible falloff */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_SHADER) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == SH_NODE_SUBSURFACE_SCATTERING) { + if (node->custom1 == SHD_SUBSURFACE_COMPATIBLE) { + node->custom1 = SHD_SUBSURFACE_CUBIC; } } } } - FOREACH_NODETREE_END; } + FOREACH_NODETREE_END; +} - if (!MAIN_VERSION_ATLEAST(bmain, 269, 2)) { - /* Initialize CDL settings for Color Balance nodes */ - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_COMPOSIT) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == CMP_NODE_COLORBALANCE) { - NodeColorBalance *n = node->storage; - if (node->custom1 == 0) { - /* LGG mode stays the same, just init CDL settings */ - ntreeCompositColorBalanceSyncFromLGG(ntree, node); - } - else if (node->custom1 == 1) { - /* CDL previously used same variables as LGG, copy them over - * and then sync LGG for comparable results in both modes. - */ - copy_v3_v3(n->offset, n->lift); - copy_v3_v3(n->power, n->gamma); - copy_v3_v3(n->slope, n->gain); - ntreeCompositColorBalanceSyncFromCDL(ntree, node); - } +if (!MAIN_VERSION_ATLEAST(bmain, 269, 2)) { + /* Initialize CDL settings for Color Balance nodes */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_COMPOSIT) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == CMP_NODE_COLORBALANCE) { + NodeColorBalance *n = node->storage; + if (node->custom1 == 0) { + /* LGG mode stays the same, just init CDL settings */ + ntreeCompositColorBalanceSyncFromLGG(ntree, node); + } + else if (node->custom1 == 1) { + /* CDL previously used same variables as LGG, copy them over + * and then sync LGG for comparable results in both modes. + */ + copy_v3_v3(n->offset, n->lift); + copy_v3_v3(n->power, n->gamma); + copy_v3_v3(n->slope, n->gain); + ntreeCompositColorBalanceSyncFromCDL(ntree, node); } } } } - FOREACH_NODETREE_END; } + FOREACH_NODETREE_END; +} - if (!MAIN_VERSION_ATLEAST(bmain, 269, 3)) { - bScreen *screen; - ScrArea *area; - SpaceLink *sl; - Scene *scene; - - /* Update files using invalid (outdated) outlinevis Outliner values. */ - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - for (area = screen->areabase.first; area; area = area->next) { - for (sl = area->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_OUTLINER) { - SpaceOutliner *space_outliner = (SpaceOutliner *)sl; +if (!MAIN_VERSION_ATLEAST(bmain, 269, 3)) { + /* Update files using invalid (outdated) outlinevis Outliner values. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_OUTLINER) { + SpaceOutliner *space_outliner = (SpaceOutliner *)sl; - if (!ELEM( - space_outliner->outlinevis, SO_SCENES, SO_LIBRARIES, SO_SEQUENCE, SO_DATA_API)) - { - space_outliner->outlinevis = SO_SCENES; - } + if (!ELEM(space_outliner->outlinevis, SO_SCENES, SO_LIBRARIES, SO_SEQUENCE, SO_DATA_API)) + { + space_outliner->outlinevis = SO_SCENES; } } } } + } - if (!DNA_struct_elem_find(fd->filesdna, "MovieTrackingTrack", "float", "weight")) { - LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) { - const MovieTracking *tracking = &clip->tracking; - LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &tracking->objects) { - const ListBase *tracksbase = (tracking_object->flag & TRACKING_OBJECT_CAMERA) ? - &tracking->tracks_legacy : - &tracking_object->tracks; - LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) { - track->weight = 1.0f; - } + if (!DNA_struct_elem_find(fd->filesdna, "MovieTrackingTrack", "float", "weight")) { + LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) { + const MovieTracking *tracking = &clip->tracking; + LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &tracking->objects) { + const ListBase *tracksbase = (tracking_object->flag & TRACKING_OBJECT_CAMERA) ? + &tracking->tracks_legacy : + &tracking_object->tracks; + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) { + track->weight = 1.0f; } } } + } - if (!DNA_struct_elem_find(fd->filesdna, "TriangulateModifierData", "int", "quad_method")) { - Object *ob; - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ModifierData *md; - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Triangulate) { - TriangulateModifierData *tmd = (TriangulateModifierData *)md; - if (tmd->flag & MOD_TRIANGULATE_BEAUTY) { - tmd->quad_method = MOD_TRIANGULATE_QUAD_BEAUTY; - tmd->ngon_method = MOD_TRIANGULATE_NGON_BEAUTY; - } - else { - tmd->quad_method = MOD_TRIANGULATE_QUAD_FIXED; - tmd->ngon_method = MOD_TRIANGULATE_NGON_EARCLIP; - } + if (!DNA_struct_elem_find(fd->filesdna, "TriangulateModifierData", "int", "quad_method")) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Triangulate) { + TriangulateModifierData *tmd = (TriangulateModifierData *)md; + if (tmd->flag & MOD_TRIANGULATE_BEAUTY) { + tmd->quad_method = MOD_TRIANGULATE_QUAD_BEAUTY; + tmd->ngon_method = MOD_TRIANGULATE_NGON_BEAUTY; + } + else { + tmd->quad_method = MOD_TRIANGULATE_QUAD_FIXED; + tmd->ngon_method = MOD_TRIANGULATE_NGON_EARCLIP; } } } } + } - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - /* this can now be turned off */ - ToolSettings *ts = scene->toolsettings; - if (ts->sculpt) { - ts->sculpt->flags |= SCULPT_DYNTOPO_SUBDIVIDE; - } + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + /* this can now be turned off */ + ToolSettings *ts = scene->toolsettings; + if (ts->sculpt) { + ts->sculpt->flags |= SCULPT_DYNTOPO_SUBDIVIDE; + } - /* 'Increment' mode disabled for nodes, use true grid snapping instead */ - if (scene->toolsettings->snap_node_mode == 0) { /* SCE_SNAP_MODE_INCREMENT */ - scene->toolsettings->snap_node_mode = 8; /* SCE_SNAP_MODE_GRID */ - } + /* 'Increment' mode disabled for nodes, use true grid snapping instead */ + if (scene->toolsettings->snap_node_mode == 0) { /* SCE_SNAP_MODE_INCREMENT */ + scene->toolsettings->snap_node_mode = 8; /* SCE_SNAP_MODE_GRID */ + } #ifdef WITH_FFMPEG - /* Update for removed "sound-only" option in FFMPEG export settings. */ - if (scene->r.ffcodecdata.type >= FFMPEG_INVALID) { - scene->r.ffcodecdata.type = FFMPEG_AVI; - } -#endif + /* Update for removed "sound-only" option in FFMPEG export settings. */ + if (scene->r.ffcodecdata.type >= FFMPEG_INVALID) { + scene->r.ffcodecdata.type = FFMPEG_AVI; } +#endif } +} - if (!MAIN_VERSION_ATLEAST(bmain, 269, 4)) { - /* Internal degrees to radians conversions... */ - { - Scene *scene; - Object *ob; - - for (Light *la = bmain->lights.first; la; la = la->id.next) { - la->spotsize = DEG2RADF(la->spotsize); - } - - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ModifierData *md; +if (!MAIN_VERSION_ATLEAST(bmain, 269, 4)) { + /* Internal degrees to radians conversions... */ + { + LISTBASE_FOREACH (Light *, la, &bmain->lights) { + la->spotsize = DEG2RADF(la->spotsize); + } - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_EdgeSplit) { - EdgeSplitModifierData *emd = (EdgeSplitModifierData *)md; - emd->split_angle = DEG2RADF(emd->split_angle); - } - else if (md->type == eModifierType_Bevel) { - BevelModifierData *bmd = (BevelModifierData *)md; - bmd->bevel_angle = DEG2RADF(bmd->bevel_angle); - } + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_EdgeSplit) { + EdgeSplitModifierData *emd = (EdgeSplitModifierData *)md; + emd->split_angle = DEG2RADF(emd->split_angle); + } + else if (md->type == eModifierType_Bevel) { + BevelModifierData *bmd = (BevelModifierData *)md; + bmd->bevel_angle = DEG2RADF(bmd->bevel_angle); } } + } - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (scene->ed) { - SEQ_for_each_callback(&scene->ed->seqbase, seq_set_wipe_angle_cb, NULL); - } + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed) { + SEQ_for_each_callback(&scene->ed->seqbase, seq_set_wipe_angle_cb, NULL); } + } - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_COMPOSIT) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == CMP_NODE_BOKEHIMAGE) { - NodeBokehImage *n = node->storage; - n->angle = DEG2RADF(n->angle); - } - if (node->type == CMP_NODE_MASK_BOX) { - NodeBoxMask *n = node->storage; - n->rotation = DEG2RADF(n->rotation); - } - if (node->type == CMP_NODE_MASK_ELLIPSE) { - NodeEllipseMask *n = node->storage; - n->rotation = DEG2RADF(n->rotation); - } + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_COMPOSIT) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == CMP_NODE_BOKEHIMAGE) { + NodeBokehImage *n = node->storage; + n->angle = DEG2RADF(n->angle); + } + if (node->type == CMP_NODE_MASK_BOX) { + NodeBoxMask *n = node->storage; + n->rotation = DEG2RADF(n->rotation); + } + if (node->type == CMP_NODE_MASK_ELLIPSE) { + NodeEllipseMask *n = node->storage; + n->rotation = DEG2RADF(n->rotation); } } } - FOREACH_NODETREE_END; } + FOREACH_NODETREE_END; + } - if (!DNA_struct_elem_find(fd->filesdna, "MovieTrackingPlaneTrack", "float", "image_opacity")) { - MovieClip *clip; - for (clip = bmain->movieclips.first; clip; clip = clip->id.next) { - MovieTrackingPlaneTrack *plane_track; - for (plane_track = clip->tracking.plane_tracks_legacy.first; plane_track; - plane_track = plane_track->next) - { - plane_track->image_opacity = 1.0f; - } + if (!DNA_struct_elem_find(fd->filesdna, "MovieTrackingPlaneTrack", "float", "image_opacity")) { + LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) { + LISTBASE_FOREACH ( + MovieTrackingPlaneTrack *, plane_track, &clip->tracking.plane_tracks_legacy) { + plane_track->image_opacity = 1.0f; } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 269, 7)) { - Scene *scene; - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - Sculpt *sd = scene->toolsettings->sculpt; +if (!MAIN_VERSION_ATLEAST(bmain, 269, 7)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + Sculpt *sd = scene->toolsettings->sculpt; - if (sd) { - enum { - SCULPT_SYMM_X = (1 << 0), - SCULPT_SYMM_Y = (1 << 1), - SCULPT_SYMM_Z = (1 << 2), - SCULPT_SYMMETRY_FEATHER = (1 << 6), - }; - int symmetry_flags = sd->flags & 7; + if (sd) { + enum { + SCULPT_SYMM_X = (1 << 0), + SCULPT_SYMM_Y = (1 << 1), + SCULPT_SYMM_Z = (1 << 2), + SCULPT_SYMMETRY_FEATHER = (1 << 6), + }; + int symmetry_flags = sd->flags & 7; - if (symmetry_flags & SCULPT_SYMM_X) { - sd->paint.symmetry_flags |= PAINT_SYMM_X; - } - if (symmetry_flags & SCULPT_SYMM_Y) { - sd->paint.symmetry_flags |= PAINT_SYMM_Y; - } - if (symmetry_flags & SCULPT_SYMM_Z) { - sd->paint.symmetry_flags |= PAINT_SYMM_Z; - } - if (symmetry_flags & SCULPT_SYMMETRY_FEATHER) { - sd->paint.symmetry_flags |= PAINT_SYMMETRY_FEATHER; - } + if (symmetry_flags & SCULPT_SYMM_X) { + sd->paint.symmetry_flags |= PAINT_SYMM_X; + } + if (symmetry_flags & SCULPT_SYMM_Y) { + sd->paint.symmetry_flags |= PAINT_SYMM_Y; + } + if (symmetry_flags & SCULPT_SYMM_Z) { + sd->paint.symmetry_flags |= PAINT_SYMM_Z; + } + if (symmetry_flags & SCULPT_SYMMETRY_FEATHER) { + sd->paint.symmetry_flags |= PAINT_SYMMETRY_FEATHER; } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 269, 8)) { - Curve *cu; - - for (cu = bmain->curves.first; cu; cu = cu->id.next) { - if (cu->str) { - cu->len_char32 = BLI_strlen_utf8(cu->str); - } +if (!MAIN_VERSION_ATLEAST(bmain, 269, 8)) { + LISTBASE_FOREACH (Curve *, cu, &bmain->curves) { + if (cu->str) { + cu->len_char32 = BLI_strlen_utf8(cu->str); } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 269, 9)) { - Object *ob; - - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - ModifierData *md; - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Build) { - BuildModifierData *bmd = (BuildModifierData *)md; - if (bmd->randomize) { - bmd->flag |= MOD_BUILD_FLAG_RANDOMIZE; - } +if (!MAIN_VERSION_ATLEAST(bmain, 269, 9)) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Build) { + BuildModifierData *bmd = (BuildModifierData *)md; + if (bmd->randomize) { + bmd->flag |= MOD_BUILD_FLAG_RANDOMIZE; } } } } +} - if (!MAIN_VERSION_ATLEAST(bmain, 269, 11)) { - bScreen *screen; - - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - ScrArea *area; - for (area = screen->areabase.first; area; area = area->next) { - SpaceLink *space_link; +if (!MAIN_VERSION_ATLEAST(bmain, 269, 11)) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, space_link, &area->spacedata) { + if (space_link->spacetype == SPACE_IMAGE) { + ListBase *lb; - for (space_link = area->spacedata.first; space_link; space_link = space_link->next) { - if (space_link->spacetype == SPACE_IMAGE) { - ARegion *region; - ListBase *lb; + if (space_link == area->spacedata.first) { + lb = &area->regionbase; + } + else { + lb = &space_link->regionbase; + } - if (space_link == area->spacedata.first) { - lb = &area->regionbase; - } - else { - lb = &space_link->regionbase; + LISTBASE_FOREACH (ARegion *, region, lb) { + if (region->regiontype == RGN_TYPE_PREVIEW) { + region->regiontype = RGN_TYPE_TOOLS; + region->alignment = RGN_ALIGN_LEFT; } - - for (region = lb->first; region; region = region->next) { - if (region->regiontype == RGN_TYPE_PREVIEW) { - region->regiontype = RGN_TYPE_TOOLS; - region->alignment = RGN_ALIGN_LEFT; - } - else if (region->regiontype == RGN_TYPE_UI) { - region->alignment = RGN_ALIGN_RIGHT; - } + else if (region->regiontype == RGN_TYPE_UI) { + region->alignment = RGN_ALIGN_RIGHT; } } } @@ -2558,6 +2281,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } +} void do_versions_after_linking_260(Main *bmain) { @@ -2605,7 +2329,7 @@ void do_versions_after_linking_260(Main *bmain) * If the fromnode/tonode pointers are NULL, this means a link from/to * the ntree interface sockets, which need to be redirected to new interface nodes. */ - for (link = ntree->links.first; link; link = next_link) { + for (link = ntree->links.first; link != NULL; link = next_link) { bool free_link = false; next_link = link->next; @@ -2671,7 +2395,7 @@ void do_versions_after_linking_260(Main *bmain) FOREACH_NODETREE_BEGIN (bmain, ntree, id) { bNodeLink *link, *next_link; - for (link = ntree->links.first; link; link = next_link) { + for (link = ntree->links.first; link != NULL; link = next_link) { next_link = link->next; if (link->fromnode == NULL || link->tonode == NULL) { nodeRemLink(ntree, link); From ee98b24d1536f27d3af0fa5a02ff738547cbd176 Mon Sep 17 00:00:00 2001 From: Iliya Katueshenock Date: Tue, 20 Jun 2023 18:56:37 +0200 Subject: [PATCH 33/36] Cleanup: Undo revertions from last cleanup Changes from 8bcad285de845a424fba were reverted when merging #108995 with main. This was missed and committed to main. This is a revert of those changes. Pull Request: https://projects.blender.org/blender/blender/pulls/109167 --- source/blender/blenloader/intern/versioning_260.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 5180fa04cd4f..20c9fbefb83d 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -519,13 +519,13 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int UNUSED(is_gro /* tree type idname */ switch (ntree->type) { case NTREE_COMPOSIT: - strcpy(ntree->idname, "CompositorNodeTree"); + STRNCPY(ntree->idname, "CompositorNodeTree"); break; case NTREE_SHADER: - strcpy(ntree->idname, "ShaderNodeTree"); + STRNCPY(ntree->idname, "ShaderNodeTree"); break; case NTREE_TEXTURE: - strcpy(ntree->idname, "TextureNodeTree"); + STRNCPY(ntree->idname, "TextureNodeTree"); break; } @@ -1732,13 +1732,13 @@ if (!MAIN_VERSION_ATLEAST(bmain, 266, 2)) { /* convert deprecated treetype setting to tree_idname */ switch (snode->treetype) { case NTREE_COMPOSIT: - strcpy(snode->tree_idname, "CompositorNodeTree"); + STRNCPY(snode->tree_idname, "CompositorNodeTree"); break; case NTREE_SHADER: - strcpy(snode->tree_idname, "ShaderNodeTree"); + STRNCPY(snode->tree_idname, "ShaderNodeTree"); break; case NTREE_TEXTURE: - strcpy(snode->tree_idname, "TextureNodeTree"); + STRNCPY(snode->tree_idname, "TextureNodeTree"); break; } } From b84d4dd16d8d85bba57f308066d069ec3576c457 Mon Sep 17 00:00:00 2001 From: salipourto Date: Tue, 20 Jun 2023 20:47:10 +0200 Subject: [PATCH 34/36] Fix various HIP RT issues * Motion blur issues due to missing ray time * Wrong bitcode path for runtime compilation * Quiet logging Pull Request: https://projects.blender.org/blender/blender/pulls/109170 --- extern/hipew/include/hiprtew.h | 2 ++ extern/hipew/src/hiprtew.cc | 2 ++ intern/cycles/device/hiprt/device_impl.cpp | 5 +++-- intern/cycles/device/hiprt/queue.cpp | 2 ++ intern/cycles/kernel/device/hiprt/bvh.h | 10 +++++----- intern/cycles/kernel/device/hiprt/common.h | 21 +++++++++------------ 6 files changed, 23 insertions(+), 19 deletions(-) diff --git a/extern/hipew/include/hiprtew.h b/extern/hipew/include/hiprtew.h index a5472896dd97..5f967e7c99c6 100644 --- a/extern/hipew/include/hiprtew.h +++ b/extern/hipew/include/hiprtew.h @@ -79,6 +79,7 @@ typedef hiprtError(thiprtSetFuncTable)(hiprtContext context, hiprtFuncDataSet set); typedef hiprtError(thiprtDestroyFuncTable)(hiprtContext context, hiprtFuncTable funcTable); +typedef void(thiprtSetLogLevel)( hiprtLogLevel level ); /* Function declarations. */ extern thiprtCreateContext *hiprtCreateContext; @@ -94,6 +95,7 @@ extern thiprtGetSceneBuildTemporaryBufferSize *hiprtGetSceneBuildTemporaryBuffer extern thiprtCreateFuncTable *hiprtCreateFuncTable; extern thiprtSetFuncTable *hiprtSetFuncTable; extern thiprtDestroyFuncTable *hiprtDestroyFuncTable; +extern thiprtSetLogLevel *hiprtSetLogLevel; /* HIPEW API. */ diff --git a/extern/hipew/src/hiprtew.cc b/extern/hipew/src/hiprtew.cc index 84403bb22834..5844d6466b33 100644 --- a/extern/hipew/src/hiprtew.cc +++ b/extern/hipew/src/hiprtew.cc @@ -41,6 +41,7 @@ thiprtGetSceneBuildTemporaryBufferSize *hiprtGetSceneBuildTemporaryBufferSize; thiprtCreateFuncTable *hiprtCreateFuncTable; thiprtSetFuncTable *hiprtSetFuncTable; thiprtDestroyFuncTable *hiprtDestroyFuncTable; +thiprtSetLogLevel *hiprtSetLogLevel; static void hipewHipRtExit(void) { @@ -89,6 +90,7 @@ bool hiprtewInit() HIPRT_LIBRARY_FIND(hiprtCreateFuncTable) HIPRT_LIBRARY_FIND(hiprtSetFuncTable) HIPRT_LIBRARY_FIND(hiprtDestroyFuncTable) + HIPRT_LIBRARY_FIND(hiprtSetLogLevel) result = true; #endif diff --git a/intern/cycles/device/hiprt/device_impl.cpp b/intern/cycles/device/hiprt/device_impl.cpp index 5d5c2d6b2a29..d23f85b07cdb 100644 --- a/intern/cycles/device/hiprt/device_impl.cpp +++ b/intern/cycles/device/hiprt/device_impl.cpp @@ -95,6 +95,8 @@ HIPRTDevice::HIPRTDevice(const DeviceInfo &info, Stats &stats, Profiler &profile set_error(string_printf("Failed to create HIPRT Function Table")); return; } + + hiprtSetLogLevel(hiprtLogLevelNone); } HIPRTDevice::~HIPRTDevice() @@ -260,8 +262,7 @@ string HIPRTDevice::compile_kernel(const uint kernel_features, const char *name, linker_options.append(" --offload-arch=").append(arch); linker_options.append(" -fgpu-rdc --hip-link --cuda-device-only "); string hiprt_ver(HIPRT_VERSION_STR); - string hiprt_bc; - hiprt_bc = hiprt_path + "\\hiprt" + hiprt_ver + "_amd_lib_win.bc"; + string hiprt_bc = hiprt_path + "\\dist\\bin\\Release\\hiprt" + hiprt_ver + "_amd_lib_win.bc"; string linker_command = string_printf("clang++ %s \"%s\" %s -o \"%s\"", linker_options.c_str(), diff --git a/intern/cycles/device/hiprt/queue.cpp b/intern/cycles/device/hiprt/queue.cpp index 28cc6673c87e..22f47ddd1a0f 100644 --- a/intern/cycles/device/hiprt/queue.cpp +++ b/intern/cycles/device/hiprt/queue.cpp @@ -60,6 +60,8 @@ bool HIPRTDeviceQueue::enqueue(DeviceKernel kernel, 0), "enqueue"); + debug_enqueue_end(); + return !(hiprt_device_->have_error()); } diff --git a/intern/cycles/kernel/device/hiprt/bvh.h b/intern/cycles/kernel/device/hiprt/bvh.h index 2c7daa2f2fa8..b352cbcb0142 100644 --- a/intern/cycles/kernel/device/hiprt/bvh.h +++ b/intern/cycles/kernel/device/hiprt/bvh.h @@ -45,11 +45,11 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, GET_TRAVERSAL_STACK() if (visibility & PATH_RAY_SHADOW_OPAQUE) { - GET_TRAVERSAL_ANY_HIT(table_closest_intersect, 0) + GET_TRAVERSAL_ANY_HIT(table_closest_intersect, 0, ray->time) hit = traversal.getNextHit(); } else { - GET_TRAVERSAL_CLOSEST_HIT(table_closest_intersect, 0) + GET_TRAVERSAL_CLOSEST_HIT(table_closest_intersect, 0, ray->time) hit = traversal.getNextHit(); } if (hit.hasHit()) { @@ -156,13 +156,13 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, payload.in_state = state; payload.max_hits = max_hits; payload.visibility = visibility; - payload.prim_type = PRIMITIVE_TRIANGLE; + payload.prim_type = PRIMITIVE_NONE; payload.ray_time = ray->time; payload.num_hits = 0; payload.r_num_recorded_hits = num_recorded_hits; payload.r_throughput = throughput; GET_TRAVERSAL_STACK() - GET_TRAVERSAL_ANY_HIT(table_shadow_intersect, 1) + GET_TRAVERSAL_ANY_HIT(table_shadow_intersect, 1, ray->time) hiprtHit hit = traversal.getNextHit(); num_recorded_hits = payload.r_num_recorded_hits; throughput = payload.r_throughput; @@ -200,7 +200,7 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg, GET_TRAVERSAL_STACK() - GET_TRAVERSAL_CLOSEST_HIT(table_volume_intersect, 3) + GET_TRAVERSAL_CLOSEST_HIT(table_volume_intersect, 3, ray->time) hiprtHit hit = traversal.getNextHit(); // return hit.hasHit(); if (hit.hasHit()) { diff --git a/intern/cycles/kernel/device/hiprt/common.h b/intern/cycles/kernel/device/hiprt/common.h index 44e9846d42ae..05f902769c16 100644 --- a/intern/cycles/kernel/device/hiprt/common.h +++ b/intern/cycles/kernel/device/hiprt/common.h @@ -52,7 +52,7 @@ struct LocalPayload { # endif # ifdef HIPRT_SHARED_STACK -# define GET_TRAVERSAL_ANY_HIT(FUNCTION_TABLE, RAY_TYPE) \ +# define GET_TRAVERSAL_ANY_HIT(FUNCTION_TABLE, RAY_TYPE, RAY_TIME) \ hiprtSceneTraversalAnyHitCustomStack traversal(kernel_data.device_bvh, \ ray_hip, \ stack, \ @@ -60,10 +60,10 @@ struct LocalPayload { hiprtTraversalHintDefault, \ &payload, \ kernel_params.FUNCTION_TABLE, \ - RAY_TYPE); \ - hiprtSceneTraversalAnyHitCustomStack traversal_simple( \ - kernel_data.device_bvh, ray_hip, stack, visibility); -# define GET_TRAVERSAL_CLOSEST_HIT(FUNCTION_TABLE, RAY_TYPE) \ + RAY_TYPE, \ + RAY_TIME); + +# define GET_TRAVERSAL_CLOSEST_HIT(FUNCTION_TABLE, RAY_TYPE, RAY_TIME) \ hiprtSceneTraversalClosestCustomStack traversal(kernel_data.device_bvh, \ ray_hip, \ stack, \ @@ -71,9 +71,8 @@ struct LocalPayload { hiprtTraversalHintDefault, \ &payload, \ kernel_params.FUNCTION_TABLE, \ - RAY_TYPE); \ - hiprtSceneTraversalClosestCustomStack traversal_simple( \ - kernel_data.device_bvh, ray_hip, stack, visibility); + RAY_TYPE, \ + RAY_TIME); # else # define GET_TRAVERSAL_ANY_HIT(FUNCTION_TABLE) \ hiprtSceneTraversalAnyHit traversal(kernel_data.device_bvh, \ @@ -81,16 +80,14 @@ struct LocalPayload { visibility, \ FUNCTION_TABLE, \ hiprtTraversalHintDefault, \ - &payload); \ - hiprtSceneTraversalAnyHit traversal_simple(kernel_data.device_bvh, ray_hip, visibility); + &payload); # define GET_TRAVERSAL_CLOSEST_HIT(FUNCTION_TABLE) \ hiprtSceneTraversalClosest traversal(kernel_data.device_bvh, \ ray_hip, \ visibility, \ FUNCTION_TABLE, \ hiprtTraversalHintDefault, \ - &payload); \ - hiprtSceneTraversalClosest traversal_simple(kernel_data.device_bvh, ray_hip, visibility); + &payload); # endif ccl_device_inline void set_intersect_point(KernelGlobals kg, From 66a6ef01636c9eb44325be335a4b96ba444665a4 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Tue, 20 Jun 2023 21:32:54 +0200 Subject: [PATCH 35/36] Fix #109045: Allow images with more than 4 channels to load Align behavior to what Cycles does in such cases which is to drop the additional channels that are not supported. This was a regression from aa3bdfd76a3 Pull Request: https://projects.blender.org/blender/blender/pulls/109063 --- source/blender/imbuf/intern/oiio/openimageio_support.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/imbuf/intern/oiio/openimageio_support.cc b/source/blender/imbuf/intern/oiio/openimageio_support.cc index 3ca17f64417b..71ade1b7f0de 100644 --- a/source/blender/imbuf/intern/oiio/openimageio_support.cc +++ b/source/blender/imbuf/intern/oiio/openimageio_support.cc @@ -178,11 +178,12 @@ static ImBuf *get_oiio_ibuf(ImageInput *in, const ReadContext &ctx, char colorsp const ImageSpec &spec = in->spec(); const int width = spec.width; const int height = spec.height; - const int channels = spec.nchannels; const bool has_alpha = spec.alpha_channel != -1; const bool is_float = spec.format.basesize() > 1; - if (channels < 1 || channels > 4) { + /* Only a maximum of 4 channels are supported by ImBuf. */ + const int channels = spec.nchannels <= 4 ? spec.nchannels : 4; + if (channels < 1) { return nullptr; } From 2ef27684c56c87158d89073afebb062e6c1081fd Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 20 Jun 2023 16:32:15 -0400 Subject: [PATCH 36/36] Fix #108930: Mirror modifier handles custom normals incorrectly Before 17d161f56537d424d369, the "vec_lnor" stored the automatically computed normal without any influence from custom normals. But after, the normals were used from the final output corner normals array, which did have the custom normal data factored in at the end. This fix is to revert part of that commit, storing the automatically computed normal per space like before. --- source/blender/blenkernel/BKE_mesh.hh | 9 ++--- .../blender/blenkernel/intern/mesh_mirror.cc | 7 ++-- .../blender/blenkernel/intern/mesh_normals.cc | 35 +++++++++---------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.hh b/source/blender/blenkernel/BKE_mesh.hh index f966c34454a4..2c40f2701e56 100644 --- a/source/blender/blenkernel/BKE_mesh.hh +++ b/source/blender/blenkernel/BKE_mesh.hh @@ -91,13 +91,15 @@ void normals_calc_poly_vert(Span vert_positions, * a regular #float3 format. */ struct CornerNormalSpace { - /** Reference vector, orthogonal to corner normal. */ + /** The automatically computed face corner normal, not including influence of custom normals. */ + float3 vec_lnor; + /** Reference vector, orthogonal to #vec_lnor. */ float3 vec_ref; - /** Third vector, orthogonal to corner normal and #vec_ref. */ + /** Third vector, orthogonal to #vec_lnor and #vec_ref. */ float3 vec_ortho; /** Reference angle around #vec_ortho, in [0, pi] range (0.0 marks space as invalid). */ float ref_alpha; - /** Reference angle around corner normal, in [0, 2pi] range (0.0 marks space as invalid). */ + /** Reference angle around #vec_lnor, in [0, 2pi] range (0.0 marks space as invalid). */ float ref_beta; }; @@ -129,7 +131,6 @@ struct CornerNormalSpaceArray { }; void lnor_space_custom_normal_to_data(const CornerNormalSpace *lnor_space, - float3 lnor_no_custom, const float custom_lnor[3], short r_clnor_data[2]); diff --git a/source/blender/blenkernel/intern/mesh_mirror.cc b/source/blender/blenkernel/intern/mesh_mirror.cc index 4cecb3430079..19abeadfbb12 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.cc +++ b/source/blender/blenkernel/intern/mesh_mirror.cc @@ -431,15 +431,12 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, mirrorj += result_polys[mirror_i].size() - (j - src_poly.start()); } - const blender::float3 orig_normal = loop_normals[mirrorj]; copy_v3_v3(loop_normals[mirrorj], loop_normals[j]); mul_m4_v3(mtx_nor, loop_normals[mirrorj]); const int space_index = lnors_spacearr.corner_space_indices[mirrorj]; - blender::bke::mesh::lnor_space_custom_normal_to_data(&lnors_spacearr.spaces[space_index], - orig_normal, - loop_normals[mirrorj], - clnors[mirrorj]); + blender::bke::mesh::lnor_space_custom_normal_to_data( + &lnors_spacearr.spaces[space_index], loop_normals[mirrorj], clnors[mirrorj]); } } } diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 545c180d2ff1..2e997ebf3d14 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -488,6 +488,8 @@ static void lnor_space_define(CornerNormalSpace *lnor_space, return; } + lnor_space->vec_lnor = lnor; + /* Compute ref alpha, average angle of all available edge vectors to lnor. */ if (!edge_vectors.is_empty()) { float alpha = 0.0f; @@ -542,7 +544,7 @@ void BKE_lnor_space_define(MLoopNorSpace *lnor_space, using namespace blender::bke::mesh; CornerNormalSpace space{}; lnor_space_define(&space, lnor, vec_ref, vec_other, edge_vectors); - copy_v3_v3(lnor_space->vec_lnor, lnor); + copy_v3_v3(lnor_space->vec_lnor, space.vec_lnor); copy_v3_v3(lnor_space->vec_ref, space.vec_ref); copy_v3_v3(lnor_space->vec_ortho, space.vec_ortho); lnor_space->ref_alpha = space.ref_alpha; @@ -586,13 +588,12 @@ MINLINE short unit_float_to_short(const float val) namespace blender::bke::mesh { static void lnor_space_custom_data_to_normal(const CornerNormalSpace *lnor_space, - const float3 lnor_no_custom, const short clnor_data[2], float r_custom_lnor[3]) { /* NOP custom normal data or invalid lnor space, return. */ if (clnor_data[0] == 0 || lnor_space->ref_alpha == 0.0f || lnor_space->ref_beta == 0.0f) { - copy_v3_v3(r_custom_lnor, lnor_no_custom); + copy_v3_v3(r_custom_lnor, lnor_space->vec_lnor); return; } @@ -605,7 +606,7 @@ static void lnor_space_custom_data_to_normal(const CornerNormalSpace *lnor_space alphafac; const float betafac = unit_short_to_float(clnor_data[1]); - mul_v3_v3fl(r_custom_lnor, lnor_no_custom, cosf(alpha)); + mul_v3_v3fl(r_custom_lnor, lnor_space->vec_lnor, cosf(alpha)); if (betafac == 0.0f) { madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ref, sinf(alpha)); @@ -627,29 +628,29 @@ void BKE_lnor_space_custom_data_to_normal(const MLoopNorSpace *lnor_space, { using namespace blender::bke::mesh; CornerNormalSpace space; + space.vec_lnor = lnor_space->vec_lnor; space.vec_ref = lnor_space->vec_ref; space.vec_ortho = lnor_space->vec_ortho; space.ref_alpha = lnor_space->ref_alpha; space.ref_beta = lnor_space->ref_beta; - lnor_space_custom_data_to_normal(&space, lnor_space->vec_lnor, clnor_data, r_custom_lnor); + lnor_space_custom_data_to_normal(&space, clnor_data, r_custom_lnor); } namespace blender::bke::mesh { void lnor_space_custom_normal_to_data(const CornerNormalSpace *lnor_space, - const float3 lnor_no_custom, const float custom_lnor[3], short r_clnor_data[2]) { /* We use nullptr vector as NOP custom normal (can be simpler than giving auto-computed `lnor`). */ - if (is_zero_v3(custom_lnor) || compare_v3v3(lnor_no_custom, custom_lnor, 1e-4f)) { + if (is_zero_v3(custom_lnor) || compare_v3v3(lnor_space->vec_lnor, custom_lnor, 1e-4f)) { r_clnor_data[0] = r_clnor_data[1] = 0; return; } { const float pi2 = float(M_PI * 2.0); - const float cos_alpha = dot_v3v3(lnor_no_custom, custom_lnor); + const float cos_alpha = dot_v3v3(lnor_space->vec_lnor, custom_lnor); float vec[3], cos_beta; float alpha; @@ -664,7 +665,7 @@ void lnor_space_custom_normal_to_data(const CornerNormalSpace *lnor_space, } /* Project custom lnor on (vec_ref, vec_ortho) plane. */ - mul_v3_v3fl(vec, lnor_no_custom, -cos_alpha); + mul_v3_v3fl(vec, lnor_space->vec_lnor, -cos_alpha); add_v3_v3(vec, custom_lnor); normalize_v3(vec); @@ -696,11 +697,12 @@ void BKE_lnor_space_custom_normal_to_data(const MLoopNorSpace *lnor_space, { using namespace blender::bke::mesh; CornerNormalSpace space; + space.vec_lnor = lnor_space->vec_lnor; space.vec_ref = lnor_space->vec_ref; space.vec_ortho = lnor_space->vec_ortho; space.ref_alpha = lnor_space->ref_alpha; space.ref_beta = lnor_space->ref_beta; - lnor_space_custom_normal_to_data(&space, lnor_space->vec_lnor, custom_lnor, r_clnor_data); + lnor_space_custom_normal_to_data(&space, custom_lnor, r_clnor_data); } namespace blender::bke::mesh { @@ -917,10 +919,8 @@ static void lnor_space_for_single_fan(LoopSplitTaskDataCommon *common_data, lnors_spacearr->corner_space_indices[ml_curr_index] = space_index; if (!clnors_data.is_empty()) { - lnor_space_custom_data_to_normal(lnor_space, - loop_normals[ml_curr_index], - clnors_data[ml_curr_index], - loop_normals[ml_curr_index]); + lnor_space_custom_data_to_normal( + lnor_space, clnors_data[ml_curr_index], loop_normals[ml_curr_index]); } if (!lnors_spacearr->corners_by_space.is_empty()) { @@ -1100,7 +1100,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, /* Extra bonus: since small-stack is local to this function, * no more need to empty it at all cost! */ - lnor_space_custom_data_to_normal(lnor_space, lnor, *clnor_ref, lnor); + lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor); } } @@ -1616,8 +1616,7 @@ static void mesh_normals_loop_custom_set(Span positions, float *nor = r_custom_loop_normals[nidx]; const int space_index = lnors_spacearr.corner_space_indices[i]; - lnor_space_custom_normal_to_data( - &lnors_spacearr.spaces[space_index], loop_normals[i], nor, r_clnors_data[i]); + lnor_space_custom_normal_to_data(&lnors_spacearr.spaces[space_index], nor, r_clnors_data[i]); done_loops[i].reset(); } else { @@ -1634,7 +1633,7 @@ static void mesh_normals_loop_custom_set(Span positions, mul_v3_fl(avg_nor, 1.0f / float(fan_corners.size())); short2 clnor_data_tmp; lnor_space_custom_normal_to_data( - &lnors_spacearr.spaces[space_index], loop_normals[i], avg_nor, clnor_data_tmp); + &lnors_spacearr.spaces[space_index], avg_nor, clnor_data_tmp); r_clnors_data.fill_indices(fan_corners, clnor_data_tmp); }