Files
test2/source/blender/draw/engines/overlay/overlay_attribute_text.hh
Clément Foucault 5bd572d4fd Cleanup: Overlay: Remove _next in overlay filename
These are not needed anymore since the legacy codebase
is gone.
2025-04-16 20:51:24 +02:00

272 lines
10 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*/
#pragma once
#include "BLI_math_quaternion_types.hh"
#include "BLI_string.h"
#include "DNA_curve_types.h"
#include "DNA_pointcloud_types.h"
#include "BKE_attribute.hh"
#include "BKE_curves.hh"
#include "BKE_duplilist.hh"
#include "BKE_geometry_set.hh"
#include "BKE_instances.hh"
#include "DRW_render.hh"
#include "UI_resources.hh"
#include "draw_manager_text.hh"
#include "overlay_base.hh"
namespace blender::draw::overlay {
/**
* Displays geometry node viewer output.
* Values are displayed as text on top of the active object.
*/
class AttributeTexts : Overlay {
public:
void begin_sync(Resources &res, const State &state) final
{
enabled_ = !res.is_selection() && state.show_attribute_viewer_text();
}
void object_sync(Manager & /*manager*/,
const ObjectRef &ob_ref,
Resources & /*res*/,
const State &state) final
{
if (!enabled_) {
return;
}
const Object &object = *ob_ref.object;
const DupliObject *dupli_object = ob_ref.dupli_object;
const bool is_preview = dupli_object != nullptr &&
dupli_object->preview_base_geometry != nullptr;
if (!is_preview) {
return;
}
DRWTextStore *dt = state.dt;
const float4x4 &object_to_world = object.object_to_world();
if (dupli_object->preview_instance_index >= 0) {
const bke::Instances *instances = dupli_object->preview_base_geometry->get_instances();
if (instances->attributes().contains(".viewer")) {
add_instance_attributes_to_text_cache(
dt, instances->attributes(), object_to_world, dupli_object->preview_instance_index);
return;
}
}
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);
break;
}
case OB_POINTCLOUD: {
const PointCloud &pointcloud = DRW_object_get_data_for_drawing<PointCloud>(object);
add_attributes_to_text_cache(dt, pointcloud.attributes(), object_to_world);
break;
}
case OB_CURVES_LEGACY: {
const Curve &curve = DRW_object_get_data_for_drawing<Curve>(object);
if (curve.curve_eval) {
const bke::CurvesGeometry &curves = curve.curve_eval->geometry.wrap();
add_attributes_to_text_cache(dt, curves.attributes(), object_to_world);
}
break;
}
case OB_CURVES: {
const Curves &curves_id = DRW_object_get_data_for_drawing<Curves>(object);
const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
add_attributes_to_text_cache(dt, curves.attributes(), object_to_world);
break;
}
}
}
private:
void add_attributes_to_text_cache(DRWTextStore *dt,
bke::AttributeAccessor attribute_accessor,
const float4x4 &object_to_world)
{
if (!attribute_accessor.contains(".viewer")) {
return;
}
const bke::GAttributeReader attribute = attribute_accessor.lookup(".viewer");
const VArraySpan<float3> positions = *attribute_accessor.lookup<float3>("position",
attribute.domain);
add_values_to_text_cache(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,
int instance_index)
{
/* Data from instances are read as a single value from a given index. The data is converted
* back to an array so one function can handle both instance and object data. */
const GVArray attribute = attribute_accessor.lookup(".viewer").varray.slice(
IndexRange(instance_index, 1));
add_values_to_text_cache(dt, attribute, {float3(0, 0, 0)}, object_to_world);
}
static void add_text_to_cache(DRWTextStore *dt,
const float3 &position,
const StringRef text,
const uchar4 &color)
{
DRW_text_cache_add(dt,
position,
text.data(),
text.size(),
0,
0,
DRW_TEXT_CACHE_GLOBALSPACE,
color,
true,
true);
}
static void add_lines_to_cache(DRWTextStore *dt,
const float3 &position,
const Span<StringRef> lines,
const uchar4 &color)
{
for (const int i : lines.index_range()) {
const StringRef line = lines[i];
DRW_text_cache_add(dt,
position,
line.data(),
line.size(),
0,
-i * 12.0f * UI_SCALE_FAC,
DRW_TEXT_CACHE_GLOBALSPACE,
color,
true,
true);
}
}
void add_values_to_text_cache(DRWTextStore *dt,
const GVArray &values,
const Span<float3> positions,
const float4x4 &object_to_world)
{
uchar col[4];
UI_GetThemeColor4ubv(TH_TEXT_HI, col);
bke::attribute_math::convert_to_static_type(values.type(), [&](auto dummy) {
using T = decltype(dummy);
const VArray<T> &values_typed = values.typed<T>();
for (const int i : values.index_range()) {
const float3 position = math::transform_point(object_to_world, positions[i]);
const T &value = values_typed[i];
if constexpr (std::is_same_v<T, bool>) {
char numstr[64];
const size_t numstr_len = SNPRINTF_RLEN(numstr, "%s", value ? "True" : "False");
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
}
else if constexpr (std::is_same_v<T, int8_t>) {
char numstr[64];
const size_t numstr_len = SNPRINTF_RLEN(numstr, "%d", int(value));
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
}
else if constexpr (std::is_same_v<T, int>) {
char numstr[64];
const size_t numstr_len = SNPRINTF_RLEN(numstr, "%d", value);
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_RLEN(numstr, "(%d, %d)", value.x, value.y);
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
}
else if constexpr (std::is_same_v<T, float>) {
char numstr[64];
const size_t numstr_len = SNPRINTF_RLEN(numstr, "%g", value);
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_RLEN(numstr, "(%g, %g)", value.x, value.y);
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
}
else if constexpr (std::is_same_v<T, float3>) {
char numstr[64];
const size_t numstr_len = SNPRINTF_RLEN(
numstr, "(%g, %g, %g)", value.x, value.y, value.z);
add_text_to_cache(dt, position, StringRef(numstr, numstr_len), col);
}
else if constexpr (std::is_same_v<T, ColorGeometry4b>) {
const ColorGeometry4f color = value.decode();
char numstr[64];
const size_t numstr_len = SNPRINTF_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);
}
else if constexpr (std::is_same_v<T, ColorGeometry4f>) {
char numstr[64];
const size_t numstr_len = SNPRINTF_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);
}
else if constexpr (std::is_same_v<T, math::Quaternion>) {
char numstr[64];
const size_t numstr_len = SNPRINTF_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);
}
else if constexpr (std::is_same_v<T, float4x4>) {
float3 location;
math::EulerXYZ rotation;
float3 scale;
math::to_loc_rot_scale_safe<true>(value, location, rotation, scale);
char location_str[64];
const size_t location_str_len = SNPRINTF_RLEN(
location_str, "Location: %.3f, %.3f, %.3f", location.x, location.y, location.z);
char rotation_str[64];
const size_t rotation_str_len = SNPRINTF_RLEN(rotation_str,
"Rotation: %.3f°, %.3f°, %.3f°",
rotation.x().degree(),
rotation.y().degree(),
rotation.z().degree());
char scale_str[64];
const size_t scale_str_len = SNPRINTF_RLEN(
scale_str, "Scale: %.3f, %.3f, %.3f", scale.x, scale.y, scale.z);
add_lines_to_cache(dt,
position,
{StringRef(location_str, location_str_len),
StringRef(rotation_str, rotation_str_len),
StringRef(scale_str, scale_str_len)},
col);
}
else {
BLI_assert_unreachable();
}
}
});
}
};
} // namespace blender::draw::overlay