Files
armorpaint/paint/sources/gizmo.c
T

351 lines
16 KiB
C
Raw Normal View History

2026-03-06 12:56:38 +01:00
2026-03-09 13:17:15 +01:00
#include "global.h"
2026-04-06 14:52:30 +02:00
vec4_t gizmo_v = (vec4_t){0.0, 0.0, 0.0, 1.0};
vec4_t gizmo_v0 = (vec4_t){0.0, 0.0, 0.0, 1.0};
quat_t gizmo_q = (quat_t){0.0, 0.0, 0.0, 1.0};
quat_t gizmo_q0 = (quat_t){0.0, 0.0, 0.0, 1.0};
2026-03-06 12:56:38 +01:00
void gizmo_update() {
2026-04-18 12:54:50 +02:00
bool is_object = g_context->tool == TOOL_TYPE_CURSOR;
2026-04-06 14:52:30 +02:00
bool is_decal = base_is_decal_layer();
2026-03-06 12:56:38 +01:00
2026-04-06 14:52:30 +02:00
object_t *gizmo = g_context->gizmo;
bool hide = operator_shortcut(any_map_get(config_keymap, "stencil_hide"), SHORTCUT_TYPE_DOWN);
gizmo->visible = (is_object || is_decal) && !hide && g_config->workspace != WORKSPACE_PLAYER;
2026-03-06 12:56:38 +01:00
if (!gizmo->visible) {
return;
}
2026-04-05 16:41:00 +02:00
object_t *paint_object = g_context->paint_object->base;
2026-03-06 12:56:38 +01:00
2026-04-18 12:54:50 +02:00
if (g_context->tool == TOOL_TYPE_CURSOR && g_context->selected_object != NULL) {
2026-04-05 16:41:00 +02:00
paint_object = g_context->selected_object;
2026-03-06 12:56:38 +01:00
}
if (is_object) {
2026-05-04 20:30:49 +02:00
gizmo->transform->loc =
(vec4_t){transform_world_x(paint_object->transform), transform_world_y(paint_object->transform), transform_world_z(paint_object->transform), 1.0};
2026-03-06 12:56:38 +01:00
}
else if (is_decal) {
2026-04-05 23:00:47 +02:00
gizmo->transform->loc = (vec4_t){g_context->layer->decal_mat.m30, g_context->layer->decal_mat.m31, g_context->layer->decal_mat.m32, 1.0};
2026-03-06 12:56:38 +01:00
}
2026-04-06 14:52:30 +02:00
camera_object_t *cam = scene_camera;
f32 fov = cam->data->fov;
f32 dist = vec4_dist(cam->base->transform->loc, gizmo->transform->loc) / 8.0 * fov;
gizmo->transform->scale = (vec4_t){dist, dist, dist, 1.0};
2026-04-05 23:00:47 +02:00
g_context->gizmo_translate_x->transform->scale = (vec4_t){dist, dist, dist, 1.0};
g_context->gizmo_translate_y->transform->scale = (vec4_t){dist, dist, dist, 1.0};
g_context->gizmo_translate_z->transform->scale = (vec4_t){dist, dist, dist, 1.0};
g_context->gizmo_scale_x->transform->scale = (vec4_t){dist, dist, dist, 1.0};
g_context->gizmo_scale_y->transform->scale = (vec4_t){dist, dist, dist, 1.0};
g_context->gizmo_scale_z->transform->scale = (vec4_t){dist, dist, dist, 1.0};
g_context->gizmo_rotate_x->transform->scale = (vec4_t){dist, dist, dist, 1.0};
g_context->gizmo_rotate_y->transform->scale = (vec4_t){dist, dist, dist, 1.0};
g_context->gizmo_rotate_z->transform->scale = (vec4_t){dist, dist, dist, 1.0};
2026-03-06 12:56:38 +01:00
transform_build_matrix(gizmo->transform);
// Scene control
if (is_object) {
2026-04-06 14:52:30 +02:00
if (g_context->translate_x || g_context->translate_y || g_context->translate_z || g_context->scale_x || g_context->scale_y || g_context->scale_z ||
g_context->rotate_x || g_context->rotate_y || g_context->rotate_z) {
2026-04-05 16:41:00 +02:00
if (g_context->translate_x) {
2026-05-04 20:30:49 +02:00
mat4_t p = paint_object->parent != NULL ? mat4_inv(paint_object->parent->transform->world) : mat4_identity();
vec4_t v = {g_context->gizmo_drag, gizmo_v.y, gizmo_v.z, 1.0};
v = vec4_apply_mat4(v, p);
paint_object->transform->loc = (vec4_t){v.x, v.y, v.z, 1.0};
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->translate_y) {
2026-05-04 20:30:49 +02:00
mat4_t p = paint_object->parent != NULL ? mat4_inv(paint_object->parent->transform->world) : mat4_identity();
vec4_t v = {gizmo_v.x, g_context->gizmo_drag, gizmo_v.z, 1.0};
v = vec4_apply_mat4(v, p);
paint_object->transform->loc = (vec4_t){v.x, v.y, v.z, 1.0};
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->translate_z) {
2026-05-04 20:30:49 +02:00
mat4_t p = paint_object->parent != NULL ? mat4_inv(paint_object->parent->transform->world) : mat4_identity();
vec4_t v = {gizmo_v.x, gizmo_v.y, g_context->gizmo_drag, 1.0};
v = vec4_apply_mat4(v, p);
paint_object->transform->loc = (vec4_t){v.x, v.y, v.z, 1.0};
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->scale_x) {
paint_object->transform->scale.x += g_context->gizmo_drag - g_context->gizmo_drag_last;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->scale_y) {
paint_object->transform->scale.y += g_context->gizmo_drag - g_context->gizmo_drag_last;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->scale_z) {
paint_object->transform->scale.z += g_context->gizmo_drag - g_context->gizmo_drag_last;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->rotate_x) {
gizmo_q0 = quat_from_axis_angle(vec4_x_axis(), -(g_context->gizmo_drag - g_context->gizmo_drag_last));
2026-03-06 12:56:38 +01:00
paint_object->transform->rot = quat_mult(paint_object->transform->rot, gizmo_q0);
}
2026-04-05 16:41:00 +02:00
else if (g_context->rotate_y) {
gizmo_q0 = quat_from_axis_angle(vec4_y_axis(), -(g_context->gizmo_drag - g_context->gizmo_drag_last));
2026-03-06 12:56:38 +01:00
paint_object->transform->rot = quat_mult(paint_object->transform->rot, gizmo_q0);
}
2026-04-05 16:41:00 +02:00
else if (g_context->rotate_z) {
gizmo_q0 = quat_from_axis_angle(vec4_z_axis(), g_context->gizmo_drag - g_context->gizmo_drag_last);
2026-03-06 12:56:38 +01:00
paint_object->transform->rot = quat_mult(paint_object->transform->rot, gizmo_q0);
}
2026-04-05 16:41:00 +02:00
g_context->gizmo_drag_last = g_context->gizmo_drag;
2026-03-06 12:56:38 +01:00
transform_build_matrix(paint_object->transform);
physics_body_t *pb = any_imap_get(physics_body_object_map, paint_object->uid);
2026-03-07 21:12:59 +01:00
if (pb != NULL) {
2026-03-06 12:56:38 +01:00
physics_body_sync_transform(pb);
}
}
}
// Decal layer control
else if (is_decal) {
2026-04-06 14:52:30 +02:00
if (g_context->translate_x || g_context->translate_y || g_context->translate_z || g_context->scale_x || g_context->scale_y || g_context->scale_z ||
g_context->rotate_x || g_context->rotate_y || g_context->rotate_z) {
2026-04-05 16:41:00 +02:00
if (g_context->translate_x) {
g_context->layer->decal_mat.m30 = g_context->gizmo_drag;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->translate_y) {
g_context->layer->decal_mat.m31 = g_context->gizmo_drag;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->translate_z) {
g_context->layer->decal_mat.m32 = g_context->gizmo_drag;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->scale_x) {
mat4_decomposed_t *dec = mat4_decompose(g_context->layer->decal_mat);
2026-03-06 12:56:38 +01:00
gizmo_v = dec->loc;
gizmo_q = dec->rot;
gizmo_v0 = dec->scl;
2026-04-05 16:41:00 +02:00
gizmo_v0.x += g_context->gizmo_drag - g_context->gizmo_drag_last;
g_context->layer->decal_mat = mat4_compose(gizmo_v, gizmo_q, gizmo_v0);
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->scale_y) {
mat4_decomposed_t *dec = mat4_decompose(g_context->layer->decal_mat);
2026-03-06 12:56:38 +01:00
gizmo_v = dec->loc;
gizmo_q = dec->rot;
gizmo_v0 = dec->scl;
2026-04-05 16:41:00 +02:00
gizmo_v0.y += g_context->gizmo_drag - g_context->gizmo_drag_last;
g_context->layer->decal_mat = mat4_compose(gizmo_v, gizmo_q, gizmo_v0);
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->scale_z) {
mat4_decomposed_t *dec = mat4_decompose(g_context->layer->decal_mat);
2026-03-06 12:56:38 +01:00
gizmo_v = dec->loc;
gizmo_q = dec->rot;
gizmo_v0 = dec->scl;
2026-04-05 16:41:00 +02:00
gizmo_v0.z += g_context->gizmo_drag - g_context->gizmo_drag_last;
g_context->layer->decal_mat = mat4_compose(gizmo_v, gizmo_q, gizmo_v0);
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->rotate_x) {
2026-04-06 14:52:30 +02:00
mat4_decomposed_t *dec = mat4_decompose(g_context->layer->decal_mat);
gizmo_v = dec->loc;
gizmo_q = dec->rot;
gizmo_v0 = dec->scl;
gizmo_q0 = quat_from_axis_angle(vec4_x_axis(), -g_context->gizmo_drag + g_context->gizmo_drag_last);
gizmo_q = quat_mult(gizmo_q0, gizmo_q);
2026-04-05 16:41:00 +02:00
g_context->layer->decal_mat = mat4_compose(gizmo_v, gizmo_q, gizmo_v0);
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->rotate_y) {
2026-04-06 14:52:30 +02:00
mat4_decomposed_t *dec = mat4_decompose(g_context->layer->decal_mat);
gizmo_v = dec->loc;
gizmo_q = dec->rot;
gizmo_v0 = dec->scl;
gizmo_q0 = quat_from_axis_angle(vec4_y_axis(), -g_context->gizmo_drag + g_context->gizmo_drag_last);
gizmo_q = quat_mult(gizmo_q0, gizmo_q);
2026-04-05 16:41:00 +02:00
g_context->layer->decal_mat = mat4_compose(gizmo_v, gizmo_q, gizmo_v0);
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (g_context->rotate_z) {
2026-04-06 14:52:30 +02:00
mat4_decomposed_t *dec = mat4_decompose(g_context->layer->decal_mat);
gizmo_v = dec->loc;
gizmo_q = dec->rot;
gizmo_v0 = dec->scl;
gizmo_q0 = quat_from_axis_angle(vec4_z_axis(), g_context->gizmo_drag - g_context->gizmo_drag_last);
gizmo_q = quat_mult(gizmo_q0, gizmo_q);
2026-04-05 16:41:00 +02:00
g_context->layer->decal_mat = mat4_compose(gizmo_v, gizmo_q, gizmo_v0);
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
g_context->gizmo_drag_last = g_context->gizmo_drag;
2026-03-06 12:56:38 +01:00
2026-04-05 16:41:00 +02:00
if (g_context->material != g_context->layer->fill_layer) {
context_set_material(g_context->layer->fill_layer);
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
layers_update_fill_layer(g_context->gizmo_started);
2026-03-06 12:56:38 +01:00
}
}
2026-04-05 16:41:00 +02:00
g_context->gizmo_started = false;
2026-03-06 12:56:38 +01:00
if (mouse_started("left") && !string_equals(paint_object->name, "Scene")) {
// Translate, scale
transform_t_array_t *trs = any_array_create_from_raw(
2026-03-08 08:39:29 +01:00
(void *[]){
2026-04-05 16:41:00 +02:00
g_context->gizmo_translate_x->transform,
g_context->gizmo_translate_y->transform,
g_context->gizmo_translate_z->transform,
g_context->gizmo_scale_x->transform,
g_context->gizmo_scale_y->transform,
g_context->gizmo_scale_z->transform,
2026-03-06 12:56:38 +01:00
},
6);
transform_t *hit = raycast_closest_box_intersect(trs, mouse_view_x(), mouse_view_y(), scene_camera);
2026-03-07 21:12:59 +01:00
if (hit != NULL) {
2026-04-05 16:41:00 +02:00
if (hit->object == g_context->gizmo_translate_x) {
g_context->translate_x = true;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (hit->object == g_context->gizmo_translate_y) {
g_context->translate_y = true;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (hit->object == g_context->gizmo_translate_z) {
g_context->translate_z = true;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (hit->object == g_context->gizmo_scale_x) {
g_context->scale_x = true;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (hit->object == g_context->gizmo_scale_y) {
g_context->scale_y = true;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (hit->object == g_context->gizmo_scale_z) {
g_context->scale_z = true;
2026-03-06 12:56:38 +01:00
}
2026-04-06 14:52:30 +02:00
if (g_context->translate_x || g_context->translate_y || g_context->translate_z || g_context->scale_x || g_context->scale_y || g_context->scale_z) {
2026-04-05 16:41:00 +02:00
g_context->gizmo_offset = 0.0;
g_context->gizmo_started = true;
2026-03-06 12:56:38 +01:00
}
}
else {
// Rotate
transform_t_array_t *trs = any_array_create_from_raw(
2026-03-08 08:39:29 +01:00
(void *[]){
2026-04-05 16:41:00 +02:00
g_context->gizmo_rotate_x->transform,
g_context->gizmo_rotate_y->transform,
g_context->gizmo_rotate_z->transform,
2026-03-06 12:56:38 +01:00
},
3);
transform_t *hit = raycast_closest_box_intersect(trs, mouse_view_x(), mouse_view_y(), scene_camera);
2026-03-07 21:12:59 +01:00
if (hit != NULL) {
2026-04-05 16:41:00 +02:00
if (hit->object == g_context->gizmo_rotate_x) {
g_context->rotate_x = true;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (hit->object == g_context->gizmo_rotate_y) {
g_context->rotate_y = true;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
else if (hit->object == g_context->gizmo_rotate_z) {
g_context->rotate_z = true;
2026-03-06 12:56:38 +01:00
}
2026-04-05 16:41:00 +02:00
if (g_context->rotate_x || g_context->rotate_y || g_context->rotate_z) {
g_context->gizmo_offset = 0.0;
g_context->gizmo_started = true;
2026-03-06 12:56:38 +01:00
}
}
2026-05-04 20:30:49 +02:00
else if (mouse_view_x() < base_w()) {
g_context->pick_object_id = true;
}
2026-03-06 12:56:38 +01:00
}
}
else if (mouse_released("left")) {
2026-04-05 16:41:00 +02:00
g_context->translate_x = g_context->translate_y = g_context->translate_z = false;
g_context->scale_x = g_context->scale_y = g_context->scale_z = false;
g_context->rotate_x = g_context->rotate_y = g_context->rotate_z = false;
2026-03-06 12:56:38 +01:00
}
2026-04-06 14:52:30 +02:00
if (g_context->translate_x || g_context->translate_y || g_context->translate_z || g_context->scale_x || g_context->scale_y || g_context->scale_z ||
g_context->rotate_x || g_context->rotate_y || g_context->rotate_z) {
2026-04-05 16:41:00 +02:00
g_context->rdirty = 2;
2026-03-06 12:56:38 +01:00
if (is_object) {
transform_t *t = paint_object->transform;
2026-04-05 23:00:47 +02:00
gizmo_v = (vec4_t){transform_world_x(t), transform_world_y(t), transform_world_z(t), 1.0};
2026-03-06 12:56:38 +01:00
}
else if (is_decal) {
2026-04-05 23:00:47 +02:00
gizmo_v = (vec4_t){g_context->layer->decal_mat.m30, g_context->layer->decal_mat.m31, g_context->layer->decal_mat.m32, 1.0};
2026-03-06 12:56:38 +01:00
}
2026-04-14 08:16:53 +02:00
// Project the world axis into screen space and map per-frame mouse delta onto it
2026-04-05 16:41:00 +02:00
if (g_context->translate_x || g_context->scale_x) {
2026-04-14 08:16:53 +02:00
if (g_context->gizmo_started) {
g_context->gizmo_drag = gizmo_v.x;
}
vec4_t s0 = vec4_apply_proj(gizmo_v, scene_camera->vp);
vec4_t s1 = vec4_apply_proj((vec4_t){gizmo_v.x + 1.0, gizmo_v.y, gizmo_v.z, 1.0}, scene_camera->vp);
f32 sax = (s1.x - s0.x) * (f32)base_w() * 0.5;
f32 say = -(s1.y - s0.y) * (f32)base_h() * 0.5;
f32 len2 = sax * sax + say * say;
if (len2 > 0.000001) {
g_context->gizmo_drag += (mouse_movement_x * sax + mouse_movement_y * say) / len2;
2026-03-06 12:56:38 +01:00
}
}
2026-04-05 16:41:00 +02:00
else if (g_context->translate_y || g_context->scale_y) {
2026-04-14 08:16:53 +02:00
if (g_context->gizmo_started) {
g_context->gizmo_drag = gizmo_v.y;
}
vec4_t s0 = vec4_apply_proj(gizmo_v, scene_camera->vp);
vec4_t s1 = vec4_apply_proj((vec4_t){gizmo_v.x, gizmo_v.y + 1.0, gizmo_v.z, 1.0}, scene_camera->vp);
f32 sax = (s1.x - s0.x) * (f32)base_w() * 0.5;
f32 say = -(s1.y - s0.y) * (f32)base_h() * 0.5;
f32 len2 = sax * sax + say * say;
if (len2 > 0.000001) {
g_context->gizmo_drag += (mouse_movement_x * sax + mouse_movement_y * say) / len2;
2026-03-06 12:56:38 +01:00
}
}
2026-04-05 16:41:00 +02:00
else if (g_context->translate_z || g_context->scale_z) {
2026-04-14 08:16:53 +02:00
if (g_context->gizmo_started) {
g_context->gizmo_drag = gizmo_v.z;
2026-03-06 12:56:38 +01:00
}
2026-04-14 08:16:53 +02:00
vec4_t s0 = vec4_apply_proj(gizmo_v, scene_camera->vp);
vec4_t s1 = vec4_apply_proj((vec4_t){gizmo_v.x, gizmo_v.y, gizmo_v.z + 1.0, 1.0}, scene_camera->vp);
f32 sax = (s1.x - s0.x) * (f32)base_w() * 0.5;
f32 say = -(s1.y - s0.y) * (f32)base_h() * 0.5;
f32 len2 = sax * sax + say * say;
if (len2 > 0.000001) {
g_context->gizmo_drag += (mouse_movement_x * sax + mouse_movement_y * say) / len2;
2026-03-06 12:56:38 +01:00
}
}
2026-04-14 08:16:53 +02:00
else if (g_context->rotate_x || g_context->rotate_y || g_context->rotate_z) {
// Project the two ring basis axes into screen pixels
vec4_t s0 = vec4_apply_proj(gizmo_v, scene_camera->vp);
f32 cx = (s0.x * 0.5 + 0.5) * (f32)base_w();
f32 cy = (-s0.y * 0.5 + 0.5) * (f32)base_h();
vec4_t e1p, e2p;
if (g_context->rotate_x) {
// Ring in YZ plane
e1p = vec4_apply_proj((vec4_t){gizmo_v.x, gizmo_v.y, gizmo_v.z + 1.0, 1.0}, scene_camera->vp);
e2p = vec4_apply_proj((vec4_t){gizmo_v.x, gizmo_v.y + 1.0, gizmo_v.z, 1.0}, scene_camera->vp);
2026-03-06 12:56:38 +01:00
}
2026-04-14 08:16:53 +02:00
else if (g_context->rotate_y) {
// Ring in XZ plane
e1p = vec4_apply_proj((vec4_t){gizmo_v.x + 1.0, gizmo_v.y, gizmo_v.z, 1.0}, scene_camera->vp);
e2p = vec4_apply_proj((vec4_t){gizmo_v.x, gizmo_v.y, gizmo_v.z + 1.0, 1.0}, scene_camera->vp);
}
else {
// Ring in XY plane
e1p = vec4_apply_proj((vec4_t){gizmo_v.x + 1.0, gizmo_v.y, gizmo_v.z, 1.0}, scene_camera->vp);
e2p = vec4_apply_proj((vec4_t){gizmo_v.x, gizmo_v.y + 1.0, gizmo_v.z, 1.0}, scene_camera->vp);
}
f32 e1x = (e1p.x * 0.5 + 0.5) * (f32)base_w() - cx;
f32 e1y = (-e1p.y * 0.5 + 0.5) * (f32)base_h() - cy;
f32 e2x = (e2p.x * 0.5 + 0.5) * (f32)base_w() - cx;
f32 e2y = (-e2p.y * 0.5 + 0.5) * (f32)base_h() - cy;
f32 det = e1x * e2y - e1y * e2x;
if (math_abs(det) > 0.000001) {
f32 mx = mouse_view_x() - cx;
f32 my = mouse_view_y() - cy;
f32 u = (mx * e2y - my * e2x) / det;
f32 v = (e1x * my - e1y * mx) / det;
2026-04-05 16:41:00 +02:00
if (g_context->gizmo_started) {
2026-04-06 14:52:30 +02:00
mat4_decomposed_t *dec = mat4_decompose(g_context->layer->decal_mat);
gizmo_v = dec->loc;
gizmo_q = dec->rot;
gizmo_v0 = dec->scl;
2026-04-14 08:16:53 +02:00
g_context->gizmo_offset = math_atan2(v, u);
2026-03-06 12:56:38 +01:00
}
2026-04-14 08:16:53 +02:00
g_context->gizmo_drag = math_atan2(v, u) - g_context->gizmo_offset;
2026-03-06 12:56:38 +01:00
}
}
2026-04-05 16:41:00 +02:00
if (g_context->gizmo_started) {
g_context->gizmo_drag_last = g_context->gizmo_drag;
2026-03-06 12:56:38 +01:00
}
}
2026-04-05 16:41:00 +02:00
_input_occupied = (g_context->translate_x || g_context->translate_y || g_context->translate_z || g_context->scale_x || g_context->scale_y ||
g_context->scale_z || g_context->rotate_x || g_context->rotate_y || g_context->rotate_z) &&
2026-03-06 12:56:38 +01:00
mouse_view_x() < base_w();
}