diff options
105 files changed, 2662 insertions, 1321 deletions
diff --git a/release/scripts/startup/bl_operators/spreadsheet.py b/release/scripts/startup/bl_operators/spreadsheet.py index 1ba7d2db2fd..aef4231d573 100644 --- a/release/scripts/startup/bl_operators/spreadsheet.py +++ b/release/scripts/startup/bl_operators/spreadsheet.py @@ -32,7 +32,6 @@ class SPREADSHEET_OT_toggle_pin(Operator): def unpin(self, context): space = context.space_data space.is_pinned = False - space.context_path.guess() classes = ( diff --git a/release/scripts/startup/bl_ui/space_spreadsheet.py b/release/scripts/startup/bl_ui/space_spreadsheet.py index 741ad544d67..846582b0142 100644 --- a/release/scripts/startup/bl_ui/space_spreadsheet.py +++ b/release/scripts/startup/bl_ui/space_spreadsheet.py @@ -11,34 +11,37 @@ class SPREADSHEET_HT_header(bpy.types.Header): space = context.space_data layout.template_header() + viewer_path = space.viewer_path.path - if len(space.context_path) == 0: - self.draw_without_context_path(layout) + if len(viewer_path) == 0: + self.draw_without_viewer_path(layout) return - root_context = space.context_path[0] - if root_context.type != 'OBJECT': - self.draw_without_context_path(layout) + root_context = viewer_path[0] + if root_context.type != 'ID': + self.draw_without_viewer_path(layout) return - obj = root_context.object + if not isinstance(root_context.id, bpy.types.Object): + self.draw_without_viewer_path(layout) + return + obj = root_context.id if obj is None: - self.draw_without_context_path(layout) + self.draw_without_viewer_path(layout) return layout.prop(space, "object_eval_state", text="") - context_path = space.context_path if space.object_eval_state == 'ORIGINAL': # Only show first context. - context_path = context_path[:1] - if space.display_context_path_collapsed: - self.draw_collapsed_context_path(context, layout, context_path) + viewer_path = viewer_path[:1] + if space.display_viewer_path_collapsed: + self.draw_collapsed_viewer_path(context, layout, viewer_path) else: - self.draw_full_context_path(context, layout, context_path) + self.draw_full_viewer_path(context, layout, viewer_path) pin_icon = 'PINNED' if space.is_pinned else 'UNPINNED' layout.operator("spreadsheet.toggle_pin", text="", icon=pin_icon, emboss=False) - if space.object_eval_state == 'VIEWER_NODE' and len(context_path) < 3: + if space.object_eval_state == 'VIEWER_NODE' and len(viewer_path) < 3: layout.label(text="No active viewer node", icon='INFO') layout.separator_spacer() @@ -49,50 +52,52 @@ class SPREADSHEET_HT_header(bpy.types.Header): sub.prop(space, "show_only_selected", text="") row.prop(space, "use_filter", toggle=True, icon='FILTER', icon_only=True) - def draw_without_context_path(self, layout): + def draw_without_viewer_path(self, layout): layout.label(text="No active context") - def draw_full_context_path(self, context, layout, context_path): + def draw_full_viewer_path(self, context, layout, viewer_path): space = context.space_data row = layout.row() - for ctx in context_path[:-1]: + for ctx in viewer_path[:-1]: subrow = row.row(align=True) self.draw_spreadsheet_context(subrow, ctx) - self.draw_spreadsheet_context_path_icon(subrow, space) + self.draw_spreadsheet_viewer_path_icon(subrow, space) - self.draw_spreadsheet_context(row, context_path[-1]) + self.draw_spreadsheet_context(row, viewer_path[-1]) - def draw_collapsed_context_path(self, context, layout, context_path): + def draw_collapsed_viewer_path(self, context, layout, viewer_path): space = context.space_data row = layout.row(align=True) - self.draw_spreadsheet_context(row, context_path[0]) - if len(context_path) == 1: + self.draw_spreadsheet_context(row, viewer_path[0]) + if len(viewer_path) == 1: return - self.draw_spreadsheet_context_path_icon(row, space) - if len(context_path) > 2: - self.draw_spreadsheet_context_path_icon(row, space, icon='DOT') - self.draw_spreadsheet_context_path_icon(row, space) - self.draw_spreadsheet_context(row, context_path[-1]) + self.draw_spreadsheet_viewer_path_icon(row, space) + if len(viewer_path) > 2: + self.draw_spreadsheet_viewer_path_icon(row, space, icon='DOT') + self.draw_spreadsheet_viewer_path_icon(row, space) + self.draw_spreadsheet_context(row, viewer_path[-1]) def draw_spreadsheet_context(self, layout, ctx): - if ctx.type == 'OBJECT': - if ctx.object is None: - layout.label(text="<no object>", icon='OBJECT_DATA') + if ctx.type == 'ID': + if ctx.id is not None and isinstance(ctx.id, bpy.types.Object): + layout.label(text=ctx.id.name, icon='OBJECT_DATA') else: - layout.label(text=ctx.object.name, icon='OBJECT_DATA') + 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') - def draw_spreadsheet_context_path_icon(self, layout, space, icon='RIGHTARROW_THIN'): - layout.prop(space, "display_context_path_collapsed", icon_only=True, emboss=False, icon=icon) + 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) def selection_filter_available(self, space): - root_context = space.context_path[0] - if root_context.type != 'OBJECT': + root_context = space.viewer_path.path[0] + if root_context.type != 'ID': + return False + if not isinstance(root_context.id, bpy.types.Object): return False - obj = root_context.object + obj = root_context.id if obj is None: return False if obj.type == 'MESH': diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index a687f3c937f..092fae671e0 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -1216,6 +1216,7 @@ class VIEW3D_MT_view(Menu): layout.operator("view3d.view_all").center = False layout.operator("view3d.view_persportho", text="Perspective/Orthographic") layout.menu("VIEW3D_MT_view_local") + layout.prop(view, "show_viewer", text="Viewer Node") layout.separator() @@ -6347,6 +6348,13 @@ class VIEW3D_PT_overlay_geometry(Panel): sub.prop(overlay, "wireframe_opacity", text="Opacity") row = col.row(align=True) + row.active = view.show_viewer + row.prop(overlay, "show_viewer_attribute", text="") + subrow = row.row(align=True) + subrow.active = overlay.show_viewer_attribute + subrow.prop(overlay, "viewer_attribute_opacity", text="Viewer Node") + + row = col.row(align=True) # These properties should be always available in the UI for all modes # other than Object. diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 28c15d9224c..4dd596ad93a 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -82,6 +82,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view2d_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view3d_enums.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view3d_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_viewer_path_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_volume_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_windowmanager_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_workspace_types.h diff --git a/source/blender/blenkernel/BKE_duplilist.h b/source/blender/blenkernel/BKE_duplilist.h index 0648cdde529..831cb5031c2 100644 --- a/source/blender/blenkernel/BKE_duplilist.h +++ b/source/blender/blenkernel/BKE_duplilist.h @@ -16,6 +16,8 @@ struct ListBase; struct Object; struct ParticleSystem; struct Scene; +struct ViewerPath; +struct GeomerySet; /* ---------------------------------------------------- */ /* Dupli-Geometry */ @@ -26,6 +28,13 @@ struct Scene; struct ListBase *object_duplilist(struct Depsgraph *depsgraph, struct Scene *sce, struct Object *ob); +/** + * \return a #ListBase of #DupliObject for the preview geometry referenced by the #ViewerPath. + */ +struct ListBase *object_duplilist_preview(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + const struct ViewerPath *viewer_path); void free_object_duplilist(struct ListBase *lb); typedef struct DupliObject { @@ -39,6 +48,10 @@ typedef struct DupliObject { short type; /* from Object.transflag */ char no_draw; + /* If this dupli object is belongs to a preview, this is non-null. */ + const struct GeometrySet *preview_base_geometry; + /* Index of the top-level instance this dupli is part of or -1 when unused. */ + int preview_instance_index; /* Persistent identifier for a dupli object, for inter-frame matching of * objects with motion blur, or inter-update matching for syncing. */ diff --git a/source/blender/blenkernel/BKE_geometry_fields.hh b/source/blender/blenkernel/BKE_geometry_fields.hh index 62aac5a4120..988e0017f04 100644 --- a/source/blender/blenkernel/BKE_geometry_fields.hh +++ b/source/blender/blenkernel/BKE_geometry_fields.hh @@ -145,6 +145,7 @@ class GeometryFieldInput : public fn::FieldInput { ResourceScope &scope) const override; virtual GVArray get_varray_for_context(const GeometryFieldContext &context, IndexMask mask) const = 0; + virtual std::optional<eAttrDomain> preferred_domain(const GeometryComponent &component) const; }; class MeshFieldInput : public fn::FieldInput { @@ -156,6 +157,7 @@ class MeshFieldInput : public fn::FieldInput { virtual GVArray get_varray_for_context(const Mesh &mesh, eAttrDomain domain, IndexMask mask) const = 0; + virtual std::optional<eAttrDomain> preferred_domain(const Mesh &mesh) const; }; class CurvesFieldInput : public fn::FieldInput { @@ -167,6 +169,7 @@ class CurvesFieldInput : public fn::FieldInput { virtual GVArray get_varray_for_context(const CurvesGeometry &curves, eAttrDomain domain, IndexMask mask) const = 0; + virtual std::optional<eAttrDomain> preferred_domain(const CurvesGeometry &curves) const; }; class PointCloudFieldInput : public fn::FieldInput { @@ -218,6 +221,7 @@ class AttributeFieldInput : public GeometryFieldInput { uint64_t hash() const override; bool is_equal_to(const fn::FieldNode &other) const override; + std::optional<eAttrDomain> preferred_domain(const GeometryComponent &component) const override; }; class IDAttributeFieldInput : public GeometryFieldInput { @@ -292,6 +296,7 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput { uint64_t hash() const override; bool is_equal_to(const fn::FieldNode &other) const override; + std::optional<eAttrDomain> preferred_domain(const GeometryComponent &component) const override; }; class CurveLengthFieldInput final : public CurvesFieldInput { @@ -304,4 +309,16 @@ class CurveLengthFieldInput final : public CurvesFieldInput { bool is_equal_to(const fn::FieldNode &other) const override; }; +bool try_capture_field_on_geometry(GeometryComponent &component, + const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const fn::GField &field); + +/** + * Try to find the geometry domain that the field should be evaluated on. If it is not obvious + * which domain is correct, none is returned. + */ +std::optional<eAttrDomain> try_detect_field_domain(const GeometryComponent &component, + const fn::GField &field); + } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_viewer_path.h b/source/blender/blenkernel/BKE_viewer_path.h new file mode 100644 index 00000000000..dfba289531f --- /dev/null +++ b/source/blender/blenkernel/BKE_viewer_path.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** + * A #ViewerPath is a path to data that is viewed/debugged by the user. It is a list of + * #ViewerPathElem. + * + * This is only used for geometry nodes currently. When the user activates a viewer node the + * corresponding path contains the following elements: + * - Object the viewer is activated on. + * - Modifier that contains the corresponding geometry node group. + * - Node tree path in case the viewer node is in a nested node group. + * - Viewer node name. + * + * The entire path is necessary (instead of just the combination of node group and viewer name), + * because the same node group may be used in many different places. + * + * This file contains basic functions for creating/deleting a #ViewerPath. For more use-case + * specific functions look in `ED_viewer_path.hh`. + */ + +#include "DNA_viewer_path_types.h" + +struct BlendWriter; +struct BlendDataReader; +struct BlendLibReader; +struct LibraryForeachIDData; +struct Library; +struct IDRemapper; + +#ifdef __cplusplus +extern "C" { +#endif + +void BKE_viewer_path_init(ViewerPath *viewer_path); +void BKE_viewer_path_clear(ViewerPath *viewer_path); +void BKE_viewer_path_copy(ViewerPath *dst, const ViewerPath *src); +bool BKE_viewer_path_equal(const ViewerPath *a, const ViewerPath *b); +void BKE_viewer_path_blend_write(struct BlendWriter *writer, const ViewerPath *viewer_path); +void BKE_viewer_path_blend_read_data(struct BlendDataReader *reader, ViewerPath *viewer_path); +void BKE_viewer_path_blend_read_lib(struct BlendLibReader *reader, + struct Library *lib, + ViewerPath *viewer_path); +void BKE_viewer_path_foreach_id(struct LibraryForeachIDData *data, ViewerPath *viewer_path); +void BKE_viewer_path_id_remap(ViewerPath *viewer_path, const struct IDRemapper *mappings); + +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); +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); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 877407a644c..ff6f279b9c6 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -297,6 +297,7 @@ set(SRC intern/unit.c intern/vfont.c intern/vfontdata_freetype.c + intern/viewer_path.cc intern/volume.cc intern/volume_render.cc intern/volume_to_mesh.cc @@ -482,6 +483,7 @@ set(SRC BKE_unit.h BKE_vfont.h BKE_vfontdata.h + BKE_viewer_path.h BKE_volume.h BKE_volume_render.h BKE_volume_to_mesh.hh diff --git a/source/blender/blenkernel/intern/geometry_fields.cc b/source/blender/blenkernel/intern/geometry_fields.cc index 56e9e9dcdff..e242154cb5b 100644 --- a/source/blender/blenkernel/intern/geometry_fields.cc +++ b/source/blender/blenkernel/intern/geometry_fields.cc @@ -157,6 +157,12 @@ GVArray GeometryFieldInput::get_varray_for_context(const fn::FieldContext &conte return {}; } +std::optional<eAttrDomain> GeometryFieldInput::preferred_domain( + const GeometryComponent & /*component*/) const +{ + return std::nullopt; +} + GVArray MeshFieldInput::get_varray_for_context(const fn::FieldContext &context, const IndexMask mask, ResourceScope & /*scope*/) const @@ -173,6 +179,11 @@ GVArray MeshFieldInput::get_varray_for_context(const fn::FieldContext &context, return {}; } +std::optional<eAttrDomain> MeshFieldInput::preferred_domain(const Mesh & /*mesh*/) const +{ + return std::nullopt; +} + GVArray CurvesFieldInput::get_varray_for_context(const fn::FieldContext &context, IndexMask mask, ResourceScope & /*scope*/) const @@ -190,6 +201,12 @@ GVArray CurvesFieldInput::get_varray_for_context(const fn::FieldContext &context return {}; } +std::optional<eAttrDomain> CurvesFieldInput::preferred_domain( + const CurvesGeometry & /*curves*/) const +{ + return std::nullopt; +} + GVArray PointCloudFieldInput::get_varray_for_context(const fn::FieldContext &context, IndexMask mask, ResourceScope & /*scope*/) const @@ -254,6 +271,20 @@ bool AttributeFieldInput::is_equal_to(const fn::FieldNode &other) const return false; } +std::optional<eAttrDomain> AttributeFieldInput::preferred_domain( + const GeometryComponent &component) const +{ + const std::optional<AttributeAccessor> attributes = component.attributes(); + if (!attributes.has_value()) { + return std::nullopt; + } + const std::optional<AttributeMetaData> meta_data = attributes->lookup_meta_data(name_); + if (!meta_data.has_value()) { + return std::nullopt; + } + return meta_data->domain; +} + static StringRef get_random_id_attribute_name(const eAttrDomain domain) { switch (domain) { @@ -325,6 +356,21 @@ bool AnonymousAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const return false; } +std::optional<eAttrDomain> AnonymousAttributeFieldInput::preferred_domain( + const GeometryComponent &component) const +{ + const std::optional<AttributeAccessor> attributes = component.attributes(); + if (!attributes.has_value()) { + return std::nullopt; + } + const std::optional<AttributeMetaData> meta_data = attributes->lookup_meta_data( + anonymous_id_.get()); + if (!meta_data.has_value()) { + return std::nullopt; + } + return meta_data->domain; +} + } // namespace blender::bke /* -------------------------------------------------------------------- */ @@ -360,6 +406,131 @@ bool NormalFieldInput::is_equal_to(const fn::FieldNode &other) const return dynamic_cast<const NormalFieldInput *>(&other) != nullptr; } +bool try_capture_field_on_geometry(GeometryComponent &component, + const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const fn::GField &field) +{ + MutableAttributeAccessor attributes = *component.attributes_for_write(); + const int domain_size = attributes.domain_size(domain); + const CPPType &type = field.cpp_type(); + const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(type); + + if (domain_size == 0) { + return attributes.add(attribute_id, domain, data_type, AttributeInitConstruct{}); + } + + bke::GeometryFieldContext field_context{component, domain}; + const IndexMask mask{IndexMask(domain_size)}; + const bke::AttributeValidator validator = attributes.lookup_validator(attribute_id); + + /* Could avoid allocating a new buffer if: + * - We are writing to an attribute that exists already with the correct domain and type. + * - The field does not depend on that attribute (we can't easily check for that yet). */ + void *buffer = MEM_mallocN(type.size() * domain_size, __func__); + + fn::FieldEvaluator evaluator{field_context, &mask}; + evaluator.add_with_destination(validator.validate_field_if_necessary(field), + GMutableSpan{type, buffer, domain_size}); + evaluator.evaluate(); + + if (GAttributeWriter attribute = attributes.lookup_for_write(attribute_id)) { + if (attribute.domain == domain && attribute.varray.type() == type) { + attribute.varray.set_all(buffer); + attribute.finish(); + type.destruct_n(buffer, domain_size); + MEM_freeN(buffer); + return true; + } + } + attributes.remove(attribute_id); + if (attributes.add(attribute_id, domain, data_type, bke::AttributeInitMoveArray{buffer})) { + return true; + } + + /* If the name corresponds to a builtin attribute, removing the attribute might fail if + * it's required, and adding the attribute might fail if the domain or type is incorrect. */ + type.destruct_n(buffer, domain_size); + MEM_freeN(buffer); + return false; +} + +std::optional<eAttrDomain> try_detect_field_domain(const GeometryComponent &component, + const fn::GField &field) +{ + const GeometryComponentType component_type = component.type(); + if (component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) { + return ATTR_DOMAIN_POINT; + } + if (component_type == GEO_COMPONENT_TYPE_INSTANCES) { + return ATTR_DOMAIN_INSTANCE; + } + const std::shared_ptr<const fn::FieldInputs> &field_inputs = field.node().field_inputs(); + if (!field_inputs) { + return std::nullopt; + } + std::optional<eAttrDomain> output_domain; + auto handle_domain = [&](const std::optional<eAttrDomain> domain) { + if (!domain.has_value()) { + return false; + } + if (output_domain.has_value()) { + if (*output_domain != *domain) { + return false; + } + return true; + } + output_domain = domain; + return true; + }; + if (component_type == GEO_COMPONENT_TYPE_MESH) { + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return std::nullopt; + } + for (const fn::FieldInput &field_input : field_inputs->deduplicated_nodes) { + if (auto geometry_field_input = dynamic_cast<const GeometryFieldInput *>(&field_input)) { + if (!handle_domain(geometry_field_input->preferred_domain(component))) { + return std::nullopt; + } + } + else if (auto mesh_field_input = dynamic_cast<const MeshFieldInput *>(&field_input)) { + if (!handle_domain(mesh_field_input->preferred_domain(*mesh))) { + return std::nullopt; + } + } + else { + return std::nullopt; + } + } + } + if (component_type == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + const Curves *curves = curve_component.get_for_read(); + if (curves == nullptr) { + return std::nullopt; + } + for (const fn::FieldInput &field_input : field_inputs->deduplicated_nodes) { + if (auto geometry_field_input = dynamic_cast<const GeometryFieldInput *>(&field_input)) { + if (!handle_domain(geometry_field_input->preferred_domain(component))) { + return std::nullopt; + } + } + else if (auto curves_field_input = dynamic_cast<const CurvesFieldInput *>(&field_input)) { + if (!handle_domain( + curves_field_input->preferred_domain(CurvesGeometry::wrap(curves->geometry)))) { + return std::nullopt; + } + } + else { + return std::nullopt; + } + } + } + return output_domain; +} + } // namespace blender::bke /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 0dcb2aa3139..7d381ed2a69 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -3114,7 +3114,7 @@ void ntreeSetOutput(bNodeTree *ntree) LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->typeinfo->nclass == NODE_CLASS_OUTPUT) { /* we need a check for which output node should be tagged like this, below an exception */ - if (node->type == CMP_NODE_OUTPUT_FILE) { + if (ELEM(node->type, CMP_NODE_OUTPUT_FILE, GEO_NODE_VIEWER)) { continue; } @@ -3125,8 +3125,8 @@ void ntreeSetOutput(bNodeTree *ntree) if (ntree->type == NTREE_COMPOSIT) { /* same type, exception for viewer */ if (tnode->type == node->type || - (ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER) && - ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER))) { + (ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER) && + ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))) { if (tnode->flag & NODE_DO_OUTPUT) { output++; if (output > 1) { diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index e28aaabb4e1..15a4a0bbb32 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -24,8 +24,10 @@ #include "DNA_anim_types.h" #include "DNA_collection_types.h" +#include "DNA_curves_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_pointcloud_types.h" #include "DNA_scene_types.h" #include "DNA_vfont_types.h" @@ -44,6 +46,7 @@ #include "BKE_mesh.h" #include "BKE_mesh_iterators.h" #include "BKE_mesh_runtime.h" +#include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_scene.h" @@ -53,13 +56,15 @@ #include "DEG_depsgraph_query.h" #include "BLI_hash.h" -#include "BLI_strict_flags.h" + +#include "NOD_geometry_nodes_log.hh" using blender::Array; using blender::float3; using blender::float4x4; using blender::Span; using blender::Vector; +namespace geo_log = blender::nodes::geo_eval_log; /* -------------------------------------------------------------------- */ /** \name Internal Duplicate Context @@ -75,6 +80,15 @@ struct DupliContext { Scene *scene; Object *object; float space_mat[4][4]; + /** + * Index of the top-level instance that contains this context or -1 when unused. + * This is an index into the instances component of #preview_base_geometry. + */ + int preview_instance_index; + /** + * Top level geometry set that is previewed. + */ + const GeometrySet *preview_base_geometry; /** * A stack that contains all the "parent" objects of a particular instance when recursive @@ -127,6 +141,8 @@ static void init_context(DupliContext *r_ctx, r_ctx->gen = get_dupli_generator(r_ctx); r_ctx->duplilist = nullptr; + r_ctx->preview_instance_index = -1; + r_ctx->preview_base_geometry = nullptr; } /** @@ -139,7 +155,7 @@ static bool copy_dupli_context( /* XXX annoying, previously was done by passing an ID* argument, * this at least is more explicit. */ - if (ctx->gen->type == OB_DUPLICOLLECTION) { + if (ctx->gen && ctx->gen->type == OB_DUPLICOLLECTION) { r_ctx->collection = ctx->object->instance_collection; } @@ -183,7 +199,9 @@ static DupliObject *make_dupli( dob->ob = ob; dob->ob_data = const_cast<ID *>(object_data); mul_m4_m4m4(dob->mat, (float(*)[4])ctx->space_mat, mat); - dob->type = ctx->gen->type; + dob->type = ctx->gen == nullptr ? 0 : ctx->gen->type; + dob->preview_base_geometry = ctx->preview_base_geometry; + dob->preview_instance_index = ctx->preview_instance_index; /* Set persistent id, which is an array with a persistent index for each level * (particle number, vertex number, ..). by comparing this we can find the same @@ -780,7 +798,8 @@ static const DupliGenerator gen_dupli_verts_font = { static void make_duplis_geometry_set_impl(const DupliContext *ctx, const GeometrySet &geometry_set, const float parent_transform[4][4], - bool geometry_set_is_instance) + bool geometry_set_is_instance, + bool use_new_curves_type) { int component_index = 0; if (ctx->object->type != OB_MESH || geometry_set_is_instance) { @@ -795,8 +814,15 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, } if (!ELEM(ctx->object->type, OB_CURVES_LEGACY, OB_FONT, OB_CURVES) || geometry_set_is_instance) { if (const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>()) { - if (const Curve *curve = component->get_curve_for_render()) { - make_dupli(ctx, ctx->object, &curve->id, parent_transform, component_index++); + if (use_new_curves_type) { + if (const Curves *curves = component->get_for_read()) { + make_dupli(ctx, ctx->object, &curves->id, parent_transform, component_index++); + } + } + else { + if (const Curve *curve = component->get_curve_for_render()) { + make_dupli(ctx, ctx->object, &curve->id, parent_transform, component_index++); + } } } } @@ -832,17 +858,26 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, const InstanceReference &reference = references[instance_reference_handles[i]]; const int id = almost_unique_ids[i]; + const DupliContext *ctx_for_instance = instances_ctx; + /* Set the #preview_instance_index when necessary. */ + DupliContext tmp_ctx_for_instance; + if (instances_ctx->preview_base_geometry == &geometry_set) { + tmp_ctx_for_instance = *instances_ctx; + tmp_ctx_for_instance.preview_instance_index = i; + ctx_for_instance = &tmp_ctx_for_instance; + } + switch (reference.type()) { case InstanceReference::Type::Object: { Object &object = reference.object(); float matrix[4][4]; mul_m4_m4m4(matrix, parent_transform, instance_offset_matrices[i].values); - make_dupli(instances_ctx, &object, matrix, id); + make_dupli(ctx_for_instance, &object, matrix, id); float space_matrix[4][4]; mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object.imat); mul_m4_m4_pre(space_matrix, parent_transform); - make_recursive_duplis(instances_ctx, &object, space_matrix, id); + make_recursive_duplis(ctx_for_instance, &object, space_matrix, id); break; } case InstanceReference::Type::Collection: { @@ -854,14 +889,15 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, mul_m4_m4_pre(collection_matrix, parent_transform); DupliContext sub_ctx; - if (!copy_dupli_context(&sub_ctx, instances_ctx, instances_ctx->object, nullptr, id)) { + if (!copy_dupli_context( + &sub_ctx, ctx_for_instance, ctx_for_instance->object, nullptr, id)) { break; } - eEvaluationMode mode = DEG_get_mode(instances_ctx->depsgraph); + eEvaluationMode mode = DEG_get_mode(ctx_for_instance->depsgraph); int object_id = 0; FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (&collection, object, mode) { - if (object == instances_ctx->object) { + if (object == ctx_for_instance->object) { continue; } @@ -879,8 +915,10 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, mul_m4_m4m4(new_transform, parent_transform, instance_offset_matrices[i].values); DupliContext sub_ctx; - if (copy_dupli_context(&sub_ctx, instances_ctx, instances_ctx->object, nullptr, id)) { - make_duplis_geometry_set_impl(&sub_ctx, reference.geometry_set(), new_transform, true); + if (copy_dupli_context( + &sub_ctx, ctx_for_instance, ctx_for_instance->object, nullptr, id)) { + make_duplis_geometry_set_impl( + &sub_ctx, reference.geometry_set(), new_transform, true, false); } break; } @@ -894,7 +932,7 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, static void make_duplis_geometry_set(const DupliContext *ctx) { const GeometrySet *geometry_set = ctx->object->runtime.geometry_set_eval; - make_duplis_geometry_set_impl(ctx, *geometry_set, ctx->object->obmat, false); + make_duplis_geometry_set_impl(ctx, *geometry_set, ctx->object->obmat, false, false); } static const DupliGenerator gen_dupli_geometry_set = { @@ -1632,6 +1670,40 @@ ListBase *object_duplilist(Depsgraph *depsgraph, Scene *sce, Object *ob) return duplilist; } +ListBase *object_duplilist_preview(Depsgraph *depsgraph, + Scene *sce, + Object *ob_eval, + const ViewerPath *viewer_path) +{ + ListBase *duplilist = MEM_cnew<ListBase>("duplilist"); + DupliContext ctx; + Vector<Object *> instance_stack; + instance_stack.append(ob_eval); + init_context(&ctx, depsgraph, sce, ob_eval, nullptr, instance_stack); + ctx.duplilist = duplilist; + + Object *ob_orig = DEG_get_original_object(ob_eval); + + LISTBASE_FOREACH (ModifierData *, md_orig, &ob_orig->modifiers) { + if (md_orig->type != eModifierType_Nodes) { + continue; + } + NodesModifierData *nmd_orig = reinterpret_cast<NodesModifierData *>(md_orig); + if (nmd_orig->runtime_eval_log == nullptr) { + continue; + } + geo_log::GeoModifierLog *log = static_cast<geo_log::GeoModifierLog *>( + nmd_orig->runtime_eval_log); + if (const geo_log::ViewerNodeLog *viewer_log = log->find_viewer_node_log_for_path( + *viewer_path)) { + ctx.preview_base_geometry = &viewer_log->geometry; + make_duplis_geometry_set_impl( + &ctx, viewer_log->geometry, ob_eval->obmat, true, ob_eval->type == OB_CURVES); + } + } + return duplilist; +} + void free_object_duplilist(ListBase *lb) { BLI_freelistN(lb); diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 8517995a0d8..3e5dbc47c40 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -46,6 +46,7 @@ #include "BKE_lib_query.h" #include "BKE_node.h" #include "BKE_screen.h" +#include "BKE_viewer_path.h" #include "BKE_workspace.h" #include "BLO_read_write.h" @@ -97,6 +98,7 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area if (v3d->localvd) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->localvd->camera, IDWALK_CB_NOP); } + BKE_viewer_path_foreach_id(data, &v3d->viewer_path); break; } case SPACE_GRAPH: { @@ -197,12 +199,7 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_SPREADSHEET: { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { - if (context->type == SPREADSHEET_CONTEXT_OBJECT) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER( - data, ((SpreadsheetContextObject *)context)->object, IDWALK_CB_NOP); - } - } + BKE_viewer_path_foreach_id(data, &sspreadsheet->viewer_path); break; } default: diff --git a/source/blender/blenkernel/intern/viewer_path.cc b/source/blender/blenkernel/intern/viewer_path.cc new file mode 100644 index 00000000000..3074a007af4 --- /dev/null +++ b/source/blender/blenkernel/intern/viewer_path.cc @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_lib_query.h" +#include "BKE_lib_remap.h" +#include "BKE_viewer_path.h" + +#include "BLI_index_range.hh" +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_string_ref.hh" + +#include "MEM_guardedalloc.h" + +#include "BLO_read_write.h" + +using blender::IndexRange; +using blender::StringRef; + +void BKE_viewer_path_init(ViewerPath *viewer_path) +{ + BLI_listbase_clear(&viewer_path->path); +} + +void BKE_viewer_path_clear(ViewerPath *viewer_path) +{ + LISTBASE_FOREACH_MUTABLE (ViewerPathElem *, elem, &viewer_path->path) { + BKE_viewer_path_elem_free(elem); + } + BLI_listbase_clear(&viewer_path->path); +} + +void BKE_viewer_path_copy(ViewerPath *dst, const ViewerPath *src) +{ + BKE_viewer_path_init(dst); + LISTBASE_FOREACH (const ViewerPathElem *, src_elem, &src->path) { + ViewerPathElem *new_elem = BKE_viewer_path_elem_copy(src_elem); + BLI_addtail(&dst->path, new_elem); + } +} + +bool BKE_viewer_path_equal(const ViewerPath *a, const ViewerPath *b) +{ + const ViewerPathElem *elem_a = static_cast<const ViewerPathElem *>(a->path.first); + const ViewerPathElem *elem_b = static_cast<const ViewerPathElem *>(b->path.first); + + while (elem_a != nullptr && elem_b != nullptr) { + if (!BKE_viewer_path_elem_equal(elem_a, elem_b)) { + return false; + } + elem_a = elem_a->next; + elem_b = elem_b->next; + } + if (elem_a == nullptr && elem_b == nullptr) { + return true; + } + return false; +} + +void BKE_viewer_path_blend_write(struct BlendWriter *writer, const ViewerPath *viewer_path) +{ + LISTBASE_FOREACH (ViewerPathElem *, elem, &viewer_path->path) { + switch (ViewerPathElemType(elem->type)) { + case VIEWER_PATH_ELEM_TYPE_ID: { + auto typed_elem = reinterpret_cast<IDViewerPathElem *>(elem); + BLO_write_struct(writer, IDViewerPathElem, typed_elem); + break; + } + case VIEWER_PATH_ELEM_TYPE_MODIFIER: { + auto typed_elem = reinterpret_cast<ModifierViewerPathElem *>(elem); + BLO_write_struct(writer, ModifierViewerPathElem, typed_elem); + BLO_write_string(writer, typed_elem->modifier_name); + break; + } + case VIEWER_PATH_ELEM_TYPE_NODE: { + auto typed_elem = reinterpret_cast<NodeViewerPathElem *>(elem); + BLO_write_struct(writer, NodeViewerPathElem, typed_elem); + BLO_write_string(writer, typed_elem->node_name); + break; + } + } + } +} + +void BKE_viewer_path_blend_read_data(struct BlendDataReader *reader, ViewerPath *viewer_path) +{ + BLO_read_list(reader, &viewer_path->path); + LISTBASE_FOREACH (ViewerPathElem *, elem, &viewer_path->path) { + switch (ViewerPathElemType(elem->type)) { + case VIEWER_PATH_ELEM_TYPE_ID: { + break; + } + case VIEWER_PATH_ELEM_TYPE_MODIFIER: { + auto typed_elem = reinterpret_cast<ModifierViewerPathElem *>(elem); + BLO_read_data_address(reader, &typed_elem->modifier_name); + break; + } + case VIEWER_PATH_ELEM_TYPE_NODE: { + auto typed_elem = reinterpret_cast<NodeViewerPathElem *>(elem); + BLO_read_data_address(reader, &typed_elem->node_name); + break; + } + } + } +} + +void BKE_viewer_path_blend_read_lib(BlendLibReader *reader, Library *lib, ViewerPath *viewer_path) +{ + LISTBASE_FOREACH (ViewerPathElem *, elem, &viewer_path->path) { + switch (ViewerPathElemType(elem->type)) { + case VIEWER_PATH_ELEM_TYPE_ID: { + auto typed_elem = reinterpret_cast<IDViewerPathElem *>(elem); + BLO_read_id_address(reader, lib, &typed_elem->id); + break; + } + case VIEWER_PATH_ELEM_TYPE_MODIFIER: + case VIEWER_PATH_ELEM_TYPE_NODE: { + break; + } + } + } +} + +void BKE_viewer_path_foreach_id(LibraryForeachIDData *data, ViewerPath *viewer_path) +{ + LISTBASE_FOREACH (ViewerPathElem *, elem, &viewer_path->path) { + switch (ViewerPathElemType(elem->type)) { + case VIEWER_PATH_ELEM_TYPE_ID: { + auto typed_elem = reinterpret_cast<IDViewerPathElem *>(elem); + BKE_LIB_FOREACHID_PROCESS_ID(data, typed_elem->id, IDWALK_CB_NOP); + break; + } + case VIEWER_PATH_ELEM_TYPE_MODIFIER: + case VIEWER_PATH_ELEM_TYPE_NODE: { + break; + } + } + } +} + +void BKE_viewer_path_id_remap(ViewerPath *viewer_path, const IDRemapper *mappings) +{ + LISTBASE_FOREACH (ViewerPathElem *, elem, &viewer_path->path) { + switch (ViewerPathElemType(elem->type)) { + case VIEWER_PATH_ELEM_TYPE_ID: { + auto typed_elem = reinterpret_cast<IDViewerPathElem *>(elem); + BKE_id_remapper_apply(mappings, &typed_elem->id, ID_REMAP_APPLY_DEFAULT); + break; + } + case VIEWER_PATH_ELEM_TYPE_MODIFIER: + case VIEWER_PATH_ELEM_TYPE_NODE: { + break; + } + } + } +} + +ViewerPathElem *BKE_viewer_path_elem_new(const ViewerPathElemType type) +{ + switch (type) { + case VIEWER_PATH_ELEM_TYPE_ID: { + IDViewerPathElem *elem = MEM_cnew<IDViewerPathElem>(__func__); + elem->base.type = type; + return &elem->base; + } + case VIEWER_PATH_ELEM_TYPE_MODIFIER: { + ModifierViewerPathElem *elem = MEM_cnew<ModifierViewerPathElem>(__func__); + elem->base.type = type; + return &elem->base; + } + case VIEWER_PATH_ELEM_TYPE_NODE: { + NodeViewerPathElem *elem = MEM_cnew<NodeViewerPathElem>(__func__); + elem->base.type = type; + return &elem->base; + } + } + BLI_assert_unreachable(); + return nullptr; +} + +IDViewerPathElem *BKE_viewer_path_elem_new_id() +{ + return reinterpret_cast<IDViewerPathElem *>(BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_ID)); +} + +ModifierViewerPathElem *BKE_viewer_path_elem_new_modifier() +{ + return reinterpret_cast<ModifierViewerPathElem *>( + BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_MODIFIER)); +} + +NodeViewerPathElem *BKE_viewer_path_elem_new_node() +{ + return reinterpret_cast<NodeViewerPathElem *>( + BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_NODE)); +} + +ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src) +{ + ViewerPathElem *dst = BKE_viewer_path_elem_new(ViewerPathElemType(src->type)); + switch (ViewerPathElemType(src->type)) { + case VIEWER_PATH_ELEM_TYPE_ID: { + auto old_elem = reinterpret_cast<const IDViewerPathElem *>(src); + auto new_elem = reinterpret_cast<IDViewerPathElem *>(dst); + new_elem->id = old_elem->id; + break; + } + case VIEWER_PATH_ELEM_TYPE_MODIFIER: { + auto old_elem = reinterpret_cast<const ModifierViewerPathElem *>(src); + auto new_elem = reinterpret_cast<ModifierViewerPathElem *>(dst); + if (old_elem->modifier_name != nullptr) { + new_elem->modifier_name = BLI_strdup(old_elem->modifier_name); + } + break; + } + case VIEWER_PATH_ELEM_TYPE_NODE: { + auto old_elem = reinterpret_cast<const NodeViewerPathElem *>(src); + auto new_elem = reinterpret_cast<NodeViewerPathElem *>(dst); + if (old_elem->node_name != nullptr) { + new_elem->node_name = BLI_strdup(old_elem->node_name); + } + break; + } + } + return dst; +} + +bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, const ViewerPathElem *b) +{ + if (a->type != b->type) { + return false; + } + switch (ViewerPathElemType(a->type)) { + case VIEWER_PATH_ELEM_TYPE_ID: { + auto a_elem = reinterpret_cast<const IDViewerPathElem *>(a); + auto b_elem = reinterpret_cast<const IDViewerPathElem *>(b); + return a_elem->id == b_elem->id; + } + case VIEWER_PATH_ELEM_TYPE_MODIFIER: { + auto a_elem = reinterpret_cast<const ModifierViewerPathElem *>(a); + auto b_elem = reinterpret_cast<const ModifierViewerPathElem *>(b); + return StringRef(a_elem->modifier_name) == StringRef(b_elem->modifier_name); + } + case VIEWER_PATH_ELEM_TYPE_NODE: { + auto a_elem = reinterpret_cast<const NodeViewerPathElem *>(a); + auto b_elem = reinterpret_cast<const NodeViewerPathElem *>(b); + return StringRef(a_elem->node_name) == StringRef(b_elem->node_name); + } + } + return false; +} + +void BKE_viewer_path_elem_free(ViewerPathElem *elem) +{ + switch (ViewerPathElemType(elem->type)) { + case VIEWER_PATH_ELEM_TYPE_ID: { + break; + } + case VIEWER_PATH_ELEM_TYPE_MODIFIER: { + auto typed_elem = reinterpret_cast<ModifierViewerPathElem *>(elem); + MEM_SAFE_FREE(typed_elem->modifier_name); + break; + } + case VIEWER_PATH_ELEM_TYPE_NODE: { + auto typed_elem = reinterpret_cast<NodeViewerPathElem *>(elem); + MEM_SAFE_FREE(typed_elem->node_name); + break; + } + } + MEM_freeN(elem); +} diff --git a/source/blender/blenkernel/intern/workspace.cc b/source/blender/blenkernel/intern/workspace.cc index 8008dacc853..a7fd433b42b 100644 --- a/source/blender/blenkernel/intern/workspace.cc +++ b/source/blender/blenkernel/intern/workspace.cc @@ -24,6 +24,7 @@ #include "BKE_main.h" #include "BKE_object.h" #include "BKE_scene.h" +#include "BKE_viewer_path.h" #include "BKE_workspace.h" #include "DNA_object_types.h" @@ -61,6 +62,7 @@ static void workspace_free_data(ID *id) } MEM_SAFE_FREE(workspace->status_text); + BKE_viewer_path_clear(&workspace->viewer_path); } static void workspace_foreach_id(ID *id, LibraryForeachIDData *data) @@ -72,6 +74,8 @@ static void workspace_foreach_id(ID *id, LibraryForeachIDData *data) LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, layout->screen, IDWALK_CB_USER); } + + BKE_viewer_path_foreach_id(data, &workspace->viewer_path); } static void workspace_blend_write(BlendWriter *writer, ID *id, const void *id_address) @@ -89,6 +93,8 @@ static void workspace_blend_write(BlendWriter *writer, ID *id, const void *id_ad IDP_BlendWrite(writer, tref->properties); } } + + BKE_viewer_path_blend_write(writer, &workspace->viewer_path); } static void workspace_blend_read_data(BlendDataReader *reader, ID *id) @@ -115,6 +121,8 @@ static void workspace_blend_read_data(BlendDataReader *reader, ID *id) workspace->status_text = nullptr; id_us_ensure_real(&workspace->id); + + BKE_viewer_path_blend_read_data(reader, &workspace->viewer_path); } static void workspace_blend_read_lib(BlendLibReader *reader, ID *id) @@ -164,6 +172,8 @@ static void workspace_blend_read_lib(BlendLibReader *reader, ID *id) BKE_workspace_layout_remove(bmain, workspace, layout); } } + + BKE_viewer_path_blend_read_lib(reader, id->lib, &workspace->viewer_path); } static void workspace_blend_read_expand(BlendExpander *expander, ID *id) diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index 7c07d373999..d627a93526e 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -2515,6 +2515,17 @@ static void lib_link_window_scene_data_restore(wmWindow *win, Scene *scene, View } } +static void lib_link_restore_viewer_path(struct IDNameLib_Map *id_map, ViewerPath *viewer_path) +{ + LISTBASE_FOREACH (ViewerPathElem *, elem, &viewer_path->path) { + if (elem->type == VIEWER_PATH_ELEM_TYPE_ID) { + auto typed_elem = reinterpret_cast<IDViewerPathElem *>(elem); + typed_elem->id = static_cast<ID *>( + restore_pointer_by_name(id_map, (ID *)typed_elem->id, USER_IGNORE)); + } + } +} + static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map, Main *newmain, WorkSpaceLayout *layout) @@ -2532,6 +2543,8 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map, restore_pointer_by_name(id_map, (ID *)v3d->camera, USER_REAL)); v3d->ob_center = static_cast<Object *>( restore_pointer_by_name(id_map, (ID *)v3d->ob_center, USER_REAL)); + + lib_link_restore_viewer_path(id_map, &v3d->viewer_path); } else if (sl->spacetype == SPACE_GRAPH) { SpaceGraph *sipo = (SpaceGraph *)sl; @@ -2741,14 +2754,7 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map, } else if (sl->spacetype == SPACE_SPREADSHEET) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - - LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { - if (context->type == SPREADSHEET_CONTEXT_OBJECT) { - SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context; - object_context->object = static_cast<Object *>( - restore_pointer_by_name(id_map, (ID *)object_context->object, USER_IGNORE)); - } - } + lib_link_restore_viewer_path(id_map, &sspreadsheet->viewer_path); } } } @@ -2770,6 +2776,7 @@ void blo_lib_link_restore(Main *oldmain, } workspace->pin_scene = static_cast<Scene *>( restore_pointer_by_name(id_map, (ID *)workspace->pin_scene, USER_IGNORE)); + lib_link_restore_viewer_path(id_map, &workspace->viewer_path); } LISTBASE_FOREACH (wmWindow *, win, &curwm->windows) { diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 5cbfcb66bf9..3e26516cd69 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3385,7 +3385,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) View3D *v3d = (View3D *)sl; v3d->flag &= ~(V3D_LOCAL_COLLECTIONS | V3D_FLAG_UNUSED_1 | V3D_FLAG_UNUSED_10 | V3D_FLAG_UNUSED_12); - v3d->flag2 &= ~(V3D_FLAG2_UNUSED_3 | V3D_FLAG2_UNUSED_6 | V3D_FLAG2_UNUSED_12 | + v3d->flag2 &= ~((1 << 3) | V3D_FLAG2_UNUSED_6 | V3D_FLAG2_UNUSED_12 | V3D_FLAG2_UNUSED_13 | V3D_FLAG2_UNUSED_14 | V3D_FLAG2_UNUSED_15); break; } diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index f0ebc6c7942..11c88a5d69b 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -3586,5 +3586,18 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + v3d->flag2 |= V3D_SHOW_VIEWER; + v3d->overlay.flag |= V3D_OVERLAY_VIEWER_ATTRIBUTE; + v3d->overlay.viewer_attribute_opacity = 0.8f; + } + } + } + } } } diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h index d832676fe2c..4a9025641f9 100644 --- a/source/blender/depsgraph/DEG_depsgraph_query.h +++ b/source/blender/depsgraph/DEG_depsgraph_query.h @@ -27,6 +27,7 @@ struct ListBase; struct PointerRNA; struct Scene; struct ViewLayer; +struct ViewerPath; #ifdef __cplusplus extern "C" { @@ -152,6 +153,12 @@ typedef struct DEGObjectIterSettings { * and will crash if you try to access it. */ uint32_t flags; + + /** + * When set, the final evaluated geometry of the corresponding object is omitted. Instead the + * geometry for the viewer path included in the iterator. + */ + const struct ViewerPath *viewer_path; } DEGObjectIterSettings; /** @@ -170,6 +177,9 @@ typedef struct DEGObjectIterData { eEvaluationMode eval_mode; + /** Object whose preview instead of evaluated geometry should be part of the iterator. */ + struct Object *object_orig_with_preview; + struct Object *next_object; /* **** Iteration over dupli-list. *** */ diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index bcae5de1e1e..5a92f483420 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -127,14 +127,11 @@ bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, DupliObject return false; } -void deg_iterator_duplis_init(DEGObjectIterData *data, Object *object) +void deg_iterator_duplis_init(DEGObjectIterData *data, Object *object, ListBase *duplis) { - if ((data->flag & DEG_ITER_OBJECT_FLAG_DUPLI) && - ((object->transflag & OB_DUPLI) || object->runtime.geometry_set_eval != nullptr)) { - data->dupli_parent = object; - data->dupli_list = object_duplilist(data->graph, data->scene, object); - data->dupli_object_next = (DupliObject *)data->dupli_list->first; - } + data->dupli_parent = object; + data->dupli_list = duplis; + data->dupli_object_next = static_cast<DupliObject *>(duplis->first); } /* Returns false when iterator is exhausted. */ @@ -250,7 +247,18 @@ bool deg_iterator_objects_step(DEGObjectIterData *data) } Object *object = (Object *)id_node->id_cow; + Object *object_orig = DEG_get_original_object(object); BLI_assert(deg::deg_validate_copy_on_write_datablock(&object->id)); + object->runtime.select_id = object_orig->runtime.select_id; + + const bool use_preview = object_orig == data->object_orig_with_preview; + if (use_preview) { + ListBase *preview_duplis = object_duplilist_preview( + data->graph, data->scene, object, data->settings->viewer_path); + deg_iterator_duplis_init(data, object, preview_duplis); + data->id_node_index++; + return true; + } int ob_visibility = OB_VISIBLE_ALL; if (data->flag & DEG_ITER_OBJECT_FLAG_VISIBLE) { @@ -261,9 +269,12 @@ bool deg_iterator_objects_step(DEGObjectIterData *data) } } - object->runtime.select_id = DEG_get_original_object(object)->runtime.select_id; if (ob_visibility & OB_VISIBLE_INSTANCES) { - deg_iterator_duplis_init(data, object); + if ((data->flag & DEG_ITER_OBJECT_FLAG_DUPLI) && + ((object->transflag & OB_DUPLI) || object->runtime.geometry_set_eval != nullptr)) { + ListBase *duplis = object_duplilist(data->graph, data->scene, object); + deg_iterator_duplis_init(data, object, duplis); + } } if (ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) { @@ -301,6 +312,22 @@ void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data) data->eval_mode = DEG_get_mode(depsgraph); deg_invalidate_iterator_work_data(data); + /* Determine if the preview of any object should be in the iterator. */ + const ViewerPath *viewer_path = data->settings->viewer_path; + if (viewer_path != nullptr) { + if (!BLI_listbase_is_empty(&viewer_path->path)) { + const ViewerPathElem *elem = static_cast<const ViewerPathElem *>(viewer_path->path.first); + if (elem->type == VIEWER_PATH_ELEM_TYPE_ID) { + const IDViewerPathElem *id_elem = reinterpret_cast<const IDViewerPathElem *>(elem); + if (id_elem->id != nullptr) { + if (GS(id_elem->id->name) == ID_OB) { + data->object_orig_with_preview = reinterpret_cast<Object *>(id_elem->id); + } + } + } + } + } + DEG_iterator_objects_next(iter); } diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 540041ed02a..45a7e3410eb 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -201,6 +201,7 @@ set(SRC engines/overlay/overlay_sculpt.cc engines/overlay/overlay_sculpt_curves.cc engines/overlay/overlay_shader.cc + engines/overlay/overlay_viewer_attribute.cc engines/overlay/overlay_volume.cc engines/overlay/overlay_wireframe.cc @@ -645,12 +646,18 @@ set(GLSL_SRC engines/overlay/shaders/overlay_particle_vert.glsl engines/overlay/shaders/overlay_point_varying_color_frag.glsl engines/overlay/shaders/overlay_point_varying_color_varying_outline_aa_frag.glsl + engines/overlay/shaders/overlay_pointcloud_only_vert.glsl engines/overlay/shaders/overlay_sculpt_curves_selection_frag.glsl engines/overlay/shaders/overlay_sculpt_curves_selection_vert.glsl engines/overlay/shaders/overlay_sculpt_mask_frag.glsl engines/overlay/shaders/overlay_sculpt_mask_vert.glsl engines/overlay/shaders/overlay_uniform_color_frag.glsl engines/overlay/shaders/overlay_varying_color.glsl + engines/overlay/shaders/overlay_viewer_attribute_curve_vert.glsl + engines/overlay/shaders/overlay_viewer_attribute_curves_vert.glsl + engines/overlay/shaders/overlay_viewer_attribute_frag.glsl + engines/overlay/shaders/overlay_viewer_attribute_mesh_vert.glsl + engines/overlay/shaders/overlay_viewer_attribute_pointcloud_vert.glsl engines/overlay/shaders/overlay_volume_gridlines_vert.glsl engines/overlay/shaders/overlay_volume_velocity_vert.glsl engines/overlay/shaders/overlay_wireframe_frag.glsl diff --git a/source/blender/draw/engines/overlay/overlay_engine.cc b/source/blender/draw/engines/overlay/overlay_engine.cc index bbcae92561d..c2c4706138f 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.cc +++ b/source/blender/draw/engines/overlay/overlay_engine.cc @@ -16,6 +16,7 @@ #include "UI_interface.h" +#include "BKE_duplilist.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -204,6 +205,7 @@ static void OVERLAY_cache_init(void *vedata) } OVERLAY_antialiasing_cache_init(data); OVERLAY_armature_cache_init(data); + OVERLAY_viewer_attribute_cache_init(data); OVERLAY_background_cache_init(data); OVERLAY_fade_cache_init(data); OVERLAY_mode_transfer_cache_init(data); @@ -300,8 +302,12 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) } const DRWContextState *draw_ctx = DRW_context_state_get(); + DupliObject *dupli_object = DRW_object_get_dupli(ob); + Object *dupli_parent = DRW_object_get_dupli_parent(ob); const bool is_select = DRW_state_is_select(); const bool renderable = DRW_object_is_renderable(ob); + const bool is_preview = dupli_object != nullptr && + dupli_object->preview_base_geometry != nullptr; const bool in_pose_mode = ob->type == OB_ARMATURE && OVERLAY_armature_is_pose_mode(ob, draw_ctx); const bool in_edit_mode = overlay_object_is_edit_mode(pd, ob); const bool is_instance = (ob->base_flag & BASE_FROM_DUPLI); @@ -313,7 +319,8 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) (pd->ctx_mode == CTX_MODE_PARTICLE); const bool in_paint_mode = (ob == draw_ctx->obact) && (draw_ctx->object_mode & OB_MODE_ALL_PAINT); - const bool in_sculpt_curve_mode = (ob == draw_ctx->obact) && + const bool in_sculpt_curve_mode = (ob == draw_ctx->obact || + (is_preview && dupli_parent == draw_ctx->obact)) && (draw_ctx->object_mode & OB_MODE_SCULPT_CURVES); const bool in_sculpt_mode = (ob == draw_ctx->obact) && (ob->sculpt != nullptr) && (ob->sculpt->mode_type == OB_MODE_SCULPT); @@ -374,6 +381,12 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) OVERLAY_pose_cache_populate(data, ob); } + if (pd->overlay.flag & V3D_OVERLAY_VIEWER_ATTRIBUTE) { + if (is_preview) { + OVERLAY_viewer_attribute_cache_populate(data, ob); + } + } + if (ob->type == OB_VOLUME) { OVERLAY_volume_cache_populate(data, ob); } @@ -627,6 +640,9 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_metaball_draw(data); OVERLAY_gpencil_draw(data); OVERLAY_extra_draw(data); + if (pd->overlay.flag & V3D_OVERLAY_VIEWER_ATTRIBUTE) { + OVERLAY_viewer_attribute_draw(data); + } if (DRW_state_is_fbo()) { GPU_framebuffer_bind(fbl->overlay_color_only_fb); diff --git a/source/blender/draw/engines/overlay/overlay_private.hh b/source/blender/draw/engines/overlay/overlay_private.hh index 0a783c44029..b1118e084a6 100644 --- a/source/blender/draw/engines/overlay/overlay_private.hh +++ b/source/blender/draw/engines/overlay/overlay_private.hh @@ -61,6 +61,7 @@ typedef struct OVERLAY_PassList { DRWPass *armature_ps[2]; DRWPass *armature_bone_select_ps; DRWPass *armature_transp_ps[2]; + DRWPass *attribute_ps; DRWPass *background_ps; DRWPass *clipping_frustum_ps; DRWPass *edit_curve_wire_ps[2]; @@ -284,6 +285,12 @@ typedef struct OVERLAY_PrivateData { DRWShadingGroup *pointcloud_dots_grp; DRWShadingGroup *sculpt_mask_grp; DRWShadingGroup *sculpt_curves_selection_grp; + DRWShadingGroup *viewer_attribute_curve_grp; + DRWShadingGroup *viewer_attribute_curves_grp; + DRWShadingGroup *viewer_attribute_mesh_grp; + DRWShadingGroup *viewer_attribute_pointcloud_grp; + DRWShadingGroup *viewer_attribute_instance_grp; + DRWShadingGroup *viewer_attribute_instance_pointcloud_grp; DRWShadingGroup *volume_selection_surface_grp; DRWShadingGroup *wires_grp[2][2]; /* With and without coloring. */ DRWShadingGroup *wires_all_grp[2][2]; /* With and without coloring. */ @@ -678,6 +685,10 @@ void OVERLAY_sculpt_curves_cache_init(OVERLAY_Data *vedata); void OVERLAY_sculpt_curves_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_sculpt_curves_draw(OVERLAY_Data *vedata); +void OVERLAY_viewer_attribute_cache_init(OVERLAY_Data *vedata); +void OVERLAY_viewer_attribute_cache_populate(OVERLAY_Data *vedata, Object *object); +void OVERLAY_viewer_attribute_draw(OVERLAY_Data *vedata); + void OVERLAY_wireframe_init(OVERLAY_Data *vedata); void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata); void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, @@ -745,6 +756,7 @@ GPUShader *OVERLAY_shader_image(void); GPUShader *OVERLAY_shader_motion_path_line(void); GPUShader *OVERLAY_shader_motion_path_vert(void); GPUShader *OVERLAY_shader_uniform_color(void); +GPUShader *OVERLAY_shader_uniform_color_pointcloud(void); GPUShader *OVERLAY_shader_outline_prepass(bool use_wire); GPUShader *OVERLAY_shader_outline_prepass_curves(void); GPUShader *OVERLAY_shader_outline_prepass_gpencil(void); @@ -761,6 +773,10 @@ GPUShader *OVERLAY_shader_particle_dot(void); GPUShader *OVERLAY_shader_particle_shape(void); GPUShader *OVERLAY_shader_sculpt_mask(void); GPUShader *OVERLAY_shader_sculpt_curves_selection(void); +GPUShader *OVERLAY_shader_viewer_attribute_curve(void); +GPUShader *OVERLAY_shader_viewer_attribute_curves(void); +GPUShader *OVERLAY_shader_viewer_attribute_mesh(void); +GPUShader *OVERLAY_shader_viewer_attribute_pointcloud(void); GPUShader *OVERLAY_shader_volume_velocity(bool use_needle, bool use_mac); GPUShader *OVERLAY_shader_volume_gridlines(bool color_with_flags, bool color_range); GPUShader *OVERLAY_shader_wireframe(bool custom_bias); diff --git a/source/blender/draw/engines/overlay/overlay_shader.cc b/source/blender/draw/engines/overlay/overlay_shader.cc index b0a6926a57f..b7e5e8c56b7 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.cc +++ b/source/blender/draw/engines/overlay/overlay_shader.cc @@ -93,6 +93,11 @@ typedef struct OVERLAY_Shaders { GPUShader *sculpt_mask; GPUShader *sculpt_curves_selection; GPUShader *uniform_color; + GPUShader *uniform_color_pointcloud; + GPUShader *viewer_attribute_mesh; + GPUShader *viewer_attribute_pointcloud; + GPUShader *viewer_attribute_curve; + GPUShader *viewer_attribute_curves; GPUShader *volume_velocity_needle_sh; GPUShader *volume_velocity_mac_sh; GPUShader *volume_velocity_sh; @@ -818,6 +823,55 @@ GPUShader *OVERLAY_shader_sculpt_curves_selection(void) return sh_data->sculpt_curves_selection; } +GPUShader *OVERLAY_shader_viewer_attribute_mesh(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->viewer_attribute_mesh) { + sh_data->viewer_attribute_mesh = GPU_shader_create_from_info_name( + draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED ? "overlay_viewer_attribute_mesh_clipped" : + "overlay_viewer_attribute_mesh"); + } + return sh_data->viewer_attribute_mesh; +} + +GPUShader *OVERLAY_shader_viewer_attribute_pointcloud(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->viewer_attribute_pointcloud) { + sh_data->viewer_attribute_pointcloud = GPU_shader_create_from_info_name( + draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED ? + "overlay_viewer_attribute_pointcloud_clipped" : + "overlay_viewer_attribute_pointcloud"); + } + return sh_data->viewer_attribute_pointcloud; +} + +GPUShader *OVERLAY_shader_viewer_attribute_curve(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->viewer_attribute_curve) { + sh_data->viewer_attribute_curve = GPU_shader_create_from_info_name( + draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED ? "overlay_viewer_attribute_curve_clipped" : + "overlay_viewer_attribute_curve"); + } + return sh_data->viewer_attribute_curve; +} + +GPUShader *OVERLAY_shader_viewer_attribute_curves(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->viewer_attribute_curves) { + sh_data->viewer_attribute_curves = GPU_shader_create_from_info_name( + draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED ? "overlay_viewer_attribute_curves_clipped" : + "overlay_viewer_attribute_curves"); + } + return sh_data->viewer_attribute_curves; +} + struct GPUShader *OVERLAY_shader_uniform_color(void) { const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -829,6 +883,18 @@ struct GPUShader *OVERLAY_shader_uniform_color(void) return sh_data->uniform_color; } +struct GPUShader *OVERLAY_shader_uniform_color_pointcloud() +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->uniform_color_pointcloud) { + sh_data->uniform_color_pointcloud = GPU_shader_create_from_info_name( + draw_ctx->sh_cfg ? "overlay_uniform_color_pointcloud_clipped" : + "overlay_uniform_color_pointcloud"); + } + return sh_data->uniform_color_pointcloud; +} + struct GPUShader *OVERLAY_shader_volume_velocity(bool use_needle, bool use_mac) { OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; diff --git a/source/blender/draw/engines/overlay/overlay_viewer_attribute.cc b/source/blender/draw/engines/overlay/overlay_viewer_attribute.cc new file mode 100644 index 00000000000..7e1949c5261 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_viewer_attribute.cc @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup draw_engine + */ + +#include "DRW_render.h" + +#include "DNA_mesh_types.h" +#include "DNA_pointcloud_types.h" + +#include "BLI_math_vector.hh" +#include "BLI_span.hh" + +#include "GPU_batch.h" + +#include "BKE_attribute.hh" +#include "BKE_curves.hh" +#include "BKE_duplilist.h" +#include "BKE_geometry_set.hh" + +#include "draw_cache_extract.hh" +#include "draw_cache_impl.h" +#include "overlay_private.hh" + +void OVERLAY_viewer_attribute_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + + const DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | + DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->attribute_ps, state | pd->clipping_state); + + GPUShader *mesh_sh = OVERLAY_shader_viewer_attribute_mesh(); + GPUShader *pointcloud_sh = OVERLAY_shader_viewer_attribute_pointcloud(); + GPUShader *curve_sh = OVERLAY_shader_viewer_attribute_curve(); + GPUShader *curves_sh = OVERLAY_shader_viewer_attribute_curves(); + GPUShader *uniform_sh = OVERLAY_shader_uniform_color(); + GPUShader *uniform_pointcloud_sh = OVERLAY_shader_uniform_color_pointcloud(); + pd->viewer_attribute_mesh_grp = DRW_shgroup_create(mesh_sh, psl->attribute_ps); + pd->viewer_attribute_pointcloud_grp = DRW_shgroup_create(pointcloud_sh, psl->attribute_ps); + pd->viewer_attribute_curve_grp = DRW_shgroup_create(curve_sh, psl->attribute_ps); + pd->viewer_attribute_curves_grp = DRW_shgroup_create(curves_sh, psl->attribute_ps); + pd->viewer_attribute_instance_grp = DRW_shgroup_create(uniform_sh, psl->attribute_ps); + pd->viewer_attribute_instance_pointcloud_grp = DRW_shgroup_create(uniform_pointcloud_sh, + psl->attribute_ps); +} + +static void populate_cache_for_instance(Object &object, + OVERLAY_PrivateData &pd, + const DupliObject &dupli_object, + const float opacity) +{ + using namespace blender; + using namespace blender::bke; + + const GeometrySet &base_geometry = *dupli_object.preview_base_geometry; + const InstancesComponent &instances = + *base_geometry.get_component_for_read<InstancesComponent>(); + const AttributeAccessor instance_attributes = *instances.attributes(); + const VArray attribute = instance_attributes.lookup<ColorGeometry4f>(".viewer"); + if (!attribute) { + return; + } + ColorGeometry4f color = attribute.get(dupli_object.preview_instance_index); + color.a *= opacity; + switch (object.type) { + case OB_MESH: { + { + DRWShadingGroup *sub_grp = DRW_shgroup_create_sub(pd.viewer_attribute_instance_grp); + DRW_shgroup_uniform_vec4_copy(sub_grp, "ucolor", color); + GPUBatch *batch = DRW_cache_mesh_surface_get(&object); + DRW_shgroup_call(sub_grp, batch, &object); + } + if (GPUBatch *batch = DRW_cache_mesh_loose_edges_get(&object)) { + DRWShadingGroup *sub_grp = DRW_shgroup_create_sub(pd.viewer_attribute_instance_grp); + DRW_shgroup_uniform_vec4_copy(sub_grp, "ucolor", color); + DRW_shgroup_call(sub_grp, batch, &object); + } + break; + } + case OB_POINTCLOUD: { + DRWShadingGroup *sub_grp = DRW_shgroup_create_sub( + pd.viewer_attribute_instance_pointcloud_grp); + DRW_shgroup_uniform_vec4_copy(sub_grp, "ucolor", color); + GPUBatch *batch = DRW_cache_pointcloud_surface_get(&object); + DRW_shgroup_call_instance_range(sub_grp, &object, batch, 0, 0); + break; + } + case OB_CURVES_LEGACY: { + DRWShadingGroup *sub_grp = DRW_shgroup_create_sub(pd.viewer_attribute_instance_grp); + DRW_shgroup_uniform_vec4_copy(sub_grp, "ucolor", color); + GPUBatch *batch = DRW_cache_curve_edge_wire_get(&object); + DRW_shgroup_call(sub_grp, batch, &object); + break; + } + case OB_CURVES: { + /* Not supported yet because instances of this type are currently drawn as legacy curves. + */ + break; + } + } +} + +static void populate_cache_for_geometry(Object &object, + OVERLAY_PrivateData &pd, + const float opacity) +{ + using namespace blender; + using namespace blender::bke; + + switch (object.type) { + case OB_MESH: { + Mesh *mesh = static_cast<Mesh *>(object.data); + if (mesh->attributes().contains(".viewer")) { + GPUBatch *batch = DRW_cache_mesh_surface_viewer_attribute_get(&object); + DRW_shgroup_uniform_float_copy(pd.viewer_attribute_mesh_grp, "opacity", opacity); + DRW_shgroup_call(pd.viewer_attribute_mesh_grp, batch, &object); + } + break; + } + case OB_POINTCLOUD: { + PointCloud *pointcloud = static_cast<PointCloud *>(object.data); + if (pointcloud->attributes().contains(".viewer")) { + GPUBatch *batch = DRW_cache_pointcloud_surface_viewer_attribute_get(&object); + DRW_shgroup_uniform_float_copy(pd.viewer_attribute_pointcloud_grp, "opacity", opacity); + DRW_shgroup_call_instance_range(pd.viewer_attribute_pointcloud_grp, &object, batch, 0, 0); + } + break; + } + case OB_CURVES_LEGACY: { + Curve *curve = static_cast<Curve *>(object.data); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curve->curve_eval->geometry); + if (curves.attributes().contains(".viewer")) { + GPUBatch *batch = DRW_cache_curve_edge_wire_viewer_attribute_get(&object); + DRW_shgroup_uniform_float_copy(pd.viewer_attribute_curve_grp, "opacity", opacity); + DRW_shgroup_call(pd.viewer_attribute_curve_grp, batch, &object); + } + break; + } + case OB_CURVES: { + Curves *curves_id = static_cast<Curves *>(object.data); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + if (curves.attributes().contains(".viewer")) { + bool is_point_domain; + GPUTexture **texture = DRW_curves_texture_for_evaluated_attribute( + curves_id, ".viewer", &is_point_domain); + DRWShadingGroup *grp = DRW_shgroup_curves_create_sub( + &object, pd.viewer_attribute_curves_grp, nullptr); + DRW_shgroup_uniform_float_copy(pd.viewer_attribute_curves_grp, "opacity", opacity); + DRW_shgroup_uniform_bool_copy(grp, "is_point_domain", is_point_domain); + DRW_shgroup_uniform_texture(grp, "color_tx", *texture); + } + break; + } + } +} + +void OVERLAY_viewer_attribute_cache_populate(OVERLAY_Data *vedata, Object *object) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + const float opacity = vedata->stl->pd->overlay.viewer_attribute_opacity; + DupliObject *dupli_object = DRW_object_get_dupli(object); + + if (dupli_object->preview_instance_index >= 0) { + const InstancesComponent &instances = + *dupli_object->preview_base_geometry->get_component_for_read<InstancesComponent>(); + if (instances.attributes()->contains(".viewer")) { + populate_cache_for_instance(*object, *pd, *dupli_object, opacity); + return; + } + } + populate_cache_for_geometry(*object, *pd, opacity); +} + +void OVERLAY_viewer_attribute_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + DRW_draw_pass(psl->attribute_ps); +} diff --git a/source/blender/draw/engines/overlay/shaders/infos/overlay_edit_mode_info.hh b/source/blender/draw/engines/overlay/shaders/infos/overlay_edit_mode_info.hh index 8c9c943393f..40b7249a997 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/overlay_edit_mode_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/overlay_edit_mode_info.hh @@ -587,8 +587,20 @@ GPU_SHADER_CREATE_INFO(overlay_uniform_color) .fragment_source("overlay_uniform_color_frag.glsl") .additional_info("draw_mesh"); +GPU_SHADER_CREATE_INFO(overlay_uniform_color_pointcloud) + .do_static_compilation(true) + .push_constant(Type::VEC4, "ucolor") + .fragment_out(0, Type::VEC4, "fragColor") + .vertex_source("overlay_pointcloud_only_vert.glsl") + .fragment_source("overlay_uniform_color_frag.glsl") + .additional_info("draw_pointcloud"); + GPU_SHADER_CREATE_INFO(overlay_uniform_color_clipped) .do_static_compilation(true) .additional_info("overlay_depth_only", "drw_clipped"); +GPU_SHADER_CREATE_INFO(overlay_uniform_color_pointcloud_clipped) + .do_static_compilation(true) + .additional_info("overlay_uniform_color_pointcloud", "drw_clipped"); + /** \} */ diff --git a/source/blender/draw/engines/overlay/shaders/infos/overlay_viewer_attribute_info.hh b/source/blender/draw/engines/overlay/shaders/infos/overlay_viewer_attribute_info.hh new file mode 100644 index 00000000000..aca80992622 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/infos/overlay_viewer_attribute_info.hh @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_INTERFACE_INFO(overlay_viewer_attribute_iface, "").smooth(Type::VEC4, "finalColor"); + +GPU_SHADER_CREATE_INFO(overlay_viewer_attribute_common).push_constant(Type::FLOAT, "opacity"); + +GPU_SHADER_CREATE_INFO(overlay_viewer_attribute_mesh) + .do_static_compilation(true) + .vertex_source("overlay_viewer_attribute_mesh_vert.glsl") + .fragment_source("overlay_viewer_attribute_frag.glsl") + .fragment_out(0, Type::VEC4, "out_color") + .vertex_in(0, Type::VEC3, "pos") + .vertex_in(1, Type::VEC4, "attribute_value") + .vertex_out(overlay_viewer_attribute_iface) + .additional_info("overlay_viewer_attribute_common", "draw_mesh"); + +GPU_SHADER_CREATE_INFO(overlay_viewer_attribute_mesh_clipped) + .do_static_compilation(true) + .additional_info("overlay_viewer_attribute_mesh", "drw_clipped"); + +GPU_SHADER_CREATE_INFO(overlay_viewer_attribute_pointcloud) + .do_static_compilation(true) + .vertex_source("overlay_viewer_attribute_pointcloud_vert.glsl") + .fragment_source("overlay_viewer_attribute_frag.glsl") + .fragment_out(0, Type::VEC4, "out_color") + .vertex_in(3, Type::VEC4, "attribute_value") + .vertex_out(overlay_viewer_attribute_iface) + .additional_info("overlay_viewer_attribute_common", "draw_pointcloud"); + +GPU_SHADER_CREATE_INFO(overlay_viewer_attribute_pointcloud_clipped) + .do_static_compilation(true) + .additional_info("overlay_viewer_attribute_pointcloud", "drw_clipped"); + +GPU_SHADER_CREATE_INFO(overlay_viewer_attribute_curve) + .do_static_compilation(true) + .vertex_source("overlay_viewer_attribute_curve_vert.glsl") + .fragment_source("overlay_viewer_attribute_frag.glsl") + .fragment_out(0, Type::VEC4, "out_color") + .vertex_in(0, Type::VEC3, "pos") + .vertex_in(1, Type::VEC4, "attribute_value") + .vertex_out(overlay_viewer_attribute_iface) + .additional_info("overlay_viewer_attribute_common", "draw_modelmat", "draw_resource_id"); + +GPU_SHADER_CREATE_INFO(overlay_viewer_attribute_curve_clipped) + .do_static_compilation(true) + .additional_info("overlay_viewer_attribute_curve", "drw_clipped"); + +GPU_SHADER_CREATE_INFO(overlay_viewer_attribute_curves) + .do_static_compilation(true) + .vertex_source("overlay_viewer_attribute_curves_vert.glsl") + .fragment_source("overlay_viewer_attribute_frag.glsl") + .fragment_out(0, Type::VEC4, "out_color") + .sampler(0, ImageType::FLOAT_BUFFER, "color_tx") + .push_constant(Type::BOOL, "is_point_domain") + .vertex_out(overlay_viewer_attribute_iface) + .additional_info("overlay_viewer_attribute_common", "draw_hair"); + +GPU_SHADER_CREATE_INFO(overlay_viewer_attribute_curves_clipped) + .do_static_compilation(true) + .additional_info("overlay_viewer_attribute_curves", "drw_clipped"); diff --git a/source/blender/draw/engines/overlay/shaders/overlay_pointcloud_only_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_pointcloud_only_vert.glsl new file mode 100644 index 00000000000..8a7e81028d3 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/overlay_pointcloud_only_vert.glsl @@ -0,0 +1,9 @@ +#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_pointcloud_lib.glsl) + +void main() +{ + vec3 world_pos = pointcloud_get_pos(); + gl_Position = point_world_to_ndc(world_pos); +} diff --git a/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_curve_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_curve_vert.glsl new file mode 100644 index 00000000000..7c2c386b2f5 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_curve_vert.glsl @@ -0,0 +1,9 @@ +#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void main() +{ + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + finalColor = attribute_value; +} diff --git a/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_curves_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_curves_vert.glsl new file mode 100644 index 00000000000..23d313e036f --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_curves_vert.glsl @@ -0,0 +1,28 @@ +#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) + +void main() +{ + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + float time, thick_time, thickness; + vec3 world_pos, tan, binor; + hair_get_pos_tan_binor_time(is_persp, + ModelMatrixInverse, + ViewMatrixInverse[3].xyz, + ViewMatrixInverse[2].xyz, + world_pos, + tan, + binor, + time, + thickness, + thick_time); + gl_Position = point_world_to_ndc(world_pos); + + if (is_point_domain) { + finalColor = texelFetch(color_tx, hair_get_base_id()); + } + else { + finalColor = texelFetch(color_tx, hair_get_strand_id()); + } +} diff --git a/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_frag.glsl new file mode 100644 index 00000000000..6176a6b96ba --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_frag.glsl @@ -0,0 +1,6 @@ + +void main() +{ + out_color = finalColor; + out_color.a *= opacity; +} diff --git a/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_mesh_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_mesh_vert.glsl new file mode 100644 index 00000000000..7c2c386b2f5 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_mesh_vert.glsl @@ -0,0 +1,9 @@ +#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void main() +{ + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + finalColor = attribute_value; +} diff --git a/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_pointcloud_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_pointcloud_vert.glsl new file mode 100644 index 00000000000..e706e8f9033 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/overlay_viewer_attribute_pointcloud_vert.glsl @@ -0,0 +1,10 @@ +#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_pointcloud_lib.glsl) + +void main() +{ + vec3 world_pos = pointcloud_get_pos(); + gl_Position = point_world_to_ndc(world_pos); + finalColor = attribute_value; +} diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 6537490c06c..6da22039c2f 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -2889,6 +2889,12 @@ GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(Object *ob) return DRW_mesh_batch_cache_get_edit_mesh_analysis(ob->data); } +GPUBatch *DRW_cache_mesh_surface_viewer_attribute_get(Object *ob) +{ + BLI_assert(ob->type == OB_MESH); + return DRW_mesh_batch_cache_get_surface_viewer_attribute(ob->data); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -2902,6 +2908,13 @@ GPUBatch *DRW_cache_curve_edge_wire_get(Object *ob) return DRW_curve_batch_cache_get_wire_edge(cu); } +GPUBatch *DRW_cache_curve_edge_wire_viewer_attribute_get(Object *ob) +{ + BLI_assert(ob->type == OB_CURVES_LEGACY); + struct Curve *cu = ob->data; + return DRW_curve_batch_cache_get_wire_edge_viewer_attribute(cu); +} + GPUBatch *DRW_cache_curve_edge_normal_get(Object *ob) { BLI_assert(ob->type == OB_CURVES_LEGACY); @@ -3005,6 +3018,12 @@ GPUBatch *DRW_cache_pointcloud_surface_get(Object *object) return DRW_pointcloud_batch_cache_get_surface(object); } +GPUBatch *DRW_cache_pointcloud_surface_viewer_attribute_get(Object *object) +{ + BLI_assert(object->type == OB_POINTCLOUD); + return DRW_pointcloud_batch_cache_get_surface_viewer_attribute(object); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 4e8788ada08..058f28f094d 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -170,10 +170,12 @@ struct GPUBatch *DRW_cache_mesh_surface_sculptcolors_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_surface_weights_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_face_wireframe_get(struct Object *ob); +struct GPUBatch *DRW_cache_mesh_surface_viewer_attribute_get(struct Object *ob); /* Curve */ struct GPUBatch *DRW_cache_curve_edge_wire_get(struct Object *ob); +struct GPUBatch *DRW_cache_curve_edge_wire_viewer_attribute_get(struct Object *ob); /* edit-mode */ @@ -226,6 +228,7 @@ struct GPUBatch *DRW_cache_curves_edge_detection_get(struct Object *ob, bool *r_ struct GPUBatch *DRW_cache_pointcloud_get_dots(struct Object *obj); struct GPUBatch *DRW_cache_pointcloud_surface_get(struct Object *obj); +struct GPUBatch *DRW_cache_pointcloud_surface_viewer_attribute_get(struct Object *obj); /* Volume */ diff --git a/source/blender/draw/intern/draw_cache_extract.hh b/source/blender/draw/intern/draw_cache_extract.hh index 203da22406c..6f4a652ce24 100644 --- a/source/blender/draw/intern/draw_cache_extract.hh +++ b/source/blender/draw/intern/draw_cache_extract.hh @@ -130,6 +130,7 @@ struct MeshBufferList { GPUVertBuf *poly_idx; GPUVertBuf *fdot_idx; GPUVertBuf *attr[GPU_MAX_ATTR]; + GPUVertBuf *attr_viewer; } vbo; /* Index Buffers: * Only need to be updated when topology changes. */ @@ -191,6 +192,7 @@ struct MeshBatchList { /* Same as wire_loops but only has uvs. */ GPUBatch *wire_loops_uvs; GPUBatch *sculpt_overlays; + GPUBatch *surface_viewer_attribute; }; #define MBC_BATCH_LEN (sizeof(MeshBatchList) / sizeof(void *)) @@ -228,6 +230,7 @@ enum DRWBatchFlag { MBC_WIRE_LOOPS = (1u << MBC_BATCH_INDEX(wire_loops)), MBC_WIRE_LOOPS_UVS = (1u << MBC_BATCH_INDEX(wire_loops_uvs)), MBC_SCULPT_OVERLAYS = (1u << MBC_BATCH_INDEX(sculpt_overlays)), + MBC_VIEWER_ATTRIBUTE_OVERLAY = (1u << MBC_BATCH_INDEX(surface_viewer_attribute)), MBC_SURFACE_PER_MAT = (1u << MBC_BATCH_LEN), }; ENUM_OPERATORS(DRWBatchFlag, MBC_SURFACE_PER_MAT); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index fd00059bac6..d3170f4c776 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -640,6 +640,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, for (int i = 0; i < GPU_MAX_ATTR; i++) { EXTRACT_ADD_REQUESTED(vbo, attr[i]); } + EXTRACT_ADD_REQUESTED(vbo, attr_viewer); EXTRACT_ADD_REQUESTED(ibo, tris); if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) { diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 7f7d0a7613f..89432b0da83 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -100,6 +100,7 @@ void DRW_curve_batch_cache_create_requested(struct Object *ob, const struct Scen int DRW_curve_material_count_get(struct Curve *cu); struct GPUBatch *DRW_curve_batch_cache_get_wire_edge(struct Curve *cu); +struct GPUBatch *DRW_curve_batch_cache_get_wire_edge_viewer_attribute(struct Curve *cu); struct GPUBatch *DRW_curve_batch_cache_get_normal_edge(struct Curve *cu); struct GPUBatch *DRW_curve_batch_cache_get_edit_edges(struct Curve *cu); struct GPUBatch *DRW_curve_batch_cache_get_edit_verts(struct Curve *cu); @@ -148,6 +149,7 @@ int DRW_pointcloud_material_count_get(struct PointCloud *pointcloud); struct GPUBatch *DRW_pointcloud_batch_cache_get_dots(struct Object *ob); struct GPUBatch *DRW_pointcloud_batch_cache_get_surface(struct Object *ob); +struct GPUBatch *DRW_pointcloud_batch_cache_get_surface_viewer_attribute(struct Object *ob); struct GPUBatch **DRW_cache_pointcloud_surface_shaded_get(struct Object *ob, struct GPUMaterial **gpumat_array, uint gpumat_array_len); @@ -198,6 +200,7 @@ struct GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(struct Object *objec struct GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(struct Object *object, struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_surface_weights(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_sculpt_overlays(struct Mesh *me); +struct GPUBatch *DRW_mesh_batch_cache_get_surface_viewer_attribute(struct Mesh *me); /** \} */ diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 695c348d8e2..6188b1e0544 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -10,6 +10,7 @@ #include "MEM_guardedalloc.h" #include "BLI_array.hh" +#include "BLI_color.hh" #include "BLI_listbase.h" #include "BLI_math_vec_types.hh" #include "BLI_math_vector.h" @@ -38,6 +39,7 @@ #include "draw_cache_impl.h" /* own include */ using blender::Array; +using blender::ColorGeometry4f; using blender::float3; using blender::IndexRange; using blender::Span; @@ -296,6 +298,7 @@ static int curve_render_data_normal_len_get(const CurveRenderData *rdata) struct CurveBatchCache { struct { GPUVertBuf *curves_pos; + GPUVertBuf *attr_viewer; } ordered; struct { @@ -314,6 +317,7 @@ struct CurveBatchCache { struct { GPUBatch *curves; + GPUBatch *curves_viewer_attribute; /* control handles and vertices */ GPUBatch *edit_edges; GPUBatch *edit_verts; @@ -474,6 +478,31 @@ static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curv GPU_vertbuf_attr_fill(vbo_curves_pos, attr_id.pos, positions.data()); } +static void curve_create_attribute(CurveRenderData *rdata, GPUVertBuf *vbo_attr) +{ + if (rdata->curve_eval == nullptr) { + return; + } + + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "attribute_value", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + + const int vert_len = curve_render_data_wire_verts_len_get(rdata); + GPU_vertbuf_init_with_format(vbo_attr, &format); + GPU_vertbuf_data_alloc(vbo_attr, vert_len); + + const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( + rdata->curve_eval->geometry); + curves.ensure_can_interpolate_to_evaluated(); + const blender::VArraySpan<ColorGeometry4f> colors = curves.attributes().lookup<ColorGeometry4f>( + ".viewer", ATTR_DOMAIN_POINT); + ColorGeometry4f *vbo_data = static_cast<ColorGeometry4f *>(GPU_vertbuf_get_data(vbo_attr)); + curves.interpolate_to_evaluated(colors, + blender::MutableSpan<ColorGeometry4f>{vbo_data, vert_len}); +} + static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_curve_lines) { if (rdata->curve_eval == nullptr) { @@ -769,6 +798,12 @@ GPUBatch *DRW_curve_batch_cache_get_wire_edge(Curve *cu) return DRW_batch_request(&cache->batch.curves); } +GPUBatch *DRW_curve_batch_cache_get_wire_edge_viewer_attribute(Curve *cu) +{ + CurveBatchCache *cache = curve_batch_cache_get(cu); + return DRW_batch_request(&cache->batch.curves_viewer_attribute); +} + GPUBatch *DRW_curve_batch_cache_get_normal_edge(Curve *cu) { CurveBatchCache *cache = curve_batch_cache_get(cu); @@ -810,6 +845,11 @@ void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scen DRW_ibo_request(cache->batch.curves, &cache->ibo.curves_lines); DRW_vbo_request(cache->batch.curves, &cache->ordered.curves_pos); } + if (DRW_batch_requested(cache->batch.curves_viewer_attribute, GPU_PRIM_LINE_STRIP)) { + DRW_ibo_request(cache->batch.curves_viewer_attribute, &cache->ibo.curves_lines); + DRW_vbo_request(cache->batch.curves_viewer_attribute, &cache->ordered.curves_pos); + DRW_vbo_request(cache->batch.curves_viewer_attribute, &cache->ordered.attr_viewer); + } /* Edit mode */ if (DRW_batch_requested(cache->batch.edit_edges, GPU_PRIM_LINES)) { @@ -833,6 +873,8 @@ void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scen /* Generate MeshRenderData flags */ int mr_flag = 0; DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->ordered.curves_pos, CU_DATATYPE_WIRE); + DRW_ADD_FLAG_FROM_VBO_REQUEST( + mr_flag, cache->ordered.attr_viewer, CU_DATATYPE_WIRE | CU_DATATYPE_OVERLAY); DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->ibo.curves_lines, CU_DATATYPE_WIRE); DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->edit.pos, CU_DATATYPE_OVERLAY); @@ -851,6 +893,9 @@ void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scen if (DRW_vbo_requested(cache->ordered.curves_pos)) { curve_create_curves_pos(rdata, cache->ordered.curves_pos); } + if (DRW_vbo_requested(cache->ordered.attr_viewer)) { + curve_create_attribute(rdata, cache->ordered.attr_viewer); + } if (DRW_ibo_requested(cache->ibo.curves_lines)) { curve_create_curves_lines(rdata, cache->ibo.curves_lines); } diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index c22382b3e09..ed78cbbda39 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -104,7 +104,8 @@ static constexpr DRWBatchFlag batches_that_use_buffer(const int buffer_index) MBC_EDIT_EDGES | MBC_EDIT_VNOR | MBC_EDIT_LNOR | MBC_EDIT_MESH_ANALYSIS | MBC_EDIT_SELECTION_VERTS | MBC_EDIT_SELECTION_EDGES | MBC_EDIT_SELECTION_FACES | MBC_ALL_VERTS | MBC_ALL_EDGES | MBC_LOOSE_EDGES | MBC_EDGE_DETECTION | - MBC_WIRE_EDGES | MBC_WIRE_LOOPS | MBC_SCULPT_OVERLAYS | MBC_SURFACE_PER_MAT; + MBC_WIRE_EDGES | MBC_WIRE_LOOPS | MBC_SCULPT_OVERLAYS | MBC_VIEWER_ATTRIBUTE_OVERLAY | + MBC_SURFACE_PER_MAT; case BUFFER_INDEX(vbo.lnor): return MBC_SURFACE | MBC_EDIT_LNOR | MBC_WIRE_LOOPS | MBC_SURFACE_PER_MAT; case BUFFER_INDEX(vbo.edge_fac): @@ -166,9 +167,12 @@ static constexpr DRWBatchFlag batches_that_use_buffer(const int buffer_index) case BUFFER_INDEX(vbo.attr[13]): case BUFFER_INDEX(vbo.attr[14]): return MBC_SURFACE | MBC_SURFACE_PER_MAT; + case BUFFER_INDEX(vbo.attr_viewer): + return MBC_VIEWER_ATTRIBUTE_OVERLAY; case BUFFER_INDEX(ibo.tris): return MBC_SURFACE | MBC_SURFACE_WEIGHTS | MBC_EDIT_TRIANGLES | MBC_EDIT_LNOR | - MBC_EDIT_MESH_ANALYSIS | MBC_EDIT_SELECTION_FACES | MBC_SCULPT_OVERLAYS; + MBC_EDIT_MESH_ANALYSIS | MBC_EDIT_SELECTION_FACES | MBC_SCULPT_OVERLAYS | + MBC_VIEWER_ATTRIBUTE_OVERLAY; case BUFFER_INDEX(ibo.lines): return MBC_EDIT_EDGES | MBC_EDIT_SELECTION_EDGES | MBC_ALL_EDGES | MBC_WIRE_EDGES; case BUFFER_INDEX(ibo.lines_loose): @@ -1057,6 +1061,16 @@ GPUBatch *DRW_mesh_batch_cache_get_sculpt_overlays(Mesh *me) return cache->batch.sculpt_overlays; } +GPUBatch *DRW_mesh_batch_cache_get_surface_viewer_attribute(Mesh *me) +{ + MeshBatchCache *cache = mesh_batch_cache_get(me); + + mesh_batch_cache_add_request(cache, MBC_VIEWER_ATTRIBUTE_OVERLAY); + DRW_batch_request(&cache->batch.surface_viewer_attribute); + + return cache->batch.surface_viewer_attribute; +} + /** \} */ /* ---------------------------------------------------------------------- */ @@ -1802,6 +1816,14 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, DRW_vbo_request(cache->batch.edituv_fdots, &mbuflist->vbo.fdots_uv); DRW_vbo_request(cache->batch.edituv_fdots, &mbuflist->vbo.fdots_edituv_data); } + assert_deps_valid( + MBC_VIEWER_ATTRIBUTE_OVERLAY, + {BUFFER_INDEX(ibo.tris), BUFFER_INDEX(vbo.pos_nor), BUFFER_INDEX(vbo.attr_viewer)}); + if (DRW_batch_requested(cache->batch.surface_viewer_attribute, GPU_PRIM_TRIS)) { + DRW_ibo_request(cache->batch.surface_viewer_attribute, &mbuflist->ibo.tris); + DRW_vbo_request(cache->batch.surface_viewer_attribute, &mbuflist->vbo.pos_nor); + DRW_vbo_request(cache->batch.surface_viewer_attribute, &mbuflist->vbo.attr_viewer); + } #ifdef DEBUG auto assert_final_deps_valid = [&](const int buffer_index) { @@ -1833,6 +1855,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, for (const int i : IndexRange(GPU_MAX_ATTR)) { assert_final_deps_valid(BUFFER_INDEX(vbo.attr[i])); } + assert_final_deps_valid(BUFFER_INDEX(vbo.attr_viewer)); assert_final_deps_valid(BUFFER_INDEX(ibo.tris)); assert_final_deps_valid(BUFFER_INDEX(ibo.lines)); diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.cc b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc index a43b23c8969..72be5b37652 100644 --- a/source/blender/draw/intern/draw_cache_impl_pointcloud.cc +++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc @@ -32,11 +32,13 @@ struct PointCloudBatchCache { GPUVertBuf *pos; /* Position and radius. */ GPUVertBuf *geom; /* Instanced geometry for each point in the cloud (small sphere). */ + GPUVertBuf *attr_viewer; GPUIndexBuf *geom_indices; GPUBatch *dots; GPUBatch *surface; GPUBatch **surface_per_mat; + GPUBatch *surface_viewer_attribute; /* settings to determine if cache is invalid */ bool is_dirty; @@ -109,6 +111,7 @@ static void pointcloud_batch_cache_clear(PointCloud &pointcloud) GPU_BATCH_DISCARD_SAFE(cache->surface); GPU_VERTBUF_DISCARD_SAFE(cache->pos); GPU_VERTBUF_DISCARD_SAFE(cache->geom); + GPU_VERTBUF_DISCARD_SAFE(cache->attr_viewer); GPU_INDEXBUF_DISCARD_SAFE(cache->geom_indices); if (cache->surface_per_mat) { @@ -116,6 +119,7 @@ static void pointcloud_batch_cache_clear(PointCloud &pointcloud) GPU_BATCH_DISCARD_SAFE(cache->surface_per_mat[i]); } } + GPU_BATCH_DISCARD_SAFE(cache->surface_viewer_attribute); MEM_SAFE_FREE(cache->surface_per_mat); } @@ -227,6 +231,30 @@ static void pointcloud_batch_cache_ensure_geom(PointCloudBatchCache &cache) cache.geom_indices = GPU_indexbuf_build(&builder); } +static void pointcloud_batch_cache_ensure_attribute_overlay(const PointCloud &pointcloud, + PointCloudBatchCache &cache) +{ + using namespace blender; + if (cache.attr_viewer != nullptr) { + return; + } + + const bke::AttributeAccessor attributes = pointcloud.attributes(); + const VArray<ColorGeometry4f> colors = attributes.lookup_or_default<ColorGeometry4f>( + ".viewer", ATTR_DOMAIN_POINT, {1.0f, 0.0f, 1.0f, 1.0f}); + + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "attribute_value", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + cache.attr_viewer = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(cache.attr_viewer, pointcloud.totpoint); + MutableSpan<ColorGeometry4f> vbo_data{ + static_cast<ColorGeometry4f *>(GPU_vertbuf_get_data(cache.attr_viewer)), + pointcloud.totpoint}; + colors.materialize(vbo_data); +} + GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob) { PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data); @@ -256,6 +284,25 @@ GPUBatch *DRW_pointcloud_batch_cache_get_surface(Object *ob) return cache->surface; } +GPUBatch *DRW_pointcloud_batch_cache_get_surface_viewer_attribute(Object *ob) +{ + PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data); + PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); + + if (cache->surface_viewer_attribute == nullptr) { + pointcloud_batch_cache_ensure_pos(pointcloud, *cache); + pointcloud_batch_cache_ensure_geom(*cache); + pointcloud_batch_cache_ensure_attribute_overlay(pointcloud, *cache); + + cache->surface_viewer_attribute = GPU_batch_create( + GPU_PRIM_TRIS, cache->geom, cache->geom_indices); + GPU_batch_instbuf_add_ex(cache->surface_viewer_attribute, cache->attr_viewer, false); + GPU_batch_instbuf_add_ex(cache->surface_viewer_attribute, cache->pos, false); + } + + return cache->surface_viewer_attribute; +} + GPUBatch **DRW_cache_pointcloud_surface_shaded_get(Object *ob, struct GPUMaterial **UNUSED(gpumat_array), uint gpumat_array_len) diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 51762e29506..99f75581a47 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -38,6 +38,7 @@ #include "BKE_pointcloud.h" #include "BKE_screen.h" #include "BKE_subdiv_modifier.h" +#include "BKE_viewer_path.h" #include "BKE_volume.h" #include "DNA_camera_types.h" @@ -1695,6 +1696,9 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, DEGObjectIterSettings deg_iter_settings = {0}; deg_iter_settings.depsgraph = depsgraph; deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS; + if (v3d->flag2 & V3D_SHOW_VIEWER) { + deg_iter_settings.viewer_path = &v3d->viewer_path; + } DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) { if ((object_type_exclude_viewport & (1 << ob->type)) != 0) { continue; @@ -2497,6 +2501,9 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, DEGObjectIterSettings deg_iter_settings = {0}; deg_iter_settings.depsgraph = depsgraph; deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS; + if (v3d->flag2 & V3D_SHOW_VIEWER) { + deg_iter_settings.viewer_path = &v3d->viewer_path; + } DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) { if (!BKE_object_is_visible_in_viewport(v3d, ob)) { continue; @@ -2662,6 +2669,9 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, DEGObjectIterSettings deg_iter_settings = {0}; deg_iter_settings.depsgraph = DST.draw_ctx.depsgraph; deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS; + if (v3d->flag2 & V3D_SHOW_VIEWER) { + deg_iter_settings.viewer_path = &v3d->viewer_path; + } DEG_OBJECT_ITER_BEGIN (°_iter_settings, ob) { if ((object_type_exclude_viewport & (1 << ob->type)) != 0) { continue; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh index 0bce05577b2..d9bb8d1d2b4 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh @@ -440,3 +440,4 @@ extern const MeshExtract extract_edge_idx; extern const MeshExtract extract_vert_idx; extern const MeshExtract extract_fdot_idx; extern const MeshExtract extract_attr[GPU_MAX_ATTR]; +extern const MeshExtract extract_attr_viewer; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index 240e9b4fa60..c602b768df9 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -14,6 +14,8 @@ #include "BLI_string.h" #include "BKE_attribute.h" +#include "BKE_attribute.hh" +#include "BKE_mesh.h" #include "draw_attributes.h" #include "draw_subdivision.h" @@ -432,6 +434,40 @@ constexpr MeshExtract create_extractor_attr(ExtractInitFn fn, ExtractInitSubdivF return extractor; } +static void extract_mesh_attr_viewer_init(const MeshRenderData *mr, + MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(tls_data)) +{ + GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf); + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "attribute_value", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + MutableSpan<ColorGeometry4f> attr{static_cast<ColorGeometry4f *>(GPU_vertbuf_get_data(vbo)), + mr->loop_len}; + + const StringRefNull attr_name = ".viewer"; + const bke::AttributeAccessor attributes = mr->me->attributes(); + attributes + .lookup_or_default<ColorGeometry4f>(attr_name, ATTR_DOMAIN_CORNER, {1.0f, 0.0f, 1.0f, 1.0f}) + .materialize(attr); +} + +constexpr MeshExtract create_extractor_attr_viewer() +{ + MeshExtract extractor = {nullptr}; + extractor.init = extract_mesh_attr_viewer_init; + extractor.data_type = MR_DATA_NONE; + extractor.data_size = 0; + extractor.use_threading = false; + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.attr_viewer); + return extractor; +} + /** \} */ } // namespace blender::draw @@ -457,3 +493,5 @@ const MeshExtract extract_attr[GPU_MAX_ATTR] = { CREATE_EXTRACTOR_ATTR(13), CREATE_EXTRACTOR_ATTR(14), }; + +const MeshExtract extract_attr_viewer = blender::draw::create_extractor_attr_viewer(); diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc index dc631483857..f238c7e25b1 100644 --- a/source/blender/draw/tests/shaders_test.cc +++ b/source/blender/draw/tests/shaders_test.cc @@ -254,6 +254,7 @@ static void test_overlay_glsl_shaders() EXPECT_NE(OVERLAY_shader_motion_path_line(), nullptr); EXPECT_NE(OVERLAY_shader_motion_path_vert(), nullptr); EXPECT_NE(OVERLAY_shader_uniform_color(), nullptr); + EXPECT_NE(OVERLAY_shader_uniform_color_pointcloud(), nullptr); EXPECT_NE(OVERLAY_shader_outline_prepass(false), nullptr); EXPECT_NE(OVERLAY_shader_outline_prepass(true), nullptr); EXPECT_NE(OVERLAY_shader_outline_prepass_curves(), nullptr); @@ -272,6 +273,10 @@ static void test_overlay_glsl_shaders() EXPECT_NE(OVERLAY_shader_particle_shape(), nullptr); EXPECT_NE(OVERLAY_shader_sculpt_mask(), nullptr); EXPECT_NE(OVERLAY_shader_sculpt_curves_selection(), nullptr); + EXPECT_NE(OVERLAY_shader_viewer_attribute_curve(), nullptr); + EXPECT_NE(OVERLAY_shader_viewer_attribute_curves(), nullptr); + EXPECT_NE(OVERLAY_shader_viewer_attribute_mesh(), nullptr); + EXPECT_NE(OVERLAY_shader_viewer_attribute_pointcloud(), nullptr); EXPECT_NE(OVERLAY_shader_volume_velocity(false, false), nullptr); EXPECT_NE(OVERLAY_shader_volume_velocity(false, true), nullptr); EXPECT_NE(OVERLAY_shader_volume_velocity(true, false), nullptr); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 144fa4e0b93..e1fd53ccdee 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -469,6 +469,8 @@ bool ED_workspace_layout_cycle(struct WorkSpace *workspace, short direction, str void ED_workspace_status_text(struct bContext *C, const char *str); +void ED_workspace_do_listen(struct bContext *C, const struct wmNotifier *note); + /* anim */ /** * Results in fully updated anim system. diff --git a/source/blender/editors/include/ED_spreadsheet.h b/source/blender/editors/include/ED_spreadsheet.h index da13e6d3636..736da367a44 100644 --- a/source/blender/editors/include/ED_spreadsheet.h +++ b/source/blender/editors/include/ED_spreadsheet.h @@ -7,7 +7,6 @@ struct Main; struct Object; struct SpaceNode; struct SpaceSpreadsheet; -struct SpreadsheetContext; struct bContext; struct bNode; @@ -15,29 +14,8 @@ struct bNode; extern "C" { #endif -struct SpreadsheetContext *ED_spreadsheet_context_new(int type); -void ED_spreadsheet_context_free(struct SpreadsheetContext *context); -void ED_spreadsheet_context_path_clear(struct SpaceSpreadsheet *sspreadsheet); -bool ED_spreadsheet_context_path_update_tag(struct SpaceSpreadsheet *sspreadsheet); -uint64_t ED_spreadsheet_context_path_hash(const struct SpaceSpreadsheet *sspreadsheet); - struct ID *ED_spreadsheet_get_current_id(const struct SpaceSpreadsheet *sspreadsheet); -void ED_spreadsheet_context_path_set_geometry_node(struct SpaceSpreadsheet *sspreadsheet, - struct SpaceNode *snode, - struct bNode *node); -void ED_spreadsheet_context_paths_set_geometry_node(struct Main *bmain, - struct SpaceNode *snode, - struct bNode *node); -void ED_spreadsheet_context_path_set_evaluated_object(struct SpaceSpreadsheet *sspreadsheet, - struct Object *object); - -void ED_spreadsheet_context_path_guess(const struct bContext *C, - struct SpaceSpreadsheet *sspreadsheet); -bool ED_spreadsheet_context_path_is_active(const struct bContext *C, - struct SpaceSpreadsheet *sspreadsheet); -bool ED_spreadsheet_context_path_exists(struct Main *bmain, struct SpaceSpreadsheet *sspreadsheet); - #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_viewer_path.hh b/source/blender/editors/include/ED_viewer_path.hh new file mode 100644 index 00000000000..957dfddb8af --- /dev/null +++ b/source/blender/editors/include/ED_viewer_path.hh @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <optional> + +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "BKE_viewer_path.h" + +struct Main; +struct SpaceNode; +struct bNode; +struct bContext; +struct Object; + +namespace blender::ed::viewer_path { + +/** + * Activates the given node in the context provided by the editor. This indirectly updates all + * non-pinned viewer paths in other editors (spreadsheet and 3d view). + */ +void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node); + +/** + * Returns the object referenced by the viewer path. This only returns something if the viewer path + * *only* contains the object and nothing more. + */ +Object *parse_object_only(const ViewerPath &viewer_path); + +/** + * Represents a parsed #ViewerPath for easier consumption. + */ +struct ViewerPathForGeometryNodesViewer { + Object *object; + blender::StringRefNull modifier_name; + blender::Vector<blender::StringRefNull> group_node_names; + blender::StringRefNull viewer_node_name; +}; + +/** + * Parses a #ViewerPath into a #ViewerPathForGeometryNodesViewer or returns none if that does not + * work. + */ +std::optional<ViewerPathForGeometryNodesViewer> parse_geometry_nodes_viewer( + const ViewerPath &viewer_path); + +/** + * Finds the node referenced by the #ViewerPath within the provided editor. If no node is + * referenced, null is returned. When two different editors show the same node group but in a + * different context, it's possible that the same node is active in one editor but not the other. + */ +bNode *find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snode); + +/** + * Checks if the node referenced by the viewer path and its entire context still exists. The node + * does not have to be visible for this to return true. + */ +bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed_viewer_path); + +/** + * 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); + +} // namespace blender::ed::viewer_path diff --git a/source/blender/editors/screen/CMakeLists.txt b/source/blender/editors/screen/CMakeLists.txt index 119758f3335..bde73402277 100644 --- a/source/blender/editors/screen/CMakeLists.txt +++ b/source/blender/editors/screen/CMakeLists.txt @@ -34,6 +34,7 @@ set(SRC screendump.c workspace_edit.c workspace_layout_edit.c + workspace_listen.cc screen_intern.h ) diff --git a/source/blender/editors/screen/workspace_listen.cc b/source/blender/editors/screen/workspace_listen.cc new file mode 100644 index 00000000000..84326007d66 --- /dev/null +++ b/source/blender/editors/screen/workspace_listen.cc @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_context.h" + +#include "ED_screen.h" +#include "ED_viewer_path.hh" + +#include "WM_api.h" + +/** + * Checks if the viewer path stored in the workspace is still active and resets it if not. + * The viewer path stored in the workspace is the ground truth for other editors, so it should be + * updated before other editors look at it. + */ +static void validate_viewer_paths(bContext &C, WorkSpace &workspace) +{ + if (BLI_listbase_is_empty(&workspace.viewer_path.path)) { + return; + } + + const std::optional<blender::ed::viewer_path::ViewerPathForGeometryNodesViewer> 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)) { + /* The current viewer path is still valid and active. */ + return; + } + /* Reset inactive viewer path. */ + BKE_viewer_path_clear(&workspace.viewer_path); + WM_event_add_notifier(&C, NC_VIEWER_PATH, nullptr); +} + +void ED_workspace_do_listen(bContext *C, const wmNotifier * /*note*/) +{ + WorkSpace *workspace = CTX_wm_workspace(C); + validate_viewer_paths(*C, *workspace); +} diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 8f72d292740..29dad103dc3 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -60,6 +60,7 @@ #include "ED_node.h" #include "ED_screen.h" #include "ED_space_api.h" +#include "ED_viewer_path.hh" #include "UI_interface.hh" #include "UI_resources.h" @@ -95,6 +96,14 @@ extern void ui_draw_dropshadow( */ struct TreeDrawContext { /** + * Whether a viewer node is active in geometry nodes can not be determined by a flag on the node + * alone. That's because if the node group with the viewer is used multiple times, it's only + * active in one of these cases. + * The active node is cached here to avoid doing the more expensive check for every viewer node + * in the tree. + */ + const bNode *active_geometry_nodes_viewer = nullptr; + /** * 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. */ @@ -639,15 +648,19 @@ static void node_update_hidden(bNode &node, uiBlock &block) node.totr.ymax); } -static int node_get_colorid(const bNode &node) +static int node_get_colorid(TreeDrawContext &tree_draw_ctx, const bNode &node) { const int nclass = (node.typeinfo->ui_class == nullptr) ? node.typeinfo->nclass : node.typeinfo->ui_class(&node); switch (nclass) { case NODE_CLASS_INPUT: return TH_NODE_INPUT; - case NODE_CLASS_OUTPUT: + case NODE_CLASS_OUTPUT: { + if (node.type == GEO_NODE_VIEWER) { + return &node == tree_draw_ctx.active_geometry_nodes_viewer ? TH_NODE_OUTPUT : TH_NODE; + } return (node.flag & NODE_DO_OUTPUT) ? TH_NODE_OUTPUT : TH_NODE; + } case NODE_CLASS_CONVERTER: return TH_NODE_CONVERTER; case NODE_CLASS_OP_COLOR: @@ -2055,7 +2068,7 @@ static void node_draw_basis(const bContext &C, const rctf &rct = node.totr; float color[4]; - int color_id = node_get_colorid(node); + int color_id = node_get_colorid(tree_draw_ctx, node); GPU_line_width(1.0f); @@ -2153,6 +2166,29 @@ static void node_draw_basis(const bContext &C, ""); UI_block_emboss_set(&block, UI_EMBOSS); } + if (node.type == GEO_NODE_VIEWER) { + const bool is_active = &node == tree_draw_ctx.active_geometry_nodes_viewer; + iconofs -= iconbutw; + UI_block_emboss_set(&block, UI_EMBOSS_NONE); + uiBut *but = uiDefIconBut(&block, + UI_BTYPE_BUT, + 0, + is_active ? ICON_HIDE_OFF : ICON_HIDE_ON, + iconofs, + rct.ymax - NODE_DY, + iconbutw, + UI_UNIT_Y, + nullptr, + 0, + 0, + 0, + 0, + ""); + /* Selection implicitly activates the node. */ + const char *operator_idname = is_active ? "NODE_OT_deactivate_viewer" : "NODE_OT_select"; + UI_but_func_set(but, node_toggle_button_cb, &node, (void *)operator_idname); + UI_block_emboss_set(&block, UI_EMBOSS); + } node_add_error_message_button(tree_draw_ctx, node, block, rct, iconofs); @@ -2341,7 +2377,7 @@ static void node_draw_hidden(const bContext &C, float scale; UI_view2d_scale_get(&v2d, &scale, nullptr); - const int color_id = node_get_colorid(node); + const int color_id = node_get_colorid(tree_draw_ctx, node); node_draw_extra_info_panel(tree_draw_ctx, snode, node, block); @@ -2698,7 +2734,8 @@ static void node_update_nodetree(const bContext &C, } } -static void frame_node_draw_label(const bNodeTree &ntree, +static void frame_node_draw_label(TreeDrawContext &tree_draw_ctx, + const bNodeTree &ntree, const bNode &node, const SpaceNode &snode) { @@ -2717,7 +2754,7 @@ static void frame_node_draw_label(const bNodeTree &ntree, BLF_size(fontid, MIN2(24.0f, font_size) * U.dpi_fac); /* title color */ - int color_id = node_get_colorid(node); + int color_id = node_get_colorid(tree_draw_ctx, node); uchar color[3]; UI_GetThemeColorBlendShade3ubv(TH_TEXT, color_id, 0.4f, 10, color); BLF_color3ubv(fontid, color); @@ -2831,7 +2868,7 @@ static void frame_node_draw(const bContext &C, } /* label and text */ - frame_node_draw_label(ntree, node, snode); + frame_node_draw_label(tree_draw_ctx, ntree, node, snode); node_draw_extra_info_panel(tree_draw_ctx, snode, node, block); @@ -3036,6 +3073,9 @@ static void draw_nodetree(const bContext &C, tree_draw_ctx.geo_tree_log->ensure_node_warnings(); tree_draw_ctx.geo_tree_log->ensure_node_run_time(); } + WorkSpace *workspace = CTX_wm_workspace(&C); + tree_draw_ctx.active_geometry_nodes_viewer = viewer_path::find_geometry_nodes_viewer( + workspace->viewer_path, *snode); } node_update_nodetree(C, tree_draw_ctx, ntree, nodes, blocks); diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index e446d5421e7..8135369f271 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -43,7 +43,7 @@ #include "ED_render.h" #include "ED_screen.h" #include "ED_select_utils.h" -#include "ED_spreadsheet.h" +#include "ED_viewer_path.hh" #include "RNA_access.h" #include "RNA_define.h" @@ -821,8 +821,8 @@ void ED_node_set_active( } } node->flag |= NODE_DO_OUTPUT; - ED_spreadsheet_context_paths_set_geometry_node(bmain, snode, node); } + blender::ed::viewer_path::activate_geometry_node(*bmain, *snode, *node); } } } @@ -1700,6 +1700,46 @@ void NODE_OT_preview_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static int node_deactivate_viewer_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode &snode = *CTX_wm_space_node(C); + WorkSpace &workspace = *CTX_wm_workspace(C); + + bNode *active_viewer = viewer_path::find_geometry_nodes_viewer(workspace.viewer_path, snode); + + LISTBASE_FOREACH (bNode *, node, &snode.edittree->nodes) { + if (node->type != GEO_NODE_VIEWER) { + continue; + } + if (!(node->flag & SELECT)) { + continue; + } + if (node == active_viewer) { + node->flag &= ~NODE_DO_OUTPUT; + BKE_ntree_update_tag_node_property(snode.edittree, node); + } + } + + ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); + + return OPERATOR_FINISHED; +} + +void NODE_OT_deactivate_viewer(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Deactivate Viewer Node"; + ot->description = "Deactivate selected viewer node in geometry nodes"; + ot->idname = __func__; + + /* callbacks */ + ot->exec = node_deactivate_viewer_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + static int node_options_toggle_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceNode *snode = CTX_wm_space_node(C); diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 70ac0e48d01..50c03489027 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -322,6 +322,7 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot); void NODE_OT_preview_toggle(wmOperatorType *ot); void NODE_OT_options_toggle(wmOperatorType *ot); void NODE_OT_node_copy_color(wmOperatorType *ot); +void NODE_OT_deactivate_viewer(wmOperatorType *ot); void NODE_OT_read_viewlayers(wmOperatorType *ot); void NODE_OT_render_changed(wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc index f02c019359d..6c52dae5b86 100644 --- a/source/blender/editors/space_node/node_ops.cc +++ b/source/blender/editors/space_node/node_ops.cc @@ -44,6 +44,7 @@ void node_operatortypes() WM_operatortype_append(NODE_OT_options_toggle); WM_operatortype_append(NODE_OT_hide_socket_toggle); WM_operatortype_append(NODE_OT_node_copy_color); + WM_operatortype_append(NODE_OT_deactivate_viewer); WM_operatortype_append(NODE_OT_duplicate); WM_operatortype_append(NODE_OT_delete); @@ -135,6 +136,7 @@ void ED_operatormacros_node() mot = WM_operatortype_macro_define(ot, "NODE_OT_select"); RNA_boolean_set(mot->ptr, "extend", false); RNA_boolean_set(mot->ptr, "socket_select", true); + RNA_boolean_set(mot->ptr, "clear_viewer", true); WM_operatortype_macro_define(ot, "NODE_OT_link_viewer"); ot = WM_operatortype_append_macro("NODE_OT_translate_attach", diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index f597bf20b55..84cef798907 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -27,8 +27,8 @@ #include "ED_render.h" #include "ED_screen.h" #include "ED_space_api.h" -#include "ED_spreadsheet.h" #include "ED_util.h" +#include "ED_viewer_path.hh" #include "RNA_access.h" #include "RNA_define.h" @@ -495,17 +495,6 @@ static bool is_viewer_node(const bNode &node) return ELEM(node.type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER); } -static Vector<const bNode *> find_viewer_nodes(const bNodeTree &tree) -{ - Vector<const bNode *> viewer_nodes; - for (const bNode *node : tree.all_nodes()) { - if (is_viewer_node(*node)) { - viewer_nodes.append(node); - } - } - return viewer_nodes; -} - static bool is_viewer_socket_in_viewer(const bNodeSocket &socket) { const bNode &node = socket.owner_node(); @@ -516,18 +505,10 @@ static bool is_viewer_socket_in_viewer(const bNodeSocket &socket) return socket.index() == 0; } -static bool is_linked_to_viewer(const bNodeSocket &socket, const bNode &viewer_node) +static bool is_viewer_socket(const bNodeSocket &socket) { - for (const bNodeSocket *target_socket : socket.directly_linked_sockets()) { - if (&target_socket->owner_node() != &viewer_node) { - continue; - } - if (!target_socket->is_available()) { - continue; - } - if (is_viewer_socket_in_viewer(*target_socket)) { - return true; - } + if (is_viewer_node(socket.owner_node())) { + return is_viewer_socket_in_viewer(socket); } return false; } @@ -549,137 +530,165 @@ static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode & } } -static const bNode *get_existing_viewer(const bNodeTree &tree) +static bNodeSocket *determine_socket_to_view(bNode &node_to_view) { - Vector<const bNode *> viewer_nodes = find_viewer_nodes(tree); - - /* Check if there is already an active viewer node that should be used. */ - for (const bNode *viewer_node : viewer_nodes) { - if (viewer_node->flag & NODE_DO_OUTPUT) { - return viewer_node; + int last_linked_socket_index = -1; + for (bNodeSocket *socket : node_to_view.output_sockets()) { + if (!socket_can_be_viewed(*socket)) { + continue; } - } - - /* If no active but non-active viewers exist, make one active. */ - if (!viewer_nodes.is_empty()) { - const_cast<bNode *>(viewer_nodes[0])->flag |= NODE_DO_OUTPUT; - return viewer_nodes[0]; - } - return nullptr; -} - -static const bNodeSocket *find_output_socket_to_be_viewed(const bNode *active_viewer_node, - const bNode &node_to_view) -{ - /* Check if any of the output sockets is selected, which is the case when the user just clicked - * on the socket. */ - for (const bNodeSocket *output_socket : node_to_view.output_sockets()) { - if (output_socket->flag & SELECT) { - return output_socket; + for (bNodeLink *link : socket->directly_linked_links()) { + bNodeSocket &target_socket = *link->tosock; + bNode &target_node = *link->tonode; + if (is_viewer_socket(target_socket)) { + if (link->is_muted() || !(target_node.flag & NODE_DO_OUTPUT)) { + /* This socket is linked to a deactivated viewer, the viewer should be activated. */ + return socket; + } + last_linked_socket_index = socket->index(); + } } } - const bNodeSocket *last_socket_linked_to_viewer = nullptr; - if (active_viewer_node != nullptr) { - for (const bNodeSocket *output_socket : node_to_view.output_sockets()) { - if (!socket_can_be_viewed(*output_socket)) { - continue; - } - if (is_linked_to_viewer(*output_socket, *active_viewer_node)) { - last_socket_linked_to_viewer = output_socket; + if (last_linked_socket_index == -1) { + /* Returnt he first socket that can be viewed. */ + for (bNodeSocket *socket : node_to_view.output_sockets()) { + if (socket_can_be_viewed(*socket)) { + return socket; } } + return nullptr; } - if (last_socket_linked_to_viewer == nullptr) { - /* If no output is connected to a viewer, use the first output that can be viewed. */ - for (const bNodeSocket *output_socket : node_to_view.output_sockets()) { - if (socket_can_be_viewed(*output_socket)) { - return output_socket; - } + + /* Pick the next socket to be linked to the viewer. */ + const int tot_outputs = node_to_view.output_sockets().size(); + for (const int offset : IndexRange(1, tot_outputs)) { + const int index = (last_linked_socket_index + offset) % tot_outputs; + bNodeSocket &output_socket = node_to_view.output_socket(index); + if (!socket_can_be_viewed(output_socket)) { + continue; } - } - else { - /* Pick the next socket to be linked to the viewer. */ - const int tot_outputs = node_to_view.output_sockets().size(); - for (const int offset : IndexRange(1, tot_outputs - 1)) { - const int index = (last_socket_linked_to_viewer->index() + offset) % tot_outputs; - const bNodeSocket &output_socket = node_to_view.output_socket(index); - if (!socket_can_be_viewed(output_socket)) { + bool is_currently_viewed = false; + for (const bNodeLink *link : output_socket.directly_linked_links()) { + bNodeSocket &target_socket = *link->tosock; + bNode &target_node = *link->tonode; + if (!is_viewer_socket(target_socket)) { continue; } - if (is_linked_to_viewer(output_socket, *active_viewer_node)) { + if (link->is_muted()) { continue; } - return &output_socket; + if (!(target_node.flag & NODE_DO_OUTPUT)) { + continue; + } + is_currently_viewed = true; + break; } + if (is_currently_viewed) { + continue; + } + return &output_socket; } return nullptr; } -static int link_socket_to_viewer(const bContext &C, - bNode *viewer_bnode, - bNode &bnode_to_view, - bNodeSocket &bsocket_to_view) +static void finalize_viewer_link(const bContext &C, + SpaceNode &snode, + bNode &viewer_node, + bNodeLink &viewer_link) { - SpaceNode &snode = *CTX_wm_space_node(&C); - bNodeTree &btree = *snode.edittree; + Main *bmain = CTX_data_main(&C); + remove_links_to_unavailable_viewer_sockets(*snode.edittree, viewer_node); + viewer_link.flag &= ~NODE_LINK_MUTED; + viewer_node.flag &= ~NODE_MUTED; + viewer_node.flag |= NODE_DO_OUTPUT; + if (snode.edittree->type == NTREE_GEOMETRY) { + viewer_path::activate_geometry_node(*bmain, snode, viewer_node); + } + ED_node_tree_propagate_change(&C, bmain, snode.edittree); +} + +static int view_socket(const bContext &C, + SpaceNode &snode, + bNodeTree &btree, + bNode &bnode_to_view, + bNodeSocket &bsocket_to_view) +{ + bNode *viewer_node = nullptr; + /* Try to find a viewer that is already active. */ + LISTBASE_FOREACH (bNode *, node, &btree.nodes) { + if (is_viewer_node(*node)) { + if (node->flag & NODE_DO_OUTPUT) { + viewer_node = node; + break; + } + } + } - if (viewer_bnode == nullptr) { - /* Create a new viewer node if none exists. */ + /* Try to reactivate existing viewer connection. */ + for (bNodeLink *link : bsocket_to_view.directly_linked_links()) { + bNodeSocket &target_socket = *link->tosock; + bNode &target_node = *link->tonode; + if (is_viewer_socket(target_socket) && ELEM(viewer_node, nullptr, &target_node)) { + finalize_viewer_link(C, snode, target_node, *link); + return OPERATOR_FINISHED; + } + } + + if (viewer_node == nullptr) { + LISTBASE_FOREACH (bNode *, node, &btree.nodes) { + if (is_viewer_node(*node)) { + viewer_node = node; + break; + } + } + } + if (viewer_node == nullptr) { const int viewer_type = get_default_viewer_type(&C); const float2 location{bsocket_to_view.locx / UI_DPI_FAC + 100, bsocket_to_view.locy / UI_DPI_FAC}; - viewer_bnode = add_static_node(C, viewer_type, location); - if (viewer_bnode == nullptr) { - return OPERATOR_CANCELLED; - } + viewer_node = add_static_node(C, viewer_type, location); } - bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, *viewer_bnode, bsocket_to_view); + bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, *viewer_node, bsocket_to_view); if (viewer_bsocket == nullptr) { return OPERATOR_CANCELLED; } - - bNodeLink *link_to_change = nullptr; - LISTBASE_FOREACH (bNodeLink *, link, &btree.links) { + bNodeLink *viewer_link = nullptr; + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) { if (link->tosock == viewer_bsocket) { - link_to_change = link; + viewer_link = link; break; } } - - if (link_to_change == nullptr) { - nodeAddLink(&btree, &bnode_to_view, &bsocket_to_view, viewer_bnode, viewer_bsocket); + if (viewer_link == nullptr) { + viewer_link = nodeAddLink( + &btree, &bnode_to_view, &bsocket_to_view, viewer_node, viewer_bsocket); } else { - link_to_change->fromnode = &bnode_to_view; - link_to_change->fromsock = &bsocket_to_view; + viewer_link->fromnode = &bnode_to_view; + viewer_link->fromsock = &bsocket_to_view; BKE_ntree_update_tag_link_changed(&btree); } - - remove_links_to_unavailable_viewer_sockets(btree, *viewer_bnode); - - if (btree.type == NTREE_GEOMETRY) { - ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(&C), &snode, viewer_bnode); - } - - ED_node_tree_propagate_change(&C, CTX_data_main(&C), &btree); - return OPERATOR_FINISHED; + finalize_viewer_link(C, snode, *viewer_node, *viewer_link); + return OPERATOR_CANCELLED; } -static int node_link_viewer(const bContext &C, bNode &bnode_to_view) +static int node_link_viewer(const bContext &C, bNode &bnode_to_view, bNodeSocket *bsocket_to_view) { SpaceNode &snode = *CTX_wm_space_node(&C); bNodeTree *btree = snode.edittree; btree->ensure_topology_cache(); - bNode *active_viewer_bnode = const_cast<bNode *>(get_existing_viewer(*btree)); - bNodeSocket *bsocket_to_view = const_cast<bNodeSocket *>( - find_output_socket_to_be_viewed(active_viewer_bnode, bnode_to_view)); if (bsocket_to_view == nullptr) { - return OPERATOR_FINISHED; + bsocket_to_view = determine_socket_to_view(bnode_to_view); } - return link_socket_to_viewer(C, active_viewer_bnode, bnode_to_view, *bsocket_to_view); + + if (bsocket_to_view == nullptr) { + return OPERATOR_CANCELLED; + } + + return view_socket(C, snode, *btree, bnode_to_view, *bsocket_to_view); } /** \} */ @@ -701,7 +710,15 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - if (viewer_linking::node_link_viewer(*C, *node) == OPERATOR_CANCELLED) { + bNodeSocket *socket_to_view = nullptr; + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + if (socket->flag & SELECT) { + socket_to_view = socket; + break; + } + } + + if (viewer_linking::node_link_viewer(*C, *node, socket_to_view) == OPERATOR_CANCELLED) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 1b47316ebd0..c11ae323115 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -23,13 +23,14 @@ #include "BKE_main.h" #include "BKE_node.h" #include "BKE_node_runtime.hh" +#include "BKE_node_tree_update.h" #include "BKE_workspace.h" #include "ED_node.h" /* own include */ #include "ED_screen.h" #include "ED_select_utils.h" -#include "ED_spreadsheet.h" #include "ED_view3d.h" +#include "ED_viewer_path.hh" #include "RNA_access.h" #include "RNA_define.h" @@ -644,6 +645,15 @@ static bool node_mouse_select(bContext *C, } } + if (RNA_boolean_get(op->ptr, "clear_viewer")) { + if (node == nullptr) { + /* Disable existing active viewer. */ + WorkSpace *workspace = CTX_wm_workspace(C); + BKE_viewer_path_clear(&workspace->viewer_path); + WM_event_add_notifier(C, NC_VIEWER_PATH, nullptr); + } + } + if (!(changed || found)) { return false; } @@ -655,7 +665,7 @@ static bool node_mouse_select(bContext *C, ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed); } else if (node != nullptr && node->type == GEO_NODE_VIEWER) { - ED_spreadsheet_context_paths_set_geometry_node(&bmain, &snode, node); + viewer_path::activate_geometry_node(bmain, snode, *node); } ED_node_set_active_viewer_key(&snode); node_sort(*snode.edittree); @@ -731,6 +741,12 @@ void NODE_OT_select(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", ""); + + RNA_def_boolean(ot->srna, + "clear_viewer", + false, + "Clear Viewer", + "Deactivate geometry nodes viewer when clicking in empty space"); } /** \} */ diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index ea551b7488a..3e5cbf88e15 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -823,6 +823,9 @@ static void node_region_listener(const wmRegionListenerParams *params) ED_region_tag_redraw(region); } break; + case NC_VIEWER_PATH: + ED_region_tag_redraw(region); + break; } } diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt index a551e096b48..08032ddbaeb 100644 --- a/source/blender/editors/space_spreadsheet/CMakeLists.txt +++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt @@ -27,7 +27,6 @@ set(SRC space_spreadsheet.cc spreadsheet_cache.cc spreadsheet_column.cc - spreadsheet_context.cc spreadsheet_data_source.cc spreadsheet_data_source_geometry.cc spreadsheet_dataset_draw.cc @@ -41,7 +40,6 @@ set(SRC spreadsheet_cache.hh spreadsheet_column.hh spreadsheet_column_values.hh - spreadsheet_context.hh spreadsheet_data_source.hh spreadsheet_data_source_geometry.hh spreadsheet_dataset_draw.hh diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index d5fa0145fe5..ee43519e260 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -10,6 +10,7 @@ #include "ED_screen.h" #include "ED_space_api.h" #include "ED_spreadsheet.h" +#include "ED_viewer_path.hh" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -34,7 +35,6 @@ #include "BLF_api.h" -#include "spreadsheet_context.hh" #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_dataset_draw.hh" #include "spreadsheet_intern.hh" @@ -107,9 +107,7 @@ static void spreadsheet_free(SpaceLink *sl) LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &sspreadsheet->columns) { spreadsheet_column_free(column); } - LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, context, &sspreadsheet->context_path) { - spreadsheet_context_free(context); - } + BKE_viewer_path_clear(&sspreadsheet->viewer_path); } static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area) @@ -143,11 +141,7 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl) BLI_addtail(&sspreadsheet_new->columns, new_column); } - BLI_listbase_clear(&sspreadsheet_new->context_path); - LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, src_context, &sspreadsheet_old->context_path) { - SpreadsheetContext *new_context = spreadsheet_context_copy(src_context); - BLI_addtail(&sspreadsheet_new->context_path, new_context); - } + BKE_viewer_path_copy(&sspreadsheet_new->viewer_path, &sspreadsheet_old->viewer_path); return (SpaceLink *)sspreadsheet_new; } @@ -163,19 +157,7 @@ static void spreadsheet_id_remap(ScrArea *UNUSED(area), const IDRemapper *mappings) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)slink; - LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { - if (context->type != SPREADSHEET_CONTEXT_OBJECT) { - continue; - } - SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context; - - if (object_context->object != nullptr && GS(object_context->object->id.name) != ID_OB) { - object_context->object = nullptr; - continue; - } - - BKE_id_remapper_apply(mappings, ((ID **)&object_context->object), ID_REMAP_APPLY_DEFAULT); - } + BKE_viewer_path_id_remap(&sspreadsheet->viewer_path, mappings); } static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region) @@ -201,54 +183,105 @@ static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region) ID *ED_spreadsheet_get_current_id(const struct SpaceSpreadsheet *sspreadsheet) { - if (BLI_listbase_is_empty(&sspreadsheet->context_path)) { + if (BLI_listbase_is_empty(&sspreadsheet->viewer_path.path)) { return nullptr; } - SpreadsheetContext *root_context = (SpreadsheetContext *)sspreadsheet->context_path.first; - if (root_context->type != SPREADSHEET_CONTEXT_OBJECT) { + ViewerPathElem *root_context = static_cast<ViewerPathElem *>( + sspreadsheet->viewer_path.path.first); + if (root_context->type != VIEWER_PATH_ELEM_TYPE_ID) { return nullptr; } - SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)root_context; - return (ID *)object_context->object; + IDViewerPathElem *id_elem = reinterpret_cast<IDViewerPathElem *>(root_context); + return id_elem->id; } -/* Check if the pinned context still exists. If it doesn't try to find a new context. */ -static void update_pinned_context_path_if_outdated(const bContext *C) +static void view_active_object(const bContext *C, SpaceSpreadsheet *sspreadsheet) { - SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); - Main *bmain = CTX_data_main(C); - if (!ED_spreadsheet_context_path_exists(bmain, sspreadsheet)) { - ED_spreadsheet_context_path_guess(C, sspreadsheet); - if (ED_spreadsheet_context_path_update_tag(sspreadsheet)) { - ED_area_tag_redraw(CTX_wm_area(C)); - } - } - - if (BLI_listbase_is_empty(&sspreadsheet->context_path)) { - /* Don't pin empty context_path, that could be annoying. */ - sspreadsheet->flag &= ~SPREADSHEET_FLAG_PINNED; + BKE_viewer_path_clear(&sspreadsheet->viewer_path); + Object *ob = CTX_data_active_object(C); + if (ob == nullptr) { + return; } + IDViewerPathElem *id_elem = BKE_viewer_path_elem_new_id(); + id_elem->id = &ob->id; + BLI_addtail(&sspreadsheet->viewer_path.path, id_elem); + ED_area_tag_redraw(CTX_wm_area(C)); } -static void update_context_path_from_context(const bContext *C) +static void spreadsheet_update_context(const bContext *C) { + using blender::ed::viewer_path::ViewerPathForGeometryNodesViewer; + SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); - if (!ED_spreadsheet_context_path_is_active(C, sspreadsheet)) { - ED_spreadsheet_context_path_guess(C, sspreadsheet); - if (ED_spreadsheet_context_path_update_tag(sspreadsheet)) { - ED_area_tag_redraw(CTX_wm_area(C)); + Object *active_object = CTX_data_active_object(C); + Object *context_object = blender::ed::viewer_path::parse_object_only(sspreadsheet->viewer_path); + switch (eSpaceSpreadsheet_ObjectEvalState(sspreadsheet->object_eval_state)) { + case SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL: + case SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED: { + if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) { + if (context_object == nullptr) { + /* Object is not available anymore, so clear the pinning. */ + sspreadsheet->flag &= ~SPREADSHEET_FLAG_PINNED; + } + else { + /* The object is still pinned, do nothing. */ + break; + } + } + else { + if (active_object != context_object) { + /* The active object has changed, so view the new active object. */ + view_active_object(C, sspreadsheet); + } + else { + /* Nothing changed. */ + break; + } + } + break; } - } -} + case SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE: { + WorkSpace *workspace = CTX_wm_workspace(C); + if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) { + const std::optional<ViewerPathForGeometryNodesViewer> parsed_path = + blender::ed::viewer_path::parse_geometry_nodes_viewer(sspreadsheet->viewer_path); + if (parsed_path.has_value()) { + if (blender::ed::viewer_path::exists_geometry_nodes_viewer(*parsed_path)) { + /* The pinned path is still valid, do nothing. */ + break; + } + else { + /* The pinned path does not exist anymore, clear pinning. */ + sspreadsheet->flag &= ~SPREADSHEET_FLAG_PINNED; + } + } + else { + /* Unknown pinned path, clear pinning. */ + sspreadsheet->flag &= ~SPREADSHEET_FLAG_PINNED; + } + } + /* Now try to update the viewer path from the workspace. */ + const std::optional<ViewerPathForGeometryNodesViewer> workspace_parsed_path = + blender::ed::viewer_path::parse_geometry_nodes_viewer(workspace->viewer_path); + if (workspace_parsed_path.has_value()) { + if (BKE_viewer_path_equal(&sspreadsheet->viewer_path, &workspace->viewer_path)) { + /* Nothing changed. */ + break; + } + else { + /* Update the viewer path from the workspace. */ + BKE_viewer_path_clear(&sspreadsheet->viewer_path); + BKE_viewer_path_copy(&sspreadsheet->viewer_path, &workspace->viewer_path); + } + } + else { + /* No active viewer node, change back to showing evaluated active object. */ + sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED; + view_active_object(C, sspreadsheet); + } -void spreadsheet_update_context_path(const bContext *C) -{ - SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); - if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) { - update_pinned_context_path_if_outdated(C); - } - else { - update_context_path_from_context(C); + break; + } } } @@ -390,7 +423,7 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); sspreadsheet->runtime->cache.set_all_unused(); - spreadsheet_update_context_path(C); + spreadsheet_update_context(C); std::unique_ptr<DataSource> data_source = get_data_source(C); if (!data_source) { @@ -439,6 +472,7 @@ static void spreadsheet_main_region_listener(const wmRegionListenerParams *param { ARegion *region = params->region; const wmNotifier *wmn = params->notifier; + SpaceSpreadsheet *sspreadsheet = static_cast<SpaceSpreadsheet *>(params->area->spacedata.first); switch (wmn->category) { case NC_SCENE: { @@ -467,6 +501,12 @@ static void spreadsheet_main_region_listener(const wmRegionListenerParams *param ED_region_tag_redraw(region); break; } + case NC_VIEWER_PATH: { + if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { + ED_region_tag_redraw(region); + } + break; + } } } @@ -477,7 +517,7 @@ static void spreadsheet_header_region_init(wmWindowManager *UNUSED(wm), ARegion static void spreadsheet_header_region_draw(const bContext *C, ARegion *region) { - spreadsheet_update_context_path(C); + spreadsheet_update_context(C); ED_region_header(C, region); } @@ -489,6 +529,7 @@ static void spreadsheet_header_region_listener(const wmRegionListenerParams *par { ARegion *region = params->region; const wmNotifier *wmn = params->notifier; + SpaceSpreadsheet *sspreadsheet = static_cast<SpaceSpreadsheet *>(params->area->spacedata.first); switch (wmn->category) { case NC_SCENE: { @@ -515,6 +556,12 @@ static void spreadsheet_header_region_listener(const wmRegionListenerParams *par ED_region_tag_redraw(region); break; } + case NC_VIEWER_PATH: { + if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { + ED_region_tag_redraw(region); + } + break; + } } } @@ -593,7 +640,7 @@ static void spreadsheet_dataset_region_listener(const wmRegionListenerParams *pa static void spreadsheet_dataset_region_draw(const bContext *C, ARegion *region) { - spreadsheet_update_context_path(C); + spreadsheet_update_context(C); ED_region_panels(C, region); } @@ -634,34 +681,13 @@ static void spreadsheet_blend_read_data(BlendDataReader *reader, SpaceLink *sl) BLO_read_data_address(reader, &column->display_name); } - BLO_read_list(reader, &sspreadsheet->context_path); - LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { - switch (context->type) { - case SPREADSHEET_CONTEXT_NODE: { - SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)context; - BLO_read_data_address(reader, &node_context->node_name); - break; - } - case SPREADSHEET_CONTEXT_MODIFIER: { - SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context; - BLO_read_data_address(reader, &modifier_context->modifier_name); - break; - } - case SPREADSHEET_CONTEXT_OBJECT: { - break; - } - } - } + BKE_viewer_path_blend_read_data(reader, &sspreadsheet->viewer_path); } static void spreadsheet_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { - if (context->type == SPREADSHEET_CONTEXT_OBJECT) { - BLO_read_id_address(reader, parent_id->lib, &((SpreadsheetContextObject *)context)->object); - } - } + BKE_viewer_path_blend_read_lib(reader, parent_id->lib, &sspreadsheet->viewer_path); } static void spreadsheet_blend_write(BlendWriter *writer, SpaceLink *sl) @@ -683,27 +709,8 @@ static void spreadsheet_blend_write(BlendWriter *writer, SpaceLink *sl) * This would ideally be cleared here. */ BLO_write_string(writer, column->display_name); } - LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { - switch (context->type) { - case SPREADSHEET_CONTEXT_OBJECT: { - SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context; - BLO_write_struct(writer, SpreadsheetContextObject, object_context); - break; - } - case SPREADSHEET_CONTEXT_MODIFIER: { - SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context; - BLO_write_struct(writer, SpreadsheetContextModifier, modifier_context); - BLO_write_string(writer, modifier_context->modifier_name); - break; - } - case SPREADSHEET_CONTEXT_NODE: { - SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)context; - BLO_write_struct(writer, SpreadsheetContextNode, node_context); - BLO_write_string(writer, node_context->node_name); - break; - } - } - } + + BKE_viewer_path_blend_write(writer, &sspreadsheet->viewer_path); } void ED_spacetype_spreadsheet() diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc deleted file mode 100644 index ec9fa72edb1..00000000000 --- a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc +++ /dev/null @@ -1,591 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "MEM_guardedalloc.h" - -#include "BLI_hash.h" -#include "BLI_hash.hh" -#include "BLI_hash_mm2a.h" -#include "BLI_listbase.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" -#include "BLI_vector.hh" - -#include "ED_screen.h" -#include "ED_spreadsheet.h" - -#include "DEG_depsgraph.h" - -#include "BKE_context.h" -#include "BKE_main.h" -#include "BKE_modifier.h" -#include "BKE_node.h" -#include "BKE_object.h" -#include "BKE_workspace.h" - -#include "DNA_modifier_types.h" -#include "DNA_windowmanager_types.h" - -#include "spreadsheet_context.hh" - -using blender::IndexRange; -using blender::Span; -using blender::StringRef; -using blender::Vector; - -namespace blender::ed::spreadsheet { - -static SpreadsheetContextObject *spreadsheet_context_object_new() -{ - SpreadsheetContextObject *context = MEM_cnew<SpreadsheetContextObject>(__func__); - context->base.type = SPREADSHEET_CONTEXT_OBJECT; - return context; -} - -static SpreadsheetContextObject *spreadsheet_context_object_copy( - const SpreadsheetContextObject *src_context) -{ - SpreadsheetContextObject *new_context = spreadsheet_context_object_new(); - new_context->object = src_context->object; - return new_context; -} - -static void spreadsheet_context_object_hash(const SpreadsheetContextObject *context, - BLI_HashMurmur2A *mm2) -{ - BLI_hash_mm2a_add(mm2, (const uchar *)&context->object, sizeof(Object *)); -} - -static void spreadsheet_context_object_free(SpreadsheetContextObject *context) -{ - MEM_freeN(context); -} - -static SpreadsheetContextModifier *spreadsheet_context_modifier_new() -{ - SpreadsheetContextModifier *context = MEM_cnew<SpreadsheetContextModifier>(__func__); - context->base.type = SPREADSHEET_CONTEXT_MODIFIER; - return context; -} - -static SpreadsheetContextModifier *spreadsheet_context_modifier_copy( - const SpreadsheetContextModifier *src_context) -{ - SpreadsheetContextModifier *new_context = spreadsheet_context_modifier_new(); - if (src_context->modifier_name) { - new_context->modifier_name = BLI_strdup(src_context->modifier_name); - } - return new_context; -} - -static void spreadsheet_context_modifier_hash(const SpreadsheetContextModifier *context, - BLI_HashMurmur2A *mm2) -{ - if (context->modifier_name) { - BLI_hash_mm2a_add(mm2, (const uchar *)context->modifier_name, strlen(context->modifier_name)); - } -} - -static void spreadsheet_context_modifier_free(SpreadsheetContextModifier *context) -{ - if (context->modifier_name) { - MEM_freeN(context->modifier_name); - } - MEM_freeN(context); -} - -static SpreadsheetContextNode *spreadsheet_context_node_new() -{ - SpreadsheetContextNode *context = MEM_cnew<SpreadsheetContextNode>(__func__); - context->base.type = SPREADSHEET_CONTEXT_NODE; - return context; -} - -static SpreadsheetContextNode *spreadsheet_context_node_copy( - const SpreadsheetContextNode *src_context) -{ - SpreadsheetContextNode *new_context = spreadsheet_context_node_new(); - if (src_context->node_name) { - new_context->node_name = BLI_strdup(src_context->node_name); - } - return new_context; -} - -static void spreadsheet_context_node_hash(const SpreadsheetContextNode *context, - BLI_HashMurmur2A *mm2) -{ - if (context->node_name) { - BLI_hash_mm2a_add(mm2, (const uchar *)context->node_name, strlen(context->node_name)); - } -} - -static void spreadsheet_context_node_free(SpreadsheetContextNode *context) -{ - if (context->node_name) { - MEM_freeN(context->node_name); - } - MEM_freeN(context); -} - -SpreadsheetContext *spreadsheet_context_new(eSpaceSpreadsheet_ContextType type) -{ - switch (type) { - case SPREADSHEET_CONTEXT_OBJECT: { - return (SpreadsheetContext *)spreadsheet_context_object_new(); - } - case SPREADSHEET_CONTEXT_MODIFIER: { - return (SpreadsheetContext *)spreadsheet_context_modifier_new(); - } - case SPREADSHEET_CONTEXT_NODE: { - return (SpreadsheetContext *)spreadsheet_context_node_new(); - } - } - BLI_assert_unreachable(); - return nullptr; -} - -SpreadsheetContext *spreadsheet_context_copy(const SpreadsheetContext *old_context) -{ - switch (old_context->type) { - case SPREADSHEET_CONTEXT_OBJECT: { - return (SpreadsheetContext *)spreadsheet_context_object_copy( - (const SpreadsheetContextObject *)old_context); - } - case SPREADSHEET_CONTEXT_MODIFIER: { - return (SpreadsheetContext *)spreadsheet_context_modifier_copy( - (const SpreadsheetContextModifier *)old_context); - } - case SPREADSHEET_CONTEXT_NODE: { - return (SpreadsheetContext *)spreadsheet_context_node_copy( - (const SpreadsheetContextNode *)old_context); - } - } - BLI_assert_unreachable(); - return nullptr; -} - -static void spreadsheet_context_hash(const SpreadsheetContext *context, BLI_HashMurmur2A *mm2) -{ - BLI_hash_mm2a_add_int(mm2, context->type); - switch (context->type) { - case SPREADSHEET_CONTEXT_OBJECT: { - spreadsheet_context_object_hash((const SpreadsheetContextObject *)context, mm2); - break; - } - case SPREADSHEET_CONTEXT_MODIFIER: { - spreadsheet_context_modifier_hash((const SpreadsheetContextModifier *)context, mm2); - break; - } - case SPREADSHEET_CONTEXT_NODE: { - spreadsheet_context_node_hash((const SpreadsheetContextNode *)context, mm2); - break; - } - } -} - -void spreadsheet_context_free(SpreadsheetContext *context) -{ - switch (context->type) { - case SPREADSHEET_CONTEXT_OBJECT: { - return spreadsheet_context_object_free((SpreadsheetContextObject *)context); - } - case SPREADSHEET_CONTEXT_MODIFIER: { - return spreadsheet_context_modifier_free((SpreadsheetContextModifier *)context); - } - case SPREADSHEET_CONTEXT_NODE: { - return spreadsheet_context_node_free((SpreadsheetContextNode *)context); - } - } - BLI_assert_unreachable(); -} - -/** - * Tag any data relevant to the spreadsheet's context for recalculation in order to collect - * information to display in the editor, which may be cached during evaluation. - * \return True when any data has been tagged for update. - */ -static bool spreadsheet_context_update_tag(SpaceSpreadsheet *sspreadsheet) -{ - using namespace blender; - Vector<const SpreadsheetContext *> context_path = sspreadsheet->context_path; - if (context_path.is_empty()) { - return false; - } - if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { - return false; - } - SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0]; - Object *object = object_context->object; - if (object == nullptr) { - return false; - } - if (context_path.size() == 1) { - /* No need to reevaluate, when the final or original object is viewed. */ - return false; - } - - DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); - return true; -} - -} // namespace blender::ed::spreadsheet - -SpreadsheetContext *ED_spreadsheet_context_new(int type) -{ - return blender::ed::spreadsheet::spreadsheet_context_new((eSpaceSpreadsheet_ContextType)type); -} - -void ED_spreadsheet_context_free(struct SpreadsheetContext *context) -{ - blender::ed::spreadsheet::spreadsheet_context_free(context); -} - -void ED_spreadsheet_context_path_clear(struct SpaceSpreadsheet *sspreadsheet) -{ - LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, context, &sspreadsheet->context_path) { - ED_spreadsheet_context_free(context); - } - BLI_listbase_clear(&sspreadsheet->context_path); -} - -bool ED_spreadsheet_context_path_update_tag(SpaceSpreadsheet *sspreadsheet) -{ - return blender::ed::spreadsheet::spreadsheet_context_update_tag(sspreadsheet); -} - -uint64_t ED_spreadsheet_context_path_hash(const SpaceSpreadsheet *sspreadsheet) -{ - BLI_HashMurmur2A mm2; - BLI_hash_mm2a_init(&mm2, 1234); - LISTBASE_FOREACH (const SpreadsheetContext *, context, &sspreadsheet->context_path) { - blender::ed::spreadsheet::spreadsheet_context_hash(context, &mm2); - } - return BLI_hash_mm2a_end(&mm2); -} - -void ED_spreadsheet_context_path_set_geometry_node(struct SpaceSpreadsheet *sspreadsheet, - struct SpaceNode *snode, - struct bNode *node) -{ - using namespace blender::ed::spreadsheet; - - Object *object = (Object *)snode->id; - /* Try to find the modifier the node tree belongs to. */ - ModifierData *modifier = BKE_object_active_modifier(object); - if (modifier && modifier->type != eModifierType_Nodes) { - modifier = nullptr; - LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { - if (md->type == eModifierType_Nodes) { - NodesModifierData *nmd = (NodesModifierData *)md; - if (nmd->node_group == snode->nodetree) { - modifier = md; - break; - } - } - } - } - if (modifier == nullptr) { - return; - } - - ED_spreadsheet_context_path_clear(sspreadsheet); - - { - SpreadsheetContextObject *context = spreadsheet_context_object_new(); - context->object = object; - BLI_addtail(&sspreadsheet->context_path, context); - } - { - SpreadsheetContextModifier *context = spreadsheet_context_modifier_new(); - context->modifier_name = BLI_strdup(modifier->name); - BLI_addtail(&sspreadsheet->context_path, context); - } - { - int i; - LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) { - if (i == 0) { - continue; - } - SpreadsheetContextNode *context = spreadsheet_context_node_new(); - context->node_name = BLI_strdup(path->node_name); - BLI_addtail(&sspreadsheet->context_path, context); - } - } - { - SpreadsheetContextNode *context = spreadsheet_context_node_new(); - context->node_name = BLI_strdup(node->name); - BLI_addtail(&sspreadsheet->context_path, context); - } - - sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE; -} - -void ED_spreadsheet_context_paths_set_geometry_node(Main *bmain, SpaceNode *snode, bNode *node) -{ - wmWindowManager *wm = (wmWindowManager *)bmain->wm.first; - if (wm == nullptr) { - return; - } - LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { - bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - SpaceLink *sl = (SpaceLink *)area->spacedata.first; - if (sl->spacetype == SPACE_SPREADSHEET) { - SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - if ((sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) == 0) { - const uint64_t context_hash_before = ED_spreadsheet_context_path_hash(sspreadsheet); - ED_spreadsheet_context_path_set_geometry_node(sspreadsheet, snode, node); - const uint64_t context_hash_after = ED_spreadsheet_context_path_hash(sspreadsheet); - if (context_hash_before != context_hash_after) { - ED_spreadsheet_context_path_update_tag(sspreadsheet); - } - ED_area_tag_redraw(area); - } - } - } - } -} - -void ED_spreadsheet_context_path_set_evaluated_object(SpaceSpreadsheet *sspreadsheet, - Object *object) -{ - using namespace blender::ed::spreadsheet; - ED_spreadsheet_context_path_clear(sspreadsheet); - - SpreadsheetContextObject *context = spreadsheet_context_object_new(); - context->object = object; - BLI_addtail(&sspreadsheet->context_path, context); -} - -static bScreen *find_screen_to_search_for_context(wmWindow *window, - SpaceSpreadsheet *current_space) -{ - bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); - if (ELEM(screen->state, SCREENMAXIMIZED, SCREENFULL)) { - /* If the spreadsheet is maximized, try to find the context in the unmaximized screen. */ - ScrArea *main_area = (ScrArea *)screen->areabase.first; - SpaceLink *sl = (SpaceLink *)main_area->spacedata.first; - if (sl == (SpaceLink *)current_space) { - return main_area->full; - } - } - return screen; -} - -void ED_spreadsheet_context_path_guess(const bContext *C, SpaceSpreadsheet *sspreadsheet) -{ - ED_spreadsheet_context_path_clear(sspreadsheet); - - Main *bmain = CTX_data_main(C); - wmWindowManager *wm = (wmWindowManager *)bmain->wm.first; - if (wm == nullptr) { - return; - } - - if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { - LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { - bScreen *screen = find_screen_to_search_for_context(window, sspreadsheet); - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - SpaceLink *sl = (SpaceLink *)area->spacedata.first; - if (sl == nullptr) { - continue; - } - if (sl->spacetype == SPACE_NODE) { - SpaceNode *snode = (SpaceNode *)sl; - if (snode->edittree != nullptr) { - if (snode->edittree->type == NTREE_GEOMETRY) { - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (node->type == GEO_NODE_VIEWER) { - if (node->flag & NODE_DO_OUTPUT) { - ED_spreadsheet_context_path_set_geometry_node(sspreadsheet, snode, node); - return; - } - } - } - } - } - } - } - } - } - - Object *active_object = CTX_data_active_object(C); - if (active_object != nullptr) { - ED_spreadsheet_context_path_set_evaluated_object(sspreadsheet, active_object); - return; - } -} - -bool ED_spreadsheet_context_path_is_active(const bContext *C, SpaceSpreadsheet *sspreadsheet) -{ - Main *bmain = CTX_data_main(C); - wmWindowManager *wm = (wmWindowManager *)bmain->wm.first; - if (wm == nullptr) { - return false; - } - Vector<SpreadsheetContext *> context_path = sspreadsheet->context_path; - if (context_path.is_empty()) { - return false; - } - if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { - return false; - } - Object *object = ((SpreadsheetContextObject *)context_path[0])->object; - if (object == nullptr) { - return false; - } - if (context_path.size() == 1) { - if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { - return false; - } - Object *active_object = CTX_data_active_object(C); - return object == active_object; - } - if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { - return false; - } - if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) { - return false; - } - const char *modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name; - const ModifierData *modifier = BKE_modifiers_findby_name(object, modifier_name); - if (modifier == nullptr) { - return false; - } - const bool modifier_is_active = modifier->flag & eModifierFlag_Active; - if (modifier->type != eModifierType_Nodes) { - return false; - } - bNodeTree *root_node_tree = ((NodesModifierData *)modifier)->node_group; - if (root_node_tree == nullptr) { - return false; - } - const Span<SpreadsheetContext *> node_context_path = context_path.as_span().drop_front(2); - if (node_context_path.is_empty()) { - return false; - } - - LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { - bScreen *screen = find_screen_to_search_for_context(window, sspreadsheet); - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - SpaceLink *sl = (SpaceLink *)area->spacedata.first; - if (sl == nullptr) { - continue; - } - if (sl->spacetype != SPACE_NODE) { - continue; - } - SpaceNode *snode = (SpaceNode *)sl; - if (snode->nodetree != root_node_tree) { - continue; - } - 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 != &object->id) { - continue; - } - Vector<bNodeTreePath *> tree_path = snode->treepath; - if (node_context_path.size() != tree_path.size()) { - continue; - } - int valid_count = 0; - for (const int i : IndexRange(tree_path.size() - 1)) { - if (node_context_path[i]->type != SPREADSHEET_CONTEXT_NODE) { - break; - } - SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)node_context_path[i]; - if (!STREQ(node_context->node_name, tree_path[i + 1]->node_name)) { - break; - } - valid_count++; - } - if (valid_count != tree_path.size() - 1) { - continue; - } - SpreadsheetContext *last_context = node_context_path.last(); - if (last_context->type != SPREADSHEET_CONTEXT_NODE) { - return false; - } - const char *node_name = ((SpreadsheetContextNode *)last_context)->node_name; - bNode *node = nodeFindNodebyName(snode->edittree, node_name); - if (node == nullptr) { - return false; - } - if (node->type != GEO_NODE_VIEWER) { - return false; - } - if (!(node->flag & NODE_DO_OUTPUT)) { - return false; - } - return true; - } - } - return false; -} - -bool ED_spreadsheet_context_path_exists(Main *UNUSED(bmain), SpaceSpreadsheet *sspreadsheet) -{ - Vector<SpreadsheetContext *> context_path = sspreadsheet->context_path; - if (context_path.is_empty()) { - return false; - } - if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { - return false; - } - Object *object = ((SpreadsheetContextObject *)context_path[0])->object; - if (object == nullptr) { - return false; - } - if (context_path.size() == 1) { - return true; - } - if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) { - return false; - } - const char *modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name; - const ModifierData *modifier = BKE_modifiers_findby_name(object, modifier_name); - if (modifier == nullptr) { - return false; - } - if (modifier->type != eModifierType_Nodes) { - return false; - } - bNodeTree *root_node_tree = ((NodesModifierData *)modifier)->node_group; - if (root_node_tree == nullptr) { - return false; - } - const Span<SpreadsheetContext *> node_context_path = context_path.as_span().drop_front(2); - if (node_context_path.is_empty()) { - return false; - } - bNodeTree *node_tree = root_node_tree; - for (const int i : node_context_path.index_range()) { - if (node_context_path[i]->type != SPREADSHEET_CONTEXT_NODE) { - return false; - } - const char *node_name = ((SpreadsheetContextNode *)node_context_path[i])->node_name; - bNode *node = nodeFindNodebyName(node_tree, node_name); - if (node == nullptr) { - return false; - } - if (node->type == GEO_NODE_VIEWER) { - if (i == node_context_path.index_range().last()) { - return true; - } - return false; - } - if (node->id != nullptr) { - if (GS(node->id->name) != ID_NT) { - return false; - } - node_tree = (bNodeTree *)node->id; - } - else { - return false; - } - } - return false; -} diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.hh b/source/blender/editors/space_spreadsheet/spreadsheet_context.hh deleted file mode 100644 index 758ae392894..00000000000 --- a/source/blender/editors/space_spreadsheet/spreadsheet_context.hh +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -#include "DNA_space_types.h" - -namespace blender::ed::spreadsheet { - -SpreadsheetContext *spreadsheet_context_new(eSpaceSpreadsheet_ContextType type); -SpreadsheetContext *spreadsheet_context_copy(const SpreadsheetContext *old_context); -void spreadsheet_context_free(SpreadsheetContext *context); - -} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index fd2ac4d39a1..8af12590b0f 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -98,7 +98,8 @@ void GeometryDataSource::foreach_default_column_ids( } SpreadsheetColumnID column_id; column_id.name = (char *)attribute_id.name().data(); - fn(column_id, false); + const bool is_front = attribute_id.name() == ".viewer"; + fn(column_id, is_front); return true; }); @@ -228,7 +229,12 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( return {}; } - return std::make_unique<ColumnValues>(column_id.name, std::move(varray)); + StringRefNull column_display_name = column_id.name; + if (column_display_name == ".viewer") { + column_display_name = "Viewer"; + } + + return std::make_unique<ColumnValues>(column_display_name, std::move(varray)); } int GeometryDataSource::tot_rows() const @@ -463,7 +469,7 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly); } else { - if (BLI_listbase_count(&sspreadsheet->context_path) == 1) { + if (BLI_listbase_count(&sspreadsheet->viewer_path.path) == 1) { /* Use final evaluated object. */ if (object_eval->runtime.geometry_set_eval != nullptr) { geometry_set = *object_eval->runtime.geometry_set_eval; @@ -471,8 +477,8 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread } else { if (const ViewerNodeLog *viewer_log = - nodes::geo_eval_log::GeoModifierLog::find_viewer_node_log_for_spreadsheet( - *sspreadsheet)) { + nodes::geo_eval_log::GeoModifierLog::find_viewer_node_log_for_path( + sspreadsheet->viewer_path)) { geometry_set = viewer_log->geometry; } } @@ -481,25 +487,6 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread return geometry_set; } -static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet, - Map<std::string, GField> &r_fields) -{ - if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { - return; - } - if (BLI_listbase_count(&sspreadsheet->context_path) <= 1) { - /* No viewer is currently referenced by the context path. */ - return; - } - if (const ViewerNodeLog *viewer_log = - nodes::geo_eval_log::GeoModifierLog::find_viewer_node_log_for_spreadsheet( - *sspreadsheet)) { - if (viewer_log->field) { - r_fields.add("Viewer", viewer_log->field); - } - } -} - class GeometryComponentCacheKey : public SpreadsheetCache::Key { public: /* Use the pointer to the geometry component as a key to detect when the geometry changed. */ @@ -531,38 +518,6 @@ class GeometryComponentCacheValue : public SpreadsheetCache::Value { Map<std::pair<eAttrDomain, GField>, GArray<>> arrays; }; -static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, - const GeometryComponent &component, - ExtraColumns &r_extra_columns) -{ - Map<std::string, GField> fields_to_show; - find_fields_to_evaluate(sspreadsheet, fields_to_show); - - GeometryComponentCacheValue &cache = - sspreadsheet->runtime->cache.lookup_or_add<GeometryComponentCacheValue>( - std::make_unique<GeometryComponentCacheKey>(component)); - - const eAttrDomain domain = (eAttrDomain)sspreadsheet->attribute_domain; - const int domain_num = component.attribute_domain_size(domain); - for (const auto item : fields_to_show.items()) { - const StringRef name = item.key; - const GField &field = item.value; - - /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */ - GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() { - GArray<> evaluated_array(field.cpp_type(), domain_num); - - bke::GeometryFieldContext field_context{component, domain}; - fn::FieldEvaluator field_evaluator{field_context, domain_num}; - field_evaluator.add_with_destination(field, evaluated_array); - field_evaluator.evaluate(); - return evaluated_array; - }); - - r_extra_columns.add(name, evaluated_array.as_span()); - } -} - std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); @@ -574,15 +529,11 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object return {}; } - const GeometryComponent &component = *geometry_set.get_component_for_read(component_type); - ExtraColumns extra_columns; - add_fields_as_extra_columns(sspreadsheet, component, extra_columns); - if (component_type == GEO_COMPONENT_TYPE_VOLUME) { return std::make_unique<VolumeDataSource>(std::move(geometry_set)); } return std::make_unique<GeometryDataSource>( - object_eval, std::move(geometry_set), component_type, domain, std::move(extra_columns)); + object_eval, std::move(geometry_set), component_type, domain); } } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh index 71bc4768949..478b3372427 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh @@ -54,7 +54,7 @@ class GeometryDataSource : public DataSource { GeometrySet geometry_set, const GeometryComponentType component_type, const eAttrDomain domain, - ExtraColumns extra_columns) + ExtraColumns extra_columns = {}) : object_eval_(object_eval), geometry_set_(std::move(geometry_set)), component_(geometry_set_.get_component_for_read(component_type)), diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 7398a109b19..345958d86ec 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -44,6 +44,7 @@ #include "BKE_object.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_viewer_path.h" #include "BKE_workspace.h" #include "ED_object.h" @@ -53,6 +54,7 @@ #include "ED_space_api.h" #include "ED_transform.h" #include "ED_undo.h" +#include "ED_viewer_path.hh" #include "GPU_matrix.h" @@ -322,6 +324,8 @@ static void view3d_free(SpaceLink *sl) IDP_FreeProperty(vd->shading.prop); vd->shading.prop = nullptr; } + + BKE_viewer_path_clear(&vd->viewer_path); } /* spacetype; init callback */ @@ -360,6 +364,8 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl) v3dn->shading.prop = IDP_CopyProperty(v3do->shading.prop); } + BKE_viewer_path_copy(&v3dn->viewer_path, &v3do->viewer_path); + /* copy or clear inside new stuff */ return (SpaceLink *)v3dn; @@ -1319,6 +1325,16 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params) /* In case the region displays workspace settings. */ ED_region_tag_redraw(region); break; + case NC_VIEWER_PATH: { + if (v3d->flag2 & V3D_SHOW_VIEWER) { + ViewLayer *view_layer = WM_window_get_active_view_layer(window); + if (Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer)) { + ED_render_view3d_update(depsgraph, window, area, true); + } + ED_region_tag_redraw(region); + } + break; + } } } @@ -1980,6 +1996,7 @@ static void view3d_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRema /* Object centers in local-view aren't used, see: T52663 */ view3d_id_remap_v3d(area, slink, view3d->localvd, mappings, true); } + BKE_viewer_path_id_remap(&view3d->viewer_path, mappings); } static void view3d_blend_read_data(BlendDataReader *reader, SpaceLink *sl) @@ -2003,6 +2020,8 @@ static void view3d_blend_read_data(BlendDataReader *reader, SpaceLink *sl) BKE_screen_view3d_shading_blend_read_data(reader, &v3d->shading); BKE_screen_view3d_do_versions_250(v3d, &sl->regionbase); + + BKE_viewer_path_blend_read_data(reader, &v3d->viewer_path); } static void view3d_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl) @@ -2015,6 +2034,8 @@ static void view3d_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLi if (v3d->localvd) { BLO_read_id_address(reader, parent_id->lib, &v3d->localvd->camera); } + + BKE_viewer_path_blend_read_lib(reader, parent_id->lib, &v3d->viewer_path); } static void view3d_blend_write(BlendWriter *writer, SpaceLink *sl) @@ -2027,6 +2048,8 @@ static void view3d_blend_write(BlendWriter *writer, SpaceLink *sl) } BKE_screen_view3d_shading_blend_write(writer, &v3d->shading); + + BKE_viewer_path_blend_write(writer, &v3d->viewer_path); } void ED_spacetype_view3d(void) diff --git a/source/blender/editors/space_view3d/view3d_draw.cc b/source/blender/editors/space_view3d/view3d_draw.cc index 9686609fa65..bc8800120f8 100644 --- a/source/blender/editors/space_view3d/view3d_draw.cc +++ b/source/blender/editors/space_view3d/view3d_draw.cc @@ -55,6 +55,7 @@ #include "ED_screen_types.h" #include "ED_transform.h" #include "ED_view3d_offscreen.h" +#include "ED_viewer_path.hh" #include "DEG_depsgraph_query.h" @@ -1294,7 +1295,7 @@ static void draw_viewport_name(ARegion *region, View3D *v3d, int xoffset, int *y * frame-number, collection, object name, bone name (if available), marker name (if available). */ static void draw_selected_name( - Scene *scene, ViewLayer *view_layer, Object *ob, int xoffset, int *yoffset) + const View3D *v3d, Scene *scene, ViewLayer *view_layer, Object *ob, int xoffset, int *yoffset) { const int cfra = scene->r.cfra; const char *msg_pin = " (Pinned)"; @@ -1409,6 +1410,12 @@ static void draw_selected_name( s += sprintf(s, " <%s>", markern); } + if (v3d->flag2 & V3D_SHOW_VIEWER) { + if (!BLI_listbase_is_empty(&v3d->viewer_path.path)) { + s += sprintf(s, IFACE_(" (Viewer)")); + } + } + BLF_enable(font_id, BLF_SHADOW); BLF_shadow(font_id, 5, float4{0.0f, 0.0f, 0.0f, 1.0f}); BLF_shadow_offset(font_id, 1, -1); @@ -1502,7 +1509,7 @@ void view3d_draw_region_info(const bContext *C, ARegion *region) if (U.uiflag & USER_DRAWVIEWINFO) { BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); - draw_selected_name(scene, view_layer, ob, xoffset, &yoffset); + draw_selected_name(v3d, scene, view_layer, ob, xoffset, &yoffset); } if (v3d->gridflag & (V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y | V3D_SHOW_Z)) { @@ -1558,11 +1565,23 @@ RenderEngineType *ED_view3d_engine_type(const Scene *scene, int drawtype) return type; } +static void view3d_update_viewer_path(const bContext *C) +{ + View3D *v3d = CTX_wm_view3d(C); + WorkSpace *workspace = CTX_wm_workspace(C); + /* Always use viewer path from workspace, pinning is not supported currently. */ + if (!BKE_viewer_path_equal(&v3d->viewer_path, &workspace->viewer_path)) { + BKE_viewer_path_clear(&v3d->viewer_path); + BKE_viewer_path_copy(&v3d->viewer_path, &workspace->viewer_path); + } +} + void view3d_main_region_draw(const bContext *C, ARegion *region) { Main *bmain = CTX_data_main(C); View3D *v3d = CTX_wm_view3d(C); + view3d_update_viewer_path(C); view3d_draw_view(C, region); DRW_cache_free_old_subdiv(); diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index a9e6adc6e60..164e594b37c 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -28,6 +28,7 @@ set(SRC ed_util.c ed_util_imbuf.c ed_util_ops.cc + ed_viewer_path.cc gizmo_utils.c numinput.c select_utils.c diff --git a/source/blender/editors/util/ed_viewer_path.cc b/source/blender/editors/util/ed_viewer_path.cc new file mode 100644 index 00000000000..5c03367cba8 --- /dev/null +++ b/source/blender/editors/util/ed_viewer_path.cc @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "ED_viewer_path.hh" +#include "ED_screen.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_node_runtime.hh" +#include "BKE_workspace.h" + +#include "BLI_listbase.h" +#include "BLI_vector.hh" + +#include "DNA_modifier_types.h" +#include "DNA_windowmanager_types.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" + +namespace blender::ed::viewer_path { + +static void viewer_path_for_geometry_node(const SpaceNode &snode, + const bNode &node, + ViewerPath &r_dst) +{ + BKE_viewer_path_init(&r_dst); + + Object *ob = reinterpret_cast<Object *>(snode.id); + IDViewerPathElem *id_elem = BKE_viewer_path_elem_new_id(); + id_elem->id = &ob->id; + BLI_addtail(&r_dst.path, id_elem); + + NodesModifierData *modifier = nullptr; + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type != eModifierType_Nodes) { + continue; + } + NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md); + if (nmd->node_group != snode.nodetree) { + continue; + } + if (snode.flag & SNODE_PIN) { + /* If the node group is pinned, use the first matching modifier. This can be improved by + * storing the modifier name in the node editor when the context is pinned. */ + modifier = nmd; + break; + } + if (md->flag & eModifierFlag_Active) { + modifier = nmd; + } + } + + 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<const bNodeTreePath *, 16> tree_path = snode.treepath; + for (const bNodeTreePath *tree_path_elem : tree_path.as_span().drop_front(1)) { + NodeViewerPathElem *node_elem = BKE_viewer_path_elem_new_node(); + node_elem->node_name = BLI_strdup(tree_path_elem->node_name); + BLI_addtail(&r_dst.path, node_elem); + } + + NodeViewerPathElem *viewer_node_elem = BKE_viewer_path_elem_new_node(); + viewer_node_elem->node_name = BLI_strdup(node.name); + BLI_addtail(&r_dst.path, viewer_node_elem); +} + +void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node) +{ + wmWindowManager *wm = (wmWindowManager *)bmain.wm.first; + if (wm == nullptr) { + return; + } + LISTBASE_FOREACH (bNode *, iter_node, &snode.edittree->nodes) { + if (iter_node->type == GEO_NODE_VIEWER) { + SET_FLAG_FROM_TEST(iter_node->flag, iter_node == &node, NODE_DO_OUTPUT); + } + } + ViewerPath new_viewer_path{}; + BLI_SCOPED_DEFER([&]() { BKE_viewer_path_clear(&new_viewer_path); }); + viewer_path_for_geometry_node(snode, node, new_viewer_path); + + bool found_view3d_with_enabled_viewer = false; + View3D *any_view3d_without_viewer = nullptr; + LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { + WorkSpace *workspace = BKE_workspace_active_get(window->workspace_hook); + bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first); + if (sl->spacetype == SPACE_SPREADSHEET) { + SpaceSpreadsheet &sspreadsheet = *reinterpret_cast<SpaceSpreadsheet *>(sl); + if (!(sspreadsheet.flag & SPREADSHEET_FLAG_PINNED)) { + sspreadsheet.object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE; + } + } + else if (sl->spacetype == SPACE_VIEW3D) { + View3D &v3d = *reinterpret_cast<View3D *>(sl); + if (v3d.flag2 & V3D_SHOW_VIEWER) { + found_view3d_with_enabled_viewer = true; + } + else { + any_view3d_without_viewer = &v3d; + } + } + } + + /* Enable viewer in one viewport if it is disable in all of them. */ + if (!found_view3d_with_enabled_viewer && any_view3d_without_viewer != nullptr) { + any_view3d_without_viewer->flag2 |= V3D_SHOW_VIEWER; + } + + BKE_viewer_path_clear(&workspace->viewer_path); + BKE_viewer_path_copy(&workspace->viewer_path, &new_viewer_path); + + /* Make sure the viewed data becomes available. */ + DEG_id_tag_update(snode.id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_VIEWER_PATH, nullptr); + } +} + +Object *parse_object_only(const ViewerPath &viewer_path) +{ + if (BLI_listbase_count(&viewer_path.path) != 1) { + return nullptr; + } + const ViewerPathElem *elem = static_cast<ViewerPathElem *>(viewer_path.path.first); + if (elem->type != VIEWER_PATH_ELEM_TYPE_ID) { + return nullptr; + } + ID *id = reinterpret_cast<const IDViewerPathElem *>(elem)->id; + if (id == nullptr) { + return nullptr; + } + if (GS(id->name) != ID_OB) { + return nullptr; + } + return reinterpret_cast<Object *>(id); +} + +std::optional<ViewerPathForGeometryNodesViewer> parse_geometry_nodes_viewer( + const ViewerPath &viewer_path) +{ + const Vector<const ViewerPathElem *, 16> elems_vec = viewer_path.path; + if (elems_vec.size() < 3) { + /* Need at least the object, modifier and viewer node name. */ + return std::nullopt; + } + Span<const ViewerPathElem *> remaining_elems = elems_vec; + const ViewerPathElem &id_elem = *remaining_elems[0]; + if (id_elem.type != VIEWER_PATH_ELEM_TYPE_ID) { + return std::nullopt; + } + ID *root_id = reinterpret_cast<const IDViewerPathElem &>(id_elem).id; + if (root_id == nullptr) { + return std::nullopt; + } + if (GS(root_id->name) != ID_OB) { + return std::nullopt; + } + Object *root_ob = reinterpret_cast<Object *>(root_id); + remaining_elems = remaining_elems.drop_front(1); + const ViewerPathElem &modifier_elem = *remaining_elems[0]; + if (modifier_elem.type != VIEWER_PATH_ELEM_TYPE_MODIFIER) { + return std::nullopt; + } + const char *modifier_name = + reinterpret_cast<const ModifierViewerPathElem &>(modifier_elem).modifier_name; + if (modifier_name == nullptr) { + return std::nullopt; + } + remaining_elems = remaining_elems.drop_front(1); + Vector<StringRefNull> node_names; + for (const ViewerPathElem *elem : remaining_elems) { + if (elem->type != VIEWER_PATH_ELEM_TYPE_NODE) { + return std::nullopt; + } + const char *node_name = reinterpret_cast<const NodeViewerPathElem *>(elem)->node_name; + if (node_name == nullptr) { + return std::nullopt; + } + node_names.append(node_name); + } + const StringRefNull viewer_node_name = node_names.pop_last(); + return ViewerPathForGeometryNodesViewer{root_ob, modifier_name, node_names, viewer_node_name}; +} + +bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed_viewer_path) +{ + const NodesModifierData *modifier = nullptr; + LISTBASE_FOREACH (const ModifierData *, md, &parsed_viewer_path.object->modifiers) { + if (md->type != eModifierType_Nodes) { + continue; + } + if (md->name != parsed_viewer_path.modifier_name) { + continue; + } + modifier = reinterpret_cast<const NodesModifierData *>(md); + break; + } + if (modifier == nullptr) { + return false; + } + if (modifier->node_group == nullptr) { + return false; + } + const bNodeTree *ngroup = modifier->node_group; + ngroup->ensure_topology_cache(); + for (const StringRefNull group_node_name : parsed_viewer_path.group_node_names) { + const bNode *group_node = nullptr; + for (const bNode *node : ngroup->group_nodes()) { + if (node->name != group_node_name) { + continue; + } + group_node = node; + break; + } + if (group_node == nullptr) { + return false; + } + if (group_node->id == nullptr) { + return false; + } + ngroup = reinterpret_cast<const bNodeTree *>(group_node->id); + } + const bNode *viewer_node = nullptr; + for (const bNode *node : ngroup->nodes_by_type("GeometryNodeViewer")) { + if (node->name != parsed_viewer_path.viewer_node_name) { + continue; + } + viewer_node = node; + break; + } + if (viewer_node == nullptr) { + return false; + } + return true; +} + +bool is_active_geometry_nodes_viewer(const bContext &C, + const ViewerPathForGeometryNodesViewer &parsed_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; + } + modifier = reinterpret_cast<const NodesModifierData *>(md); + break; + } + if (modifier == nullptr) { + return false; + } + if (modifier->node_group == nullptr) { + return false; + } + const bool modifier_is_active = modifier->modifier.flag & eModifierFlag_Active; + + const Main *bmain = CTX_data_main(&C); + const wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first); + if (wm == nullptr) { + return false; + } + LISTBASE_FOREACH (const wmWindow *, window, &wm->windows) { + const bScreen *active_screen = BKE_workspace_active_screen_get(window->workspace_hook); + Vector<const bScreen *> screens = {active_screen}; + if (ELEM(active_screen->state, SCREENMAXIMIZED, SCREENFULL)) { + const ScrArea *area = static_cast<ScrArea *>(active_screen->areabase.first); + screens.append(area->full); + } + for (const bScreen *screen : screens) { + LISTBASE_FOREACH (const ScrArea *, area, &screen->areabase) { + const SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first); + if (sl == nullptr) { + continue; + } + if (sl->spacetype != SPACE_NODE) { + continue; + } + const SpaceNode &snode = *reinterpret_cast<const SpaceNode *>(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) { + continue; + } + if (snode.nodetree != modifier->node_group) { + continue; + } + Vector<const bNodeTreePath *, 16> tree_path = snode.treepath; + if (tree_path.size() != parsed_viewer_path.group_node_names.size() + 1) { + continue; + } + bool valid_path = true; + for (const int i : parsed_viewer_path.group_node_names.index_range()) { + if (parsed_viewer_path.group_node_names[i] != tree_path[i + 1]->node_name) { + valid_path = false; + break; + } + } + if (!valid_path) { + continue; + } + const bNodeTree *ngroup = snode.edittree; + ngroup->ensure_topology_cache(); + const bNode *viewer_node = nullptr; + for (const bNode *node : ngroup->nodes_by_type("GeometryNodeViewer")) { + if (node->name != parsed_viewer_path.viewer_node_name) { + continue; + } + viewer_node = node; + } + if (viewer_node == nullptr) { + continue; + } + if (!(viewer_node->flag & NODE_DO_OUTPUT)) { + continue; + } + return true; + } + } + } + return false; +} + +bNode *find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snode) +{ + const std::optional<ViewerPathForGeometryNodesViewer> parsed_viewer_path = + parse_geometry_nodes_viewer(viewer_path); + if (!parsed_viewer_path.has_value()) { + return nullptr; + } + + snode.edittree->ensure_topology_cache(); + bNode *possible_viewer = nullptr; + for (bNode *node : snode.edittree->nodes_by_type("GeometryNodeViewer")) { + if (node->name == parsed_viewer_path->viewer_node_name) { + possible_viewer = node; + break; + } + } + if (possible_viewer == nullptr) { + return nullptr; + } + ViewerPath tmp_viewer_path; + BLI_SCOPED_DEFER([&]() { BKE_viewer_path_clear(&tmp_viewer_path); }); + viewer_path_for_geometry_node(snode, *possible_viewer, tmp_viewer_path); + + if (BKE_viewer_path_equal(&viewer_path, &tmp_viewer_path)) { + return possible_viewer; + } + return nullptr; +} + +} // namespace blender::ed::viewer_path diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index a322922e86e..779dc052649 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -550,6 +550,7 @@ set(SRC_SHADER_CREATE_INFOS ../draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh ../draw/engines/overlay/shaders/infos/overlay_antialiasing_info.hh ../draw/engines/overlay/shaders/infos/overlay_armature_info.hh + ../draw/engines/overlay/shaders/infos/overlay_viewer_attribute_info.hh ../draw/engines/overlay/shaders/infos/overlay_background_info.hh ../draw/engines/overlay/shaders/infos/overlay_edit_mode_info.hh ../draw/engines/overlay/shaders/infos/overlay_extra_info.hh diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index dbda24fb8b7..963567133d9 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1598,6 +1598,8 @@ typedef struct NodeGeometryImageTexture { typedef struct NodeGeometryViewer { /* eCustomDataType. */ int8_t data_type; + /* eAttrDomain. */ + int8_t domain; } NodeGeometryViewer; typedef struct NodeGeometryUVUnwrap { diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 5a819ead4ec..7f0dd2f9be6 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -22,6 +22,7 @@ #include "DNA_vec_types.h" /* Hum ... Not really nice... but needed for spacebuts. */ #include "DNA_view2d_types.h" +#include "DNA_viewer_path_types.h" #ifdef __cplusplus extern "C" { @@ -1888,32 +1889,6 @@ typedef struct SpreadsheetColumn { char *display_name; } SpreadsheetColumn; -/** - * An item in SpaceSpreadsheet.context_path. - * This is a bases struct for the structs below. - */ -typedef struct SpreadsheetContext { - struct SpreadsheetContext *next, *prev; - /* eSpaceSpreadsheet_ContextType. */ - int type; - char _pad[4]; -} SpreadsheetContext; - -typedef struct SpreadsheetContextObject { - SpreadsheetContext base; - struct Object *object; -} SpreadsheetContextObject; - -typedef struct SpreadsheetContextModifier { - SpreadsheetContext base; - char *modifier_name; -} SpreadsheetContextModifier; - -typedef struct SpreadsheetContextNode { - SpreadsheetContext base; - char *node_name; -} SpreadsheetContextNode; - typedef struct SpaceSpreadsheet { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -1930,12 +1905,11 @@ typedef struct SpaceSpreadsheet { ListBase row_filters; /** - * List of #SpreadsheetContext. - * This is a path to the data that is displayed in the spreadsheet. - * It can be set explicitly by an action of the user (e.g. clicking the preview icon in a - * geometry node) or it can be derived from context automatically based on some heuristic. + * Context that is currently displayed in the editor. This is usually a either a single object + * (in original/evaluated mode) or path to a viewer node. This is retrieved from the workspace + * but can be pinned so that it stays constant even when the active node changes. */ - ListBase context_path; + ViewerPath viewer_path; /* eSpaceSpreadsheet_FilterFlag. */ uint8_t filter_flag; diff --git a/source/blender/makesdna/DNA_view3d_defaults.h b/source/blender/makesdna/DNA_view3d_defaults.h index b2d17b0ea22..c6c93c33086 100644 --- a/source/blender/makesdna/DNA_view3d_defaults.h +++ b/source/blender/makesdna/DNA_view3d_defaults.h @@ -36,8 +36,10 @@ #define _DNA_DEFAULT_View3DOverlay \ { \ + .flag = V3D_OVERLAY_VIEWER_ATTRIBUTE, \ .wireframe_threshold = 1.0f, \ .wireframe_opacity = 1.0f, \ + .viewer_attribute_opacity = 0.8f, \ .xray_alpha_bone = 0.5f, \ .bone_wire_alpha = 1.0f, \ .fade_alpha = 0.40f, \ @@ -81,7 +83,7 @@ .gridflag = V3D_SHOW_X | V3D_SHOW_Y | V3D_SHOW_FLOOR | V3D_SHOW_ORTHO_GRID, \ \ .flag = V3D_SELECT_OUTLINE, \ - .flag2 = V3D_SHOW_RECONSTRUCTION | V3D_SHOW_ANNOTATION, \ + .flag2 = V3D_SHOW_RECONSTRUCTION | V3D_SHOW_ANNOTATION | V3D_SHOW_VIEWER, \ \ .lens = 50.0f, \ .clip_start = 0.01f, \ diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 2b422f9aebf..f3e56ba7039 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -22,6 +22,7 @@ struct wmTimer; #include "DNA_movieclip_types.h" #include "DNA_object_types.h" #include "DNA_view3d_enums.h" +#include "DNA_viewer_path_types.h" #ifdef __cplusplus extern "C" { @@ -205,6 +206,7 @@ typedef struct View3DOverlay { float weight_paint_mode_opacity; float sculpt_mode_mask_opacity; float sculpt_mode_face_sets_opacity; + float viewer_attribute_opacity; /** Armature edit/pose mode settings. */ float xray_alpha_bone; @@ -227,8 +229,6 @@ typedef struct View3DOverlay { float gpencil_vertex_paint_opacity; /** Handles display type for curves. */ int handle_display; - - char _pad[4]; } View3DOverlay; /** #View3DOverlay.handle_display */ @@ -348,6 +348,9 @@ typedef struct View3D { View3DShading shading; View3DOverlay overlay; + /** Path to the viewer node that is currently previewed. This is retrieved from the workspace. */ + ViewerPath viewer_path; + /** Runtime evaluation data (keep last). */ View3D_Runtime runtime; } View3D; @@ -443,7 +446,7 @@ enum { /** #View3D.flag2 (int) */ #define V3D_HIDE_OVERLAYS (1 << 2) -#define V3D_FLAG2_UNUSED_3 (1 << 3) /* cleared */ +#define V3D_SHOW_VIEWER (1 << 3) #define V3D_SHOW_ANNOTATION (1 << 4) #define V3D_LOCK_CAMERA (1 << 5) #define V3D_FLAG2_UNUSED_6 (1 << 6) /* cleared */ @@ -528,6 +531,7 @@ enum { V3D_OVERLAY_HIDE_OBJECT_ORIGINS = (1 << 10), V3D_OVERLAY_STATS = (1 << 11), V3D_OVERLAY_FADE_INACTIVE = (1 << 12), + V3D_OVERLAY_VIEWER_ATTRIBUTE = (1 << 13), }; /** #View3DOverlay.edit_flag */ diff --git a/source/blender/makesdna/DNA_viewer_path_types.h b/source/blender/makesdna/DNA_viewer_path_types.h new file mode 100644 index 00000000000..8f470b66ca0 --- /dev/null +++ b/source/blender/makesdna/DNA_viewer_path_types.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_listbase.h" +#include "BLI_utildefines.h" + +struct ID; + +typedef enum ViewerPathElemType { + VIEWER_PATH_ELEM_TYPE_ID = 0, + VIEWER_PATH_ELEM_TYPE_MODIFIER = 1, + VIEWER_PATH_ELEM_TYPE_NODE = 2, +} ViewerPathElemType; + +typedef struct ViewerPathElem { + struct ViewerPathElem *next, *prev; + int type; + char _pad[4]; +} ViewerPathElem; + +typedef struct IDViewerPathElem { + ViewerPathElem base; + struct ID *id; +} IDViewerPathElem; + +typedef struct ModifierViewerPathElem { + ViewerPathElem base; + char *modifier_name; +} ModifierViewerPathElem; + +typedef struct NodeViewerPathElem { + ViewerPathElem base; + char *node_name; +} NodeViewerPathElem; + +typedef struct ViewerPath { + /** List of #ViewerPathElem. */ + ListBase path; +} ViewerPath; diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h index 1a6e19c31ad..e99f317f057 100644 --- a/source/blender/makesdna/DNA_workspace_types.h +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -10,6 +10,7 @@ #include "DNA_ID.h" #include "DNA_asset_types.h" +#include "DNA_viewer_path_types.h" #ifdef __cplusplus extern "C" { @@ -143,6 +144,13 @@ typedef struct WorkSpace { /** Workspace-wide active asset library, for asset UIs to use (e.g. asset view UI template). The * Asset Browser has its own and doesn't use this. */ AssetLibraryReference asset_library_ref; + + /** + * Ground truth for the currently active viewer node. When a viewer node is activated its path is + * set here. Editors can check here for which node is active (currently the node editor, + * spreadsheet and viewport do this). + */ + ViewerPath viewer_path; } WorkSpace; /** diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index c9f63e7c315..4ec2799186e 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10803,6 +10803,12 @@ static void def_geo_viewer(StructRNA *srna) RNA_def_property_enum_default(prop, CD_PROP_FLOAT); RNA_def_property_ui_text(prop, "Data Type", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_with_auto_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "Domain to evaluate the field on"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } static void def_geo_realize_instances(StructRNA *srna) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 9bbe37b5187..738b41828e0 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -19,6 +19,7 @@ #include "BKE_movieclip.h" #include "BKE_node.h" #include "BKE_studiolight.h" +#include "BKE_viewer_path.h" #include "ED_asset.h" #include "ED_spreadsheet.h" @@ -3279,59 +3280,21 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UN return item_array; } -static SpreadsheetContext *rna_SpaceSpreadsheet_context_path_append(SpaceSpreadsheet *sspreadsheet, - int type) +static StructRNA *rna_viewer_path_elem_refine(PointerRNA *ptr) { - SpreadsheetContext *context = ED_spreadsheet_context_new(type); - BLI_addtail(&sspreadsheet->context_path, context); - ED_spreadsheet_context_path_update_tag(sspreadsheet); - WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL); - return context; -} - -static void rna_SpaceSpreadsheet_context_path_clear(SpaceSpreadsheet *sspreadsheet) -{ - ED_spreadsheet_context_path_clear(sspreadsheet); - ED_spreadsheet_context_path_update_tag(sspreadsheet); - WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL); -} - -static StructRNA *rna_spreadsheet_context_refine(PointerRNA *ptr) -{ - SpreadsheetContext *context = ptr->data; - switch (context->type) { - case SPREADSHEET_CONTEXT_OBJECT: - return &RNA_SpreadsheetContextObject; - case SPREADSHEET_CONTEXT_MODIFIER: - return &RNA_SpreadsheetContextModifier; - case SPREADSHEET_CONTEXT_NODE: - return &RNA_SpreadsheetContextNode; + ViewerPathElem *elem = ptr->data; + switch (elem->type) { + case VIEWER_PATH_ELEM_TYPE_ID: + return &RNA_IDViewerPathElem; + case VIEWER_PATH_ELEM_TYPE_MODIFIER: + return &RNA_ModifierViewerPathElem; + case VIEWER_PATH_ELEM_TYPE_NODE: + return &RNA_NodeViewerPathElem; } BLI_assert_unreachable(); return NULL; } -static void rna_spreadsheet_context_update(Main *UNUSED(bmain), - Scene *UNUSED(scene), - PointerRNA *ptr) -{ - bScreen *screen = (bScreen *)ptr->owner_id; - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - SpaceLink *sl = area->spacedata.first; - if (sl->spacetype == SPACE_SPREADSHEET) { - SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - ED_spreadsheet_context_path_update_tag(sspreadsheet); - } - } -} - -static void rna_SpaceSpreadsheet_context_path_guess(SpaceSpreadsheet *sspreadsheet, bContext *C) -{ - ED_spreadsheet_context_path_guess(C, sspreadsheet); - ED_spreadsheet_context_path_update_tag(sspreadsheet); - WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL); -} - static void rna_FileAssetSelectParams_catalog_id_get(PointerRNA *ptr, char *value) { const FileAssetSelectParams *params = ptr->data; @@ -4456,6 +4419,20 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "show_viewer_attribute", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_VIEWER_ATTRIBUTE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Viewer Node", "Show attribute overlay for active viewer node"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "viewer_attribute_opacity", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "overlay.viewer_attribute_opacity"); + RNA_def_property_ui_text( + prop, "Viewer Attribute Opacity", "Opacity of the attribute that is currently visualized"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "show_paint_wire", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "overlay.paint_flag", V3D_OVERLAY_PAINT_WIRE); RNA_def_property_ui_text(prop, "Show Wire", "Use wireframe display in painting modes"); @@ -5107,6 +5084,11 @@ static void rna_def_space_view3d(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Visibility Icon", ""); + prop = RNA_def_property(srna, "show_viewer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", V3D_SHOW_VIEWER); + RNA_def_property_ui_text(prop, "Show Viewer", "Display non-final geometry from viewer nodes"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); + /* Nested Structs */ prop = RNA_def_property(srna, "shading", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); @@ -7883,93 +7865,77 @@ static void rna_def_spreadsheet_row_filter(BlenderRNA *brna) RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); } -static const EnumPropertyItem spreadsheet_context_type_items[] = { - {SPREADSHEET_CONTEXT_OBJECT, "OBJECT", ICON_NONE, "Object", ""}, - {SPREADSHEET_CONTEXT_MODIFIER, "MODIFIER", ICON_NONE, "Modifier", ""}, - {SPREADSHEET_CONTEXT_NODE, "NODE", ICON_NONE, "Node", ""}, +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", ""}, {0, NULL, 0, NULL, NULL}, }; -static void rna_def_space_spreadsheet_context(BlenderRNA *brna) +static void rna_def_viewer_path_elem(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; - srna = RNA_def_struct(brna, "SpreadsheetContext", NULL); - RNA_def_struct_ui_text(srna, "Spreadsheet Context", "Element of spreadsheet context path"); - RNA_def_struct_refine_func(srna, "rna_spreadsheet_context_refine"); + srna = RNA_def_struct(brna, "ViewerPathElem", NULL); + RNA_def_struct_ui_text(srna, "Viewer Path Element", "Element of a viewer path"); + RNA_def_struct_refine_func(srna, "rna_viewer_path_elem_refine"); prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, spreadsheet_context_type_items); - RNA_def_property_ui_text(prop, "Type", "Type of the context"); + 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); - - rna_def_space_generic_show_region_toggles(srna, - (1 << RGN_TYPE_CHANNELS) | (1 << RGN_TYPE_FOOTER)); } -static void rna_def_space_spreadsheet_context_object(BlenderRNA *brna) +static void rna_def_id_viewer_path_elem(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; - srna = RNA_def_struct(brna, "SpreadsheetContextObject", "SpreadsheetContext"); + srna = RNA_def_struct(brna, "IDViewerPathElem", "ViewerPathElem"); - prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "Object"); - RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, "rna_spreadsheet_context_update"); + prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "ID", ""); } -static void rna_def_space_spreadsheet_context_modifier(BlenderRNA *brna) +static void rna_def_modifier_viewer_path_elem(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; - srna = RNA_def_struct(brna, "SpreadsheetContextModifier", "SpreadsheetContext"); + srna = RNA_def_struct(brna, "ModifierViewerPathElem", "ViewerPathElem"); prop = RNA_def_property(srna, "modifier_name", PROP_STRING, PROP_NONE); RNA_def_property_ui_text(prop, "Modifier Name", ""); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, "rna_spreadsheet_context_update"); } -static void rna_def_space_spreadsheet_context_node(BlenderRNA *brna) +static void rna_def_node_viewer_path_elem(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; - srna = RNA_def_struct(brna, "SpreadsheetContextNode", "SpreadsheetContext"); + srna = RNA_def_struct(brna, "NodeViewerPathElem", "ViewerPathElem"); prop = RNA_def_property(srna, "node_name", PROP_STRING, PROP_NONE); RNA_def_property_ui_text(prop, "Node Name", ""); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, "rna_spreadsheet_context_update"); } -static void rna_def_space_spreadsheet_context_path(BlenderRNA *brna, PropertyRNA *cprop) +static void rna_def_viewer_path(BlenderRNA *brna) { StructRNA *srna; - PropertyRNA *parm; - FunctionRNA *func; - - RNA_def_property_srna(cprop, "SpreadsheetContextPath"); - srna = RNA_def_struct(brna, "SpreadsheetContextPath", NULL); - RNA_def_struct_sdna(srna, "SpaceSpreadsheet"); + PropertyRNA *prop; - func = RNA_def_function(srna, "append", "rna_SpaceSpreadsheet_context_path_append"); - RNA_def_function_ui_description(func, "Append a context path element"); - parm = RNA_def_property(func, "type", PROP_ENUM, PROP_NONE); - RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - RNA_def_property_enum_items(parm, spreadsheet_context_type_items); - parm = RNA_def_pointer( - func, "context", "SpreadsheetContext", "", "Newly created context path element"); - RNA_def_function_return(func, parm); + 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); - func = RNA_def_function(srna, "clear", "rna_SpaceSpreadsheet_context_path_clear"); - RNA_def_function_ui_description(func, "Clear entire context path"); + srna = RNA_def_struct(brna, "ViewerPath", NULL); + RNA_def_struct_ui_text(srna, "Viewer Path", "Path to data that is viewed"); - func = RNA_def_function(srna, "guess", "rna_SpaceSpreadsheet_context_path_guess"); - RNA_def_function_ui_description(func, "Guess the context path from the current context"); - RNA_def_function_flag(func, FUNC_USE_CONTEXT); + prop = RNA_def_property(srna, "path", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "ViewerPathElem"); + RNA_def_property_ui_text(prop, "Viewer Path", NULL); } static void rna_def_space_spreadsheet(BlenderRNA *brna) @@ -7996,11 +7962,6 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - rna_def_space_spreadsheet_context(brna); - rna_def_space_spreadsheet_context_object(brna); - rna_def_space_spreadsheet_context_modifier(brna); - rna_def_space_spreadsheet_context_node(brna); - srna = RNA_def_struct(brna, "SpaceSpreadsheet", "Space"); RNA_def_struct_ui_text(srna, "Space Spreadsheet", "Spreadsheet space data"); @@ -8017,15 +7978,14 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use Filter", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); - prop = RNA_def_property(srna, "display_context_path_collapsed", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "display_viewer_path_collapsed", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_FLAG_CONTEXT_PATH_COLLAPSED); RNA_def_property_ui_text(prop, "Display Context Path Collapsed", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); - prop = RNA_def_property(srna, "context_path", PROP_COLLECTION, PROP_NONE); - RNA_def_property_struct_type(prop, "SpreadsheetContext"); - RNA_def_property_ui_text(prop, "Context Path", "Context path to the data being displayed"); - rna_def_space_spreadsheet_context_path(brna, prop); + prop = RNA_def_property(srna, "viewer_path", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text( + prop, "Viewer Path", "Path to the data that is displayed in the spreadsheet"); prop = RNA_def_property(srna, "show_only_selected", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "filter_flag", SPREADSHEET_FILTER_SELECTED_ONLY); @@ -8073,6 +8033,7 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna) void RNA_def_space(BlenderRNA *brna) { rna_def_space(brna); + rna_def_viewer_path(brna); rna_def_space_image(brna); rna_def_space_sequencer(brna); rna_def_space_text(brna); diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 7c5bb988012..ba4c03f68de 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -33,6 +33,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "DNA_view3d_types.h" #include "DNA_windowmanager_types.h" #include "BKE_attribute_math.hh" @@ -80,6 +81,7 @@ #include "ED_screen.h" #include "ED_spreadsheet.h" #include "ED_undo.h" +#include "ED_viewer_path.hh" #include "NOD_geometry.h" #include "NOD_geometry_nodes_lazy_function.hh" @@ -828,25 +830,6 @@ static void initialize_group_input(NodesModifierData &nmd, } } -static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain) -{ - wmWindowManager *wm = (wmWindowManager *)bmain->wm.first; - if (wm == nullptr) { - return {}; - } - Vector<SpaceSpreadsheet *> spreadsheets; - LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { - bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - SpaceLink *sl = (SpaceLink *)area->spacedata.first; - if (sl->spacetype == SPACE_SPREADSHEET) { - spreadsheets.append((SpaceSpreadsheet *)sl); - } - } - } - return spreadsheets; -} - static const lf::FunctionNode &find_viewer_lf_node(const bNode &viewer_bnode) { return *blender::nodes::ensure_geometry_nodes_lazy_function_graph(viewer_bnode.owner_tree()) @@ -858,50 +841,33 @@ static const lf::FunctionNode &find_group_lf_node(const bNode &group_bnode) ->mapping.group_node_map.lookup(&group_bnode); } -static void find_side_effect_nodes_for_spreadsheet( - const SpaceSpreadsheet &sspreadsheet, +static void find_side_effect_nodes_for_viewer_path( + const ViewerPath &viewer_path, const NodesModifierData &nmd, const ModifierEvalContext &ctx, - const bNodeTree &root_tree, MultiValueMap<blender::ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes) { - Vector<SpreadsheetContext *> context_path = sspreadsheet.context_path; - if (context_path.size() < 3) { - return; - } - if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { - return; - } - if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) { + const std::optional<blender::ed::viewer_path::ViewerPathForGeometryNodesViewer> parsed_path = + blender::ed::viewer_path::parse_geometry_nodes_viewer(viewer_path); + if (!parsed_path.has_value()) { return; } - SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0]; - if (object_context->object != DEG_get_original_object(ctx.object)) { + if (parsed_path->object != DEG_get_original_object(ctx.object)) { return; } - SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context_path[1]; - if (StringRef(modifier_context->modifier_name) != nmd.modifier.name) { + if (parsed_path->modifier_name != nmd.modifier.name) { return; } - for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) { - if (context->type != SPREADSHEET_CONTEXT_NODE) { - return; - } - } blender::ComputeContextBuilder compute_context_builder; - compute_context_builder.push<blender::bke::ModifierComputeContext>(nmd.modifier.name); - - const Span<SpreadsheetContextNode *> nested_group_contexts = - context_path.as_span().drop_front(2).drop_back(1).cast<SpreadsheetContextNode *>(); - const SpreadsheetContextNode *last_context = (SpreadsheetContextNode *)context_path.last(); + compute_context_builder.push<blender::bke::ModifierComputeContext>(parsed_path->modifier_name); + const bNodeTree *group = nmd.node_group; Stack<const bNode *> group_node_stack; - const bNodeTree *group = &root_tree; - for (SpreadsheetContextNode *node_context : nested_group_contexts) { + for (const StringRefNull group_node_name : parsed_path->group_node_names) { const bNode *found_node = nullptr; for (const bNode *node : group->group_nodes()) { - if (STREQ(node->name, node_context->node_name)) { + if (node->name == group_node_name) { found_node = node; break; } @@ -913,13 +879,13 @@ static void find_side_effect_nodes_for_spreadsheet( return; } group_node_stack.push(found_node); - group = reinterpret_cast<const bNodeTree *>(found_node->id); - compute_context_builder.push<blender::bke::NodeGroupComputeContext>(node_context->node_name); + group = reinterpret_cast<bNodeTree *>(found_node->id); + compute_context_builder.push<blender::bke::NodeGroupComputeContext>(group_node_name); } const bNode *found_viewer_node = nullptr; for (const bNode *viewer_node : group->nodes_by_type("GeometryNodeViewer")) { - if (STREQ(viewer_node->name, last_context->node_name)) { + if (viewer_node->name == parsed_path->viewer_node_name) { found_viewer_node = viewer_node; break; } @@ -943,16 +909,29 @@ static void find_side_effect_nodes_for_spreadsheet( static void find_side_effect_nodes( const NodesModifierData &nmd, const ModifierEvalContext &ctx, - const bNodeTree &tree, MultiValueMap<blender::ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes) { Main *bmain = DEG_get_bmain(ctx.depsgraph); - - /* Based on every visible spreadsheet context path, get a list of sockets that need to have their - * intermediate geometries cached for display. */ - Vector<SpaceSpreadsheet *> spreadsheets = find_spreadsheet_editors(bmain); - for (SpaceSpreadsheet *sspreadsheet : spreadsheets) { - find_side_effect_nodes_for_spreadsheet(*sspreadsheet, nmd, ctx, tree, r_side_effect_nodes); + wmWindowManager *wm = (wmWindowManager *)bmain->wm.first; + if (wm == nullptr) { + return; + } + LISTBASE_FOREACH (const wmWindow *, window, &wm->windows) { + const bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); + const WorkSpace *workspace = BKE_workspace_active_get(window->workspace_hook); + find_side_effect_nodes_for_viewer_path(workspace->viewer_path, nmd, ctx, r_side_effect_nodes); + LISTBASE_FOREACH (const ScrArea *, area, &screen->areabase) { + const SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first); + if (sl->spacetype == SPACE_SPREADSHEET) { + const SpaceSpreadsheet &sspreadsheet = *reinterpret_cast<const SpaceSpreadsheet *>(sl); + find_side_effect_nodes_for_viewer_path( + sspreadsheet.viewer_path, nmd, ctx, r_side_effect_nodes); + } + if (sl->spacetype == SPACE_VIEW3D) { + const View3D &v3d = *reinterpret_cast<const View3D *>(sl); + find_side_effect_nodes_for_viewer_path(v3d.viewer_path, nmd, ctx, r_side_effect_nodes); + } + } } } @@ -1162,7 +1141,7 @@ static GeometrySet compute_geometry( geo_nodes_modifier_data.eval_log = eval_log.get(); } MultiValueMap<blender::ComputeContextHash, const lf::FunctionNode *> r_side_effect_nodes; - find_side_effect_nodes(*nmd, *ctx, btree, r_side_effect_nodes); + find_side_effect_nodes(*nmd, *ctx, r_side_effect_nodes); geo_nodes_modifier_data.side_effect_nodes = &r_side_effect_nodes; blender::nodes::GeoNodesLFUserData user_data; user_data.modifier_data = &geo_nodes_modifier_data; diff --git a/source/blender/nodes/NOD_geometry_nodes_log.hh b/source/blender/nodes/NOD_geometry_nodes_log.hh index cf59c99bc79..2b0c16c8656 100644 --- a/source/blender/nodes/NOD_geometry_nodes_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_log.hh @@ -33,6 +33,7 @@ #include "BKE_attribute.h" #include "BKE_geometry_set.hh" +#include "BKE_viewer_path.h" #include "FN_field.hh" @@ -156,7 +157,6 @@ class GeometryInfoLog : public ValueLog { class ViewerNodeLog { public: GeometrySet geometry; - GField field; }; using Clock = std::chrono::steady_clock; @@ -214,7 +214,7 @@ class GeoTreeLogger { ~GeoTreeLogger(); void log_value(const bNode &node, const bNodeSocket &socket, GPointer value); - void log_viewer_node(const bNode &viewer_node, const GeometrySet &geometry, const GField &field); + void log_viewer_node(const bNode &viewer_node, GeometrySet geometry); }; /** @@ -333,8 +333,7 @@ class GeoModifierLog { * Utility accessor to logged data. */ static GeoTreeLog *get_tree_log_for_node_editor(const SpaceNode &snode); - static const ViewerNodeLog *find_viewer_node_log_for_spreadsheet( - const SpaceSpreadsheet &sspreadsheet); + static const ViewerNodeLog *find_viewer_node_log_for_path(const ViewerPath &viewer_path); }; } // namespace blender::nodes::geo_eval_log diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc index 4ad6efebfa8..613375bca57 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc @@ -285,6 +285,12 @@ template<typename T> class AccumulateFieldInput final : public bke::GeometryFiel } return false; } + + std::optional<eAttrDomain> preferred_domain( + const GeometryComponent & /*component*/) const override + { + return source_domain_; + } }; template<typename T> class TotalFieldInput final : public bke::GeometryFieldInput { @@ -355,6 +361,11 @@ template<typename T> class TotalFieldInput final : public bke::GeometryFieldInpu } return false; } + + std::optional<eAttrDomain> preferred_domain(const GeometryComponent & /*component*/) const + { + return source_domain_; + } }; template<typename T> std::string identifier_suffix() diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index c9df66f1eea..99dd4d9f798 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -106,33 +106,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -static void try_capture_field_on_geometry(GeometryComponent &component, - const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const GField &field) -{ - const int domain_size = component.attribute_domain_size(domain); - if (domain_size == 0) { - return; - } - bke::GeometryFieldContext field_context{component, domain}; - MutableAttributeAccessor attributes = *component.attributes_for_write(); - const IndexMask mask{IndexMask(domain_size)}; - - const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type()); - GAttributeWriter output_attribute = attributes.lookup_or_add_for_write( - attribute_id, domain, data_type); - if (!output_attribute) { - return; - } - - fn::FieldEvaluator evaluator{field_context, &mask}; - evaluator.add_with_destination(field, output_attribute.varray); - evaluator.evaluate(); - - output_attribute.finish(); -} - static StringRefNull identifier_suffix(eCustomDataType data_type) { switch (data_type) { @@ -206,7 +179,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_instances()) { GeometryComponent &component = geometry_set.get_component_for_write( GEO_COMPONENT_TYPE_INSTANCES); - try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + bke::try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); } } else { @@ -217,7 +190,7 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponentType type : types) { if (geometry_set.has(type)) { GeometryComponent &component = geometry_set.get_component_for_write(type); - try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + bke::try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); } } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc index 28d979facac..d3d0dcfb794 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -90,6 +90,11 @@ class EndpointFieldInput final : public bke::CurvesFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const CurvesGeometry & /*curves*/) const + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index b1c3bbfb81e..b4181528696 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -108,6 +108,11 @@ class HandleTypeFieldInput final : public bke::CurvesFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const CurvesGeometry & /*curves*/) const + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc index b5d8d1f020a..6fe806adb7c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc @@ -280,6 +280,11 @@ class IndexOnSplineFieldInput final : public bke::CurvesFieldInput { { return dynamic_cast<const IndexOnSplineFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const CurvesGeometry & /*curves*/) const + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc index 9ef9ee8ad6e..f0bd01a012b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc @@ -106,6 +106,11 @@ class PathToEdgeSelectionFieldInput final : public bke::MeshFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc index 4d7e4b00c5a..d5feaae46ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc @@ -141,6 +141,12 @@ class FieldAtIndex final : public bke::GeometryFieldInput { return output_array; } + + std::optional<eAttrDomain> preferred_domain( + const GeometryComponent & /*component*/) const override + { + return value_field_domain_; + } }; static StringRefNull identifier_suffix(eCustomDataType data_type) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc index bff2e7831c6..2979d0e4639 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc @@ -84,6 +84,11 @@ class HandlePositionFieldInput final : public bke::CurvesFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const CurvesGeometry & /*curves*/) const + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc index f2e7379b3a2..0b9084d3715 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc @@ -95,6 +95,11 @@ class AngleFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const AngleFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } }; class SignedAngleFieldInput final : public bke::MeshFieldInput { @@ -162,6 +167,11 @@ class SignedAngleFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const SignedAngleFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc index bfe8753c039..a579202738d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc @@ -48,6 +48,11 @@ class EdgeNeighborCountFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const EdgeNeighborCountFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc index c8ceae239a4..c80b3797874 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc @@ -73,6 +73,11 @@ class EdgeVerticesFieldInput final : public bke::MeshFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } }; static VArray<float3> construct_edge_positions_gvarray(const Mesh &mesh, @@ -127,6 +132,11 @@ class EdgePositionFieldInput final : public bke::MeshFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc index be921c1f1c5..a8670209014 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc @@ -55,6 +55,11 @@ class FaceAreaFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const FaceAreaFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_FACE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc index 90fa47c8217..7b084995fc3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc @@ -87,6 +87,11 @@ class PlanarFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const PlanarFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_FACE; + } }; static void geo_node_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc index 9e85eae3a31..429526588ce 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc @@ -66,6 +66,11 @@ class FaceNeighborCountFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const FaceNeighborCountFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_FACE; + } }; static VArray<int> construct_vertex_count_varray(const Mesh &mesh, const eAttrDomain domain) @@ -102,6 +107,11 @@ class FaceVertexCountFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const FaceVertexCountFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_FACE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc index 9d7735e707d..6d19d3be511 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc @@ -61,6 +61,11 @@ class IslandFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const IslandFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; + } }; class IslandCountFieldInput final : public bke::MeshFieldInput { @@ -100,6 +105,11 @@ class IslandCountFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const IslandCountFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc index ab44a6c8515..6b7f62a944c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc @@ -58,6 +58,11 @@ class VertexCountFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const VertexCountFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; + } }; static VArray<int> construct_face_count_gvarray(const Mesh &mesh, const eAttrDomain domain) @@ -98,6 +103,11 @@ class VertexFaceCountFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const VertexFaceCountFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc index a54daabde3b..00c92e30443 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc @@ -143,6 +143,11 @@ class ShortestEdgePathsNextVertFieldInput final : public bke::MeshFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; + } }; class ShortestEdgePathsCostFieldInput final : public bke::MeshFieldInput { @@ -206,6 +211,11 @@ class ShortestEdgePathsCostFieldInput final : public bke::MeshFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc index df1427ac5b9..0326c55b65a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc @@ -111,6 +111,12 @@ class InterpolateDomain final : public bke::GeometryFieldInput { return attributes.adapt_domain( GVArray::ForGArray(std::move(values)), src_domain_, context.domain()); } + + std::optional<eAttrDomain> preferred_domain( + const GeometryComponent & /*component*/) const override + { + return src_domain_; + } }; static StringRefNull identifier_suffix(eCustomDataType data_type) diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc index 628688f3b47..dfb4181926e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -100,6 +100,12 @@ class MaterialSelectionFieldInput final : public bke::GeometryFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain( + const GeometryComponent & /*component*/) const override + { + return ATTR_DOMAIN_FACE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc index 88cccfb2f94..1b9852cf7b9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc @@ -65,6 +65,11 @@ class BoundaryFieldInput final : public bke::MeshFieldInput { return mesh.attributes().adapt_domain<bool>( VArray<bool>::ForContainer(std::move(boundary)), ATTR_DOMAIN_EDGE, domain); } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc index d0b770aadad..51b21b218fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc @@ -87,56 +87,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -static void try_capture_field_on_geometry(GeometryComponent &component, - const StringRef name, - const eAttrDomain domain, - const GField &field, - std::atomic<bool> &r_failure) -{ - MutableAttributeAccessor attributes = *component.attributes_for_write(); - const int domain_size = attributes.domain_size(domain); - if (domain_size == 0) { - return; - } - - bke::GeometryFieldContext field_context{component, domain}; - const IndexMask mask{IndexMask(domain_size)}; - - const CPPType &type = field.cpp_type(); - const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(type); - const bke::AttributeValidator validator = attributes.lookup_validator(name); - - /* Could avoid allocating a new buffer if: - * - We are writing to an attribute that exists already with the correct domain and type. - * - The field does not depend on that attribute (we can't easily check for that yet). */ - void *buffer = MEM_mallocN(type.size() * domain_size, __func__); - - fn::FieldEvaluator evaluator{field_context, &mask}; - evaluator.add_with_destination(validator.validate_field_if_necessary(field), - GMutableSpan{type, buffer, domain_size}); - evaluator.evaluate(); - - if (GAttributeWriter attribute = attributes.lookup_for_write(name)) { - if (attribute.domain == domain && attribute.varray.type() == type) { - attribute.varray.set_all(buffer); - attribute.finish(); - type.destruct_n(buffer, domain_size); - MEM_freeN(buffer); - return; - } - } - attributes.remove(name); - if (attributes.add(name, domain, data_type, bke::AttributeInitMoveArray{buffer})) { - return; - } - - /* If the name corresponds to a builtin attribute, removing the attribute might fail if - * it's required, and adding the attribute might fail if the domain or type is incorrect. */ - type.destruct_n(buffer, domain_size); - MEM_freeN(buffer); - r_failure = true; -} - static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -192,7 +142,9 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_instances()) { GeometryComponent &component = geometry_set.get_component_for_write( GEO_COMPONENT_TYPE_INSTANCES); - try_capture_field_on_geometry(component, name, domain, field, failure); + if (!bke::try_capture_field_on_geometry(component, name, domain, field)) { + failure.store(true); + } } } else { @@ -201,7 +153,9 @@ static void node_geo_exec(GeoNodeExecParams params) {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) { if (geometry_set.has(type)) { GeometryComponent &component = geometry_set.get_component_for_write(type); - try_capture_field_on_geometry(component, name, domain, field, failure); + if (!bke::try_capture_field_on_geometry(component, name, domain, field)) { + failure.store(true); + } } } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc index ccb489f6e29..67e35f1f023 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc @@ -118,6 +118,11 @@ class PackIslandsFieldInput final : public bke::MeshFieldInput { { return construct_uv_gvarray(mesh, selection_field, uv_field, rotate, margin, domain); } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_CORNER; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc index 801bc3f4642..ae9a532ed66 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc @@ -160,6 +160,11 @@ class UnwrapFieldInput final : public bke::MeshFieldInput { { return construct_uv_gvarray(mesh, selection, seam, fill_holes, margin, method, domain); } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_CORNER; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc index 2c8a70901f8..ec900a41bbc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc @@ -6,7 +6,7 @@ #include "UI_resources.h" #include "ED_node.h" -#include "ED_spreadsheet.h" +#include "ED_viewer_path.hh" #include "NOD_socket_search_link.hh" @@ -30,12 +30,18 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryViewer *data = MEM_cnew<NodeGeometryViewer>(__func__); data->data_type = CD_PROP_FLOAT; + data->domain = ATTR_DOMAIN_AUTO; node->storage = data; } static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +static void node_layout_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); } @@ -79,7 +85,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) SpaceNode *snode = CTX_wm_space_node(¶ms.C); Main *bmain = CTX_data_main(¶ms.C); ED_node_set_active(bmain, snode, ¶ms.node_tree, &viewer_node, nullptr); - ED_spreadsheet_context_paths_set_geometry_node(bmain, snode, &viewer_node); + ed::viewer_path::activate_geometry_node(*bmain, *snode, viewer_node); }; const std::optional<eCustomDataType> type = node_socket_to_custom_data_type( @@ -132,7 +138,9 @@ void register_node_type_geo_viewer() node_type_update(&ntype, file_ns::node_update); node_type_init(&ntype, file_ns::node_init); ntype.declare = file_ns::node_declare; - ntype.draw_buttons_ex = file_ns::node_layout; + ntype.draw_buttons = file_ns::node_layout; + ntype.draw_buttons_ex = file_ns::node_layout_ex; ntype.gather_link_search_ops = file_ns::node_gather_link_searches; + ntype.no_muting = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 7a78547b10b..0dec27da0bd 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -543,19 +543,53 @@ class LazyFunctionForViewerNode : public LazyFunction { } GeometrySet geometry = params.extract_input<GeometrySet>(0); + const NodeGeometryViewer *storage = static_cast<NodeGeometryViewer *>(bnode_.storage); - GField field; if (use_field_input_) { const void *value_or_field = params.try_get_input_data_ptr(1); BLI_assert(value_or_field != nullptr); const ValueOrFieldCPPType &value_or_field_type = static_cast<const ValueOrFieldCPPType &>( *inputs_[1].type); - field = value_or_field_type.as_field(value_or_field); + GField field = value_or_field_type.as_field(value_or_field); + const eAttrDomain domain = eAttrDomain(storage->domain); + const StringRefNull viewer_attribute_name = ".viewer"; + if (domain == ATTR_DOMAIN_INSTANCE) { + if (geometry.has_instances()) { + GeometryComponent &component = geometry.get_component_for_write( + GEO_COMPONENT_TYPE_INSTANCES); + bke::try_capture_field_on_geometry( + component, viewer_attribute_name, ATTR_DOMAIN_INSTANCE, field); + } + } + else { + geometry.modify_geometry_sets([&](GeometrySet &geometry) { + for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE}) { + if (geometry.has(type)) { + GeometryComponent &component = geometry.get_component_for_write(type); + eAttrDomain used_domain = domain; + if (used_domain == ATTR_DOMAIN_AUTO) { + if (const std::optional<eAttrDomain> detected_domain = + bke::try_detect_field_domain(component, field)) { + used_domain = *detected_domain; + } + else { + used_domain = type == GEO_COMPONENT_TYPE_MESH ? ATTR_DOMAIN_CORNER : + ATTR_DOMAIN_POINT; + } + } + bke::try_capture_field_on_geometry( + component, viewer_attribute_name, used_domain, field); + } + } + }); + } } geo_eval_log::GeoTreeLogger &tree_logger = user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context); - tree_logger.log_viewer_node(bnode_, geometry, field); + tree_logger.log_viewer_node(bnode_, std::move(geometry)); } }; diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc index aadf69f10c5..909f7779e95 100644 --- a/source/blender/nodes/intern/geometry_nodes_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -6,12 +6,15 @@ #include "BKE_compute_contexts.hh" #include "BKE_curves.hh" #include "BKE_node_runtime.hh" +#include "BKE_viewer_path.h" #include "FN_field_cpp_type.hh" #include "DNA_modifier_types.h" #include "DNA_space_types.h" +#include "ED_viewer_path.hh" + namespace blender::nodes::geo_eval_log { using fn::FieldInput; @@ -188,13 +191,10 @@ void GeoTreeLogger::log_value(const bNode &node, const bNodeSocket &socket, cons } } -void GeoTreeLogger::log_viewer_node(const bNode &viewer_node, - const GeometrySet &geometry, - const GField &field) +void GeoTreeLogger::log_viewer_node(const bNode &viewer_node, GeometrySet geometry) { destruct_ptr<ViewerNodeLog> log = this->allocator->construct<ViewerNodeLog>(); - log->geometry = geometry; - log->field = field; + log->geometry = std::move(geometry); log->geometry.ensure_owns_direct_data(); this->viewer_node_logs.append({this->allocator->copy_string(viewer_node.name), std::move(log)}); } @@ -545,29 +545,17 @@ GeoTreeLog *GeoModifierLog::get_tree_log_for_node_editor(const SpaceNode &snode) return &modifier_log->get_tree_log(compute_context_builder.hash()); } -const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_spreadsheet( - const SpaceSpreadsheet &sspreadsheet) +const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_path(const ViewerPath &viewer_path) { - Vector<const SpreadsheetContext *> context_path = sspreadsheet.context_path; - if (context_path.size() < 3) { - return nullptr; - } - if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { - return nullptr; - } - if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) { - return nullptr; - } - const SpreadsheetContextObject *object_context = - reinterpret_cast<const SpreadsheetContextObject *>(context_path[0]); - const SpreadsheetContextModifier *modifier_context = - reinterpret_cast<const SpreadsheetContextModifier *>(context_path[1]); - if (object_context->object == nullptr) { + const std::optional<ed::viewer_path::ViewerPathForGeometryNodesViewer> parsed_path = + ed::viewer_path::parse_geometry_nodes_viewer(viewer_path); + if (!parsed_path.has_value()) { return nullptr; } + const Object *object = parsed_path->object; NodesModifierData *nmd = nullptr; - LISTBASE_FOREACH (ModifierData *, md, &object_context->object->modifiers) { - if (STREQ(md->name, modifier_context->modifier_name)) { + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->name == parsed_path->modifier_name) { if (md->type == eModifierType_Nodes) { nmd = reinterpret_cast<NodesModifierData *>(md); } @@ -583,27 +571,16 @@ const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_spreadsheet( static_cast<nodes::geo_eval_log::GeoModifierLog *>(nmd->runtime_eval_log); ComputeContextBuilder compute_context_builder; - compute_context_builder.push<bke::ModifierComputeContext>(modifier_context->modifier_name); - for (const SpreadsheetContext *context : context_path.as_span().drop_front(2).drop_back(1)) { - if (context->type != SPREADSHEET_CONTEXT_NODE) { - return nullptr; - } - const SpreadsheetContextNode &node_context = *reinterpret_cast<const SpreadsheetContextNode *>( - context); - compute_context_builder.push<bke::NodeGroupComputeContext>(node_context.node_name); + compute_context_builder.push<bke::ModifierComputeContext>(parsed_path->modifier_name); + for (const StringRef group_node_name : parsed_path->group_node_names) { + compute_context_builder.push<bke::NodeGroupComputeContext>(group_node_name); } const ComputeContextHash context_hash = compute_context_builder.hash(); nodes::geo_eval_log::GeoTreeLog &tree_log = modifier_log->get_tree_log(context_hash); tree_log.ensure_viewer_node_logs(); - const SpreadsheetContext *last_context = context_path.last(); - if (last_context->type != SPREADSHEET_CONTEXT_NODE) { - return nullptr; - } - const SpreadsheetContextNode &last_node_context = - *reinterpret_cast<const SpreadsheetContextNode *>(last_context); const ViewerNodeLog *viewer_log = tree_log.viewer_node_logs.lookup_default( - last_node_context.node_name, nullptr); + parsed_path->viewer_node_name, nullptr); return viewer_log; } diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index ba1d8d3ccb7..e25169109e7 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -353,6 +353,8 @@ typedef struct wmNotifier { #define NC_LIGHTPROBE (26 << 24) /* Changes to asset data in the current .blend. */ #define NC_ASSET (27 << 24) +/* Changes to the active viewer path. */ +#define NC_VIEWER_PATH (28 << 24) /* data type, 256 entries is enough, it can overlap */ #define NOTE_DATA 0x00FF0000 diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index d88a5a5a18b..38e37d73eb9 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -632,6 +632,7 @@ void wm_event_do_notifiers(bContext *C) win->screen->id.name + 2, note->category); # endif + ED_workspace_do_listen(C, note); ED_screen_do_listen(C, note); LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { |