From 8607aa48ec83de841b5d72c6a206eba0921ef202 Mon Sep 17 00:00:00 2001
From: veclavtalica <veclavtalica@tutamail.com>
Date: Tue, 11 Mar 2025 07:40:18 +0300
Subject: [PATCH] /apps/tools/twndel: face hovering

---
 apps/tools/twndel/state.h |  3 ++
 apps/tools/twndel/tool.c  | 76 +++++++++++++++++++++++++++++++--------
 2 files changed, 65 insertions(+), 14 deletions(-)

diff --git a/apps/tools/twndel/state.h b/apps/tools/twndel/state.h
index fe2efd8..d3ee078 100644
--- a/apps/tools/twndel/state.h
+++ b/apps/tools/twndel/state.h
@@ -109,6 +109,9 @@ typedef struct State {
     /* order: x, y, z */
     bool axis_mask[3];
     char *current_texture;
+
+    uint8_t current_hovered_obj;
+    uint16_t current_hovered_face;
 } State;
 
 
diff --git a/apps/tools/twndel/tool.c b/apps/tools/twndel/tool.c
index d1919a1..5564cc6 100644
--- a/apps/tools/twndel/tool.c
+++ b/apps/tools/twndel/tool.c
@@ -664,24 +664,29 @@ static void process_operations(void) {
             }
 
         /* texture setting */
-        } else if (input_action_pressed("select")) {
+        } else {
             uint8_t obj_select; uint16_t face_select;
             if (find_closest_face(&obj_select, &face_select)) {
-                uint8_t new_tex = push_texture(obj_select, state.current_texture);
-                uint8_t cur_tex = state.objects[obj_select].faces[face_select].texture;
+                state.current_hovered_face = face_select;
+                state.current_hovered_obj = obj_select;
 
-                if (new_tex != cur_tex) {
-                    state.objects[obj_select].faces[face_select].texture = new_tex;
-                    audio_play("/data/bong.ogg", NULL, false, 0.5f, 0.0f);
+                if (input_action_pressed("select")) {
+                    uint8_t new_tex = push_texture(obj_select, state.current_texture);
+                    uint8_t cur_tex = state.objects[obj_select].faces[face_select].texture;
 
-                    push_operation((Operation){
-                        .kind = OPERATION_SET_TEXTURE,
-                        .data = { .set_texture = {
-                            .face = face_select,
-                            .object = obj_select,
-                            .delta_texture = cur_tex - new_tex,
-                        }},
-                    }, false );
+                    if (new_tex != cur_tex) {
+                        state.objects[obj_select].faces[face_select].texture = new_tex;
+                        audio_play("/data/bong.ogg", NULL, false, 0.5f, 0.0f);
+
+                        push_operation((Operation){
+                            .kind = OPERATION_SET_TEXTURE,
+                            .data = { .set_texture = {
+                                .face = face_select,
+                                .object = obj_select,
+                                .delta_texture = cur_tex - new_tex,
+                            }},
+                        }, false );
+                    }
                 }
             }
         }
@@ -737,6 +742,44 @@ static void draw_axes(void) {
 }
 
 
+static void draw_hovered_face_border(void) {
+    if (state.current_hovered_obj == INVALID_OBJECT || state.current_hovered_face == INVALID_FACE)
+        return;
+
+    Object *o = &state.objects[state.current_hovered_obj];
+    Face *f = &o->faces[state.current_hovered_face];
+
+    if (f->p[3] != INVALID_POINT) {
+        Vec3 p0 = point_to_vec3(state.current_hovered_obj, f->p[0]);
+        Vec3 p1 = point_to_vec3(state.current_hovered_obj, f->p[1]);
+        Vec3 p2 = point_to_vec3(state.current_hovered_obj, f->p[2]);
+        Vec3 p3 = point_to_vec3(state.current_hovered_obj, f->p[3]);
+        Vec3 center = vec3_add(p0, vec3_scale(vec3_sub(p2, p0), 0.5));
+        float size = sinf(ctx.frame_number / 10) * 0.05f + 0.1f;
+        Vec3 c0 = vec3_add(p0, vec3_scale(vec3_sub(p0, center), size));
+        Vec3 c1 = vec3_add(p1, vec3_scale(vec3_sub(p1, center), size));
+        Vec3 c2 = vec3_add(p2, vec3_scale(vec3_sub(p2, center), size));
+        Vec3 c3 = vec3_add(p3, vec3_scale(vec3_sub(p3, center), size));
+        draw_line_3d(c0, c1, 1, (Color){255,255,255,255});
+        draw_line_3d(c1, c2, 1, (Color){255,255,255,255});
+        draw_line_3d(c2, c3, 1, (Color){255,255,255,255});
+        draw_line_3d(c3, c0, 1, (Color){255,255,255,255});
+    } else {
+        Vec3 p0 = point_to_vec3(state.current_hovered_obj, f->p[0]);
+        Vec3 p1 = point_to_vec3(state.current_hovered_obj, f->p[1]);
+        Vec3 p2 = point_to_vec3(state.current_hovered_obj, f->p[2]);
+        Vec3 center = vec3_scale(vec3_add(p0, vec3_add(p1, p2)), 0.33f);
+        float size = sinf(ctx.frame_number / 10) * 0.05f + 0.1f;
+        Vec3 c0 = vec3_add(p0, vec3_scale(vec3_sub(p0, center), size));
+        Vec3 c1 = vec3_add(p1, vec3_scale(vec3_sub(p1, center), size));
+        Vec3 c2 = vec3_add(p2, vec3_scale(vec3_sub(p2, center), size));
+        draw_line_3d(c0, c1, 1, (Color){255,255,255,255});
+        draw_line_3d(c1, c2, 1, (Color){255,255,255,255});
+        draw_line_3d(c2, c0, 1, (Color){255,255,255,255});
+    }
+}
+
+
 static void display_textures(void) {
     String list = file_read("/data/assets/", ":images");
     if (!list.data)
@@ -785,6 +828,9 @@ void game_tick(void) {
         init = true;
     }
 
+    state.current_hovered_face = INVALID_FACE;
+    state.current_hovered_obj = INVALID_OBJECT;
+
     input_action("toggle_display_mode", "Q");
     input_action("toggle_projection", "E");
 
@@ -820,7 +866,9 @@ void game_tick(void) {
     process_camera_movement();
     process_operations();
 
+    /* helpres */
     draw_axes();
+    draw_hovered_face_border();
 
     for (uint8_t obj = 0; obj < state.objects_sz; ++obj)
         render_object(obj);