Files
test/source/blender/draw/engines/overlay/overlay_next_relation.hh
Sybren A. Stüvel 266d1e8d2f Anim: do not draw constraint relationship lines for invalid targets
Only draw relationship lines between a constrained object/bone and its
target when there is actually a valid target. Previously Blender would
always draw a line, which would go to the world origin when the
constraint has no target. This was visually rather noisy and potentially
even misleading when there is actually an object at the origin.

Fixes #131477

Pull Request: https://projects.blender.org/blender/blender/pulls/132592
2025-01-03 15:51:40 +01:00

230 lines
8.0 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "BKE_constraint.h"
#include "DEG_depsgraph_query.hh"
#include "DNA_constraint_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_modifier_types.h"
#include "DNA_rigidbody_types.h"
#include "overlay_next_base.hh"
namespace blender::draw::overlay {
/**
* Display object relations as dashed lines.
* Covers parenting relationships and constraints.
*/
class Relations : Overlay {
private:
PassSimple ps_ = {"Relations"};
LinePrimitiveBuf relations_buf_;
PointPrimitiveBuf points_buf_;
public:
Relations(SelectionType selection_type)
: relations_buf_(selection_type, "relations_buf_"),
points_buf_(selection_type, "points_buf_")
{
}
void begin_sync(Resources &res, const State &state) final
{
enabled_ = state.is_space_v3d();
enabled_ &= (state.v3d_flag & V3D_HIDE_HELPLINES) == 0;
enabled_ &= !res.is_selection();
points_buf_.clear();
relations_buf_.clear();
}
void object_sync(Manager & /*manager*/,
const ObjectRef &ob_ref,
Resources &res,
const State &state) final
{
if (!enabled_) {
return;
}
/* Don't show object extras in set's. */
if (is_from_dupli_or_set(ob_ref)) {
return;
}
Object *ob = ob_ref.object;
const float4 &relation_color = res.theme_settings.color_wire;
const float4 &constraint_color = res.theme_settings.color_grid_axis_z; /* ? */
if (ob->parent && (DRW_object_visibility_in_active_context(ob->parent) & OB_VISIBLE_SELF)) {
const float3 &parent_pos = ob->runtime->parent_display_origin;
/* Reverse order to have less stipple overlap. */
relations_buf_.append(ob->object_to_world().location(), parent_pos, relation_color);
}
/* Drawing the hook lines. */
for (ModifierData *md : ListBaseWrapper<ModifierData>(&ob->modifiers)) {
if (md->type == eModifierType_Hook) {
HookModifierData *hmd = reinterpret_cast<HookModifierData *>(md);
const float3 center = math::transform_point(ob->object_to_world(), float3(hmd->cent));
if (hmd->object) {
relations_buf_.append(hmd->object->object_to_world().location(), center, relation_color);
}
points_buf_.append(center, relation_color);
}
}
for (GpencilModifierData *md :
ListBaseWrapper<GpencilModifierData>(ob->greasepencil_modifiers))
{
if (md->type == eGpencilModifierType_Hook) {
HookGpencilModifierData *hmd = reinterpret_cast<HookGpencilModifierData *>(md);
const float3 center = math::transform_point(ob->object_to_world(), float3(hmd->cent));
if (hmd->object) {
relations_buf_.append(hmd->object->object_to_world().location(), center, relation_color);
}
points_buf_.append(center, relation_color);
}
}
if (ob->rigidbody_constraint) {
Object *rbc_ob1 = ob->rigidbody_constraint->ob1;
Object *rbc_ob2 = ob->rigidbody_constraint->ob2;
if (rbc_ob1 && (DRW_object_visibility_in_active_context(rbc_ob1) & OB_VISIBLE_SELF)) {
relations_buf_.append(rbc_ob1->object_to_world().location(),
ob->object_to_world().location(),
relation_color);
}
if (rbc_ob2 && (DRW_object_visibility_in_active_context(rbc_ob2) & OB_VISIBLE_SELF)) {
relations_buf_.append(rbc_ob2->object_to_world().location(),
ob->object_to_world().location(),
relation_color);
}
}
/* Drawing the constraint lines */
if (!BLI_listbase_is_empty(&ob->constraints)) {
Scene *scene = (Scene *)state.scene;
bConstraintOb *cob = BKE_constraints_make_evalob(
state.depsgraph, (Scene *)state.scene, ob, nullptr, CONSTRAINT_OBTYPE_OBJECT);
for (bConstraint *constraint : ListBaseWrapper<bConstraint>(ob->constraints)) {
if (ELEM(constraint->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_OBJECTSOLVER)) {
/* special case for object solver and follow track constraints because they don't fill
* constraint targets properly (design limitation -- scene is needed for their target
* but it can't be accessed from get_targets callback) */
Object *camob = nullptr;
if (constraint->type == CONSTRAINT_TYPE_FOLLOWTRACK) {
bFollowTrackConstraint *data = (bFollowTrackConstraint *)constraint->data;
camob = data->camera ? data->camera : scene->camera;
}
else if (constraint->type == CONSTRAINT_TYPE_OBJECTSOLVER) {
bObjectSolverConstraint *data = (bObjectSolverConstraint *)constraint->data;
camob = data->camera ? data->camera : scene->camera;
}
if (camob) {
relations_buf_.append(camob->object_to_world().location(),
ob->object_to_world().location(),
constraint_color);
}
}
else {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(constraint);
ListBase targets = {nullptr, nullptr};
if ((constraint->ui_expand_flag & (1 << 0)) &&
BKE_constraint_targets_get(constraint, &targets))
{
BKE_constraint_custom_object_space_init(cob, constraint);
for (bConstraintTarget *target : ListBaseWrapper<bConstraintTarget>(targets)) {
/* Calculate target's position. */
float3 target_pos = float3(0.0f);
bool has_target = false;
if (target->flag & CONSTRAINT_TAR_CUSTOM_SPACE) {
target_pos = cob->space_obj_world_matrix[3];
has_target = true;
}
else if (cti->get_target_matrix &&
cti->get_target_matrix(state.depsgraph,
constraint,
cob,
target,
DEG_get_ctime(state.depsgraph)))
{
has_target = true;
target_pos = target->matrix[3];
}
if (has_target) {
/* Only draw this relationship line when there is actually a target. Otherwise it
* would always draw to the world origin, which is visually rather noisy and not
* that useful. */
relations_buf_.append(
target_pos, ob->object_to_world().location(), constraint_color);
}
}
BKE_constraint_targets_flush(constraint, &targets, true);
}
}
}
/* NOTE: Don't use #BKE_constraints_clear_evalob here as that will reset `ob->constinv`.
*/
MEM_freeN(cob);
}
}
void end_sync(Resources &res, const State &state) final
{
if (!enabled_) {
return;
}
ps_.init();
ps_.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
res.select_bind(ps_);
{
PassSimple::Sub &sub_pass = ps_.sub("lines");
sub_pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
DRW_STATE_DEPTH_LESS_EQUAL,
state.clipping_plane_count);
sub_pass.shader_set(res.shaders.extra_wire.get());
relations_buf_.end_sync(sub_pass);
}
{
PassSimple::Sub &sub_pass = ps_.sub("loose_points");
sub_pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
DRW_STATE_DEPTH_LESS_EQUAL,
state.clipping_plane_count);
sub_pass.shader_set(res.shaders.extra_loose_points.get());
points_buf_.end_sync(sub_pass);
}
}
void draw_line(Framebuffer &framebuffer, Manager &manager, View &view) final
{
if (!enabled_) {
return;
}
GPU_framebuffer_bind(framebuffer);
manager.submit(ps_, view);
}
};
} // namespace blender::draw::overlay