Spreadsheet: expore more domains and point cloud data
Ref T86135. Differential Revision: https://developer.blender.org/D10681
This commit is contained in:
parent
74f3edc343
commit
5f1f233dc9
Notes:
blender-bot
2023-02-13 19:25:13 +01:00
Referenced by issue #86500, Cycles: tapering hair transparency fails in '3D Curves' mode [regression] (probably caused by embree) Referenced by issue #86135, Expose other domains for attribute spreadsheet as menu entry
|
@ -31,6 +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, "attribute_domain", text="")
|
||||
|
||||
if used_id:
|
||||
layout.label(text=used_id.name, icon="OBJECT_DATA")
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ static std::unique_ptr<SpreadsheetDrawer> generate_spreadsheet_drawer(const bCon
|
|||
return {};
|
||||
}
|
||||
Object *object_orig = (Object *)used_id;
|
||||
if (object_orig->type != OB_MESH) {
|
||||
if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD)) {
|
||||
return {};
|
||||
}
|
||||
Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig);
|
||||
|
|
|
@ -332,10 +332,11 @@ static void add_columns_for_attribute(const ReadAttribute *attribute,
|
|||
}
|
||||
}
|
||||
|
||||
static GeometrySet get_display_geometry_set(Object *object_eval)
|
||||
static GeometrySet get_display_geometry_set(Object *object_eval,
|
||||
const GeometryComponentType used_component_type)
|
||||
{
|
||||
GeometrySet geometry_set;
|
||||
if (object_eval->mode == OB_MODE_EDIT) {
|
||||
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;
|
||||
|
@ -354,10 +355,87 @@ static GeometrySet get_display_geometry_set(Object *object_eval)
|
|||
return geometry_set;
|
||||
}
|
||||
|
||||
static Span<int64_t> filter_visible_mesh_vertex_rows(const bContext *C,
|
||||
Object *object_eval,
|
||||
const MeshComponent *component,
|
||||
ResourceCollector &resources)
|
||||
using IsVertexSelectedFn = FunctionRef<bool(int vertex_index)>;
|
||||
|
||||
static void get_selected_vertex_indices(const Mesh &mesh,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
Vector<int64_t> &r_vertex_indices)
|
||||
{
|
||||
for (const int i : IndexRange(mesh.totvert)) {
|
||||
if (is_vertex_selected_fn(i)) {
|
||||
r_vertex_indices.append(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_selected_corner_indices(const Mesh &mesh,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
Vector<int64_t> &r_corner_indices)
|
||||
{
|
||||
for (const int i : IndexRange(mesh.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[i];
|
||||
if (is_vertex_selected_fn(loop.v)) {
|
||||
r_corner_indices.append(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_selected_polygon_indices(const Mesh &mesh,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
Vector<int64_t> &r_polygon_indices)
|
||||
{
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
bool is_selected = true;
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
if (!is_vertex_selected_fn(loop.v)) {
|
||||
is_selected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_selected) {
|
||||
r_polygon_indices.append(poly_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_selected_edge_indices(const Mesh &mesh,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
Vector<int64_t> &r_edge_indices)
|
||||
{
|
||||
for (const int i : IndexRange(mesh.totedge)) {
|
||||
const MEdge &edge = mesh.medge[i];
|
||||
if (is_vertex_selected_fn(edge.v1) && is_vertex_selected_fn(edge.v2)) {
|
||||
r_edge_indices.append(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_selected_indices_on_domain(const Mesh &mesh,
|
||||
const AttributeDomain domain,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
Vector<int64_t> &r_indices)
|
||||
{
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return get_selected_vertex_indices(mesh, is_vertex_selected_fn, r_indices);
|
||||
case ATTR_DOMAIN_POLYGON:
|
||||
return get_selected_polygon_indices(mesh, is_vertex_selected_fn, r_indices);
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return get_selected_corner_indices(mesh, is_vertex_selected_fn, r_indices);
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return get_selected_edge_indices(mesh, is_vertex_selected_fn, r_indices);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static Span<int64_t> filter_mesh_elements_by_selection(const bContext *C,
|
||||
Object *object_eval,
|
||||
const MeshComponent *component,
|
||||
const AttributeDomain domain,
|
||||
ResourceCollector &resources)
|
||||
{
|
||||
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
|
||||
const bool show_only_selected = sspreadsheet->filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY;
|
||||
|
@ -372,47 +450,55 @@ static Span<int64_t> filter_visible_mesh_vertex_rows(const bContext *C,
|
|||
int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
|
||||
if (orig_indices != nullptr) {
|
||||
/* Use CD_ORIGINDEX layer if it exists. */
|
||||
for (const int i_eval : IndexRange(mesh_eval->totvert)) {
|
||||
const int i_orig = orig_indices[i_eval];
|
||||
if (i_orig >= 0 && i_orig < bm->totvert) {
|
||||
BMVert *vert = bm->vtable[i_orig];
|
||||
if (BM_elem_flag_test(vert, BM_ELEM_SELECT)) {
|
||||
visible_rows.append(i_eval);
|
||||
}
|
||||
auto is_vertex_selected = [&](int vertex_index) -> bool {
|
||||
const int i_orig = orig_indices[vertex_index];
|
||||
if (i_orig < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (i_orig >= bm->totvert) {
|
||||
return false;
|
||||
}
|
||||
BMVert *vert = bm->vtable[i_orig];
|
||||
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
|
||||
};
|
||||
get_selected_indices_on_domain(*mesh_eval, domain, is_vertex_selected, visible_rows);
|
||||
}
|
||||
else if (mesh_eval->totvert == bm->totvert) {
|
||||
/* Use a simple heuristic to match original vertices to evaluated ones. */
|
||||
for (const int i : IndexRange(mesh_eval->totvert)) {
|
||||
BMVert *vert = bm->vtable[i];
|
||||
if (BM_elem_flag_test(vert, BM_ELEM_SELECT)) {
|
||||
visible_rows.append(i);
|
||||
}
|
||||
}
|
||||
auto is_vertex_selected = [&](int vertex_index) -> bool {
|
||||
BMVert *vert = bm->vtable[vertex_index];
|
||||
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
|
||||
};
|
||||
get_selected_indices_on_domain(*mesh_eval, domain, is_vertex_selected, visible_rows);
|
||||
}
|
||||
/* This is safe, because the vector lives in the resource collector. */
|
||||
return visible_rows.as_span();
|
||||
}
|
||||
/* No filter is used. */
|
||||
const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
const int domain_size = component->attribute_domain_size(domain);
|
||||
return IndexRange(domain_size).as_span();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* 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),
|
||||
"geometry set");
|
||||
GeometrySet &geometry_set = resources->add_value(
|
||||
get_display_geometry_set(object_eval, component_type), "geometry set");
|
||||
|
||||
const AttributeDomain domain = ATTR_DOMAIN_POINT;
|
||||
const GeometryComponentType component_type = GEO_COMPONENT_TYPE_MESH;
|
||||
const GeometryComponent *component = geometry_set.get_component_for_read(component_type);
|
||||
if (component == nullptr) {
|
||||
return {};
|
||||
}
|
||||
if (!component->attribute_domain_supported(domain)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Vector<std::string> attribute_names = get_sorted_attribute_names_to_display(*component, domain);
|
||||
|
||||
|
@ -425,9 +511,14 @@ std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_geometry_attributes(c
|
|||
}
|
||||
|
||||
/* The filter below only works for mesh vertices currently. */
|
||||
BLI_assert(domain == ATTR_DOMAIN_POINT && component_type == GEO_COMPONENT_TYPE_MESH);
|
||||
Span<int64_t> visible_rows = filter_visible_mesh_vertex_rows(
|
||||
C, object_eval, static_cast<const MeshComponent *>(component), *resources);
|
||||
Span<int64_t> visible_rows;
|
||||
if (component_type == GEO_COMPONENT_TYPE_MESH) {
|
||||
visible_rows = filter_mesh_elements_by_selection(
|
||||
C, object_eval, static_cast<const MeshComponent *>(component), domain, *resources);
|
||||
}
|
||||
else {
|
||||
visible_rows = IndexRange(component->attribute_domain_size(domain)).as_span();
|
||||
}
|
||||
|
||||
const int domain_size = component->attribute_domain_size(domain);
|
||||
return std::make_unique<GeometryAttributeSpreadsheetDrawer>(
|
||||
|
|
|
@ -1858,7 +1858,12 @@ typedef struct SpaceSpreadsheet {
|
|||
/* eSpaceSpreadsheet_FilterFlag. */
|
||||
uint8_t filter_flag;
|
||||
|
||||
char _pad1[7];
|
||||
/* #GeometryComponentType. */
|
||||
uint8_t geometry_component_type;
|
||||
/* #AttributeDomain. */
|
||||
uint8_t attribute_domain;
|
||||
|
||||
char _pad1[5];
|
||||
} SpaceSpreadsheet;
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_geometry_set.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_movieclip.h"
|
||||
|
@ -2991,6 +2993,48 @@ static void rna_SpaceSpreadsheet_pinned_id_set(PointerRNA *ptr,
|
|||
sspreadsheet->pinned_id = value.data;
|
||||
}
|
||||
|
||||
static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bmain),
|
||||
Scene *UNUSED(scene),
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
|
||||
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
|
||||
sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
|
||||
}
|
||||
}
|
||||
|
||||
const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UNUSED(C),
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *UNUSED(prop),
|
||||
bool *r_free)
|
||||
{
|
||||
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
|
||||
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 (!ELEM(item->value,
|
||||
ATTR_DOMAIN_CORNER,
|
||||
ATTR_DOMAIN_EDGE,
|
||||
ATTR_DOMAIN_POINT,
|
||||
ATTR_DOMAIN_POLYGON)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
|
||||
if (item->value != ATTR_DOMAIN_POINT) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
RNA_enum_item_add(&item_array, &items_len, item);
|
||||
}
|
||||
RNA_enum_item_end(&item_array, &items_len);
|
||||
|
||||
*r_free = true;
|
||||
return item_array;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static const EnumPropertyItem dt_uv_items[] = {
|
||||
|
@ -7196,6 +7240,20 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
|
|||
PropertyRNA *prop;
|
||||
StructRNA *srna;
|
||||
|
||||
static const EnumPropertyItem geometry_component_type_items[] = {
|
||||
{GEO_COMPONENT_TYPE_MESH,
|
||||
"MESH",
|
||||
ICON_MESH_DATA,
|
||||
"Mesh",
|
||||
"Mesh component containing point, corner, edge and polygon data"},
|
||||
{GEO_COMPONENT_TYPE_POINT_CLOUD,
|
||||
"POINTCLOUD",
|
||||
ICON_POINTCLOUD_DATA,
|
||||
"Point Cloud",
|
||||
"Point cloud component containing only point data"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
srna = RNA_def_struct(brna, "SpaceSpreadsheet", "Space");
|
||||
RNA_def_struct_ui_text(srna, "Space Spreadsheet", "Spreadsheet space data");
|
||||
|
||||
|
@ -7210,6 +7268,20 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(
|
||||
prop, "Show Only Selected", "Only include rows that correspond to selected elements");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "geometry_component_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, geometry_component_type_items);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Geometry Component", "Part of the geometry to display data from");
|
||||
RNA_def_property_update(prop,
|
||||
NC_SPACE | ND_SPACE_SPREADSHEET,
|
||||
"rna_SpaceSpreadsheet_geometry_component_type_update");
|
||||
|
||||
prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
|
||||
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);
|
||||
}
|
||||
|
||||
void RNA_def_space(BlenderRNA *brna)
|
||||
|
|
Loading…
Reference in New Issue