Blender V5.0
overlay_motion_path.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#pragma once
10
11#include "BLI_string.h"
12
14
15#include "draw_manager_text.hh"
16
17#include "overlay_armature.hh"
18#include "overlay_base.hh"
19
20namespace blender::draw::overlay {
21
27
28 private:
29 PassSimple motion_path_ps_ = {"motion_path_ps_"};
30
31 PassSimple::Sub *line_ps_ = nullptr;
32 PassSimple::Sub *vert_ps_ = nullptr;
33
34 public:
35 void begin_sync(Resources &res, const State &state) final
36 {
37 enabled_ = state.v3d && state.show_motion_paths() && !res.is_selection();
38 if (!enabled_) {
39 /* Not used. But release the data. */
40 motion_path_ps_.init();
41 return;
42 }
43
44 {
45 PassSimple &pass = motion_path_ps_;
46 pass.init();
47 pass.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
48 pass.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
49 pass.state_set(DRW_STATE_WRITE_COLOR, state.clipping_plane_count);
50 {
51 PassSimple::Sub &sub = pass.sub("Lines");
52 sub.shader_set(res.shaders->motion_path_line.get());
53 line_ps_ = ⊂
54 }
55 {
56 PassSimple::Sub &sub = pass.sub("Points");
57 sub.shader_set(res.shaders->motion_path_vert.get());
58 vert_ps_ = ⊂
59 }
60 }
61 }
62
63 void object_sync(Manager & /*manager*/,
64 const ObjectRef &ob_ref,
65 Resources & /*res*/,
66 const State &state) final
67 {
68 if (!enabled_) {
69 return;
70 }
71
72 const Object *object = ob_ref.object;
73
74 if (object->type == OB_ARMATURE) {
75 if (Armatures::is_pose_mode(object, state)) {
77 if (pchan->mpath) {
78 motion_path_sync(state, object, pchan, object->pose->avs, pchan->mpath);
79 }
80 }
81 }
82 }
83
84 if (object->mpath) {
85 motion_path_sync(state, object, nullptr, object->avs, object->mpath);
86 }
87 }
88
89 void draw_color_only(Framebuffer &framebuffer, Manager &manager, View &view) final
90 {
91 if (!enabled_) {
92 return;
93 }
94 GPU_framebuffer_bind(framebuffer);
95 manager.submit(motion_path_ps_, view);
96 }
97
98 private:
99 void motion_path_sync(const State &state,
100 const Object *ob,
101 const bPoseChannel *pchan,
102 const bAnimVizSettings &avs,
103 bMotionPath *mpath)
104 {
105 /* Avoid 0 size allocations. Current code to calculate motion paths should
106 * sanitize this already [see animviz_verify_motionpaths()], we might however
107 * encounter an older file where this was still possible. */
108 if (mpath->length == 0) {
109 return;
110 }
111
112 const bool show_keyframes = (avs.path_viewflag & MOTIONPATH_VIEW_KFRAS);
113 const bool show_keyframes_number = (avs.path_viewflag & MOTIONPATH_VIEW_KFNOS);
114 const bool show_frame_number = (avs.path_viewflag & MOTIONPATH_VIEW_FNUMS);
115 const bool show_lines = (mpath->flag & MOTIONPATH_FLAG_LINES);
116 const bool custom_color = (mpath->flag & MOTIONPATH_FLAG_CUSTOM);
117 const bool selected = (pchan) ? (pchan->flag & POSE_SELECTED) :
118 (ob->base_flag & BASE_SELECTED);
119
120 const float3 color_pre = custom_color ? float3(mpath->color) : float3(-1.0f);
121 const float3 color_post = custom_color ? float3(mpath->color_post) : float3(-1.0f);
122
123 int stride = max_ii(avs.path_step, 1);
124 int current_frame = state.cfra;
125
126 IndexRange frame_range;
127 {
128 int start, end;
129 if (avs.path_type == MOTIONPATH_TYPE_ACFRA) {
130 start = current_frame - avs.path_bc;
131 end = current_frame + avs.path_ac;
132 }
133 else {
134 start = avs.path_sf;
135 end = avs.path_ef;
136 }
137
138 if (start > end) {
139 std::swap(start, end);
140 }
141 start = math::clamp(start, mpath->start_frame, mpath->end_frame);
142 end = math::clamp(end, mpath->start_frame, mpath->end_frame);
143
144 frame_range = IndexRange::from_begin_end_inclusive(start, end);
145 }
146
147 if (frame_range.is_empty()) {
148 return;
149 }
150
151 int start_index = frame_range.start() - mpath->start_frame;
152
153 Object *camera_eval = nullptr;
155 state.v3d->camera)
156 {
157 camera_eval = DEG_get_evaluated(state.depsgraph, state.v3d->camera);
158 }
159
160 /* Draw curve-line of path. */
161 if (show_lines) {
162 const int4 motion_path_settings(
163 current_frame, int(frame_range.start()), int(frame_range.last()), mpath->start_frame);
164
165 auto &sub = *line_ps_;
166 sub.push_constant("mpath_line_settings", motion_path_settings);
167 sub.push_constant("line_thickness", mpath->line_thickness);
168 sub.push_constant("selected", selected);
169 sub.push_constant("custom_color_pre", color_pre);
170 sub.push_constant("custom_color_post", color_post);
171 sub.push_constant("camera_space_matrix",
172 camera_eval ? camera_eval->object_to_world() : float4x4::identity());
173
174 gpu::Batch *geom = mpath_batch_points_get(mpath);
175 /* Only draw the required range. */
176 sub.draw_expand(geom, GPU_PRIM_TRIS, 2, 1, frame_range.size() - 1, start_index);
177 }
178
179 /* Draw points. */
180 {
181 int pt_size = max_ii(mpath->line_thickness - 1, 1);
182 const int4 motion_path_settings = {pt_size, current_frame, mpath->start_frame, stride};
183
184 auto &sub = *vert_ps_;
185 sub.push_constant("mpath_point_settings", motion_path_settings);
186 sub.push_constant("show_key_frames", show_keyframes);
187 sub.push_constant("custom_color_pre", color_pre);
188 sub.push_constant("custom_color_post", color_post);
189 sub.push_constant("camera_space_matrix",
190 camera_eval ? camera_eval->object_to_world() : float4x4::identity());
191
192 gpu::Batch *geom = mpath_batch_points_get(mpath);
193 /* Only draw the required range. */
194 sub.draw(geom, 1, frame_range.size(), start_index);
195 }
196
197 /* Draw frame numbers at each frame-step value. */
198 if (show_frame_number || (show_keyframes_number && show_keyframes)) {
199 uchar4 col, col_kf;
200 /* Color Management: Exception here as texts are drawn in sRGB space directly. */
203 col.w = col_kf.w = 255;
204
205 auto safe_index = [&](int index) { return math::clamp(index, 0, mpath->length - 1); };
206
207 Span<bMotionPathVert> mpv(mpath->points, mpath->length);
208 for (int i = 0; i < frame_range.size(); i += stride) {
209 const bMotionPathVert &mpv_curr = mpv[safe_index(start_index + i)];
210
211 int frame = frame_range.start() + i;
212 bool is_keyframe = (mpv_curr.flag & MOTIONPATH_VERT_KEY) != 0;
213
214 float3 vert_coordinate(mpv_curr.co);
215 if (camera_eval) {
216 /* Projecting the point into world space from the camera's POV. */
217 vert_coordinate = math::transform_point(camera_eval->object_to_world(), vert_coordinate);
218 }
219
220 if ((show_keyframes && show_keyframes_number && is_keyframe) ||
221 (show_frame_number && (i == 0)))
222 {
223 char numstr[32];
224 size_t numstr_len = SNPRINTF_RLEN(numstr, " %d", frame);
226 vert_coordinate,
227 numstr,
228 numstr_len,
229 0,
230 0,
232 (is_keyframe) ? col_kf : col);
233 }
234 else if (show_frame_number) {
235 const bMotionPathVert &mpv_prev = mpv[safe_index(start_index + i - stride)];
236 const bMotionPathVert &mpv_next = mpv[safe_index(start_index + i + stride)];
237 /* Only draw frame number if several consecutive highlighted points
238 * don't occur on same point. */
239 if (!math::is_equal(float3(mpv_curr.co), float3(mpv_prev.co)) ||
240 !math::is_equal(float3(mpv_curr.co), float3(mpv_next.co)))
241 {
242 char numstr[32];
243 size_t numstr_len = SNPRINTF_RLEN(numstr, " %d", frame);
245 vert_coordinate,
246 numstr,
247 numstr_len,
248 0,
249 0,
251 col);
252 }
253 }
254 }
255 }
256 }
257
258 /* Just convert the CPU cache to GPU cache. */
259 /* TODO(fclem) This should go into a draw_cache_impl_motionpath. */
260 blender::gpu::VertBuf *mpath_vbo_get(bMotionPath *mpath)
261 {
262 if (!mpath->points_vbo) {
263 GPUVertFormat format = {0};
264 /* Match structure of #bMotionPathVert. */
265 GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
266 GPU_vertformat_attr_add(&format, "flag", gpu::VertAttrType::SINT_32);
269 /* meh... a useless `memcpy`. */
270 mpath->points_vbo->data<bMotionPathVert>().copy_from({mpath->points, mpath->length});
271 }
272 return mpath->points_vbo;
273 }
274
275 blender::gpu::Batch *mpath_batch_points_get(bMotionPath *mpath)
276 {
277 if (!mpath->batch_points) {
278 mpath->batch_points = GPU_batch_create(GPU_PRIM_POINTS, mpath_vbo_get(mpath), nullptr);
279 }
280 return mpath->batch_points;
281 }
282};
283
284} // namespace blender::draw::overlay
MINLINE int max_ii(int a, int b)
#define SNPRINTF_RLEN(dst, format,...)
Definition BLI_string.h:605
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ MOTIONPATH_BAKE_CAMERA_SPACE
@ MOTIONPATH_TYPE_ACFRA
@ MOTIONPATH_VERT_KEY
@ MOTIONPATH_VIEW_KFNOS
@ MOTIONPATH_VIEW_FNUMS
@ MOTIONPATH_VIEW_KFRAS
struct bMotionPathVert bMotionPathVert
@ MOTIONPATH_FLAG_LINES
@ MOTIONPATH_FLAG_CUSTOM
struct bMotionPath bMotionPath
@ POSE_SELECTED
enum eMotionPaths_BakeFlag eMotionPath_BakeFlag
@ OB_ARMATURE
struct Object Object
#define BASE_SELECTED(v3d, base)
static AppView * view
#define GPU_batch_create(primitive_type, vertex_buf, index_buf)
Definition GPU_batch.hh:141
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
@ GPU_PRIM_POINTS
@ GPU_PRIM_TRIS
static blender::gpu::VertBuf * GPU_vertbuf_create_with_format(const GPUVertFormat &format)
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
@ TH_VERTEX_SELECT
@ TH_TEXT_HI
void UI_GetThemeColor3ubv(int colorid, unsigned char col[3])
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr bool is_empty() const
constexpr int64_t start() const
static constexpr IndexRange from_begin_end_inclusive(const int64_t begin, const int64_t last)
void shader_set(gpu::Shader *shader)
PassBase< DrawCommandBufType > & sub(const char *name)
Definition draw_pass.hh:690
void state_set(DRWState state, int clip_plane_count=0)
void bind_ubo(const char *name, gpu::UniformBuf *buffer)
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:499
static bool is_pose_mode(const Object *armature_ob, const State &state)
void object_sync(Manager &, const ObjectRef &ob_ref, Resources &, const State &state) final
void begin_sync(Resources &res, const State &state) final
void draw_color_only(Framebuffer &framebuffer, Manager &manager, View &view) final
#define DRW_CLIPPING_UBO_SLOT
#define OVERLAY_GLOBALS_SLOT
void DRW_text_cache_add(DRWTextStore *dt, const float co[3], const char *str, const int str_len, short xoffs, short yoffs, short flag, const uchar col[4], const bool shadow, const bool align_center)
@ DRW_TEXT_CACHE_GLOBALSPACE
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
uint col
format
static ulong state[N]
detail::Pass< command::DrawCommandBuf > PassSimple
T clamp(const T &a, const T &min, const T &max)
bool is_equal(const MatBase< T, NumCol, NumRow > &a, const MatBase< T, NumCol, NumRow > &b, const T epsilon=T(0))
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
VecBase< int32_t, 4 > int4
blender::VecBase< uint8_t, 4 > uchar4
ListBaseWrapperTemplate< ListBase, T > ListBaseWrapper
VecBase< float, 3 > float3
short base_flag
struct bPose * pose
bMotionPath * mpath
bAnimVizSettings avs
bMotionPathVert * points
float color_post[3]
GPUVertBufHandle * points_vbo
GPUBatchHandle * batch_points
ListBase chanbase
bAnimVizSettings avs
uchar w
i
Definition text_draw.cc:230