Geometry Nodes: viewport preview

This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.

**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
  makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
  is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
  its active or not.

**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
  When pinned, the spreadsheet still references the viewer node even
  when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
  the active viewer.

**Attribute**
* When a field is linked to the second input of the viewer node it is
  displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
  automatically. This does not work in all cases. It falls back to the
  face corner domain on meshes and the point domain on curves. When
  necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
  that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.

**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
  setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
  by disabling the "Viewer Node" option in the "View" menu.

**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
  is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
  field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
  attribute.
* The ground truth for the active viewer node is stored in the workspace
  now. Node editors, spreadsheets and viewports retrieve the active
  viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
  the viewed geometry of the corresponding object is part of the iterator
  instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
  to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
  existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
  "preferred domain" for every field input. If there is not exactly one
  preferred domain, the fallback is used.

Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
  added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
  the values might have to be normalized or some should be drawn as arrays.
  For now, we encourage users to build node groups that generate appropriate
  viewer-geometry. We might include some of that functionality in future versions.
  Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
  nvidia gpus, to be investigated.

Differential Revision: https://developer.blender.org/D15954
This commit is contained in:
Jacques Lucke 2022-09-28 17:54:59 +02:00
parent 2312915b96
commit c55d38f00b
Notes: blender-bot 2025-02-14 01:09:36 +00:00
Referenced by commit ed852c8401, Fix: Curves sculptmode: deduplicate checks for being in mode
Referenced by issue #104261, Geometry Node: Crash if Preview node trying to show instaces of Collection with curve with Beveling
Referenced by issue #104015, Geometry Nodes: Viewer overlay disappears when enabling In Front
Referenced by issue #103985, Crash When Adding Hair Subdivision upon Viewer node
Referenced by issue #103988, Geometry nodes preview, clipping Region in the viewport its not working
Referenced by issue #102882, Geometry nodes: Crash for cycle links in node group when try to find viewer node
Referenced by issue #102700, Regression: Viewer node crash on empty by non-null mesh after join geometry with attribute output on viewer
Referenced by issue #102153, Regression: Geometry Nodes: Crash when muting node group that contains active viewer node
Referenced by issue #90914, Spreadsheet editor doesn't update when changing active object
105 changed files with 2671 additions and 1330 deletions

View File

@ -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 = (

View File

@ -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
obj = root_context.object
if not isinstance(root_context.id, bpy.types.Object):
return False
obj = root_context.id
if obj is None:
return False
if obj.type == 'MESH':

View File

@ -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()
@ -6346,6 +6347,13 @@ class VIEW3D_PT_overlay_geometry(Panel):
sub.prop(overlay, "wireframe_threshold", text="Wireframe")
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

View File

@ -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

View File

@ -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. */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
/** \} */

View File

@ -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) {

View File

@ -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);

View File

@ -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:

View File

@ -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);
}

View File

@ -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)

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}
}
}
}
}
}

View File

@ -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. *** */

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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];

View File

@ -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);
}

View File

@ -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");
/** \} */

View File

@ -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");

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -0,0 +1,6 @@
void main()
{
out_color = finalColor;
out_color.a *= opacity;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -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 */

View File

@ -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);

View File

@ -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)) {

View File

@ -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);
/** \} */

View File

@ -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);
}

View File

@ -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));

View File

@ -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)

View File

@ -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 (&deg_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 (&deg_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 (&deg_iter_settings, ob) {
if ((object_type_exclude_viewport & (1 << ob->type)) != 0) {
continue;

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -34,6 +34,7 @@ set(SRC
screendump.c
workspace_edit.c
workspace_layout_edit.c
workspace_listen.cc
screen_intern.h
)

View File

@ -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);
}

View File

@ -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"
@ -94,6 +95,14 @@ extern void ui_draw_dropshadow(
* This is passed to many functions which draw the node editor.
*/
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);

View File

@ -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);

View File

@ -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);

View File

@ -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",

View File

@ -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;
}
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();
}
}
}
/* 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];
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;
}
/* 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;
}
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 (link->is_muted()) {
continue;
}
if (!(target_node.flag & NODE_DO_OUTPUT)) {
continue;
}
is_currently_viewed = true;
break;
}
if (is_currently_viewed) {
continue;
}
return &output_socket;
}
return nullptr;
}
static const bNodeSocket *find_output_socket_to_be_viewed(const bNode *active_viewer_node,
const bNode &node_to_view)
static void finalize_viewer_link(const bContext &C,
SpaceNode &snode,
bNode &viewer_node,
bNodeLink &viewer_link)
{
/* 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;
}
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);
}
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_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;
}
}
}
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)) {
continue;
}
if (is_linked_to_viewer(output_socket, *active_viewer_node)) {
continue;
}
return &output_socket;
}
}
return nullptr;
ED_node_tree_propagate_change(&C, bmain, snode.edittree);
}
static int link_socket_to_viewer(const bContext &C,
bNode *viewer_bnode,
bNode &bnode_to_view,
bNodeSocket &bsocket_to_view)
static int view_socket(const bContext &C,
SpaceNode &snode,
bNodeTree &btree,
bNode &bnode_to_view,
bNodeSocket &bsocket_to_view)
{
SpaceNode &snode = *CTX_wm_space_node(&C);
bNodeTree &btree = *snode.edittree;
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;
}

View File

@ -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");
}
/** \} */

View File

@ -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;
}
}

View File

@ -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

View File

@ -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,57 +183,108 @@ 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)
{
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 spreadsheet_update_context(const bContext *C)
{
using blender::ed::viewer_path::ViewerPathForGeometryNodesViewer;
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));
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);
}
if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
/* Don't pin empty context_path, that could be annoying. */
sspreadsheet->flag &= ~SPREADSHEET_FLAG_PINNED;
}
}
static void update_context_path_from_context(const bContext *C)
{
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));
break;
}
}
}
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);
}
}
Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet,
const Depsgraph *depsgraph)
{
@ -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()

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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)),

View File

@ -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)

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1598,6 +1598,8 @@ typedef struct NodeGeometryImageTexture {
typedef struct NodeGeometryViewer {
/* eCustomDataType. */
int8_t data_type;
/* eAttrDomain. */
int8_t domain;
} NodeGeometryViewer;
typedef struct NodeGeometryUVUnwrap {

View File

@ -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;

View File

@ -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, \

View File

@ -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 */

View File

@ -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;

View File

@ -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;
/**

View File

@ -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)

View File

@ -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;
PropertyRNA *prop;
RNA_def_property_srna(cprop, "SpreadsheetContextPath");
srna = RNA_def_struct(brna, "SpreadsheetContextPath", NULL);
RNA_def_struct_sdna(srna, "SpaceSpreadsheet");
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, "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);
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, "clear", "rna_SpaceSpreadsheet_context_path_clear");
RNA_def_function_ui_description(func, "Clear entire context path");
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);

View File

@ -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) {
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;
}
if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
if (parsed_path->object != DEG_get_original_object(ctx.object)) {
return;
}
if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
if (parsed_path->modifier_name != nmd.modifier.name) {
return;
}
SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0];
if (object_context->object != DEG_get_original_object(ctx.object)) {
return;
}
SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context_path[1];
if (StringRef(modifier_context->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;

View File

@ -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

View File

@ -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()

View File

@ -106,33 +106,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
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);
}
}
});

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -87,56 +87,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
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);
}
}
}
});

View File

@ -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)

View File

@ -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)

Some files were not shown because too many files have changed in this diff Show More