1
1

Compare commits

...

25 Commits

Author SHA1 Message Date
83ccca8675 cleanup 2021-04-01 14:53:30 +02:00
77c8070b11 Spreadsheet: Show data of active node (WIP).
Differential Revision: https://developer.blender.org/D10875
2021-04-01 14:52:51 +02:00
1db976521e Merge branch 'master' into spreadsheet-active-node 2021-04-01 14:39:39 +02:00
f053bc2ddf fix 2021-04-01 14:31:05 +02:00
51b4162fed direct data ownership 2021-04-01 14:30:51 +02:00
4a7736ce1d Merge branch 'master' into spreadsheet-active-node 2021-04-01 13:16:48 +02:00
26071531d0 cleanup 2021-04-01 12:52:57 +02:00
131550dac1 Merge branch 'master' into spreadsheet-active-node 2021-04-01 12:45:55 +02:00
7bcd0e01af cleanup 2021-04-01 12:18:49 +02:00
d6fcecc471 cleanup 2021-04-01 12:12:18 +02:00
e945564068 Merge branch 'master' into spreadsheet-active-node 2021-04-01 11:55:34 +02:00
1598d57663 cleanup 2021-04-01 11:55:07 +02:00
42a40f4e7c support showing data of group input/output nodes 2021-04-01 11:41:30 +02:00
80865399a8 cleanup 2021-04-01 11:32:58 +02:00
9ed2a7e680 improved handling of skipped sockets 2021-04-01 11:22:06 +02:00
3af1903ac7 log more socket values 2021-04-01 10:38:19 +02:00
214a49aae4 refactor 2021-04-01 10:28:36 +02:00
45aa341dd4 initial callback 2021-04-01 10:18:44 +02:00
e0aab87f54 initial node group support 2021-04-01 10:07:59 +02:00
fca8d0f91f separate node from display name 2021-04-01 09:29:01 +02:00
8262c2577e cleanup 2021-03-31 12:52:45 +02:00
993e8af943 cleanup 2021-03-31 12:41:26 +02:00
a535c07417 avoid some copies 2021-03-31 12:24:14 +02:00
5cbecde9c7 node in spreadsheet 2021-03-31 12:16:01 +02:00
4472178094 initial code 2021-03-31 12:03:26 +02:00
14 changed files with 250 additions and 21 deletions

View File

@@ -141,6 +141,12 @@ class GeometryComponent {
/* The returned component should be of the same type as the type this is called on. */
virtual GeometryComponent *copy() const = 0;
/* Direct data is everything except for instances of objects/collections.
* If this returns true, the geometry set can be cached and is still valid after e.g. modifier
* evaluation ends. Instances can only be valid as long as the data they instance is valid. */
virtual bool owns_direct_data() const = 0;
virtual void ensure_owns_direct_data() = 0;
void user_add() const;
void user_remove() const;
bool is_mutable() const;
@@ -315,6 +321,8 @@ struct GeometrySet {
void clear();
void ensure_owns_direct_data();
/* Utility methods for creation. */
static GeometrySet create_with_mesh(
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
@@ -374,6 +382,9 @@ class MeshComponent : public GeometryComponent {
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_MESH;
private:
@@ -404,6 +415,9 @@ class PointCloudComponent : public GeometryComponent {
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_POINT_CLOUD;
private:
@@ -444,6 +458,9 @@ class InstancesComponent : public GeometryComponent {
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES;
};
@@ -466,5 +483,8 @@ class VolumeComponent : public GeometryComponent {
const Volume *get_for_read() const;
Volume *get_for_write();
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_VOLUME;
};

View File

@@ -34,6 +34,7 @@ struct Base;
struct BoundBox;
struct Curve;
struct Depsgraph;
struct GeometrySet;
struct GpencilModifierData;
struct HookGpencilModifierData;
struct HookModifierData;
@@ -69,6 +70,8 @@ void BKE_object_free_curve_cache(struct Object *ob);
void BKE_object_free_derived_caches(struct Object *ob);
void BKE_object_free_caches(struct Object *object);
void BKE_object_preview_geometry_set(struct Object *ob, struct GeometrySet *geometry_set);
void BKE_object_modifier_hook_reset(struct Object *ob, struct HookModifierData *hmd);
void BKE_object_modifier_gpencil_hook_reset(struct Object *ob,
struct HookGpencilModifierData *hmd);

View File

@@ -108,6 +108,16 @@ bool InstancesComponent::is_empty() const
return transforms_.size() == 0;
}
bool InstancesComponent::owns_direct_data() const
{
return true;
}
void InstancesComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
}
static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
{
using namespace blender;

View File

@@ -157,6 +157,20 @@ bool MeshComponent::is_empty() const
return mesh_ == nullptr;
}
bool MeshComponent::owns_direct_data() const
{
return ownership_ == GeometryOwnershipType::Owned;
}
void MeshComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
ownership_ = GeometryOwnershipType::Owned;
}
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -107,6 +107,20 @@ bool PointCloudComponent::is_empty() const
return pointcloud_ == nullptr;
}
bool PointCloudComponent::owns_direct_data() const
{
return ownership_ == GeometryOwnershipType::Owned;
}
void PointCloudComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
ownership_ = GeometryOwnershipType::Owned;
}
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -97,4 +97,18 @@ Volume *VolumeComponent::get_for_write()
return volume_;
}
bool VolumeComponent::owns_direct_data() const
{
return ownership_ == GeometryOwnershipType::Owned;
}
void VolumeComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
volume_ = BKE_volume_copy_for_eval(volume_, false);
ownership_ = GeometryOwnershipType::Owned;
}
}
/** \} */

View File

@@ -211,6 +211,19 @@ void GeometrySet::clear()
components_.clear();
}
/* Make sure that the geometry can be cached. This does not ensure ownership of object/collection
* instances. */
void GeometrySet::ensure_owns_direct_data()
{
for (GeometryComponentType type : components_.keys()) {
const GeometryComponent *component = this->get_component_for_read(type);
if (!component->owns_direct_data()) {
GeometryComponent &component_for_write = this->get_component_for_write(type);
component_for_write.ensure_owns_direct_data();
}
}
}
/* Returns a read-only mesh or null. */
const Mesh *GeometrySet::get_mesh_for_read() const
{

View File

@@ -1756,6 +1756,10 @@ void BKE_object_free_derived_caches(Object *ob)
BKE_geometry_set_free(ob->runtime.geometry_set_eval);
ob->runtime.geometry_set_eval = NULL;
}
if (ob->runtime.geometry_set_preview != NULL) {
BKE_geometry_set_free(ob->runtime.geometry_set_preview);
ob->runtime.geometry_set_preview = NULL;
}
}
void BKE_object_free_caches(Object *object)
@@ -1806,6 +1810,18 @@ void BKE_object_free_caches(Object *object)
}
}
/* Can be called from multiple threads. */
void BKE_object_preview_geometry_set(Object *ob, struct GeometrySet *geometry_set)
{
static ThreadMutex mutex = BLI_MUTEX_INITIALIZER;
BLI_mutex_lock(&mutex);
if (ob->runtime.geometry_set_preview != NULL) {
BKE_geometry_set_free(ob->runtime.geometry_set_preview);
}
ob->runtime.geometry_set_preview = geometry_set;
BLI_mutex_unlock(&mutex);
}
/**
* Actual check for internal data, not context or flags.
*/

View File

@@ -41,6 +41,7 @@
#include "BKE_node.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_workspace.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
@@ -670,6 +671,21 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
nodeSetActive(ntree, node);
/* Tag for update, so that dependend objects are reevaluated. This is necessary when a
* spreadsheet editor displays data from a node. */
LISTBASE_FOREACH (wmWindow *, window, &((wmWindowManager *)bmain->wm.first)->windows) {
bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype == SPACE_SPREADSHEET) {
SpaceSpreadsheet *sspreadsheet = area->spacedata.first;
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_NODE) {
DEG_id_tag_update(&ntree->id, ID_RECALC_COPY_ON_WRITE);
ED_area_tag_redraw(area);
}
}
}
}
if (node->type != NODE_GROUP) {
const bool was_output = (node->flag & NODE_DO_OUTPUT) != 0;
bool do_update = false;

View File

@@ -200,25 +200,7 @@ static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
const GeometryComponentType used_component_type)
{
GeometrySet geometry_set;
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_FINAL) {
if (used_component_type == GEO_COMPONENT_TYPE_MESH && object_eval->mode == OB_MODE_EDIT) {
Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false);
if (mesh == nullptr) {
return geometry_set;
}
BKE_mesh_wrapper_ensure_mdata(mesh);
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
mesh_component.copy_vertex_group_names_from_object(*object_eval);
}
else {
if (object_eval->runtime.geometry_set_eval != nullptr) {
/* This does not copy the geometry data itself. */
geometry_set = *object_eval->runtime.geometry_set_eval;
}
}
}
else {
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
Object *object_orig = DEG_get_original_object(object_eval);
if (object_orig->type == OB_MESH) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
@@ -247,6 +229,30 @@ static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly);
}
}
else {
if (used_component_type == GEO_COMPONENT_TYPE_MESH && object_eval->mode == OB_MODE_EDIT) {
Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false);
if (mesh == nullptr) {
return geometry_set;
}
BKE_mesh_wrapper_ensure_mdata(mesh);
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
mesh_component.copy_vertex_group_names_from_object(*object_eval);
}
else {
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_NODE) {
if (object_eval->runtime.geometry_set_preview != nullptr) {
geometry_set = *object_eval->runtime.geometry_set_preview;
}
}
else if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_FINAL) {
if (object_eval->runtime.geometry_set_eval != nullptr) {
geometry_set = *object_eval->runtime.geometry_set_eval;
}
}
}
}
return geometry_set;
}
@@ -377,7 +383,7 @@ static Span<int64_t> filter_mesh_elements_by_selection(const bContext *C,
static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_FINAL) {
if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
return (GeometryComponentType)sspreadsheet->geometry_component_type;
}
if (object_eval->type == OB_POINTCLOUD) {

View File

@@ -168,6 +168,11 @@ typedef struct Object_Runtime {
*/
struct GeometrySet *geometry_set_eval;
/**
* Data from this geometry set is previewed in the spreadsheet editor.
*/
struct GeometrySet *geometry_set_preview;
/**
* Mesh structure created during object evaluation.
* It has deformation only modifiers applied on it.

View File

@@ -1886,6 +1886,7 @@ typedef enum eSpaceSpreadsheet_FilterFlag {
typedef enum eSpaceSpreadsheet_ObjectEvalState {
SPREADSHEET_OBJECT_EVAL_STATE_FINAL = 0,
SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL = 1,
SPREADSHEET_OBJECT_EVAL_STATE_NODE = 2,
} eSpaceSpreadsheet_Context;
/* -------------------------------------------------------------------- */

View File

@@ -7344,6 +7344,11 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
ICON_NONE,
"Original",
"Use data from original object without any modifiers applied"},
{SPREADSHEET_OBJECT_EVAL_STATE_NODE,
"NODE",
ICON_NONE,
"Node",
"Use data from the first geometry output of the active node"},
{0, NULL, 0, NULL, NULL},
};

View File

@@ -43,17 +43,22 @@
#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h"
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_node_ui_storage.hh"
#include "BKE_object.h"
#include "BKE_pointcloud.h"
#include "BKE_screen.h"
#include "BKE_simulation.h"
#include "BKE_workspace.h"
#include "BLO_read_write.h"
@@ -1088,6 +1093,71 @@ static void reset_tree_ui_storage(Span<const blender::nodes::NodeTreeRef *> tree
}
}
static DNode find_matching_active_derived_node(const SpaceNode &snode, const DerivedNodeTree &tree)
{
const DTreeContext &root_context = tree.root_context();
bNodeTree *root_tree_eval = root_context.tree().btree();
bNodeTree *root_tree_orig = (bNodeTree *)DEG_get_original_id(&root_tree_eval->id);
if (snode.nodetree != root_tree_orig) {
return {};
}
const DTreeContext *current_context = &root_context;
bool is_first = true;
LISTBASE_FOREACH (const bNodeTreePath *, path, &snode.treepath) {
if (is_first) {
is_first = false;
continue;
}
StringRef parent_node_name = path->node_name;
const NodeTreeRef &tree_ref = current_context->tree();
const NodeRef *parent_node_ref = nullptr;
for (const NodeRef *node_ref : tree_ref.nodes()) {
if (node_ref->name() == parent_node_name) {
parent_node_ref = node_ref;
break;
}
}
if (parent_node_ref == nullptr) {
return {};
}
current_context = current_context->child_context(*parent_node_ref);
if (current_context == nullptr) {
return {};
}
}
const NodeTreeRef &tree_ref = current_context->tree();
for (const NodeRef *node_ref : tree_ref.nodes()) {
if (node_ref->bnode()->flag & NODE_ACTIVE) {
return {current_context, node_ref};
}
}
return {};
}
static DNode find_matching_active_derived_node(Depsgraph *depsgraph, const DerivedNodeTree &tree)
{
Main *bmain = DEG_get_bmain(depsgraph);
wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
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_NODE) {
continue;
}
SpaceNode *snode = (SpaceNode *)sl;
DNode active_node = find_matching_active_derived_node(*snode, tree);
if (!active_node) {
continue;
}
return active_node;
}
}
return {};
}
/**
* Evaluate a node group to compute the output geometry.
* Currently, this uses a fairly basic and inefficient algorithm that might compute things more
@@ -1143,6 +1213,28 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
Vector<DInputSocket> group_outputs;
group_outputs.append({root_context, &socket_to_compute});
const DNode active_node = find_matching_active_derived_node(ctx->depsgraph, tree);
auto log_socket_value = [&](const DSocket socket, const Span<GPointer> values) {
const DNode node = socket.node();
if (node != active_node) {
return;
}
if (socket->is_input() && !node->outputs().is_empty()) {
return;
}
if (values.size() != 1) {
return;
}
const GPointer value = values[0];
if (*value.type() != CPPType::get<GeometrySet>()) {
return;
}
GeometrySet geometry_set = *(const GeometrySet *)value.get();
geometry_set.ensure_owns_direct_data();
BKE_object_preview_geometry_set(ctx->object, new GeometrySet(std::move(geometry_set)));
};
GeometryNodesEvaluator evaluator{group_inputs,
group_outputs,
mf_by_node,
@@ -1150,7 +1242,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
ctx->object,
(ModifierData *)nmd,
ctx->depsgraph,
{}};
log_socket_value};
Vector<GMutablePointer> results = evaluator.execute();
BLI_assert(results.size() == 1);