Advertisement
Guest User

level_bullet_manager.cpp

a guest
Sep 7th, 2016
187
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 34.70 KB | None | 0 0
  1. // Level_Bullet_Manager.cpp: для обеспечения полета пули по траектории
  2. // все пули и осколки передаются сюда
  3. //////////////////////////////////////////////////////////////////////
  4.  
  5. #include "stdafx.h"
  6. #include "Level.h"
  7. #include "Level_Bullet_Manager.h"
  8. #include "game_cl_base.h"
  9. #include "Actor.h"
  10. #include "gamepersistent.h"
  11. #include "mt_config.h"
  12. #include "game_cl_base_weapon_usage_statistic.h"
  13. #include "game_cl_mp.h"
  14. #include "reward_event_generator.h"
  15.  
  16. #include "../Include/xrRender/UIRender.h"
  17. #include "../Include/xrRender/Kinematics.h"
  18.  
  19. #ifdef DEBUG
  20. # include "debug_renderer.h"
  21. #endif
  22.  
  23. #define HIT_POWER_EPSILON 0.05f
  24. #define WALLMARK_SIZE 0.04f
  25.  
  26. float CBulletManager::m_fMinBulletSpeed = 2.f;
  27. float const CBulletManager::parent_ignore_distance = 3.f;
  28.  
  29. #ifdef DEBUG
  30. float air_resistance_epsilon = .1f;
  31. #else // #ifdef DEBUG
  32. static float const air_resistance_epsilon = .1f;
  33. #endif // #ifdef DEBUG
  34. float g_bullet_time_factor = 1.f;
  35.  
  36. SBullet::SBullet()
  37. {
  38. }
  39.  
  40. SBullet::~SBullet()
  41. {
  42. }
  43.  
  44.  
  45. void SBullet::Init(const Fvector& position,
  46. const Fvector& direction,
  47. float starting_speed,
  48. float power,
  49. //. float power_critical,
  50. float impulse,
  51. u16 sender_id,
  52. u16 sendersweapon_id,
  53. ALife::EHitType e_hit_type,
  54. float maximum_distance,
  55. const CCartridge& cartridge,
  56. float const air_resistance_factor,
  57. bool SendHit)
  58. {
  59. flags._storage = 0;
  60. bullet_pos = position;
  61. speed = max_speed = starting_speed;
  62. VERIFY (speed > 0.f);
  63.  
  64. start_position = position;
  65. start_velocity.mul (direction, starting_speed);
  66. born_time = Device.dwTimeGlobal;
  67. life_time = 0.f;
  68.  
  69. VERIFY (direction.magnitude() > 0.f);
  70. dir.normalize (direction);
  71.  
  72. hit_param.power = power * cartridge.param_s.kHit;
  73. hit_param.impulse = impulse * cartridge.param_s.kImpulse;
  74.  
  75. max_dist = maximum_distance * cartridge.param_s.kDist;
  76. fly_dist = 0;
  77. tracer_start_position = bullet_pos;
  78.  
  79. parent_id = sender_id;
  80. flags.allow_sendhit = SendHit;
  81. weapon_id = sendersweapon_id;
  82. hit_type = e_hit_type;
  83.  
  84. armor_piercing = cartridge.param_s.kAP;
  85. air_resistance = cartridge.param_s.kAirRes*air_resistance_factor;
  86. wallmark_size = cartridge.param_s.fWallmarkSize;
  87. m_u8ColorID = cartridge.param_s.u8ColorID;
  88.  
  89. bullet_material_idx = cartridge.bullet_material_idx;
  90. VERIFY ( u16(-1) != bullet_material_idx );
  91.  
  92. flags.allow_tracer = !!cartridge.m_flags.test(CCartridge::cfTracer);
  93. flags.allow_ricochet = !!cartridge.m_flags.test(CCartridge::cfRicochet);
  94. flags.explosive = !!cartridge.m_flags.test(CCartridge::cfExplosive);
  95. flags.magnetic_beam = !!cartridge.m_flags.test(CCartridge::cfMagneticBeam);
  96. // flags.skipped_frame = 0;
  97.  
  98. init_frame_num = Device.dwFrame;
  99.  
  100. targetID = 0;
  101. density_mode = 0;
  102. }
  103.  
  104.  
  105. CBulletManager::CBulletManager()
  106. #if 0//def PROFILE_CRITICAL_SECTIONS
  107. : m_Lock(MUTEX_PROFILE_ID(CBulletManager))
  108. # ifdef DEBUG
  109. ,m_thread_id(GetCurrentThreadId())
  110. # endif // #ifdef DEBUG
  111. #else // #ifdef PROFILE_CRITICAL_SECTIONS
  112. # ifdef DEBUG
  113. : m_thread_id(GetCurrentThreadId())
  114. # endif // #ifdef DEBUG
  115. #endif // #ifdef PROFILE_CRITICAL_SECTIONS
  116. {
  117. m_Bullets.clear ();
  118. m_Bullets.reserve (100);
  119. }
  120.  
  121. CBulletManager::~CBulletManager()
  122. {
  123. m_Bullets.clear ();
  124. m_WhineSounds.clear ();
  125. m_Events.clear ();
  126. }
  127.  
  128. void CBulletManager::Load ()
  129. {
  130. char const * bullet_manager_sect = "bullet_manager";
  131. if (!IsGameTypeSingle())
  132. {
  133. bullet_manager_sect = "mp_bullet_manager";
  134. }
  135. m_fTracerWidth = pSettings->r_float(bullet_manager_sect, "tracer_width");
  136. m_fTracerLengthMax = pSettings->r_float(bullet_manager_sect, "tracer_length_max");
  137. m_fTracerLengthMin = pSettings->r_float(bullet_manager_sect, "tracer_length_min");
  138.  
  139. m_fGravityConst = pSettings->r_float(bullet_manager_sect, "gravity_const");
  140. m_fAirResistanceK = pSettings->r_float(bullet_manager_sect, "air_resistance_k");
  141.  
  142. m_fMinBulletSpeed = pSettings->r_float(bullet_manager_sect, "min_bullet_speed");
  143. m_fCollisionEnergyMin = pSettings->r_float(bullet_manager_sect, "collision_energy_min");
  144. m_fCollisionEnergyMax = pSettings->r_float(bullet_manager_sect, "collision_energy_max");
  145.  
  146. m_fHPMaxDist = pSettings->r_float(bullet_manager_sect, "hit_probability_max_dist");
  147.  
  148. if (pSettings->line_exist(bullet_manager_sect, "bullet_velocity_time_factor"))
  149. {
  150. g_bullet_time_factor = pSettings->r_float(bullet_manager_sect, "bullet_velocity_time_factor");
  151. }
  152.  
  153.  
  154. LPCSTR whine_sounds = pSettings->r_string(bullet_manager_sect, "whine_sounds");
  155. int cnt = _GetItemCount(whine_sounds);
  156. xr_string tmp;
  157. for (int k=0; k<cnt; ++k)
  158. {
  159. m_WhineSounds.push_back (ref_sound());
  160. m_WhineSounds.back().create(_GetItem(whine_sounds,k,tmp),st_Effect,sg_SourceType);
  161. }
  162.  
  163. LPCSTR explode_particles= pSettings->r_string(bullet_manager_sect, "explode_particles");
  164. cnt = _GetItemCount(explode_particles);
  165. for (int k=0; k<cnt; ++k)
  166. m_ExplodeParticles.push_back (_GetItem(explode_particles,k,tmp));
  167. }
  168.  
  169. void CBulletManager::PlayExplodePS( const Fmatrix& xf )
  170. {
  171. if ( m_ExplodeParticles.empty() )
  172. return;
  173.  
  174. shared_str const& ps_name = m_ExplodeParticles[Random.randI(0, m_ExplodeParticles.size())];
  175. CParticlesObject* const ps = CParticlesObject::Create(*ps_name,TRUE);
  176. ps->UpdateParent (xf,zero_vel);
  177. GamePersistent().ps_needtoplay.push_back(ps);
  178. }
  179.  
  180. void CBulletManager::PlayWhineSound(SBullet* bullet, CObject* object, const Fvector& pos)
  181. {
  182. if (m_WhineSounds.empty()) return;
  183. if (bullet->m_whine_snd._feedback() != NULL) return;
  184. if(bullet->hit_type!=ALife::eHitTypeFireWound ) return;
  185.  
  186. bullet->m_whine_snd = m_WhineSounds[Random.randI(0, m_WhineSounds.size())];
  187. bullet->m_whine_snd.play_at_pos (object,pos);
  188. }
  189.  
  190. void CBulletManager::Clear ()
  191. {
  192. m_Bullets.clear ();
  193. m_Events.clear ();
  194. }
  195.  
  196. void CBulletManager::AddBullet(const Fvector& position,
  197. const Fvector& direction,
  198. float starting_speed,
  199. float power,
  200. //. float power_critical,
  201. float impulse,
  202. u16 sender_id,
  203. u16 sendersweapon_id,
  204. ALife::EHitType e_hit_type,
  205. float maximum_distance,
  206. const CCartridge& cartridge,
  207. float const air_resistance_factor,
  208. bool SendHit,
  209. bool AimBullet)
  210. {
  211. VERIFY ( m_thread_id == GetCurrentThreadId() );
  212.  
  213. VERIFY (u16(-1)!=cartridge.bullet_material_idx);
  214. // u32 CurID = Level().CurrentControlEntity()->ID();
  215. // u32 OwnerID = sender_id;
  216. m_Bullets.push_back (SBullet());
  217. SBullet& bullet = m_Bullets.back();
  218. bullet.Init (position, direction, starting_speed, power, /*power_critical,*/ impulse, sender_id, sendersweapon_id, e_hit_type, maximum_distance, cartridge, air_resistance_factor, SendHit);
  219. // bullet.frame_num = Device.dwFrame;
  220. bullet.flags.aim_bullet = AimBullet;
  221. if (!IsGameTypeSingle())
  222. {
  223. if (SendHit)
  224. Game().m_WeaponUsageStatistic->OnBullet_Fire(&bullet, cartridge);
  225. game_cl_mp* tmp_cl_game = smart_cast<game_cl_mp*>(&Game());
  226. if (tmp_cl_game->get_reward_generator())
  227. tmp_cl_game->get_reward_generator()->OnBullet_Fire(sender_id, sendersweapon_id, position, direction);
  228. }
  229.  
  230. }
  231.  
  232. void CBulletManager::UpdateWorkload()
  233. {
  234. // VERIFY ( m_thread_id == GetCurrentThreadId() );
  235.  
  236. rq_storage.r_clear ();
  237.  
  238. u32 const time_delta = Device.dwTimeDelta;
  239. if (!time_delta)
  240. return;
  241.  
  242. collide::rq_result dummy;
  243.  
  244. // this is because of ugly nature of removing bullets
  245. // when index in vector passed through the tgt_material field
  246. // and we can remove them only in case when we iterate bullets
  247. // in the reversed order
  248. BulletVec::reverse_iterator i = m_Bullets.rbegin();
  249. BulletVec::reverse_iterator e = m_Bullets.rend();
  250. for (u16 j=u16(e - i); i != e; ++i, --j) {
  251. if ( process_bullet( rq_storage, *i, u32(time_delta*g_bullet_time_factor)) )
  252. continue;
  253.  
  254. VERIFY (j > 0);
  255. RegisterEvent (EVENT_REMOVE, FALSE, &*i, Fvector().set(0, 0, 0), dummy, j - 1);
  256. }
  257. }
  258.  
  259. static Fvector parabolic_velocity (
  260. Fvector const& start_velocity,
  261. Fvector const& gravity,
  262. float const air_resistance,
  263. float const time
  264. )
  265. {
  266. return (
  267. Fvector(start_velocity).mul(
  268. _max( 0.f, 1.f - air_resistance*time)
  269. ).mad(
  270. gravity,
  271. time
  272. )
  273. );
  274. }
  275.  
  276. static Fvector trajectory_velocity (
  277. Fvector const& start_velocity,
  278. Fvector const& gravity,
  279. float const air_resistance,
  280. float const time
  281. )
  282. {
  283. float const parabolic_time = _max( 0.f, 2.f/air_resistance - air_resistance_epsilon);
  284. float const fall_down_time = time - parabolic_time;
  285. // float const fake_velocity = start_velocity*2.f;
  286. if ( fall_down_time < 0.f ) {
  287. Fvector const xz_velocity = Fvector().set( start_velocity.x, 0.f, start_velocity.z);
  288. // this could be since we could fire in different directions
  289. // for example, vertically into the ground
  290. if ( !fis_zero(xz_velocity.square_magnitude()) ) {
  291. return (
  292. parabolic_velocity(
  293. start_velocity,
  294. gravity,
  295. air_resistance,
  296. time
  297. )
  298. );
  299. }
  300.  
  301. // this fake since our formula doesn't take into account
  302. // directions correctly
  303. return (
  304. Fvector(start_velocity).mad(
  305. gravity,
  306. time
  307. )
  308. );
  309. }
  310.  
  311. Fvector parabolic_velocity =
  312. ::parabolic_velocity(
  313. start_velocity,
  314. gravity,
  315. air_resistance,
  316. parabolic_time
  317. );
  318.  
  319. VERIFY (!fis_zero(air_resistance_epsilon) || fis_zero(_sqr(parabolic_velocity.x) + _sqr(parabolic_velocity.z), EPS_L) );
  320. return (
  321. parabolic_velocity.mad(
  322. gravity,
  323. fall_down_time
  324. )
  325. );
  326. }
  327.  
  328. static Fvector parabolic_position (
  329. Fvector const& start_position,
  330. Fvector const& start_velocity,
  331. Fvector const& gravity,
  332. float const air_resistance,
  333. float const time
  334. )
  335. {
  336. float const sqr_t_div_2 = _sqr(time)*.5f;
  337. return (
  338. Fvector().mad(
  339. start_position,
  340. start_velocity,
  341. time
  342. ).mad(
  343. Fvector(start_velocity).mul(-air_resistance),
  344. sqr_t_div_2
  345. ).mad(
  346. gravity,
  347. sqr_t_div_2
  348. )
  349. );
  350. }
  351.  
  352. //BOOL g_use_new_ballistics = 0;
  353. #ifdef DEBUG
  354. float dbg_bullet_time_factor = 1.f;
  355. #endif
  356.  
  357. static Fvector trajectory_position (
  358. Fvector const& start_position,
  359. Fvector const& base_start_velocity,
  360. Fvector const& base_gravity,
  361. float base_air_resistance,
  362. float const base_time
  363. )
  364. {
  365. Fvector const & gravity = base_gravity;//g_use_new_ballistics ? Fvector(base_gravity).mul(_sqr(factor)) : base_gravity;
  366. float const & air_resistance = base_air_resistance;//g_use_new_ballistics ? base_air_resistance*factor : base_air_resistance;
  367. Fvector const & start_velocity = base_start_velocity;//g_use_new_ballistics ? Fvector(base_start_velocity).mul( factor ) : base_start_velocity;
  368. float const time = base_time;
  369.  
  370. float const parabolic_time = _max( 0.f, 1.f/air_resistance - air_resistance_epsilon);
  371. float const fall_down_time = time - parabolic_time;
  372. if ( fall_down_time < 0.f ) {
  373. Fvector const xz_velocity = Fvector().set( start_velocity.x, 0.f, start_velocity.z);
  374. if ( !fis_zero(xz_velocity.square_magnitude()) )
  375. return (
  376. parabolic_position(
  377. start_position,
  378. start_velocity,
  379. gravity,
  380. air_resistance,
  381. time
  382. )
  383. );
  384.  
  385. return (
  386. Fvector(start_position).mad (
  387. start_velocity,
  388. time
  389. ).mad(
  390. gravity,
  391. _sqr(time)*.5f
  392. )
  393. );
  394. }
  395.  
  396. Fvector const parabolic_position =
  397. ::parabolic_position(
  398. start_position,
  399. start_velocity,
  400. gravity,
  401. air_resistance,
  402. parabolic_time
  403. );
  404. Fvector const parabolic_velocity =
  405. ::parabolic_velocity(
  406. start_velocity,
  407. gravity,
  408. air_resistance,
  409. parabolic_time
  410. );
  411. return (
  412. Fvector(parabolic_position).mad (
  413. parabolic_velocity,
  414. fall_down_time
  415. ).mad(
  416. gravity,
  417. _sqr(fall_down_time)*.5f
  418. )
  419. );
  420. }
  421.  
  422. inline static float trajectory_max_error_time (
  423. float const t0,
  424. float const t1
  425. )
  426. {
  427. return ( (t1 + t0)*.5f );
  428. // this is correct even in our case
  429. // y(t) = V0y*t - V0y*ar*t^2/2 - g*t^2/2
  430. // x(t) = V0x*t - V0x*ar*t^2/2
  431. }
  432.  
  433. static float trajectory_pick_error (
  434. float const low,
  435. float const high,
  436. Fvector const& position,
  437. Fvector const& velocity,
  438. Fvector const& gravity,
  439. float const air_resistance
  440. )
  441. {
  442. float max_error_time = trajectory_max_error_time(low, high);
  443.  
  444. Fvector const start = trajectory_position(position, velocity, gravity, air_resistance, low);
  445. Fvector const target = trajectory_position(position, velocity, gravity, air_resistance, high);
  446. Fvector const max_error = trajectory_position(position, velocity, gravity, air_resistance, max_error_time);
  447.  
  448. Fvector start_to_max_error = Fvector().sub(max_error,start);
  449. float magnitude = start_to_max_error.magnitude();
  450. start_to_max_error.mul (1.f/magnitude);
  451. Fvector start_to_target = Fvector().sub(target,start).normalize();
  452. float cosine_alpha = _max(-1.f, _min(start_to_max_error.dotproduct(start_to_target), 1.f));
  453. float sine_alpha = _sqrt(1.f - _sqr(cosine_alpha));
  454. return (magnitude*sine_alpha);
  455. }
  456.  
  457. static float trajectory_select_pick_gravity (
  458. SBullet& bullet,
  459. float start_low,
  460. float const high,
  461. Fvector const& gravity,
  462. float const air_resistance
  463. )
  464. {
  465. float const max_test_distance = bullet.max_dist - bullet.fly_dist;
  466. float const time_delta = high - start_low;
  467. float const time_to_fly = Fvector(bullet.start_velocity).mul(time_delta).mad(gravity, _sqr(time_delta)*.5f).magnitude();
  468. if (time_to_fly <= max_test_distance)
  469. return (high);
  470.  
  471. float const fall_down_velocity_magnitude = bullet.speed;
  472. float const positive_gravity = -gravity.y;
  473. float time = (
  474. _sqrt( _sqr(fall_down_velocity_magnitude) + 2.f*max_test_distance*positive_gravity ) -
  475. fall_down_velocity_magnitude
  476. )/positive_gravity;
  477. VERIFY (time >= 0.f);
  478.  
  479. VERIFY (high >= start_low);
  480. float result = start_low + time;
  481. clamp (result, start_low, high);
  482. VERIFY2 ( result <= high, make_string("result[%f], high[%f], start_low[%f], air_resistance[%f]", result, high, start_low, air_resistance) );
  483. return ( result );
  484. }
  485.  
  486. static float trajectory_select_pick_parabolic (
  487. SBullet& bullet,
  488. float const start_low,
  489. float high,
  490. Fvector const& gravity,
  491. float const air_resistance
  492. )
  493. {
  494. float const max_test_distance = bullet.max_dist - bullet.fly_dist;
  495. Fvector const start = trajectory_position(bullet.start_position, bullet.start_velocity, gravity, air_resistance, start_low);
  496. float const start_high = high;
  497. float low = start_low;
  498. float check_time = high;
  499. while ( !fsimilar(low, high) ) {
  500. Fvector const intermediate = trajectory_position(bullet.start_position, bullet.start_velocity, gravity, air_resistance, start_low + (check_time - start_low)*.5f);
  501. Fvector const target = trajectory_position(bullet.start_position, bullet.start_velocity, gravity, air_resistance, check_time);
  502. float const distance = start.distance_to(intermediate) + intermediate.distance_to(target);
  503. if (distance < max_test_distance)
  504. low = check_time;
  505. else
  506. high = check_time;
  507.  
  508. check_time = (low + high)*.5f;
  509. }
  510.  
  511. VERIFY ( low <= start_high );
  512. return (low);
  513. }
  514.  
  515. static bool trajectory_select_pick_ranges(
  516. float& result,
  517. SBullet& bullet,
  518. float const low,
  519. float const high,
  520. Fvector const& gravity,
  521. float const air_resistance
  522. )
  523. {
  524. float const max_test_distance = bullet.max_dist - bullet.fly_dist;
  525. VERIFY (max_test_distance > 0.f);
  526.  
  527. if ( air_resistance*(low + air_resistance_epsilon) >= 1.f ) {
  528. result = trajectory_select_pick_gravity(bullet, low, high, gravity, air_resistance);
  529. return (true);
  530. }
  531.  
  532. if ( air_resistance*(high + air_resistance_epsilon) < 1.f ) {
  533. result = trajectory_select_pick_parabolic(bullet, low, high, gravity, air_resistance);
  534. return (false);
  535. }
  536.  
  537. float const fall_down_time = _max( 0.f, 1.f/air_resistance - air_resistance_epsilon);
  538. if ( !fsimilar(fall_down_time, low) ) {
  539. result = trajectory_select_pick_parabolic(bullet, low, fall_down_time, gravity, air_resistance);
  540. return (false);
  541. }
  542.  
  543. result = trajectory_select_pick_gravity(bullet, fall_down_time, high, gravity, air_resistance);
  544. return (false);
  545. }
  546.  
  547. static float trajectory_select_pick_time (
  548. SBullet& bullet,
  549. float const start_low,
  550. float high,
  551. Fvector const& gravity,
  552. float const air_resistance
  553. )
  554. {
  555. VERIFY2 ( start_low < high, make_string("start_low[%f] high[%f]", start_low, high) );
  556. float const start_high = high;
  557. if (trajectory_select_pick_ranges(high, bullet, start_low, high, gravity, air_resistance)) {
  558. if (high <= start_high)
  559. return (high);
  560.  
  561. return (start_high);
  562. }
  563.  
  564. float low = start_low;
  565. float check_time = high;
  566. float const epsilon = .1f;
  567. while ( !fsimilar(low, high) ) {
  568. float distance = trajectory_pick_error(start_low, check_time, bullet.start_position, bullet.start_velocity, gravity, air_resistance);
  569.  
  570. if (distance < epsilon)
  571. low = check_time;
  572. else
  573. high = check_time;
  574.  
  575. check_time = (low + high)*.5f;
  576. }
  577.  
  578. VERIFY2 (low <= start_high, make_string("low[%f], high[%f]", low, start_high));
  579. return (low);
  580. }
  581.  
  582. void CBulletManager::add_bullet_point (
  583. Fvector const& start_position,
  584. Fvector& previous_position,
  585. Fvector const& start_velocity,
  586. Fvector const& gravity,
  587. float const air_resistance,
  588. float const current_time
  589. )
  590. {
  591. #ifdef DEBUG
  592. Fvector const temp = trajectory_position(start_position, start_velocity, gravity, air_resistance, current_time);
  593. m_bullet_points.push_back (previous_position);
  594. m_bullet_points.push_back (temp);
  595. previous_position = temp;
  596. #endif // #ifdef DEBUG
  597. }
  598.  
  599. static void update_bullet_parabolic (
  600. SBullet& bullet,
  601. bullet_test_callback_data& data,
  602. Fvector const& gravity,
  603. float const air_resistance
  604. )
  605. {
  606. Fvector xz_projection = Fvector(data.collide_position).sub(bullet.start_position);
  607. xz_projection.y = 0;
  608. float const xz_range = xz_projection.magnitude();
  609. Fvector const xz_velocity = Fvector().set(bullet.start_velocity.x, 0.f, bullet.start_velocity.z);
  610.  
  611. VERIFY (air_resistance >= 0.f);
  612. if ( air_resistance > 0.f ) {
  613. float value = 2*air_resistance*xz_range/xz_velocity.magnitude();
  614. clamp (value, 0.f, 1.f);
  615. VERIFY (value <= 1.f);
  616. VERIFY (value >= 0.f);
  617. data.collide_time = (1.f - _sqrt(1.f - value))/air_resistance;
  618. }
  619. else
  620. data.collide_time = xz_range/xz_velocity.magnitude();
  621.  
  622. VERIFY (data.collide_time >= 0.f);
  623.  
  624. // VERIFY (data.collide_time <= data.high_time);
  625. // VERIFY (data.collide_time >= bullet.life_time);
  626. // VERIFY (data.collide_time <= bullet.life_time + Device.fTimeGlobal);
  627. clamp (data.collide_time, bullet.life_time, data.high_time);
  628.  
  629. data.collide_position = trajectory_position(bullet.start_position, bullet.start_velocity, gravity, air_resistance, data.collide_time);
  630. Fvector const new_velocity = trajectory_velocity(bullet.start_velocity, gravity, air_resistance, data.collide_time);
  631. bullet.speed = new_velocity.magnitude();
  632. bullet.dir = Fvector(new_velocity).normalize_safe();
  633. }
  634.  
  635. static void update_bullet_gravitation (
  636. SBullet& bullet,
  637. bullet_test_callback_data& data,
  638. Fvector const& gravity,
  639. float const air_resistance,
  640. float const fall_down_time
  641. )
  642. {
  643. Fvector const fall_down_position= trajectory_position(bullet.start_position, bullet.start_velocity, gravity, air_resistance, fall_down_time);
  644. Fvector const fall_down_velocity= trajectory_velocity(bullet.start_velocity, gravity, air_resistance, fall_down_time);
  645. VERIFY ( !fis_zero(air_resistance_epsilon) || fis_zero( _sqr(fall_down_velocity.x) + _sqr(fall_down_velocity.z), EPS_L) );
  646. float const fall_down_velocity_magnitude = fall_down_velocity.magnitude();
  647.  
  648. Fvector xz_projection = Fvector(data.collide_position).sub(fall_down_position);
  649. xz_projection.y = 0;
  650. float const xz_range = xz_projection.magnitude();
  651. Fvector const xz_velocity = Fvector().set(fall_down_velocity.x, 0.f, fall_down_velocity.z);
  652.  
  653. if ( !fis_zero(xz_velocity.magnitude()) ) {
  654. data.collide_time = fall_down_time + xz_range/xz_velocity.magnitude();
  655. VERIFY (data.collide_time >= 0.f);
  656.  
  657. // VERIFY (data.collide_time <= data.high_time);
  658. // VERIFY (data.collide_time >= bullet.life_time);
  659. // VERIFY (data.collide_time <= bullet.life_time + Device.fTimeGlobal);
  660. clamp (data.collide_time, bullet.life_time, data.high_time);
  661. }
  662. else {
  663. float const positive_gravity = -gravity.y;
  664. float const distance = fall_down_position.distance_to(data.collide_position);
  665. data.collide_time = fall_down_time +
  666. (
  667. _sqrt( _sqr(fall_down_velocity_magnitude) + 2.f*distance*positive_gravity ) -
  668. fall_down_velocity_magnitude
  669. )/positive_gravity;
  670. VERIFY (data.collide_time >= 0.f);
  671.  
  672. // VERIFY (data.collide_time <= data.high_time);
  673. // VERIFY (data.collide_time >= bullet.life_time);
  674. // VERIFY (data.collide_time <= bullet.life_time + Device.fTimeGlobal);
  675. clamp (data.collide_time, bullet.life_time, data.high_time);
  676. }
  677.  
  678. Fvector const new_velocity = trajectory_velocity(bullet.start_velocity, gravity, air_resistance, data.collide_time);
  679. bullet.speed = new_velocity.magnitude();
  680. bullet.dir = Fvector(new_velocity).normalize_safe();
  681. }
  682.  
  683. static void update_bullet (
  684. SBullet& bullet,
  685. bullet_test_callback_data& data,
  686. Fvector const& gravity,
  687. float const air_resistance
  688. )
  689. {
  690. if ( air_resistance*(bullet.life_time + air_resistance_epsilon) >= 1.f ) {
  691. update_bullet_gravitation (bullet, data, gravity, air_resistance, _max( 0.f, 1.f/air_resistance - air_resistance_epsilon));
  692. return;
  693. }
  694.  
  695. Fvector const xz_velocity = Fvector().set( bullet.start_velocity.x, 0.f, bullet.start_velocity.z );
  696. if ( fis_zero(xz_velocity.square_magnitude()) ) {
  697. update_bullet_gravitation (bullet, data, gravity, air_resistance, 0.f);
  698. return;
  699. }
  700.  
  701. update_bullet_parabolic (bullet, data, gravity, air_resistance);
  702. }
  703.  
  704. BOOL CBulletManager::firetrace_callback (collide::rq_result& result, LPVOID params)
  705. {
  706. bullet_test_callback_data& data = *(bullet_test_callback_data*)params;
  707. SBullet& bullet = *data.pBullet;
  708.  
  709. Fvector& collide_position = data.collide_position;
  710. collide_position = Fvector().mad(bullet.bullet_pos, bullet.dir, result.range);
  711.  
  712. float const air_resistance = (GameID() == eGameIDSingle) ? Level().BulletManager().m_fAirResistanceK : bullet.air_resistance;
  713.  
  714. CBulletManager& bullet_manager = Level().BulletManager();
  715. Fvector const gravity = { 0.f, -bullet_manager.m_fGravityConst, 0.f };
  716. update_bullet ( bullet, data, gravity, air_resistance);
  717. if ( fis_zero(bullet.speed) )
  718. return (FALSE);
  719.  
  720. if ( fis_zero(data.collide_time) )
  721. return (TRUE);
  722.  
  723. //статический объект
  724. if (!result.O) {
  725. CDB::TRI const& triangle = *(Level().ObjectSpace.GetStaticTris() + result.element);
  726. bullet_manager.RegisterEvent(EVENT_HIT, FALSE, &bullet, collide_position, result, triangle.material);
  727. return (FALSE);
  728. }
  729.  
  730. //динамический объект
  731. VERIFY ( !(result.O->ID() == bullet.parent_id && bullet.fly_dist < parent_ignore_distance) );
  732. IKinematics* const kinematics = smart_cast<IKinematics*>(result.O->Visual());
  733. if (!kinematics)
  734. return (FALSE);
  735.  
  736. CBoneData const& bone_data = kinematics->LL_GetData( (u16)result.element );
  737. bullet_manager.RegisterEvent ( EVENT_HIT, TRUE, &bullet, collide_position, result, bone_data.game_mtl_idx );
  738. return (FALSE);
  739. }
  740.  
  741. bool CBulletManager::trajectory_check_error (
  742. Fvector& previous_position,
  743. collide::rq_results& storage,
  744. SBullet& bullet,
  745. float& low,
  746. float& high,
  747. Fvector const& gravity,
  748. float const air_resistance
  749. )
  750. {
  751. Fvector const& position = bullet.start_position;
  752. Fvector const& velocity = bullet.start_velocity;
  753. Fvector const start = trajectory_position(position, velocity, gravity, air_resistance, low);
  754. Fvector const target = trajectory_position(position, velocity, gravity, air_resistance, high);
  755. Fvector start_to_target = Fvector().sub(target,start);
  756. float const distance = start_to_target.magnitude();
  757. if ( fis_zero(distance) )
  758. return (true);
  759.  
  760. start_to_target.mul ( 1.f/distance );
  761.  
  762. bullet_test_callback_data data;
  763. data.pBullet = &bullet;
  764. #if 1//def DEBUG
  765. data.high_time = high;
  766. #endif // #ifdef DEBUG
  767. bullet.flags.ricochet_was = 0;
  768. bullet.dir = start_to_target;
  769.  
  770. collide::ray_defs RD (start, start_to_target, distance, CDB::OPT_FULL_TEST, collide::rqtBoth);
  771. BOOL const result = Level().ObjectSpace.RayQuery(storage, RD, CBulletManager::firetrace_callback, &data, CBulletManager::test_callback, NULL);
  772. if ( !result || (data.collide_time == 0.f) ) {
  773. add_bullet_point (bullet.start_position, previous_position, bullet.start_velocity, gravity, air_resistance, high);
  774. return (true);
  775. }
  776.  
  777. add_bullet_point (bullet.start_position, previous_position, bullet.start_velocity, gravity, air_resistance, data.collide_time);
  778.  
  779. low = 0.f;
  780.  
  781. VERIFY (high >= data.collide_time);
  782. high -= data.collide_time;
  783.  
  784. ++bullet.change_rajectory_count;
  785. bullet.start_position = data.collide_position;
  786. bullet.tracer_start_position = bullet.bullet_pos;
  787. bullet.bullet_pos = data.collide_position;
  788. bullet.start_velocity = Fvector().mul(bullet.dir, bullet.speed);
  789. bullet.born_time += iFloor(data.collide_time*1000.f);
  790. bullet.life_time = 0.f;
  791. return (false);
  792. }
  793.  
  794. static bool try_update_bullet (SBullet& bullet, Fvector const& gravity, float const air_resistance, float const time)
  795. {
  796. Fvector const new_position = trajectory_position(bullet.start_position, bullet.start_velocity, gravity, air_resistance, time);
  797. bullet.fly_dist += bullet.bullet_pos.distance_to(new_position);
  798.  
  799. if (bullet.fly_dist >= bullet.max_dist)
  800. return (false);
  801.  
  802. Fbox const level_box = Level().ObjectSpace.GetBoundingVolume();
  803. if (
  804. (bullet.bullet_pos.x < level_box.x1) ||
  805. (bullet.bullet_pos.x > level_box.x2) ||
  806. (bullet.bullet_pos.y < level_box.y1) ||
  807. // (bullet.bullet_pos.y > level_box.y2) ||
  808. (bullet.bullet_pos.z < level_box.z1) ||
  809. (bullet.bullet_pos.z > level_box.z2)
  810. )
  811. return (false);
  812.  
  813. Fvector const new_velocity = trajectory_velocity(bullet.start_velocity, gravity, air_resistance, bullet.life_time);
  814. bullet.speed = new_velocity.magnitude();
  815. if ( fis_zero(bullet.speed) )
  816. return (false);
  817.  
  818. bullet.bullet_pos = new_position;
  819. bullet.dir = Fvector(new_velocity).normalize_safe();
  820. bullet.life_time = time;
  821. return (true);
  822. }
  823.  
  824.  
  825. bool CBulletManager::process_bullet (collide::rq_results & storage, SBullet& bullet, u32 const delta_time)
  826. {
  827. float const time_delta = float(delta_time)/1000.f;
  828. Fvector const gravity = Fvector().set( 0.f, -m_fGravityConst, 0.f);
  829.  
  830. float const air_resistance = (GameID() == eGameIDSingle) ? m_fAirResistanceK : bullet.air_resistance;
  831. bullet.tracer_start_position= bullet.bullet_pos;
  832.  
  833. #if 0//def DEBUG
  834. extern BOOL g_bDrawBulletHit;
  835. if (g_bDrawBulletHit)
  836. {
  837. Msg (
  838. "free fly velocity: %f",
  839. trajectory_velocity(
  840. bullet.start_velocity,
  841. gravity,
  842. air_resistance,
  843. fis_zero(air_resistance) ?
  844. 0.f :
  845. (1.f/air_resistance - air_resistance_epsilon)
  846. ).magnitude()
  847. );
  848. }
  849. #endif
  850.  
  851. Fvector const&start_position= bullet.bullet_pos;
  852. Fvector previous_position = start_position;
  853. float low = bullet.life_time;
  854. float high = bullet.life_time + time_delta;
  855. // Msg ("process_bullet0: low[%f], high[%f]", low, high);
  856.  
  857. bullet.change_rajectory_count = 0;
  858.  
  859. for (;;) {
  860. for (;;) {
  861. if ( bullet.speed < 1.f )
  862. return (false);
  863.  
  864. if ( bullet.change_rajectory_count >= 32 )
  865. return (false);
  866.  
  867. float time = trajectory_select_pick_time(bullet, low, high, gravity, air_resistance);
  868. if (time == low)
  869. return (false);
  870.  
  871. float safe_time = time;
  872. VERIFY2 ( safe_time <= high, make_string("safe_time[%f], high[%f]", safe_time, high) );
  873. if ( !trajectory_check_error(previous_position, storage, bullet, low, time, gravity, air_resistance) ) {
  874. VERIFY2 ( safe_time >= time, make_string("safe_time[%f], time[%f]", safe_time, time) );
  875. VERIFY2 ( safe_time <= high, make_string("safe_time[%f], high[%f]", safe_time, high) );
  876. // clamp (safe_time, time, high);
  877. high = high - safe_time + time;
  878. VERIFY2 ( low <= high, make_string("start_low[%f] high[%f]", low, high) );
  879. if ( fsimilar(low, high) )
  880. return ( !fis_zero(bullet.speed) );
  881.  
  882. break;
  883. }
  884.  
  885. if ( !try_update_bullet(bullet, gravity, air_resistance, time) )
  886. return (false);
  887.  
  888. if ( fsimilar(time, high) )
  889. return ( true );
  890.  
  891. VERIFY2 ( low < high, make_string("start_low[%f] high[%f]", low, high) );
  892. low = time;
  893. VERIFY2 ( low < high, make_string("start_low[%f] high[%f]", low, high) );
  894. }
  895.  
  896. if ( fis_zero(bullet.speed) )
  897. return (false);
  898. }
  899. }
  900.  
  901. #ifdef DEBUG
  902. BOOL g_bDrawBulletHit = FALSE;
  903. #endif
  904.  
  905. float SqrDistancePointToSegment(const Fvector& pt, const Fvector& orig, const Fvector& dir)
  906. {
  907. Fvector diff; diff.sub(pt,orig);
  908. float fT = diff.dotproduct(dir);
  909.  
  910. if ( fT <= 0.0f ){
  911. fT = 0.0f;
  912. }else{
  913. float fSqrLen= dir.square_magnitude();
  914. if ( fT >= fSqrLen ){
  915. fT = 1.0f;
  916. diff.sub(dir);
  917. }else{
  918. fT /= fSqrLen;
  919. diff.sub(Fvector().mul(dir,fT));
  920. }
  921. }
  922.  
  923. return diff.square_magnitude();
  924. }
  925.  
  926. void CBulletManager::Render ()
  927. {
  928. #ifdef DEBUG
  929. if (g_bDrawBulletHit && !m_bullet_points.empty()) {
  930. VERIFY (!(m_bullet_points.size() % 2));
  931. CDebugRenderer& renderer = Level().debug_renderer();
  932. Fmatrix sphere = Fmatrix().scale(.05f, .05f, .05f);
  933. BulletPoints::const_iterator i = m_bullet_points.begin();
  934. BulletPoints::const_iterator e = m_bullet_points.end();
  935. for ( ; i != e; i+=2) {
  936. sphere.c = *i;
  937. renderer.draw_ellipse (sphere, D3DCOLOR_XRGB(255, 0, 0));
  938.  
  939. renderer.draw_line (Fidentity, *i, *(i + 1), D3DCOLOR_XRGB(0, 255, 0));
  940.  
  941. sphere.c = *(i + 1);
  942. renderer.draw_ellipse (sphere, D3DCOLOR_XRGB(255, 0, 0));
  943. }
  944.  
  945. if (m_bullet_points.size() > 32768)
  946. m_bullet_points.clear_not_free ();
  947. }
  948. else
  949. m_bullet_points.clear_not_free ();
  950.  
  951. //0-рикошет
  952. //1-застрявание пули в материале
  953. //2-пробивание материала
  954. if (g_bDrawBulletHit) {
  955. extern FvectorVec g_hit[];
  956. FvectorIt it;
  957. u32 C[3] = {0xffff0000,0xff00ff00,0xff0000ff};
  958. //RCache.set_xform_world(Fidentity);
  959. DRender->CacheSetXformWorld(Fidentity);
  960. for(int i=0; i<3; ++i)
  961. for(it=g_hit[i].begin();it!=g_hit[i].end();++it){
  962. Level().debug_renderer().draw_aabb(*it,0.01f,0.01f,0.01f,C[i]);
  963. }
  964. }
  965. #endif
  966.  
  967. if(m_BulletsRendered.empty()) return;
  968.  
  969. //u32 vOffset = 0 ;
  970. u32 bullet_num = m_BulletsRendered.size();
  971.  
  972. UIRender->StartPrimitive((u32)bullet_num*12, IUIRender::ptTriList, IUIRender::pttLIT);
  973.  
  974. for(BulletVecIt it = m_BulletsRendered.begin(); it!=m_BulletsRendered.end(); it++){
  975. SBullet* bullet = &(*it);
  976. if(!bullet->flags.allow_tracer)
  977. continue;
  978.  
  979. if (!bullet->CanBeRenderedNow())
  980. continue;
  981.  
  982. Fvector const tracer = Fvector().sub(bullet->bullet_pos, bullet->tracer_start_position);
  983. float length = tracer.magnitude();
  984. Fvector const tracer_direction = length >= EPS_L ? Fvector(tracer).mul(1.f/length) : Fvector().set(0.f, 0.f, 1.f);
  985.  
  986. if (length < m_fTracerLengthMin)
  987. continue;
  988.  
  989. if (length > m_fTracerLengthMax)
  990. length = m_fTracerLengthMax;
  991.  
  992. float width = m_fTracerWidth;
  993. float dist2segSqr = SqrDistancePointToSegment(Device.vCameraPosition, bullet->bullet_pos, tracer);
  994. //---------------------------------------------
  995. float MaxDistSqr = 1.0f;
  996. float MinDistSqr = 0.09f;
  997. if (dist2segSqr < MaxDistSqr)
  998. {
  999. if (dist2segSqr < MinDistSqr) dist2segSqr = MinDistSqr;
  1000.  
  1001. width *= _sqrt(dist2segSqr/MaxDistSqr);
  1002. }
  1003. if (Device.vCameraPosition.distance_to_sqr(bullet->bullet_pos)<(length*length))
  1004. {
  1005. length = Device.vCameraPosition.distance_to(bullet->bullet_pos) - 0.3f;
  1006. }
  1007.  
  1008. Fvector center;
  1009. center.mad (bullet->bullet_pos, tracer_direction, -length*.5f);
  1010. bool bActor = false;
  1011. if(Level().CurrentViewEntity())
  1012. {
  1013. bActor = ( bullet->parent_id == Level().CurrentViewEntity()->ID() );
  1014. }
  1015. tracers.Render (bullet->bullet_pos, center, tracer_direction, length, width, bullet->m_u8ColorID, bullet->speed, bActor);
  1016. }
  1017.  
  1018. UIRender->CacheSetCullMode (IUIRender::cmNONE);
  1019. UIRender->CacheSetXformWorld (Fidentity);
  1020. UIRender->SetShader (*tracers.sh_Tracer);
  1021. UIRender->FlushPrimitive ();
  1022. UIRender->CacheSetCullMode (IUIRender::cmCCW);
  1023. }
  1024.  
  1025. void CBulletManager::CommitRenderSet () // @ the end of frame
  1026. {
  1027. m_BulletsRendered = m_Bullets ;
  1028. if (g_mt_config.test(mtBullets)) {
  1029. Device.seqParallel.push_back (fastdelegate::FastDelegate0<>(this,&CBulletManager::UpdateWorkload));
  1030. } else {
  1031. UpdateWorkload ();
  1032. }
  1033. }
  1034. void CBulletManager::CommitEvents () // @ the start of frame
  1035. {
  1036. if (m_Events.size() > 1000)
  1037. Msg ("! too many bullets during single frame: %d", m_Events.size());
  1038.  
  1039. for (u32 _it=0; _it<m_Events.size(); _it++) {
  1040. _event& E = m_Events[_it];
  1041. switch (E.Type)
  1042. {
  1043. case EVENT_HIT:
  1044. {
  1045. if (E.dynamic) DynamicObjectHit (E);
  1046. else StaticObjectHit (E);
  1047. }break;
  1048. case EVENT_REMOVE:
  1049. {
  1050. if (E.bullet.flags.allow_sendhit && GameID() != eGameIDSingle)
  1051. Game().m_WeaponUsageStatistic->OnBullet_Remove(&E.bullet);
  1052. m_Bullets[E.tgt_material] = m_Bullets.back();
  1053. m_Bullets.pop_back();
  1054. }break;
  1055. }
  1056. }
  1057. m_Events.clear_and_reserve () ;
  1058. }
  1059.  
  1060. void CBulletManager::RegisterEvent (EventType Type, BOOL _dynamic, SBullet* bullet, const Fvector& end_point, collide::rq_result& R, u16 tgt_material)
  1061. {
  1062. #if 0//def DEBUG
  1063. if (m_Events.size() > 1000) {
  1064. static bool breakpoint = true;
  1065. if (breakpoint)
  1066. __asm int 3;
  1067. }
  1068. #endif // #ifdef DEBUG
  1069.  
  1070. m_Events.push_back (_event()) ;
  1071. _event& E = m_Events.back() ;
  1072. E.Type = Type ;
  1073. E.bullet = *bullet ;
  1074.  
  1075. switch(Type)
  1076. {
  1077. case EVENT_HIT:
  1078. {
  1079. E.dynamic = _dynamic ;
  1080. E.point = end_point ;
  1081. E.R = R ;
  1082. E.tgt_material = tgt_material ;
  1083.  
  1084. ObjectHit( &E.hit_result, bullet, end_point, R, tgt_material, E.normal );
  1085.  
  1086. if (_dynamic)
  1087. {
  1088. // E.Repeated = (R.O->ID() == E.bullet.targetID);
  1089. // bullet->targetID = R.O->ID();
  1090.  
  1091. E.Repeated = (R.O->ID() == E.bullet.targetID);
  1092. if (GameID() == eGameIDSingle)
  1093. {
  1094. bullet->targetID = R.O->ID();
  1095. }
  1096. else
  1097. {
  1098. if (bullet->targetID != R.O->ID())
  1099. {
  1100. CGameObject* pGO = smart_cast<CGameObject*>(R.O);
  1101. if (!pGO || !pGO->BonePassBullet(R.element))
  1102. bullet->targetID = R.O->ID();
  1103. }
  1104. }
  1105. };
  1106. }break;
  1107. case EVENT_REMOVE:
  1108. {
  1109. E.tgt_material = tgt_material ;
  1110. }break;
  1111. }
  1112. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement