From f1eb4ea855d51bb0fd3db0d4e4879cf7f26d2160 Mon Sep 17 00:00:00 2001 From: jacob Date: Tue, 10 Feb 2026 21:36:37 -0600 Subject: [PATCH] rotation-agnostic crosshair movement --- src/base/base_math.c | 8 ++- src/base/base_math.h | 2 +- src/pp/pp.c | 45 ++++++++---- src/pp/pp.h | 3 +- src/pp/pp_res/sprite/bla3.ase | 4 +- src/pp/pp_vis/pp_vis_core.c | 124 +++++++++++--------------------- src/pp/pp_vis/pp_vis_shared.cgh | 4 +- 7 files changed, 87 insertions(+), 103 deletions(-) diff --git a/src/base/base_math.c b/src/base/base_math.c index dc302dd8..cd030b94 100644 --- a/src/base/base_math.c +++ b/src/base/base_math.c @@ -453,7 +453,7 @@ Vec2 Vec2WithLen(Vec2 a, f32 len) return a; } -Vec2 ClampVec2Len(Vec2 a, f32 max) +Vec2 ClampVec2Len(Vec2 a, f32 min, f32 max) { f32 l_sq = a.x * a.x + a.y * a.y; if (l_sq > max * max) @@ -462,6 +462,12 @@ Vec2 ClampVec2Len(Vec2 a, f32 max) a.x *= denom; a.y *= denom; } + else if (l_sq < min * min) + { + f32 denom = min / SqrtF32(l_sq); + a.x *= denom; + a.y *= denom; + } return a; } diff --git a/src/base/base_math.h b/src/base/base_math.h index a0fd73c5..692ba4fa 100644 --- a/src/base/base_math.h +++ b/src/base/base_math.h @@ -366,7 +366,7 @@ f32 Vec2LenSq(Vec2 a); Vec2 Vec2WithLen(Vec2 a, f32 len); f32 Vec2Distance(Vec2 a, Vec2 b); Vec2 NormVec2(Vec2 a); -Vec2 ClampVec2Len(Vec2 a, f32 max); +Vec2 ClampVec2Len(Vec2 a, f32 min, f32 max); //- Clamp Vec2 ClampVec2(Vec2 v, Rng2 r); diff --git a/src/pp/pp.c b/src/pp/pp.c index 42884441..c9c67797 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -2082,7 +2082,7 @@ void P_StepFrame(P_Frame *frame) // Normalize controls for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) { - ent->control.move = ClampVec2Len(ent->control.move, 1); + ent->control.move = ClampVec2Len(ent->control.move, 0, 1); } } @@ -2133,7 +2133,7 @@ void P_StepFrame(P_Frame *frame) } } } -\ + ////////////////////////////// //- Setup constraints store @@ -2719,7 +2719,8 @@ void P_StepFrame(P_Frame *frame) Vec2 fire_pos = Zi; Vec2 fire_dir = Zi; - Vec2 fire_base = Zi; + Vec2 fire_base0 = Zi; + Vec2 fire_base1 = Zi; if (can_fire) { Vec2 look = firer->control.look; @@ -2753,7 +2754,7 @@ void P_StepFrame(P_Frame *frame) SPR_Ray body_ap_ray = body.rays[SPR_RayKind_Ap]; wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, InvertRot(body_anchor_ray.dir)); wep_pix_to_ent_af = TranslateAffine(wep_pix_to_ent_af, SubVec2(body_ap_ray.pos, body_anchor_ray.pos)); - wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, InvertRot(body_ap_ray.dir)); + wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, NegVec2(body_ap_ray.dir)); SPR_Ray anchor_ray = wep.rays[SPR_RayKind_Anchor]; wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, anchor_ray.dir); @@ -2768,11 +2769,18 @@ void P_StepFrame(P_Frame *frame) fire_pos = MulAffineVec2(wep_pix_to_world_af, fire_ray.pos); fire_dir = NormRot(MulAffineBasisVec2(wep_pix_to_world_af, fire_ray.dir)); - fire_base = MulVec2(PerpVec2(NegVec2(firer->xf.r)), WedgeVec2(firer->xf.r, SubVec2(firer->xf.t, fire_pos))); - fire_base = AddVec2(fire_base, firer->xf.t); + fire_base0 = MulVec2(PerpVec2(NegVec2(firer->xf.r)), WedgeVec2(firer->xf.r, SubVec2(firer->xf.t, fire_pos))); + fire_base0 = AddVec2(fire_base0, firer->xf.t); - P_DebugDrawLine(fire_base, fire_pos, Color_Yellow); - P_DebugDrawPoint(fire_base, Color_Yellow); + Vec2 chamber_pos = MulAffineVec2(body_pix_to_world_af, body.rays[SPR_RayKind_Ap].pos); + + fire_base1 = MulVec2(PerpVec2(fire_dir), WedgeVec2(fire_dir, SubVec2(fire_pos, chamber_pos))); + fire_base1 = AddVec2(fire_base1, chamber_pos); + + P_DebugDrawLine(fire_base0, fire_base1, Color_Yellow); + P_DebugDrawLine(fire_base1, fire_pos, Color_Green); + P_DebugDrawPoint(fire_base0, Color_Yellow); + P_DebugDrawPoint(fire_base1, Color_Green); P_DebugDrawPoint(fire_pos, Color_Red); } @@ -2805,7 +2813,8 @@ void P_StepFrame(P_Frame *frame) Vec2 dir = Vec2FromAngle(angle); - bullet->bullet_base = fire_base; + bullet->bullet_base0 = fire_base0; + bullet->bullet_base1 = fire_base1; bullet->bullet_start = fire_pos; bullet->bullet_end = AddVec2(bullet->bullet_start, MulVec2(dir, speed)); bullet->bullet_firer = firer->key; @@ -2844,10 +2853,20 @@ void P_StepFrame(P_Frame *frame) // wasn't obstructed (e.g. to prevent shooting through walls), so we // insert a path from the bullet's base to its starting position before // its actual firing path - BulletPath *path = PushStruct(scratch.arena, BulletPath); - SllQueuePush(first_bullet_path, last_bullet_path, path); - path->start = bullet->bullet_base; - path->end = bullet->bullet_start; + { + // Firer origin -> weapon base path + BulletPath *path = PushStruct(scratch.arena, BulletPath); + SllQueuePush(first_bullet_path, last_bullet_path, path); + path->start = bullet->bullet_base0; + path->end = bullet->bullet_base1; + } + { + // Weapon base -> bullet start path + BulletPath *path = PushStruct(scratch.arena, BulletPath); + SllQueuePush(first_bullet_path, last_bullet_path, path); + path->start = bullet->bullet_base1; + path->end = bullet->bullet_start; + } } { diff --git a/src/pp/pp.h b/src/pp/pp.h index 5a563ad4..5d4f8079 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -130,7 +130,8 @@ Struct(P_Ent) P_EntKey bullet_firer; b32 is_bullet; - Vec2 bullet_base; + Vec2 bullet_base0; + Vec2 bullet_base1; Vec2 bullet_start; Vec2 bullet_end; diff --git a/src/pp/pp_res/sprite/bla3.ase b/src/pp/pp_res/sprite/bla3.ase index 31463a27..383be1d9 100644 --- a/src/pp/pp_res/sprite/bla3.ase +++ b/src/pp/pp_res/sprite/bla3.ase @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aae562db3b8adf3af25dc82e69a7e4176162f137f1775aa69658aef8bd4cffda -size 3071 +oid sha256:970fa7fd4366377968765630188fa04531e6534e20b814c46404aacca01cf6fa +size 3551 diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 6944ca6f..b2660c1d 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -841,7 +841,8 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Compute movement & look - f32 look_radius = 5; + f32 max_look_radius = 5; + f32 min_look_radius = 0.01; frame->is_looking = !frame->is_editing && !frame->palette.is_showing && !frame->held_buttons[Button_Alt]; frame->is_moving = !frame->is_editing; @@ -854,17 +855,26 @@ void V_TickForever(WaveLaneCtx *lane) if (frame->held_buttons[Button_W]) move.y -= 1; if (frame->held_buttons[Button_S]) move.y += 1; } - move = ClampVec2Len(move, 1); + move = ClampVec2Len(move, 0, 1); f32 fire_held = frame->held_buttons[Button_M1]; Vec2 look = Zi; f32 fire_presses = fire_held && !prev_frame->held_buttons[Button_M1]; { - // TODO: Adjustable sensitivity f32 mouse_sensitivity = TweakFloat("Mouse sensitivity", 1.0, 0.1, 5.0); f32 mouse_scale_factor = 0.007; + + Vec2 look_delta = mouse_delta; + look_delta = MulVec2(look_delta, mouse_sensitivity * mouse_scale_factor); + + Vec2 prev_crosshair_rot = NormRot(SubVec2(prev_frame->world_crosshair, prev_frame->world_crosshair_base)); + Vec2 prev_look_rot = NormRot(prev_frame->look); + Vec2 rot = RotateVec2(prev_look_rot, InvertRot(prev_crosshair_rot)); + + look_delta = RotateVec2(look_delta, rot); + look = frame->look; - look = AddVec2(look, MulVec2(mouse_delta, mouse_sensitivity * mouse_scale_factor)); - look = ClampVec2Len(look, look_radius); + look = AddVec2(look, look_delta); + look = ClampVec2Len(look, min_look_radius, max_look_radius); } if (frame->is_looking) @@ -931,86 +941,24 @@ void V_TickForever(WaveLaneCtx *lane) } else { - // // Vec2 look_ratio = VEC2(0.5, 0.5); - // Vec2 look_ratio = VEC2(0.75, 0.75); - // // Vec2 look_ratio = VEC2(1.0, 1.0); - // { - // // look_ratio.y = 0.5; - // // look_ratio.x = look_ratio.y / (16.0 / 9.0); - // } - - // Vec2 look_ratio = Zi; - // { - // Vec2 max_look = Vec2WithLen(frame->look, look_radius); - - // Vec2 look_ratio_of_max = DivVec2Vec2(frame->look, Vec2WithLen(frame->look, look_radius)); - - // look_ratio = look_ratio_of_max; - - // // Vec2 screen_look = MulAffineVec2(frame->af.world_to_screen, frame->look); - // // Vec2 screen_max_look = MulAffineVec2(frame->af.world_to_screen, frame->look) - - // // look_ratio.x = screen_look.x / frame->screen_dims.x; - // // look_ratio.x = frame->look.x / look_radius; - - - // if (IsNan(look_ratio.x) || IsNan(look_ratio.y)) - // { - // look_ratio = VEC2(0, 0); - // } - // } - - - - - - // Vec2 look_ratio = Zi; - // { - // Vec2 max_look = Vec2WithLen(frame->look, look_radius); - - // Vec2 look_ratio_of_radius = DivVec2Vec2(frame->look, Vec2WithLen(frame->look, look_radius)); - - // look_ratio = look_ratio_of_radius; - - // // Vec2 screen_look = MulAffineVec2(frame->af.world_to_screen, frame->look); - // // Vec2 screen_max_look = MulAffineVec2(frame->af.world_to_screen, frame->look) - - // // look_ratio.x = screen_look.x / frame->screen_dims.x; - // // look_ratio.x = frame->look.x / look_radius; - - - // if (IsNan(look_ratio.x) || IsNan(look_ratio.y)) - // { - // look_ratio = VEC2(0, 0); - // } - - // // look_ratio = MulVec2(look_ratio, 0.5); - // look_ratio = VEC2(0.5, 0.5); - // } - - - - - - - Vec2 look_ratio = Zi; { look_ratio = VEC2(0.5, 0.5); - // look_ratio = VEC2(0.5, 0.5 * (16.0 / 9.0)); } + Vec2 prev_crosshair_rot = NormRot(SubVec2(prev_frame->world_crosshair, prev_frame->world_crosshair_base)); + Vec2 prev_look_rot = NormRot(prev_frame->look); + Vec2 rot = RotateVec2(prev_look_rot, InvertRot(prev_crosshair_rot)); - - - + Vec2 look_pos = frame->look; + look_pos = RotateVec2(look_pos, InvertRot(rot)); P_Ent *player = P_EntFromKey(local_world->last_frame, V.player_key); P_Ent *guy = P_EntFromKey(local_world->last_frame, player->guy); Vec2 guy_center = P_WorldShapeFromEnt(guy).centroid; Vec2 screen_center = MulVec2(frame->screen_dims, 0.5); target_camera_pos = guy_center; - target_camera_pos = AddVec2(target_camera_pos, MulVec2Vec2(frame->look, look_ratio)); + target_camera_pos = AddVec2(target_camera_pos, MulVec2Vec2(look_pos, look_ratio)); target_camera_zoom = 1; } target_camera_pos.x = ClampF32(target_camera_pos.x, -world_pitch / 2, world_pitch / 2); @@ -1872,6 +1820,7 @@ void V_TickForever(WaveLaneCtx *lane) //- Compute crosshair position // TODO: Alive / weapon check + if (!P_IsEntNil(local_guy)) { P_Ent *ent = local_guy; @@ -1916,7 +1865,7 @@ void V_TickForever(WaveLaneCtx *lane) SPR_Ray body_ap_ray = body.rays[SPR_RayKind_Ap]; wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, InvertRot(body_anchor_ray.dir)); wep_pix_to_ent_af = TranslateAffine(wep_pix_to_ent_af, SubVec2(body_ap_ray.pos, body_anchor_ray.pos)); - wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, InvertRot(body_ap_ray.dir)); + wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, NegVec2(body_ap_ray.dir)); SPR_Ray anchor_ray = wep.rays[SPR_RayKind_Anchor]; wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, anchor_ray.dir); @@ -1936,25 +1885,31 @@ void V_TickForever(WaveLaneCtx *lane) Vec2 desired_fire_pos = MulAffineVec2(desired_wep_pix_to_world_af, fire_ray.pos); Vec2 desired_fire_dir = NormRot(MulAffineBasisVec2(desired_wep_pix_to_world_af, fire_ray.dir)); + Vec2 desired_hold_pos = MulAffineVec2(desired_body_pix_to_world_af, body.rays[SPR_RayKind_Ap].pos); + + Vec2 desired_chamber_pos = MulVec2(PerpVec2(desired_fire_dir), WedgeVec2(desired_fire_dir, SubVec2(desired_fire_pos, desired_hold_pos))); + desired_chamber_pos = AddVec2(desired_chamber_pos, desired_hold_pos); + + Vec2 desired_crosshair_base = MulVec2(PerpVec2(NegVec2(desired_ent_to_world_af.bx)), WedgeVec2(desired_ent_to_world_af.bx, SubVec2(desired_ent_to_world_af.og, desired_chamber_pos))); + desired_crosshair_base = AddVec2(desired_crosshair_base, desired_ent_to_world_af.og); + + frame->world_guy_origin = desired_ent_to_world_af.og; + frame->world_crosshair_base = desired_crosshair_base; + Vec2 look_rot = NormRot(look); Vec2 line_start = cur_fire_pos; Vec2 line_end = AddVec2(line_start, cur_fire_dir); - // P_DebugDrawLine(line_start, line_end, Color_Yellow); - // Vec2 cross = fire_pos; - // Vec2 cross = MulAffineVec2(frame->af.screen_to_world, look); + f32 cross_dist = Vec2Len(look); - // f32 cross_dist = 1.0; - f32 cross_dist = Vec2Len(look) - Vec2Len(SubVec2(desired_fire_pos, desired_ent_to_world_af.og)); + frame->world_crosshair = AddVec2(frame->world_crosshair_base, MulVec2(NormVec2(desired_fire_dir), cross_dist)); - frame->world_crosshair = AddVec2(desired_fire_pos, MulVec2(look_rot, cross_dist)); frame->screen_crosshair = MulAffineVec2(frame->af.world_to_screen, frame->world_crosshair); frame->shade_crosshair = MulAffineVec2(frame->af.world_to_shade, frame->world_crosshair); - // P_DebugDrawPoint(AddVec2(MulAffineVec2(frame->af.screen_to_world, MulVec2(frame->screen_dims, 0.5)), look), Color_Red); - // P_DebugDrawPoint(AddVec2(desired_ent_to_world_af.og, look), Color_Red); - // P_DebugDrawPoint(frame->crosshair, Color_Green); + P_DebugDrawPoint(AddVec2(desired_ent_to_world_af.og, look), Color_Red); + P_DebugDrawPoint(frame->world_crosshair_base, Color_Yellow); } ////////////////////////////// @@ -2150,7 +2105,8 @@ void V_TickForever(WaveLaneCtx *lane) SPR_Ray body_ap_ray = body.rays[SPR_RayKind_Ap]; wep_pix_to_world_af = RotateAffine(wep_pix_to_world_af, InvertRot(body_anchor_ray.dir)); wep_pix_to_world_af = TranslateAffine(wep_pix_to_world_af, SubVec2(body_ap_ray.pos, body_anchor_ray.pos)); - wep_pix_to_world_af = RotateAffine(wep_pix_to_world_af, InvertRot(body_ap_ray.dir)); + // wep_pix_to_world_af = RotateAffine(wep_pix_to_world_af, InvertRot(body_ap_ray.dir)); + wep_pix_to_world_af = RotateAffine(wep_pix_to_world_af, NegVec2(body_ap_ray.dir)); SPR_Ray anchor_ray = wep.rays[SPR_RayKind_Anchor]; wep_pix_to_world_af = RotateAffine(wep_pix_to_world_af, anchor_ray.dir); diff --git a/src/pp/pp_vis/pp_vis_shared.cgh b/src/pp/pp_vis/pp_vis_shared.cgh index 567b9aed..cad59ecc 100644 --- a/src/pp/pp_vis/pp_vis_shared.cgh +++ b/src/pp/pp_vis/pp_vis_shared.cgh @@ -115,9 +115,11 @@ Struct(V_SharedFrame) //- Crosshair + Vec2 world_guy_origin; + Vec2 world_crosshair_base; + Vec2 world_crosshair; Vec2 screen_crosshair; Vec2 shade_crosshair; - Vec2 world_crosshair; //- Control