From da491a9bcd81d4e7618498d44470a740f1801191 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Sat, 20 Sep 2025 13:12:46 +0900 Subject: [PATCH 1/5] Add helper function to calculate rotation from landmark --- src/helper.cpp | 18 ++++++++++++++++++ src/helper.hpp | 1 + 2 files changed, 19 insertions(+) diff --git a/src/helper.cpp b/src/helper.cpp index 924a6ed..c33e90b 100644 --- a/src/helper.cpp +++ b/src/helper.cpp @@ -73,6 +73,24 @@ float landmark_area(const std::vector &landmark) return ret; } +float landmark_rotation(const std::vector &landmark) +{ + if (landmark.size() == 5) { + // 2 -> 0: outer points of eyes + float x1 = landmark[0].x - landmark[2].x; + float y1 = landmark[0].y - landmark[2].y; + + // 3 -> 1: inner points of eyes + float x2 = landmark[1].x - landmark[3].x; + float y2 = landmark[1].y - landmark[3].y; + + // 4: unused, the mouth + + return atan2f(y1 + y2, x1 + x2); + } + return 0.0f; +} + pointf_s landmark_center(const std::vector &landmark) { pointf_s ret = {0.0f, 0.0f}; diff --git a/src/helper.hpp b/src/helper.hpp index cf8df07..7c7391a 100644 --- a/src/helper.hpp +++ b/src/helper.hpp @@ -148,6 +148,7 @@ void draw_rect_upsize(rect_s r, float upsize_l = 0.0f, float upsize_r = 0.0f, fl float upsize_b = 0.0f); void draw_landmark(const std::vector &landmark); float landmark_area(const std::vector &landmark); +float landmark_rotation(const std::vector &landmark); pointf_s landmark_center(const std::vector &landmark); inline double from_dB(double x) From 80ea2eb612849f7e543caedbc4b6202c34b01f6e Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Sat, 20 Sep 2025 18:02:49 +0900 Subject: [PATCH 2/5] Add struct f4 to calculate with rotation --- src/helper.hpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/helper.hpp b/src/helper.hpp index 7c7391a..44903da 100644 --- a/src/helper.hpp +++ b/src/helper.hpp @@ -74,11 +74,58 @@ struct f3 f3 hp(const f3 &a) const { return f3(v[0] * a.v[0], v[1] * a.v[1], v[2] * a.v[2]); } }; +struct f4 +{ + float v[4]; + + f4(const f4 &a) { *this = a; } + f4(float a, float b, float c, float d) + { + v[0] = a; + v[1] = b; + v[2] = c; + v[3] = d; + } + f4(const rect_s &a) + { + v[0] = (float)(a.x0 + a.x1) * 0.5f; + v[1] = (float)(a.y0 + a.y1) * 0.5f; + v[2] = sqrtf((float)(a.x1 - a.x0) * (float)(a.y1 - a.y0)); + v[3] = 0.0f; + } + f4(const rectf_s &a) + { + v[0] = (a.x0 + a.x1) * 0.5f; + v[1] = (a.y0 + a.y1) * 0.5f; + v[2] = sqrtf((a.x1 - a.x0) * (a.y1 - a.y0)); + v[3] = 0.0f; + } + f4 operator+(const f4 &a) { return f4(v[0] + a.v[0], v[1] + a.v[1], v[2] + a.v[2], v[3] + a.v[3]); } + f4 operator-(const f4 &a) { return f4(v[0] - a.v[0], v[1] - a.v[1], v[2] - a.v[2], v[3] - a.v[3]); } + f4 operator*(float a) { return f4(v[0] * a, v[1] * a, v[2] * a, v[3] * a); } + f4 &operator+=(const f4 &a) { return *this = *this + a; } + f4 &operator=(const f4 &a) + { + v[0] = a.v[0]; + v[1] = a.v[1]; + v[2] = a.v[2]; + v[3] = a.v[3]; + return *this; + } + + f4 hp(const f4 &a) const { return f4(v[0] * a.v[0], v[1] * a.v[1], v[2] * a.v[2], v[3] * a.v[3]); } +}; + static inline bool isnan(const f3 &a) { return isnan(a.v[0]) || isnan(a.v[1]) || isnan(a.v[2]); } +static inline bool isnan(const f4 &a) +{ + return isnan(a.v[0]) || isnan(a.v[1]) || isnan(a.v[2]) || isnan(a.v[3]); +} + static inline int get_width(const rect_s &r) { return r.x1 - r.x0; @@ -144,6 +191,19 @@ static inline rectf_s f3_to_rectf(const f3 &u, float w, float h) return r; } +static inline rectf_s f4_to_rectf(const f4 &u, float w, float h) +{ + const float srwh = sqrtf(w * h); + const float s2h = h / srwh; + const float s2w = w / srwh; + rectf_s r; + r.x0 = u.v[0] - s2w * u.v[2] * 0.5f; + r.x1 = u.v[0] + s2w * u.v[2] * 0.5f; + r.y0 = u.v[1] - s2h * u.v[2] * 0.5f; + r.y1 = u.v[1] + s2h * u.v[2] * 0.5f; + return r; +} + void draw_rect_upsize(rect_s r, float upsize_l = 0.0f, float upsize_r = 0.0f, float upsize_t = 0.0f, float upsize_b = 0.0f); void draw_landmark(const std::vector &landmark); From dee699b13e7d533443e12fd47dad51ba8a1f5b81 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Sun, 28 Sep 2025 16:02:21 +0900 Subject: [PATCH 3/5] face-tracker: Add feature to rotate cropped region Major functionality is working but the debug info is not rotating. --- src/face-tracker.cpp | 126 +++++++++++++++++++++++++++++++++---------- src/face-tracker.hpp | 21 ++++---- 2 files changed, 108 insertions(+), 39 deletions(-) diff --git a/src/face-tracker.cpp b/src/face-tracker.cpp index 78605fa..11fdc00 100644 --- a/src/face-tracker.cpp +++ b/src/face-tracker.cpp @@ -64,6 +64,7 @@ static void ftf_update(void *data, obs_data_t *settings) s->track_x = obs_data_get_double(settings, "track_x"); s->track_y = obs_data_get_double(settings, "track_y"); s->scale_max = obs_data_get_double(settings, "scale_max"); + s->rotate = obs_data_get_bool(settings, "rotate"); double kp = obs_data_get_double(settings, "Kp"); float ki = (float)obs_data_get_double(settings, "Ki"); @@ -71,10 +72,12 @@ static void ftf_update(void *data, obs_data_t *settings) double att2 = from_dB(obs_data_get_double(settings, "att2_dB")); s->kp.v[0] = s->kp.v[1] = (float)kp; s->kp.v[2] = (float)(att2 * kp); + s->kp.v[3] = (float)kp; s->ki = ki; s->klpf = s->kp * td; s->tlpf.v[0] = s->tlpf.v[1] = (float)obs_data_get_double(settings, "Tdlpf"); s->tlpf.v[2] = (float)obs_data_get_double(settings, "Tdlpf_z"); + s->tlpf.v[3] = s->tlpf.v[0]; s->e_deadband.v[0] = (float)obs_data_get_double(settings, "e_deadband_x") * 1e-2; s->e_deadband.v[1] = (float)obs_data_get_double(settings, "e_deadband_y") * 1e-2; s->e_deadband.v[2] = (float)obs_data_get_double(settings, "e_deadband_z") * 1e-2; @@ -191,10 +194,10 @@ static bool ftf_reset_tracking(obs_properties_t *, obs_property_t *, void *data) float w = s->known_width; float h = s->known_height; float z = sqrtf(s->width_with_aspect * s->height_with_aspect); - s->detect_err = f3(0, 0, 0); - s->filter_int_out = f3(w * 0.5f, h * 0.5f, z); - s->filter_int = f3(0, 0, 0); - s->filter_lpf = f3(0, 0, 0); + s->detect_err = f4(0, 0, 0, 0); + s->filter_int_out = f4(w * 0.5f, h * 0.5f, z, 0); + s->filter_int = f4(0, 0, 0, 0); + s->filter_lpf = f4(0, 0, 0, 0); s->ftm->reset_requested = true; return true; @@ -239,6 +242,7 @@ static obs_properties_t *ftf_properties(void *data) obs_properties_add_float(pp, "scale_max", obs_module_text("Scale max"), 1.0, 20.0, 1.0); obs_properties_add_group(props, "track", obs_module_text("Tracking target location"), OBS_GROUP_NORMAL, pp); + obs_properties_add_bool(pp, "rotate", obs_module_text("Rotate")); } { @@ -329,9 +333,9 @@ static void tick_filter(struct face_tracker_filter *s, float second) { const float srwh = sqrtf((float)s->known_width * s->known_height); - f3 e = s->detect_err; - f3 e_int = e; - for (int i = 0; i < 3; i++) { + f4 e = s->detect_err; + f4 e_int = e; + for (int i = 0; i < 4; i++) { float x = e.v[i]; float d = srwh * s->e_deadband.v[i]; float n = srwh * s->e_nonlinear.v[i]; @@ -359,12 +363,12 @@ static void tick_filter(struct face_tracker_filter *s, float second) s->filter_int_out += (e + s->filter_int).hp(s->kp * second); s->filter_int += e_int * (second * s->ki); - for (int i = 0; i < 3; i++) + for (int i = 0; i < 4; i++) s->filter_lpf.v[i] = (s->filter_lpf.v[i] * s->tlpf.v[i] + e.v[i] * second) / (s->tlpf.v[i] + second); - f3 u = s->filter_int_out + s->filter_lpf.hp(s->klpf); + f4 u = s->filter_int_out + s->filter_lpf.hp(s->klpf); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 4; i++) { if (isnan(u.v[i])) u.v[i] = (s->range_max.v[i] + s->range_min_out.v[i]) * 0.5f; else if (u.v[i] < s->range_min_out.v[i]) { @@ -384,7 +388,7 @@ static void tick_filter(struct face_tracker_filter *s, float second) fprintf(s->debug_data_control, "%f\t%f\t%f\t%f\n", os_gettime_ns() * 1e-9, u.v[0], u.v[1], u.v[2]); } - s->ftm->crop_cur = f3_to_rectf(u, s->width_with_aspect, s->height_with_aspect); + s->ftm->crop_cur = f4_to_rectf(u, s->width_with_aspect, s->height_with_aspect); } static void ftf_activate(void *data) @@ -480,7 +484,7 @@ static void ft_tick_internal(struct face_tracker_filter *s, float second, bool w if (s->ftm->crop_cur.x1 < -1 || s->ftm->crop_cur.y1 < -1) { ftf_reset_tracking(NULL, NULL, s); - s->ftm->crop_cur = f3_to_rectf(s->filter_int_out, s->width_with_aspect, s->height_with_aspect); + s->ftm->crop_cur = f4_to_rectf(s->filter_int_out, s->width_with_aspect, s->height_with_aspect); } else if (was_rendered && !s->is_paused) { s->range_min.v[0] = get_width(s->ftm->crop_cur) * 0.5f; s->range_max.v[0] = s->known_width - get_width(s->ftm->crop_cur) * 0.5f; @@ -488,6 +492,8 @@ static void ft_tick_internal(struct face_tracker_filter *s, float second, bool w s->range_max.v[1] = s->known_height - get_height(s->ftm->crop_cur) * 0.5f; s->range_min.v[2] = sqrtf(s->known_width * s->known_height) / s->scale_max; s->range_max.v[2] = sqrtf(s->width_with_aspect * s->height_with_aspect); + s->range_min.v[3] = -M_PI; + s->range_max.v[3] = M_PI; s->range_min_out = s->range_min; s->range_min_out.v[2] = std::max(std::min(s->range_min.v[2], s->u_last.v[2]), 1.0f); calculate_error(s); @@ -579,7 +585,7 @@ static inline void render_target(struct face_tracker_filter *s, obs_source_t *ta s->ftm->release_cvtex(); } -static inline f3 ensure_range(f3 u, const struct face_tracker_filter *s) +static inline f4 ensure_range(f4 u, const struct face_tracker_filter *s) { if (isnan(u.v[2])) u.v[2] = s->range_min.v[2]; @@ -588,7 +594,14 @@ static inline f3 ensure_range(f3 u, const struct face_tracker_filter *s) else if (u.v[2] > s->range_max.v[2]) u.v[2] = s->range_max.v[2]; - rectf_s r = f3_to_rectf(u, s->width_with_aspect, s->height_with_aspect); + if (isnan(u.v[3])) + u.v[3] = 0.0f; + else if (u.v[3] < s->range_min.v[3]) + u.v[3] = s->range_min.v[3]; + else if (u.v[3] > s->range_max.v[3]) + u.v[3] = s->range_max.v[3]; + + rectf_s r = f4_to_rectf(u, s->width_with_aspect, s->height_with_aspect); if (r.x0 < 0) u.v[0] += -r.x0; @@ -605,12 +618,12 @@ static inline f3 ensure_range(f3 u, const struct face_tracker_filter *s) static inline void calculate_error(struct face_tracker_filter *s) { - f3 e_tot(0.0f, 0.0f, 0.0f); + f4 e_tot(0.0f, 0.0f, 0.0f, 0.0f); float sc_tot = 0.0f; bool found = false; auto &tracker_rects = s->ftm->tracker_rects; for (size_t i = 0; i < tracker_rects.size(); i++) { - f3 r(tracker_rects[i].rect); + f4 r(tracker_rects[i].rect); float score = tracker_rects[i].rect.score; if (s->ftm->landmark_detection_data) { @@ -619,9 +632,12 @@ static inline void calculate_error(struct face_tracker_filter *s) if (area <= 0.0f) continue; + float rotation = landmark_rotation(tracker_rects[i].landmark); + r.v[0] = center.x; r.v[1] = center.y; r.v[2] = sqrtf(area * (float)(4.0f / M_PI)); + r.v[3] = rotation - s->u_last.v[3]; } if (s->debug_data_tracker) { @@ -633,9 +649,12 @@ static inline void calculate_error(struct face_tracker_filter *s) r.v[1] += get_height(tracker_rects[i].crop_rect) * s->track_y; r.v[2] /= s->track_z; r = ensure_range(r, s); - f3 w(tracker_rects[i].crop_rect); + f4 w(tracker_rects[i].crop_rect); + + f4 e = (r - w) * score; + if (!s->ftm->landmark_detection_data) + e.v[3] = 0; - f3 e = (r - w) * score; if (score > 0.0f && !isnan(e)) { e_tot += e; sc_tot += score; @@ -646,7 +665,7 @@ static inline void calculate_error(struct face_tracker_filter *s) if (found) s->detect_err = e_tot * (1.0f / sc_tot); else - s->detect_err = f3(0, 0, 0); + s->detect_err = f4(0, 0, 0, 0); if (s->debug_data_error) { fprintf(s->debug_data_error, "%f\t%f\t%f\t%f\n", os_gettime_ns() * 1e-9, s->detect_err.v[0], @@ -654,7 +673,7 @@ static inline void calculate_error(struct face_tracker_filter *s) } } -static inline void draw_sprite_crop(float width, float height, float x0, float y0, float x1, float y1); +static inline void draw_sprite(float width, float height); static inline void scale_texture(struct face_tracker_filter *s, float scale) { @@ -672,7 +691,7 @@ static inline void scale_texture(struct face_tracker_filter *s, float scale) gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, tex); while (gs_effect_loop(effect, "Draw")) - draw_sprite_crop(cx, cy, 0, 0, 1, 1); + draw_sprite(cx, cy); } gs_texrender_end(s->texrender_scaled); } @@ -729,7 +748,7 @@ static inline std::shared_ptr surface_to_cvtex(struct face_track return cvtex; } -static inline void draw_sprite_crop(float width, float height, float x0, float y0, float x1, float y1) +static inline void draw_sprite(float width, float height) { gs_render_start(false); gs_vertex2f(0.0f, 0.0f); @@ -737,22 +756,72 @@ static inline void draw_sprite_crop(float width, float height, float x0, float y gs_vertex2f(0.0f, height); gs_vertex2f(width, height); struct vec2 tv; - vec2_set(&tv, x0, y0); + vec2_set(&tv, 0.0f, 0.0f); gs_texcoord2v(&tv, 0); - vec2_set(&tv, x1, y0); + vec2_set(&tv, 1.0f, 0.0f); gs_texcoord2v(&tv, 0); - vec2_set(&tv, x0, y1); + vec2_set(&tv, 0.0f, 1.0f); gs_texcoord2v(&tv, 0); - vec2_set(&tv, x1, y1); + vec2_set(&tv, 1.0f, 1.0f); gs_texcoord2v(&tv, 0); gs_render_stop(GS_TRISTRIP); } +static inline float get_center(float x0, float x1, float t) +{ + return (x0 + x1) * 0.5 + (x1 - x0) * t; +} + +static inline void draw_sprite_with_angle(struct face_tracker_filter *s, uint32_t width, uint32_t height) +{ + float rotation = s->rotate ? s->u_last.v[3] : 0.0f; + + const rectf_s &crop_cur = s->ftm->crop_cur; + + float xc = get_center(crop_cur.x0, crop_cur.x1, s->track_x); + float yc = get_center(crop_cur.y0, crop_cur.y1, -s->track_y); + + float x0 = crop_cur.x0 - xc; + float x1 = crop_cur.x1 - xc; + float y0 = crop_cur.y0 - yc; + float y1 = crop_cur.y1 - yc; + float cr = cosf(rotation); + float sr = sinf(rotation); + + float xt0 = cr * x0 - sr * y0; + float yt0 = sr * x0 + cr * y0; + + float xt1 = cr * x1 - sr * y0; + float yt1 = sr * x1 + cr * y0; + + float xt2 = cr * x0 - sr * y1; + float yt2 = sr * x0 + cr * y1; + + float xt3 = cr * x1 - sr * y1; + float yt3 = sr * x1 + cr * y1; + + gs_render_start(false); + gs_vertex2f(0.0f, 0.0f); + gs_vertex2f(width, 0.0f); + gs_vertex2f(0.0f, height); + gs_vertex2f(width, height); + auto f = [](float x, float y) { + struct vec2 tv; + vec2_set(&tv, x, y); + gs_texcoord2v(&tv, 0); + }; + f((xt0 + xc) / s->known_width, (yt0 + yc) / s->known_height); + f((xt1 + xc) / s->known_width, (yt1 + yc) / s->known_height); + f((xt2 + xc) / s->known_width, (yt2 + yc) / s->known_height); + f((xt3 + xc) / s->known_width, (yt3 + yc) / s->known_height); + + gs_render_stop(GS_TRISTRIP); +} + static inline void draw_frame_texture(struct face_tracker_filter *s, bool debug_notrack) { uint32_t width = s->width_with_aspect; uint32_t height = s->height_with_aspect; - const rectf_s &crop_cur = s->ftm->crop_cur; // TODO: linear_srgb, 27 only? @@ -770,8 +839,7 @@ static inline void draw_frame_texture(struct face_tracker_filter *s, bool debug_ if (debug_notrack) gs_draw_sprite(tex, 0, s->known_width, s->known_height); else - draw_sprite_crop(width, height, crop_cur.x0 / s->known_width, crop_cur.y0 / s->known_height, - crop_cur.x1 / s->known_width, crop_cur.y1 / s->known_height); + draw_sprite_with_angle(s, width, height); } } diff --git a/src/face-tracker.hpp b/src/face-tracker.hpp index 3b6b818..4de0e38 100644 --- a/src/face-tracker.hpp +++ b/src/face-tracker.hpp @@ -18,24 +18,25 @@ struct face_tracker_filter bool rendered; bool is_active; - f3 detect_err; - f3 range_min, range_max, range_min_out; + f4 detect_err; + f4 range_min, range_max, range_min_out; class ft_manager_for_ftf *ftm; float track_z, track_x, track_y; float scale_max; - f3 kp; + f4 kp; float ki; - f3 klpf; - f3 tlpf; - f3 e_deadband, e_nonlinear; // deadband and nonlinear amount for error input - f3 filter_int_out; - f3 filter_int; - f3 filter_lpf; - f3 u_last; + f4 klpf; + f4 tlpf; + f4 e_deadband, e_nonlinear; // deadband and nonlinear amount for error input + f4 filter_int_out; + f4 filter_int; + f4 filter_lpf; + f4 u_last; int aspect_x, aspect_y; + bool rotate = false; // face tracker source char *target_name; From ef50cc582328753a467700a6f10b344e93c3e5bc Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Fri, 3 Oct 2025 21:39:55 +0900 Subject: [PATCH 4/5] Add 68-point landmark angle calculation In addition to the commit da491a9bcd, which implemented 5-point case, also implements 68-point case. --- src/helper.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/helper.cpp b/src/helper.cpp index c33e90b..659bf11 100644 --- a/src/helper.cpp +++ b/src/helper.cpp @@ -88,6 +88,25 @@ float landmark_rotation(const std::vector &landmark) return atan2f(y1 + y2, x1 + x2); } + + if (landmark.size() == 68) { + float x = 0.0f, y = 0.0f; + + // left eye + for (int i = 36; i <= 41; i++) { + x -= landmark[i].x; + y -= landmark[i].y; + } + + // right eye + for (int i = 42; i <= 47; i++) { + x += landmark[i].x; + y += landmark[i].y; + } + + return atan2f(y, x); + } + return 0.0f; } From d7dfd13fb960efd85f3844450b1eecbda0b6ab9e Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Fri, 3 Oct 2025 21:43:09 +0900 Subject: [PATCH 5/5] face-tracker: Rotate result rectangle and landmark The commit dee699b13e implemented the rotation feature but didn't implemented the rotation of the result rectangle and landmark when the "rotate" was enabled. This commit finalizes the "rotate" feature. --- src/face-tracker.cpp | 56 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/face-tracker.cpp b/src/face-tracker.cpp index 11fdc00..fcc85c9 100644 --- a/src/face-tracker.cpp +++ b/src/face-tracker.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "plugin-macros.generated.h" #include "texture-object.h" @@ -772,7 +773,7 @@ static inline float get_center(float x0, float x1, float t) return (x0 + x1) * 0.5 + (x1 - x0) * t; } -static inline void draw_sprite_with_angle(struct face_tracker_filter *s, uint32_t width, uint32_t height) +static void crop_points(struct vec3 res[4], const struct face_tracker_filter *s) { float rotation = s->rotate ? s->u_last.v[3] : 0.0f; @@ -788,32 +789,27 @@ static inline void draw_sprite_with_angle(struct face_tracker_filter *s, uint32_ float cr = cosf(rotation); float sr = sinf(rotation); - float xt0 = cr * x0 - sr * y0; - float yt0 = sr * x0 + cr * y0; - - float xt1 = cr * x1 - sr * y0; - float yt1 = sr * x1 + cr * y0; - - float xt2 = cr * x0 - sr * y1; - float yt2 = sr * x0 + cr * y1; + vec3_set(res + 0, cr * x0 - sr * y0 + xc, sr * x0 + cr * y0 + yc, 0.0f); + vec3_set(res + 1, cr * x1 - sr * y0 + xc, sr * x1 + cr * y0 + yc, 0.0f); + vec3_set(res + 2, cr * x0 - sr * y1 + xc, sr * x0 + cr * y1 + yc, 0.0f); + vec3_set(res + 3, cr * x1 - sr * y1 + xc, sr * x1 + cr * y1 + yc, 0.0f); +} - float xt3 = cr * x1 - sr * y1; - float yt3 = sr * x1 + cr * y1; +static inline void draw_sprite_with_angle(struct face_tracker_filter *s, uint32_t width, uint32_t height) +{ + struct vec3 points[4]; + crop_points(points, s); gs_render_start(false); gs_vertex2f(0.0f, 0.0f); gs_vertex2f(width, 0.0f); gs_vertex2f(0.0f, height); gs_vertex2f(width, height); - auto f = [](float x, float y) { + for (int i = 0; i < 4; i++) { struct vec2 tv; - vec2_set(&tv, x, y); + vec2_set(&tv, points[i].x / s->known_width, points[i].y / s->known_height); gs_texcoord2v(&tv, 0); - }; - f((xt0 + xc) / s->known_width, (yt0 + yc) / s->known_height); - f((xt1 + xc) / s->known_width, (yt1 + yc) / s->known_height); - f((xt2 + xc) / s->known_width, (yt2 + yc) / s->known_height); - f((xt3 + xc) / s->known_width, (yt3 + yc) / s->known_height); + } gs_render_stop(GS_TRISTRIP); } @@ -856,12 +852,17 @@ static inline void draw_frame_info(struct face_tracker_filter *s, bool debug_not uint32_t height = s->height_with_aspect; const float scale = sqrtf((float)(width * height) / ((crop_cur.x1 - crop_cur.x0) * (crop_cur.y1 - crop_cur.y0))); + const float rotation = s->rotate ? s->u_last.v[3] : 0.0f; gs_matrix_push(); struct matrix4 tr; matrix4_identity(&tr); - matrix4_translate3f(&tr, &tr, -(crop_cur.x0 + crop_cur.x1) * 0.5f, -(crop_cur.y0 + crop_cur.y1) * 0.5f, - 0.0f); + float xc = width * s->track_x / scale; + float yc = height * s->track_y / scale; + matrix4_translate3f(&tr, &tr, -(crop_cur.x0 + crop_cur.x1) * 0.5f - xc, + -(crop_cur.y0 + crop_cur.y1) * 0.5f + yc, 0.0f); + matrix4_rotate_aa4f(&tr, &tr, 0.0f, 0.0f, 1.0f, -rotation); + matrix4_translate3f(&tr, &tr, xc, -yc, 0.0f); matrix4_scale3f(&tr, &tr, scale, scale, 1.0f); matrix4_translate3f(&tr, &tr, width / 2, height / 2, 0.0f); gs_matrix_mul(&tr); @@ -887,15 +888,14 @@ static inline void draw_frame_info(struct face_tracker_filter *s, bool debug_not if (debug_notrack && draw_ref) { gs_effect_set_color(gs_effect_get_param_by_name(effect, "color"), 0xFFFFFF00); // amber const rectf_s &r = s->ftm->crop_cur; + struct vec3 points[4]; + crop_points(points, s); gs_render_start(false); - gs_vertex2f(r.x0, r.y0); - gs_vertex2f(r.x0, r.y1); - gs_vertex2f(r.x0, r.y1); - gs_vertex2f(r.x1, r.y1); - gs_vertex2f(r.x1, r.y1); - gs_vertex2f(r.x1, r.y0); - gs_vertex2f(r.x1, r.y0); - gs_vertex2f(r.x0, r.y0); + static const int tris_to_rect[5] = {0, 1, 3, 2, 0}; + for (int i = 0; i < 4; i++) { + gs_vertex3v(&points[tris_to_rect[i]]); + gs_vertex3v(&points[tris_to_rect[i + 1]]); + } const float srwhr2 = sqrtf((r.x1 - r.x0) * (r.y1 - r.y0)) * 0.5f; const float rcx = (r.x0 + r.x1) * 0.5f + (r.x1 - r.x0) * s->track_x; const float rcy = (r.y0 + r.y1) * 0.5f - (r.y1 - r.y0) * s->track_y;