Geometry Nodes: improve Viewer Attribute Text Readability
- Corner Domain Text Overlap: Text is now offset along the angle bisector of each corner. The offset distance is dynamically adjusted based on the view zoom and corner angle sharpness. - Wrap Long Line: Split Vectors, Colors, and Quaternions into labeled line for each component. Pull Request: https://projects.blender.org/blender/blender/pulls/145290
This commit is contained in:
@@ -19,9 +19,13 @@
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_instances.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "ED_view3d.hh"
|
||||
|
||||
#include "UI_interface_c.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "draw_manager_text.hh"
|
||||
@@ -72,7 +76,7 @@ class AttributeTexts : Overlay {
|
||||
switch (object.type) {
|
||||
case OB_MESH: {
|
||||
const Mesh &mesh = DRW_object_get_data_for_drawing<Mesh>(object);
|
||||
add_attributes_to_text_cache(dt, mesh.attributes(), object_to_world);
|
||||
add_mesh_attributes_to_text_cache(state, mesh, object_to_world);
|
||||
break;
|
||||
}
|
||||
case OB_POINTCLOUD: {
|
||||
@@ -113,6 +117,63 @@ class AttributeTexts : Overlay {
|
||||
add_values_to_text_cache(dt, attribute.varray, positions, object_to_world);
|
||||
}
|
||||
|
||||
void add_mesh_attributes_to_text_cache(const State &state,
|
||||
const Mesh &mesh,
|
||||
const float4x4 &object_to_world)
|
||||
{
|
||||
const bke::AttributeAccessor attributes = mesh.attributes();
|
||||
if (!attributes.contains(".viewer")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bke::GAttributeReader attribute = attributes.lookup(".viewer");
|
||||
const bke::AttrDomain domain = attribute.domain;
|
||||
const VArraySpan<float3> positions = *attributes.lookup<float3>("position", domain);
|
||||
|
||||
if (domain == bke::AttrDomain::Corner) {
|
||||
const CPPType &type = attribute.varray.type();
|
||||
float offset_by_type = 1.0f;
|
||||
if (type.is<int2>() || type.is<float2>() || type.is<float3>() ||
|
||||
type.is<ColorGeometry4b>() || type.is<ColorGeometry4f>() || type.is<math::Quaternion>())
|
||||
{
|
||||
offset_by_type = 1.5f;
|
||||
}
|
||||
else if (type.is<float4x4>()) {
|
||||
offset_by_type = 3.0f;
|
||||
}
|
||||
|
||||
Array<float3> corner_positions(positions.size());
|
||||
const Span<float3> positions = mesh.vert_positions();
|
||||
const OffsetIndices<int> faces = mesh.faces();
|
||||
const Span<int> corner_verts = mesh.corner_verts();
|
||||
const Span<float3> face_normals = mesh.face_normals();
|
||||
|
||||
threading::parallel_for(faces.index_range(), 512, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const float3 &face_normal = face_normals[face_index];
|
||||
const IndexRange face = faces[face_index];
|
||||
for (const int corner : face) {
|
||||
const int corner_prev = bke::mesh::face_corner_prev(face, corner);
|
||||
const int corner_next = bke::mesh::face_corner_next(face, corner);
|
||||
corner_positions[corner] = calc_corner_text_position(
|
||||
positions[corner_verts[corner]],
|
||||
positions[corner_verts[corner_prev]],
|
||||
positions[corner_verts[corner_next]],
|
||||
face_normal,
|
||||
state.rv3d,
|
||||
object_to_world,
|
||||
offset_by_type);
|
||||
}
|
||||
}
|
||||
});
|
||||
add_values_to_text_cache(
|
||||
state.dt, attribute.varray, corner_positions.as_span(), object_to_world);
|
||||
}
|
||||
else {
|
||||
add_values_to_text_cache(state.dt, attribute.varray, positions, object_to_world);
|
||||
}
|
||||
}
|
||||
|
||||
void add_instance_attributes_to_text_cache(DRWTextStore *dt,
|
||||
bke::AttributeAccessor attribute_accessor,
|
||||
const float4x4 &object_to_world,
|
||||
@@ -148,6 +209,9 @@ class AttributeTexts : Overlay {
|
||||
const Span<StringRef> lines,
|
||||
const uchar4 &color)
|
||||
{
|
||||
const float text_size = UI_style_get()->widget.points;
|
||||
const float line_height = text_size * 1.1f * UI_SCALE_FAC;
|
||||
const float center_offset = (lines.size() - 1) / 2.0f;
|
||||
for (const int i : lines.index_range()) {
|
||||
const StringRef line = lines[i];
|
||||
DRW_text_cache_add(dt,
|
||||
@@ -155,7 +219,7 @@ class AttributeTexts : Overlay {
|
||||
line.data(),
|
||||
line.size(),
|
||||
0,
|
||||
-i * 12.0f * UI_SCALE_FAC,
|
||||
(center_offset - i) * line_height,
|
||||
DRW_TEXT_CACHE_GLOBALSPACE,
|
||||
color,
|
||||
true,
|
||||
@@ -194,9 +258,11 @@ class AttributeTexts : Overlay {
|
||||
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, int2>) {
|
||||
char numstr[64];
|
||||
const size_t numstr_len = SNPRINTF_UTF8_RLEN(numstr, "(%d, %d)", value.x, value.y);
|
||||
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
|
||||
char x_str[64], y_str[64];
|
||||
const size_t x_str_len = SNPRINTF_UTF8_RLEN(x_str, "X: %d", value.x);
|
||||
const size_t y_str_len = SNPRINTF_UTF8_RLEN(y_str, "Y: %d", value.y);
|
||||
add_lines_to_cache(
|
||||
dt, position, {StringRef(x_str, x_str_len), StringRef(y_str, y_str_len)}, col);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, float>) {
|
||||
char numstr[64];
|
||||
@@ -204,34 +270,66 @@ class AttributeTexts : Overlay {
|
||||
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, float2>) {
|
||||
char numstr[64];
|
||||
const size_t numstr_len = SNPRINTF_UTF8_RLEN(numstr, "(%g, %g)", value.x, value.y);
|
||||
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
|
||||
char x_str[64], y_str[64];
|
||||
const size_t x_str_len = SNPRINTF_UTF8_RLEN(x_str, "X: %g", value.x);
|
||||
const size_t y_str_len = SNPRINTF_UTF8_RLEN(y_str, "Y: %g", value.y);
|
||||
add_lines_to_cache(
|
||||
dt, position, {StringRef(x_str, x_str_len), StringRef(y_str, y_str_len)}, col);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, float3>) {
|
||||
char numstr[64];
|
||||
const size_t numstr_len = SNPRINTF_UTF8_RLEN(
|
||||
numstr, "(%g, %g, %g)", value.x, value.y, value.z);
|
||||
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
|
||||
char x_str[64], y_str[64], z_str[64];
|
||||
const size_t x_str_len = SNPRINTF_UTF8_RLEN(x_str, "X: %g", value.x);
|
||||
const size_t y_str_len = SNPRINTF_UTF8_RLEN(y_str, "Y: %g", value.y);
|
||||
const size_t z_str_len = SNPRINTF_UTF8_RLEN(z_str, "Z: %g", value.z);
|
||||
add_lines_to_cache(dt,
|
||||
position,
|
||||
{StringRef(x_str, x_str_len),
|
||||
StringRef(y_str, y_str_len),
|
||||
StringRef(z_str, z_str_len)},
|
||||
col);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, ColorGeometry4b>) {
|
||||
const ColorGeometry4f color = color::decode(value);
|
||||
char numstr[64];
|
||||
const size_t numstr_len = SNPRINTF_UTF8_RLEN(
|
||||
numstr, "(%.3f, %.3f, %.3f, %.3f)", color.r, color.g, color.b, color.a);
|
||||
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
|
||||
char r_str[64], g_str[64], b_str[64], a_str[64];
|
||||
const size_t r_str_len = SNPRINTF_UTF8_RLEN(r_str, "R: %.3f", color.r);
|
||||
const size_t g_str_len = SNPRINTF_UTF8_RLEN(g_str, "G: %.3f", color.g);
|
||||
const size_t b_str_len = SNPRINTF_UTF8_RLEN(b_str, "B: %.3f", color.b);
|
||||
const size_t a_str_len = SNPRINTF_UTF8_RLEN(a_str, "A: %.3f", color.a);
|
||||
add_lines_to_cache(dt,
|
||||
position,
|
||||
{StringRef(r_str, r_str_len),
|
||||
StringRef(g_str, g_str_len),
|
||||
StringRef(b_str, b_str_len),
|
||||
StringRef(a_str, a_str_len)},
|
||||
col);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, ColorGeometry4f>) {
|
||||
char numstr[64];
|
||||
const size_t numstr_len = SNPRINTF_UTF8_RLEN(
|
||||
numstr, "(%.3f, %.3f, %.3f, %.3f)", value.r, value.g, value.b, value.a);
|
||||
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
|
||||
char r_str[64], g_str[64], b_str[64], a_str[64];
|
||||
const size_t r_str_len = SNPRINTF_UTF8_RLEN(r_str, "R: %.3f", value.r);
|
||||
const size_t g_str_len = SNPRINTF_UTF8_RLEN(g_str, "G: %.3f", value.g);
|
||||
const size_t b_str_len = SNPRINTF_UTF8_RLEN(b_str, "B: %.3f", value.b);
|
||||
const size_t a_str_len = SNPRINTF_UTF8_RLEN(a_str, "A: %.3f", value.a);
|
||||
add_lines_to_cache(dt,
|
||||
position,
|
||||
{StringRef(r_str, r_str_len),
|
||||
StringRef(g_str, g_str_len),
|
||||
StringRef(b_str, b_str_len),
|
||||
StringRef(a_str, a_str_len)},
|
||||
col);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, math::Quaternion>) {
|
||||
char numstr[64];
|
||||
const size_t numstr_len = SNPRINTF_UTF8_RLEN(
|
||||
numstr, "(%.3f, %.3f, %.3f, %.3f)", value.w, value.x, value.y, value.z);
|
||||
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
|
||||
char w_str[64], x_str[64], y_str[64], z_str[64];
|
||||
const size_t w_str_len = SNPRINTF_UTF8_RLEN(w_str, "W: %.3f", value.w);
|
||||
const size_t x_str_len = SNPRINTF_UTF8_RLEN(x_str, "X: %.3f", value.x);
|
||||
const size_t y_str_len = SNPRINTF_UTF8_RLEN(y_str, "Y: %.3f", value.y);
|
||||
const size_t z_str_len = SNPRINTF_UTF8_RLEN(z_str, "Z: %.3f", value.z);
|
||||
add_lines_to_cache(dt,
|
||||
position,
|
||||
{StringRef(w_str, w_str_len),
|
||||
StringRef(x_str, x_str_len),
|
||||
StringRef(y_str, y_str_len),
|
||||
StringRef(z_str, z_str_len)},
|
||||
col);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, float4x4>) {
|
||||
float3 location;
|
||||
@@ -264,6 +362,42 @@ class AttributeTexts : Overlay {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static float3 calc_corner_text_position(const float3 &corner_pos,
|
||||
const float3 &prev_corner_pos,
|
||||
const float3 &next_corner_pos,
|
||||
const float3 &face_normal,
|
||||
const RegionView3D *rv3d,
|
||||
const float4x4 &object_to_world,
|
||||
const float offset_scale = 1.0f)
|
||||
{
|
||||
const float3 prev_edge_vec = prev_corner_pos - corner_pos;
|
||||
const float3 next_edge_vec = next_corner_pos - corner_pos;
|
||||
const float3 prev_edge_dir = math::normalize(prev_edge_vec);
|
||||
const float3 next_edge_dir = math::normalize(next_edge_vec);
|
||||
|
||||
const float pre_edge_len = math::length(prev_edge_vec);
|
||||
const float next_edge_len = math::length(next_edge_vec);
|
||||
const float max_offset = math::min(pre_edge_len, next_edge_len) / 2;
|
||||
|
||||
const float3 corner_normal = math::cross(next_edge_dir, prev_edge_dir);
|
||||
const float concavity_check = math::dot(corner_normal, face_normal);
|
||||
const float direction_correct = concavity_check > 0.0f ? 1.0f : -1.0f;
|
||||
const float3 bisector_dir = (prev_edge_dir + next_edge_dir) / 2 * direction_correct;
|
||||
|
||||
const float sharp_factor = std::clamp(math::dot(prev_edge_dir, next_edge_dir), 0.0f, 1.0f);
|
||||
const float sharp_multiplier = math::pow(sharp_factor, 4.0f) * 2 + 1;
|
||||
|
||||
const float3 pos_o_world = math::transform_point(object_to_world, corner_pos);
|
||||
const float pixel_size = ED_view3d_pixel_size(rv3d, pos_o_world);
|
||||
const float pixel_offset = UI_style_get()->widget.points * 7.0f * UI_SCALE_FAC;
|
||||
const float screen_space_offset = pixel_size * pixel_offset;
|
||||
|
||||
const float offset_distance = std::clamp(
|
||||
screen_space_offset * sharp_multiplier * offset_scale, 0.0f, max_offset);
|
||||
|
||||
return corner_pos + bisector_dir * offset_distance;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::draw::overlay
|
||||
|
||||
Reference in New Issue
Block a user