diff --git a/apps/tools/twndel/state.h b/apps/tools/twndel/state.h index f24bdab..2af877d 100644 --- a/apps/tools/twndel/state.h +++ b/apps/tools/twndel/state.h @@ -19,6 +19,7 @@ #define INVALID_TEXTURE (TEXTURE_LIMIT+1) #define CAMERA_ROTATION_SPEED 0.04f +#define CAMERA_TRANSLATION_SPEED 0.04f #define SELECTION_SPHERE_RADIUS 32 /* should be an odd number */ #define SNAP_LINES_SHOW 7 @@ -68,6 +69,7 @@ typedef struct State { uint32_t op_stack_ptr; bool op_active; + Vec3 active_center; Vec3 camera_position; Vec3 camera_direction; float camera_zoom; diff --git a/apps/tools/twndel/tool.c b/apps/tools/twndel/tool.c index 82b5050..22f24e0 100644 --- a/apps/tools/twndel/tool.c +++ b/apps/tools/twndel/tool.c @@ -132,12 +132,7 @@ static uint8_t new_cube(Point pos, Point size) { } -static void process_camera_movement(void) { - input_action("camera_rotate_left", "A"); - input_action("camera_rotate_right", "D"); - input_action("camera_rotate_up", "W"); - input_action("camera_rotate_down", "S"); - +static void process_camera_rotation(void) { float horizontal_rotation = 0; float vertical_rotation = 0; @@ -151,18 +146,70 @@ static void process_camera_movement(void) { vertical_rotation += CAMERA_ROTATION_SPEED; Vec3 front = vec3_cross(state.camera_direction, (Vec3){0,1,0}); + Vec3 local_position = vec3_sub(state.active_center, state.camera_position); + Vec3 new_local_position = vec3_rotate(local_position, horizontal_rotation, (Vec3){0,1,0}); - state.camera_position = vec3_rotate(state.camera_position, horizontal_rotation, (Vec3){0,1,0}); state.camera_direction = vec3_rotate(state.camera_direction, horizontal_rotation, (Vec3){0,1,0}); - Vec3 new_rot = vec3_rotate(state.camera_direction, vertical_rotation, front); /* only apply if it's in limits */ float d = vec3_dot(new_rot, (Vec3){0,-1,0}); if (fabsf(d) <= 0.999f) { - state.camera_position = vec3_rotate(state.camera_position, vertical_rotation, front); + new_local_position = vec3_rotate(new_local_position, vertical_rotation, front); state.camera_direction = new_rot; } + + state.camera_position = vec3_sub(state.active_center, new_local_position); +} + + +static void process_camera_translation(void) { + Vec3 right = vec3_norm(vec3_cross(state.camera_direction, (Vec3){0,1,0})); + Vec3 up = vec3_norm(vec3_cross(state.camera_direction, right)); + Vec3 was = state.camera_position; + + if (input_action_pressed("camera_rotate_left")) + state.camera_position = vec3_sub(state.camera_position, vec3_scale(right, CAMERA_TRANSLATION_SPEED)); + if (input_action_pressed("camera_rotate_right")) + state.camera_position = vec3_add(state.camera_position, vec3_scale(right, CAMERA_TRANSLATION_SPEED)); + if (input_action_pressed("camera_rotate_up")) + state.camera_position = vec3_sub(state.camera_position, vec3_scale(up, CAMERA_TRANSLATION_SPEED)); + if (input_action_pressed("camera_rotate_down")) + state.camera_position = vec3_add(state.camera_position, vec3_scale(up, CAMERA_TRANSLATION_SPEED)); + + state.active_center = vec3_add(state.active_center, vec3_sub(state.camera_position, was)); + + draw_billboard("/data/camera.png", + vec3_add(state.camera_position, vec3_scale(state.camera_direction, vec3_length(state.camera_position))), + (Vec2){0.2f,0.2f}, + (Rect){0,0,16,16}, + (Color){255,255,255,255}, + false); + + /* show relation to origin */ + draw_billboard("/data/center.png", + (Vec3){0}, + (Vec2){0.1f,0.1f}, + (Rect){0,0,16,16}, + (Color){255,255,255,255}, + false); + + draw_line_3d((Vec3){0}, state.active_center, 1, (Color){255,255,255,255}); +} + + +static void process_camera_movement(void) { + input_action("camera_rotate_left", "A"); + input_action("camera_rotate_right", "D"); + input_action("camera_rotate_up", "W"); + input_action("camera_rotate_down", "S"); + input_action("camera_lock_rotation", "SPACE"); + + if (input_action_pressed("camera_lock_rotation")) { + process_camera_translation(); + } else { + process_camera_rotation(); + } } @@ -401,6 +448,35 @@ static void process_operations(void) { } +static void draw_axes(void) { + /* axis helpers */ + /* idea: double selection of axes for diagonal edits */ + draw_line_3d(state.active_center, (Vec3){256,0,0}, 1, state.axis_mask[0] ? (Color){0,0,0,75} : (Color){255,0,0,125}); + draw_billboard("/data/x.png", + vec3_add(state.active_center, (Vec3){2,0,0}), + (Vec2){0.1f, 0.1f}, + (Rect){0}, + state.axis_mask[0] ? (Color){0,0,0,255} : (Color){255,0,0,255}, + false); + + draw_line_3d(state.active_center, (Vec3){0,256,0}, 1, state.axis_mask[1] ? (Color){0,0,0,75} : (Color){75,125,25,125}); + draw_billboard("/data/y.png", + vec3_add(state.active_center, (Vec3){0,1.5f,0}), + (Vec2){0.1f, 0.1f}, + (Rect){0}, + state.axis_mask[1] ? (Color){0,0,0,255} : (Color){75,125,25,255}, + false); + + draw_line_3d(state.active_center, (Vec3){0,0,256}, 1, state.axis_mask[2] ? (Color){0,0,0,75} : (Color){0,0,255,125}); + draw_billboard("/data/z.png", + vec3_add(state.active_center, (Vec3){0,0,2}), + (Vec2){0.1f, 0.1f}, + (Rect){0}, + state.axis_mask[2] ? (Color){0,0,0,255} : (Color){0,0,255,255}, + false); +} + + void game_tick(void) { if (!init) { /* default state */ @@ -448,32 +524,7 @@ void game_tick(void) { process_camera_movement(); process_operations(); - /* axis helpers */ - /* idea: black out inactives when dragging points */ - /* idea: double selection of axes for diagonal edits */ - draw_line_3d((Vec3){0}, (Vec3){256,0,0}, 1, state.axis_mask[0] ? (Color){0,0,0,75} : (Color){255,0,0,125}); - draw_billboard("/data/x.png", - (Vec3){2,0,0}, - (Vec2){0.1f, 0.1f}, - (Rect){0}, - state.axis_mask[0] ? (Color){0,0,0,255} : (Color){255,0,0,255}, - false); - - draw_line_3d((Vec3){0}, (Vec3){0,256,0}, 1, state.axis_mask[1] ? (Color){0,0,0,75} : (Color){75,125,25,125}); - draw_billboard("/data/y.png", - (Vec3){0,1.5f,0}, - (Vec2){0.1f, 0.1f}, - (Rect){0}, - state.axis_mask[1] ? (Color){0,0,0,255} : (Color){75,125,25,255}, - false); - - draw_line_3d((Vec3){0}, (Vec3){0,0,256}, 1, state.axis_mask[2] ? (Color){0,0,0,75} : (Color){0,0,255,125}); - draw_billboard("/data/z.png", - (Vec3){0,0,2}, - (Vec2){0.1f, 0.1f}, - (Rect){0}, - state.axis_mask[2] ? (Color){0,0,0,255} : (Color){0,0,255,255}, - false); + draw_axes(); for (uint8_t obj = 0; obj < state.objects_sz; ++obj) render_object(obj); diff --git a/src/rendering/twn_draw.c b/src/rendering/twn_draw.c index 0705155..6ef9ac0 100644 --- a/src/rendering/twn_draw.c +++ b/src/rendering/twn_draw.c @@ -531,8 +531,8 @@ DrawCameraUnprojectResult draw_camera_unproject(Vec2 point, /* simpler case, just shoot a point from viewbox face along the supplied direction */ if (orthographic) { - Vec3 right = vec3_cross(direction, up); - Vec3 aup = vec3_cross(direction, right); + Vec3 right = vec3_norm(vec3_cross(direction, up)); + Vec3 aup = vec3_norm(vec3_cross(direction, right)); return (DrawCameraUnprojectResult){ .direction = direction, .position = vec3_add(vec3_add(position, vec3_scale(right, v.x * aspect/zoom)), vec3_scale(aup, -v.y * aspect/zoom)),