diff --git a/code/model/modelrender.h b/code/model/modelrender.h index 4d9d55842a2..e27b8aae728 100644 --- a/code/model/modelrender.h +++ b/code/model/modelrender.h @@ -313,6 +313,8 @@ void model_render_insignias(const insignia_draw_data* insignia); void model_render_set_wireframe_color(const color* clr); bool render_tech_model(tech_render_type model_type, int x1, int y1, int x2, int y2, float zoom, bool lighting, int class_idx, const matrix* orient, const SCP_string& pof_filename = "", float closeup_zoom = 0, const vec3d* closeup_pos = &vmd_zero_vector); +float convert_distance_and_diameter_to_pixel_size(float distance, float diameter, float field_of_view_deg, int screen_height); + float model_render_get_diameter_clamped_to_min_pixel_size(const vec3d* pos, float diameter, float min_pixel_size); void model_render_determine_color(color* clr, float alpha, gr_alpha_blend blend_mode, bool no_texturing, bool desaturate); diff --git a/code/weapon/weapon.h b/code/weapon/weapon.h index 1611e4035df..c2870e734db 100644 --- a/code/weapon/weapon.h +++ b/code/weapon/weapon.h @@ -344,6 +344,8 @@ enum class FiringPattern { float weapon_get_lifetime_pct(const weapon& wp); float weapon_get_age(const weapon& wp); +float weapon_get_viewing_angle(const weapon& wp); +float weapon_get_apparent_size(const weapon& wp); float beam_get_warmup_lifetime_pct(const beam& wp); float beam_get_warmdown_lifetime_pct(const beam& wp); @@ -689,6 +691,7 @@ struct weapon_info LASER_HEADON_SWITCH_ANG_MULT, LASER_HEADON_SWITCH_RATE_MULT, LASER_ANIM_STATE, + LASER_ANIM_STATE_ADD, LASER_ALPHA_MULT, LASER_BITMAP_R_MULT, LASER_BITMAP_G_MULT, @@ -722,6 +725,7 @@ struct weapon_info std::pair {"Laser Headon Transition Angle Mult", WeaponCurveOutputs::LASER_HEADON_SWITCH_ANG_MULT}, std::pair {"Laser Headon Transition Rate Mult", WeaponCurveOutputs::LASER_HEADON_SWITCH_RATE_MULT}, std::pair {"Laser Animation State", WeaponCurveOutputs::LASER_ANIM_STATE}, + std::pair {"Laser Animation State Add", WeaponCurveOutputs::LASER_ANIM_STATE_ADD}, std::pair {"Laser Alpha Mult", WeaponCurveOutputs::LASER_ALPHA_MULT}, std::pair {"Laser Bitmap R Mult", WeaponCurveOutputs::LASER_BITMAP_R_MULT}, std::pair {"Laser Bitmap G Mult", WeaponCurveOutputs::LASER_BITMAP_G_MULT}, @@ -742,6 +746,7 @@ struct weapon_info std::pair {"Lifetime", modular_curves_functional_input{}}, std::pair {"Age", modular_curves_functional_input{}}, std::pair {"Base Velocity", modular_curves_submember_input<&weapon::weapon_max_vel>{}}, + std::pair {"Base Damage", modular_curves_submember_input<&weapon::weapon_info_index, &Weapon_info, &weapon_info::damage>{}}, std::pair {"Max Hitpoints", modular_curves_submember_input<&weapon::weapon_info_index, &Weapon_info, &weapon_info::weapon_hitpoints>{}}, std::pair {"Current Hitpoints", modular_curves_submember_input<&weapon::objnum, &Objects, &object::hull_strength>{}}, std::pair {"Hitpoints Fraction", modular_curves_math_input< @@ -749,7 +754,9 @@ struct weapon_info modular_curves_submember_input<&weapon::weapon_info_index, &Weapon_info, &weapon_info::weapon_hitpoints>, ModularCurvesMathOperators::division >{}}, - std::pair {"Parent Radius", modular_curves_submember_input<&weapon::objnum, &Objects, &object::parent, &Objects, &object::radius>{}} + std::pair {"Parent Radius", modular_curves_submember_input<&weapon::objnum, &Objects, &object::parent, &Objects, &object::radius>{}}, + std::pair {"Viewing Angle", modular_curves_functional_input{}}, + std::pair {"Apparent Size", modular_curves_functional_input{}} ); public: diff --git a/code/weapon/weapons.cpp b/code/weapon/weapons.cpp index cfda04a9532..877243c4828 100644 --- a/code/weapon/weapons.cpp +++ b/code/weapon/weapons.cpp @@ -61,6 +61,7 @@ #include "tracing/Monitor.h" #include "tracing/tracing.h" #include "weapon.h" +#include "model/modelrender.h" // Since SSMs are parsed after weapons, if we want to allow SSM strikes to be specified by name, we need to store those names until after SSMs are parsed. @@ -9089,7 +9090,8 @@ float weapon_render_headon_bitmap(object* wep_objp, vec3d* headp, vec3d* tailp, weapon_info* wip = &Weapon_info[wp->weapon_info_index]; vec3d center, reye; - vm_vec_avg(¢er, headp, &wep_objp->pos); + vm_vec_avg(¢er, headp, tailp); + vm_vec_sub(&reye, &Eye_position, ¢er); vm_vec_normalize(&reye); float ang = vm_vec_delta_ang_norm(&reye, &wep_objp->orient.vec.fvec, nullptr); @@ -9186,6 +9188,10 @@ void weapon_render(object* obj, model_draw_list *scene) float switch_rate_mult = wip->weapon_curves.get_output(weapon_info::WeaponCurveOutputs::LASER_HEADON_SWITCH_RATE_MULT, *wp, &wp->modular_curves_instance); bool anim_has_curve = wip->weapon_curves.has_curve(weapon_info::WeaponCurveOutputs::LASER_ANIM_STATE); float anim_state = wip->weapon_curves.get_output(weapon_info::WeaponCurveOutputs::LASER_ANIM_STATE, *wp, &wp->modular_curves_instance); + float anim_state_add = 0.f; + if (wip->weapon_curves.has_curve(weapon_info::WeaponCurveOutputs::LASER_ANIM_STATE_ADD)) { + anim_state_add = wip->weapon_curves.get_output(weapon_info::WeaponCurveOutputs::LASER_ANIM_STATE_ADD, *wp, &wp->modular_curves_instance); + } float alpha_mult = wip->weapon_curves.get_output(weapon_info::WeaponCurveOutputs::LASER_ALPHA_MULT, *wp, &wp->modular_curves_instance); float bitmap_r_mult = wip->weapon_curves.get_output(weapon_info::WeaponCurveOutputs::LASER_BITMAP_R_MULT, *wp, &wp->modular_curves_instance); float bitmap_g_mult = wip->weapon_curves.get_output(weapon_info::WeaponCurveOutputs::LASER_BITMAP_G_MULT, *wp, &wp->modular_curves_instance); @@ -9212,6 +9218,9 @@ void weapon_render(object* obj, model_draw_list *scene) if (laser_length < 0.0001f) return; + if (head_radius < 0.0001f && tail_radius < 0.0001f) + return; + if (head_radius <= 0.0001f) { head_radius = 0.0001f; } @@ -9245,7 +9254,7 @@ void weapon_render(object* obj, model_draw_list *scene) wp->laser_bitmap_frame += flFrametime; if (anim_has_curve) { - framenum = fl2i(i2fl(wip->laser_bitmap.num_frames - 1) * anim_state); + framenum = fl2i(i2fl(wip->laser_bitmap.num_frames - 1) * (anim_state + anim_state_add)); } else { framenum = bm_get_anim_frame(wip->laser_bitmap.first_frame, wp->laser_bitmap_frame, wip->laser_bitmap.total_time, true); } @@ -9257,12 +9266,12 @@ void weapon_render(object* obj, model_draw_list *scene) wp->laser_headon_bitmap_frame += flFrametime; if (anim_has_curve) { - framenum = fl2i(i2fl(wip->laser_headon_bitmap.num_frames - 1) * anim_state); + headon_framenum = fl2i(i2fl(wip->laser_headon_bitmap.num_frames - 1) * (anim_state + anim_state_add)); } else { headon_framenum = bm_get_anim_frame(wip->laser_headon_bitmap.first_frame, wp->laser_headon_bitmap_frame, wip->laser_headon_bitmap.total_time, true); } - CLAMP(framenum, 0, wip->laser_headon_bitmap.num_frames - 1); + CLAMP(headon_framenum, 0, wip->laser_headon_bitmap.num_frames - 1); } if (wip->wi_flags[Weapon::Info_Flags::Transparent]) { @@ -9342,7 +9351,7 @@ void weapon_render(object* obj, model_draw_list *scene) wp->laser_glow_bitmap_frame -= wip->laser_glow_bitmap.total_time; if (anim_has_curve) { - framenum = fl2i(i2fl(wip->laser_glow_bitmap.num_frames) * anim_state); + framenum = fl2i(i2fl(wip->laser_glow_bitmap.num_frames - 1) * (anim_state + anim_state_add)); } else { framenum = fl2i( (wp->laser_glow_bitmap_frame * wip->laser_glow_bitmap.num_frames) / wip->laser_glow_bitmap.total_time ); } @@ -9363,7 +9372,7 @@ void weapon_render(object* obj, model_draw_list *scene) wp->laser_glow_headon_bitmap_frame -= wip->laser_glow_headon_bitmap.total_time; if (anim_has_curve) { - framenum = fl2i(i2fl(wip->laser_glow_headon_bitmap.num_frames) * anim_state); + headon_framenum = fl2i(i2fl(wip->laser_glow_headon_bitmap.num_frames - 1) * (anim_state + anim_state_add)); } else { headon_framenum = fl2i((wp->laser_glow_headon_bitmap_frame * wip->laser_glow_headon_bitmap.num_frames) / wip->laser_glow_headon_bitmap.total_time); } @@ -10467,4 +10476,36 @@ float weapon_get_lifetime_pct(const weapon& wp) { float weapon_get_age(const weapon& wp) { return f2fl(Missiontime - wp.creation_time); +} + +float weapon_get_viewing_angle(const weapon& wp) { + object* wep_objp = &Objects[wp.objnum]; + weapon_info* wip = &Weapon_info[wp.weapon_info_index]; + vec3d center; + if (wip->render_type != WRT_LASER) { + center = wep_objp->pos; + } else { + vec3d rotated_offset; + vm_vec_unrotate(&rotated_offset, &wip->laser_pos_offset, &wep_objp->orient); + + center = wep_objp->pos + (wep_objp->orient.vec.fvec * wip->laser_length * 0.5f); + center += rotated_offset * wip->laser_length; + } + + vec3d reye; + vm_vec_sub(&reye, ¢er, &Eye_position); + vm_vec_normalize(&reye); + return vm_vec_dot(&reye, &wep_objp->orient.vec.fvec); +} + +float weapon_get_apparent_size(const weapon& wp) { + object* wep_objp = &Objects[wp.objnum]; + + float dist = vm_vec_dist(&Eye_position, &wep_objp->pos); + + return convert_distance_and_diameter_to_pixel_size( + dist, + wep_objp->radius, + fl_degrees(g3_get_hfov(Eye_fov)), + gr_screen.max_h) / i2fl(gr_screen.max_h); } \ No newline at end of file