Spreadsheet: support showing data from original/unevaluated object

There are two caveats of the current implementation which still need
to be resolved in a separate step:
* In theory the data on the original object can be editable in the spreadsheet.
* If a complex object is in edit mode, and its original data is displayed,
  the drawing code can be slow, because the bmesh is converted to a mesh
  every time. The proper solution is to draw the data from the bmesh directly.
  This should become easier after an upcoming refactor.

Ref T86141.

Differential Revision: https://developer.blender.org/D10701
This commit is contained in:
Jacques Lucke 2021-03-15 10:16:11 +01:00
parent b617b44419
commit 4ed208bcd8
Notes: blender-bot 2023-02-13 19:24:11 +01:00
Referenced by issue #86141, Context data for attribute spreadsheet
4 changed files with 110 additions and 20 deletions

View File

@ -31,7 +31,9 @@ class SPREADSHEET_HT_header(bpy.types.Header):
pinned_id = space.pinned_id
used_id = pinned_id if pinned_id else context.active_object
layout.prop(space, "geometry_component_type", text="")
layout.prop(space, "object_eval_state", text="")
if space.object_eval_state != "ORIGINAL":
layout.prop(space, "geometry_component_type", text="")
layout.prop(space, "attribute_domain", text="")
if used_id:

View File

@ -24,6 +24,8 @@
#include "BKE_context.h"
#include "BKE_editmesh.h"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
@ -333,24 +335,56 @@ static void add_columns_for_attribute(const ReadAttribute *attribute,
}
}
static GeometrySet get_display_geometry_set(Object *object_eval,
static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
Object *object_eval,
const GeometryComponentType used_component_type)
{
GeometrySet geometry_set;
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;
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;
}
}
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;
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>();
if (object_orig->mode == OB_MODE_EDIT) {
Mesh *mesh = (Mesh *)object_orig->data;
BMEditMesh *em = mesh->edit_mesh;
if (em != nullptr) {
Mesh *new_mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
/* This is a potentially heavy operation to do on every redraw. The best solution here is
* to display the data directly from the bmesh without a conversion, which can be
* implemented a bit later. */
BM_mesh_bm_to_me_for_eval(em->bm, new_mesh, nullptr);
mesh_component.replace(new_mesh, GeometryOwnershipType::Owned);
}
}
else {
Mesh *mesh = (Mesh *)object_orig->data;
mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
}
mesh_component.copy_vertex_group_names_from_object(*object_orig);
}
else if (object_orig->type == OB_POINTCLOUD) {
PointCloud *pointcloud = (PointCloud *)object_orig->data;
PointCloudComponent &pointcloud_component =
geometry_set.get_component_for_write<PointCloudComponent>();
pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly);
}
}
return geometry_set;
@ -480,18 +514,29 @@ static Span<int64_t> filter_mesh_elements_by_selection(const bContext *C,
return IndexRange(domain_size).as_span();
}
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) {
return (GeometryComponentType)sspreadsheet->geometry_component_type;
}
if (object_eval->type == OB_POINTCLOUD) {
return GEO_COMPONENT_TYPE_POINT_CLOUD;
}
return GEO_COMPONENT_TYPE_MESH;
}
std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_geometry_attributes(const bContext *C,
Object *object_eval)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
const GeometryComponentType component_type = (GeometryComponentType)
sspreadsheet->geometry_component_type;
const GeometryComponentType component_type = get_display_component_type(C, object_eval);
/* Create a resource collector that owns stuff that needs to live until drawing is done. */
std::unique_ptr<ResourceCollector> resources = std::make_unique<ResourceCollector>();
GeometrySet &geometry_set = resources->add_value(
get_display_geometry_set(object_eval, component_type), "geometry set");
get_display_geometry_set(sspreadsheet, object_eval, component_type), "geometry set");
const GeometryComponent *component = geometry_set.get_component_for_read(component_type);
if (component == nullptr) {

View File

@ -1865,8 +1865,10 @@ typedef struct SpaceSpreadsheet {
uint8_t geometry_component_type;
/* #AttributeDomain. */
uint8_t attribute_domain;
/* eSpaceSpreadsheet_ObjectContext. */
uint8_t object_eval_state;
char _pad1[5];
char _pad1[4];
SpaceSpreadsheet_Runtime *runtime;
} SpaceSpreadsheet;
@ -1877,6 +1879,11 @@ typedef enum eSpaceSpreadsheet_FilterFlag {
SPREADSHEET_FILTER_SELECTED_ONLY = (1 << 0),
} eSpaceSpreadsheet_FilterFlag;
typedef enum eSpaceSpreadsheet_ObjectEvalState {
SPREADSHEET_OBJECT_EVAL_STATE_FINAL = 0,
SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL = 1,
} eSpaceSpreadsheet_Context;
/* -------------------------------------------------------------------- */
/** \name Space Defines (eSpace_Type)
* \{ */

View File

@ -26,6 +26,7 @@
#include "BLT_translation.h"
#include "BKE_attribute.h"
#include "BKE_context.h"
#include "BKE_geometry_set.h"
#include "BKE_image.h"
#include "BKE_key.h"
@ -3003,17 +3004,33 @@ static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bma
}
}
const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UNUSED(C),
const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *C,
PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
GeometryComponentType component_type = sspreadsheet->geometry_component_type;
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
Object *active_object = CTX_data_active_object(C);
Object *used_object = (sspreadsheet->pinned_id && GS(sspreadsheet->pinned_id->name) == ID_OB) ?
(Object *)sspreadsheet->pinned_id :
active_object;
if (used_object != NULL) {
if (used_object->type == OB_POINTCLOUD) {
component_type = GEO_COMPONENT_TYPE_POINT_CLOUD;
}
else {
component_type = GEO_COMPONENT_TYPE_MESH;
}
}
}
EnumPropertyItem *item_array = NULL;
int items_len = 0;
for (const EnumPropertyItem *item = rna_enum_attribute_domain_items; item->identifier != NULL;
item++) {
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_MESH) {
if (component_type == GEO_COMPONENT_TYPE_MESH) {
if (!ELEM(item->value,
ATTR_DOMAIN_CORNER,
ATTR_DOMAIN_EDGE,
@ -3022,7 +3039,7 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UN
continue;
}
}
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
if (component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
if (item->value != ATTR_DOMAIN_POINT) {
continue;
}
@ -7254,6 +7271,20 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem object_eval_state_items[] = {
{SPREADSHEET_OBJECT_EVAL_STATE_FINAL,
"FINAL",
ICON_NONE,
"Final",
"Use data from object with all modifiers applied"},
{SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL,
"ORIGINAL",
ICON_NONE,
"Original",
"Use data from original object without any modifiers applied"},
{0, NULL, 0, NULL, NULL},
};
srna = RNA_def_struct(brna, "SpaceSpreadsheet", "Space");
RNA_def_struct_ui_text(srna, "Space Spreadsheet", "Spreadsheet space data");
@ -7284,6 +7315,11 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_SpaceSpreadsheet_attribute_domain_itemf");
RNA_def_property_ui_text(prop, "Attribute Domain", "Attribute domain to display");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
prop = RNA_def_property(srna, "object_eval_state", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, object_eval_state_items);
RNA_def_property_ui_text(prop, "Object Evaluation State", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
}
void RNA_def_space(BlenderRNA *brna)