Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Physical.cpp
Go to the documentation of this file.
1 /* Project nGenEx
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: nGenEx.lib
6  FILE: Physical.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Abstract Physical Object
13 */
14 
15 #include "MemDebug.h"
16 #include "Physical.h"
17 #include "Graphic.h"
18 #include "Light.h"
19 #include "Director.h"
20 
21 // +--------------------------------------------------------------------+
22 
23 int Physical::id_key = 1;
24 double Physical::sub_frame = 1.0 / 60.0;
25 
26 static const double GRAV = 6.673e-11;
27 
28 // +--------------------------------------------------------------------+
29 
31 : id(id_key++), obj_type(0), rep(0), light(0),
32 thrust(0.0f), drag(0.0f), lat_thrust(false),
33 trans_x(0.0f), trans_y(0.0f), trans_z(0.0f), straight(false),
34 roll(0.0f), pitch(0.0f), yaw(0.0f), dr(0.0f), dp(0.0f), dy(0.0f),
35 dr_acc(0.0f), dp_acc(0.0f), dy_acc(0.0f),
36 dr_drg(0.0f), dp_drg(0.0f), dy_drg(0.0f),
37 flight_path_yaw(0.0f), flight_path_pitch(0.0f), primary_mass(0),
38 roll_rate(1.0f), pitch_rate(1.0f), yaw_rate(1.0f), shake(0.0f),
39 radius(0.0f), mass(1.0f), integrity(1.0f), life(-1), dir(0),
40 g_accel(0.0f), Do(0.0f), CL(0.0f), CD(0.0f), alpha(0.0f), stall(0.0f)
41 {
42  strcpy_s(name, "unknown object");
43 }
44 
45 // +--------------------------------------------------------------------+
46 
47 Physical::Physical(const char* n, int t)
48 : id(id_key++), obj_type(t), rep(0), light(0),
49 thrust(0.0f), drag(0.0f), lat_thrust(false),
50 trans_x(0.0f), trans_y(0.0f), trans_z(0.0f), straight(false),
51 roll(0.0f), pitch(0.0f), yaw(0.0f), dr(0.0f), dp(0.0f), dy(0.0f),
52 dr_acc(0.0f), dp_acc(0.0f), dy_acc(0.0f),
53 dr_drg(0.0f), dp_drg(0.0f), dy_drg(0.0f),
54 flight_path_yaw(0.0f), flight_path_pitch(0.0f), primary_mass(0),
55 roll_rate(1.0f), pitch_rate(1.0f), yaw_rate(1.0f), shake(0.0f),
56 radius(0.0f), mass(1.0f), integrity(1.0f), life(-1), dir(0),
57 g_accel(0.0f), Do(0.0f), CL(0.0f), CD(0.0f), alpha(0.0f), stall(0.0f)
58 {
59  strncpy_s(name, n, NAMELEN-1);
60  name[NAMELEN-1] = 0;
61 }
62 
63 // +--------------------------------------------------------------------+
64 
66 {
67  // inform graphic rep and light that we are leaving:
70 
71  // we own the director
72  delete dir;
73  dir = 0;
74 }
75 
76 // +--------------------------------------------------------------------+
77 
78 inline double random() { return rand()-16384; }
79 
80 void
82 {
83  Point orig_velocity = Velocity();
85 
86  // if this object is under direction,
87  // but doesn't need subframe accuracy,
88  // update the control parameters:
89  if (dir && !dir->Subframe())
90  dir->ExecFrame(s);
91 
92  // decrement life before destroying the frame time:
93  if (life > 0)
94  life -= s;
95 
96  // integrate equations
97  // using slices no larger
98  // than sub_frame:
99 
100  double seconds = s;
101 
102  while (s > 0.0) {
103  if (s > sub_frame)
104  seconds = sub_frame;
105  else
106  seconds = s;
107 
108  // if the director needs subframe accuracy, run it now:
109  if (dir && dir->Subframe())
110  dir->ExecFrame(seconds);
111 
112  if (!straight)
113  AngularFrame(seconds);
114 
115  // LINEAR MOVEMENT ----------------------------
116  Point pos = cam.Pos();
117 
118  // if the object is thrusting,
119  // accelerate along the camera normal:
120  if (thrust) {
121  Point thrustvec = cam.vpn();
122  thrustvec *= ((thrust/mass) * seconds);
123  velocity += thrustvec;
124  }
125 
126  LinearFrame(seconds);
127 
128  // move the position by the (time-frame scaled) velocity:
129  pos += velocity * seconds;
130  cam.MoveTo(pos);
131 
132  s -= seconds;
133  }
134 
135  alpha = 0.0f;
136 
137  // now update the graphic rep and light sources:
138  if (rep) {
139  rep->MoveTo(cam.Pos());
141  }
142 
143  if (light) {
144  light->MoveTo(cam.Pos());
145  }
146 
147  if (!straight)
148  CalcFlightPath();
149 
150  accel = (Velocity() - orig_velocity) * (1/seconds);
151  if (!_finite(accel.x) || !_finite(accel.y) || !_finite(accel.z))
152  accel = Point();
153 }
154 
155 // +--------------------------------------------------------------------+
156 
157 void
159 {
161 
162  // if this object is under direction,
163  // but doesn't need subframe accuracy,
164  // update the control parameters:
165  if (dir && !dir->Subframe())
166  dir->ExecFrame(s);
167 
168  // decrement life before destroying the frame time:
169  if (life > 0)
170  life -= s;
171 
172  // integrate equations
173  // using slices no larger
174  // than sub_frame:
175 
176  double seconds = s;
177 
178  while (s > 0.0) {
179  if (s > sub_frame)
180  seconds = sub_frame;
181  else
182  seconds = s;
183 
184  // if the director needs subframe accuracy, run it now:
185  if (dir && dir->Subframe())
186  dir->ExecFrame(seconds);
187 
188  AngularFrame(seconds);
189 
190  // LINEAR MOVEMENT ----------------------------
191  Point pos = cam.Pos();
192 
193  // if the object is thrusting,
194  // accelerate along the camera normal:
195  if (thrust) {
196  Point thrustvec = cam.vpn();
197  thrustvec *= ((thrust/mass) * seconds);
198  velocity += thrustvec;
199  }
200 
201  // AERODYNAMICS ------------------------------
202 
203  if (lat_thrust)
204  LinearFrame(seconds);
205 
206  // if no thrusters, do constant gravity:
207  else if (g_accel > 0)
208  velocity += Point(0, -g_accel, 0) * seconds;
209 
210  // compute alpha, rho, drag, and lift:
211 
212  Point vfp = velocity;
213  double v = vfp.Normalize();
214  double v_2 = 0;
215  double rho = GetDensity();
216  double lift = 0;
217 
218  if (v > 150) {
219  v_2 = (v-150) * (v-150);
220 
221  Point vfp1 = vfp - cam.vrt() * (vfp * cam.vrt());
222  vfp1.Normalize();
223 
224  double cos_alpha = vfp1 * cam.vpn();
225 
226  if (cos_alpha >= 1) {
227  alpha = 0.0f;
228  }
229  else {
230  alpha = (float) acos(cos_alpha);
231  }
232 
233  // if flight path is above nose, alpha is negative:
234  if (vfp1 * cam.vup() > 0)
235  alpha = -alpha;
236 
237  if (alpha <= stall) {
238  lift = CL * alpha * rho * v_2;
239  }
240  else {
241  lift = CL * (2*stall - alpha) * rho * v_2;
242  }
243 
244  // add lift to velocity:
245  if (_finite(lift))
246  velocity += cam.vup() * lift * seconds;
247  else
248  lift = 0;
249 
250  // if drag applies, decellerate:
251  double alpha_2 = alpha*alpha;
252  double drag_eff = (drag + (CD * alpha_2)) * rho * v_2;
253 
254  Point vn = velocity;
255  vn.Normalize();
256 
257  velocity += vn * -drag_eff * seconds;
258  }
259  else {
260  velocity *= exp(-drag * seconds);
261  }
262 
263  // move the position by the (time-frame scaled) velocity:
264  pos += velocity * seconds;
265  cam.MoveTo(pos);
266 
267  s -= seconds;
268  }
269 
270  // now update the graphic rep and light sources:
271  if (rep) {
272  rep->MoveTo(cam.Pos());
274  }
275 
276  if (light) {
277  light->MoveTo(cam.Pos());
278  }
279 }
280 
281 double
283 {
284  double alt = cam.Pos().y;
285  double rho = 0.75 * Do * (250e3-alt)/250e3;
286 
287  return rho;
288 }
289 
290 // +--------------------------------------------------------------------+
291 
292 void
294 {
295  // if this object is under direction,
296  // but doesn't need subframe accuracy,
297  // update the control parameters:
298  if (dir && !dir->Subframe())
299  dir->ExecFrame(s);
300 
301  // decrement life before destroying the frame time:
302  if (life > 0)
303  life -= s;
304 
305  // integrate equations
306  // using slices no larger
307  // than sub_frame:
308 
309  double seconds = s;
310 
311  while (s > 0.0) {
312  if (s > sub_frame)
313  seconds = sub_frame;
314  else
315  seconds = s;
316 
317  // if the director needs subframe accuracy, run it now:
318  if (dir && dir->Subframe())
319  dir->ExecFrame(seconds);
320 
321  if (!straight)
322  AngularFrame(seconds);
323 
324  Point pos = cam.Pos();
325 
326  // ARCADE FLIGHT MODEL:
327  // arcade_velocity vector is always in line with heading
328 
329  double speed = arcade_velocity.Normalize();
330  double bleed = arcade_velocity * cam.vpn();
331 
332  speed *= pow(bleed, 30);
333  arcade_velocity = cam.vpn() * speed;
334 
335  if (thrust) {
336  Point thrustvec = cam.vpn();
337  thrustvec *= ((thrust/mass) * seconds);
338  arcade_velocity += thrustvec;
339  }
340 
341  if (drag)
342  arcade_velocity *= exp(-drag * seconds);
343 
344  LinearFrame(seconds);
345 
346  // move the position by the (time-frame scaled) velocity:
347  pos += arcade_velocity * seconds +
348  velocity * seconds;
349 
350  cam.MoveTo(pos);
351 
352  s -= seconds;
353  }
354 
355  alpha = 0.0f;
356 
357  // now update the graphic rep and light sources:
358  if (rep) {
359  rep->MoveTo(cam.Pos());
361  }
362 
363  if (light) {
364  light->MoveTo(cam.Pos());
365  }
366 }
367 
368 // +--------------------------------------------------------------------+
369 
370 void
371 Physical::AngularFrame(double seconds)
372 {
373  if (!straight) {
374  dr += (float) (dr_acc * seconds);
375  dy += (float) (dy_acc * seconds);
376  dp += (float) (dp_acc * seconds);
377 
378  dr *= (float) exp(-dr_drg * seconds);
379  dy *= (float) exp(-dy_drg * seconds);
380  dp *= (float) exp(-dp_drg * seconds);
381 
382  roll = (float) (dr * seconds);
383  pitch = (float) (dp * seconds);
384  yaw = (float) (dy * seconds);
385 
386  if (shake > 0.01) {
387  vibration = Point(random(), random(), random());
389  vibration *= (float) (shake * seconds);
390 
391  shake *= (float) exp(-1.5 * seconds);
392  }
393  else {
394  vibration.x = vibration.y = vibration.z = 0.0f;
395  shake = 0.0f;
396  }
397 
398  cam.Aim(roll, pitch, yaw);
399  }
400 }
401 
402 // +--------------------------------------------------------------------+
403 
404 void
405 Physical::LinearFrame(double seconds)
406 {
407  // deal with lateral thrusters:
408 
409  if (trans_x) { // side-to-side
410  Point transvec = cam.vrt();
411  transvec *= ((trans_x/mass) * seconds);
412 
413  velocity += transvec;
414  }
415 
416  if (trans_y) { // fore-and-aft
417  Point transvec = cam.vpn();
418  transvec *= ((trans_y/mass) * seconds);
419 
420  velocity += transvec;
421  }
422 
423  if (trans_z) { // up-and-down
424  Point transvec = cam.vup();
425  transvec *= ((trans_z/mass) * seconds);
426 
427  velocity += transvec;
428  }
429 
430  // if gravity applies, attract:
431  if (primary_mass > 0) {
432  Point g = primary_loc - cam.Pos();
433  double r = g.Normalize();
434 
435  g *= GRAV * primary_mass / (r*r);
436 
437  velocity += g * seconds;
438  }
439 
440  // constant gravity:
441  else if (g_accel > 0)
442  velocity += Point(0, -g_accel, 0) * seconds;
443 
444  // if drag applies, decellerate:
445  if (drag)
446  velocity *= exp(-drag * seconds);
447 }
448 
449 // +--------------------------------------------------------------------+
450 
451 void
453 {
454  flight_path_yaw = 0.0f;
455  flight_path_pitch = 0.0f;
456 
457  // transform flight path into camera frame:
458  Point flight_path = velocity;
459  if (flight_path.Normalize() < 1)
460  return;
461 
462  Point tmp = flight_path;
463  flight_path.x = tmp * cam.vrt();
464  flight_path.y = tmp * cam.vup();
465  flight_path.z = tmp * cam.vpn();
466 
467  if (flight_path.z < 0.1)
468  return;
469 
470  // first, compute azimuth:
471  flight_path_yaw = (float) atan(flight_path.x / flight_path.z);
472  if (flight_path.z < 0) flight_path_yaw -= (float) PI;
473  if (flight_path_yaw < -PI) flight_path_yaw += (float) (2*PI);
474 
475  // then, rotate path into azimuth frame to compute elevation:
476  Camera yaw_cam;
477  yaw_cam.Clone(cam);
478  yaw_cam.Yaw(flight_path_yaw);
479 
480  flight_path.x = tmp * yaw_cam.vrt();
481  flight_path.y = tmp * yaw_cam.vup();
482  flight_path.z = tmp * yaw_cam.vpn();
483 
484  flight_path_pitch = (float) atan(flight_path.y / flight_path.z);
485 }
486 
487 // +--------------------------------------------------------------------+
488 
489 void
490 Physical::MoveTo(const Point& new_loc)
491 {
492  cam.MoveTo(new_loc);
493 }
494 
495 void
497 {
498  Point new_loc = cam.Pos() - ref;
499  cam.MoveTo(new_loc);
500 }
501 
502 void
504 {
505  velocity += force/mass;
506 }
507 
508 void
510 {
511  dr += (float) (torque.x/mass);
512  dp += (float) (torque.y/mass);
513  dy += (float) (torque.z/mass);
514 }
515 
516 void
518 {
519  thrust = (float) t;
520 }
521 
522 void
524 {
525  trans_x = (float) t;
526 }
527 
528 void
530 {
531  trans_y = (float) t;
532 }
533 
534 void
536 {
537  trans_z = (float) t;
538 }
539 
540 // +--------------------------------------------------------------------+
541 
542 void
543 Physical::SetHeading(double r, double p, double y)
544 {
545  roll = (float) r;
546  pitch = (float) p;
547  yaw = (float) y;
548 
549  cam.Aim(roll, pitch, yaw);
550 }
551 
552 void
554 {
555  cam.LookAt(dst);
556 }
557 
558 void
560 {
561  cam.Clone(c);
562 }
563 
564 void
565 Physical::SetAbsoluteOrientation(double r, double p, double y)
566 {
567  roll = (float) r;
568  pitch = (float) p;
569  yaw = (float) y;
570 
571  Camera work(Location().x, Location().y, Location().z);
572  work.Aim(r,p,y);
573  cam.Clone(work);
574 }
575 
576 void
578 {
579  if (r > 1) r = 1;
580  else if (r < -1) r = -1;
581 
582  dr_acc = (float) r * roll_rate;
583 }
584 
585 void
587 {
588  if (p > 1) p = 1;
589  else if (p < -1) p = -1;
590 
591  dp_acc = (float) p * pitch_rate;
592 }
593 
594 void
596 {
597  if (y > 1) y = 1;
598  else if (y < -1) y = -1;
599 
600  dy_acc = (float) y * yaw_rate;
601 }
602 
603 void
604 Physical::SetAngularRates(double r, double p, double y)
605 {
606  roll_rate = (float) r;
607  pitch_rate = (float) p;
608  yaw_rate = (float) y;
609 }
610 
611 void
612 Physical::GetAngularRates(double& r, double& p, double& y)
613 {
614  r = roll_rate;
615  p = pitch_rate;
616  y = yaw_rate;
617 }
618 
619 void
620 Physical::SetAngularDrag(double r, double p, double y)
621 {
622  dr_drg = (float) r;
623  dp_drg = (float) p;
624  dy_drg = (float) y;
625 }
626 
627 void
628 Physical::GetAngularDrag(double& r, double& p, double& y)
629 {
630  r = dr_drg;
631  p = dp_drg;
632  y = dy_drg;
633 }
634 
635 void
636 Physical::GetAngularThrust(double& r, double& p, double& y)
637 {
638  r = 0;
639  p = 0;
640  y = 0;
641 
642  if (dr_acc > 0.05 * roll_rate) r = 1;
643  else if (dr_acc < -0.05 * roll_rate) r = -1;
644  else if (dr > 0.01 * roll_rate) r = -1;
645  else if (dr < -0.01 * roll_rate) r = 1;
646 
647  if (dy_acc > 0.05 * yaw_rate) y = 1;
648  else if (dy_acc < -0.05 * yaw_rate) y = -1;
649  else if (dy > 0.01 * yaw_rate) y = -1;
650  else if (dy < -0.01 * yaw_rate) y = 1;
651 
652  if (dp_acc > 0.05 * pitch_rate) p = 1;
653  else if (dp_acc < -0.05 * pitch_rate) p = -1;
654  else if (dp > 0.01 * pitch_rate) p = -1;
655  else if (dp < -0.01 * pitch_rate) p = 1;
656 }
657 
658 
659 void
660 Physical::SetPrimary(const Point& l, double m)
661 {
662  primary_loc = l;
663  primary_mass = m;
664 }
665 
666 void
668 {
669  if (g >= 0)
670  g_accel = (float) g;
671 }
672 
673 void
675 {
676  if (d >= 0)
677  Do = (float) d;
678 }
679 
680 // +--------------------------------------------------------------------+
681 
682 void
683 Physical::InflictDamage(double damage, int /*type*/)
684 {
685  integrity -= (float) damage;
686 
687  if (integrity < 1.0f)
688  integrity = 0.0f;
689 }
690 
691 // +--------------------------------------------------------------------+
692 
693 int
695 {
696  // representation collision test (will do bounding spheres first):
697  if (rep && o.rep)
698  return rep->CollidesWith(*o.rep);
699 
700  Point delta_loc = Location() - o.Location();
701 
702  // bounding spheres test:
703  if (delta_loc.length() > radius + o.radius)
704  return 0;
705 
706  // assume collision:
707  return 1;
708 }
709 
710 
711 // +--------------------------------------------------------------------+
712 
713 void
715 {
716  double mass_sum = a.mass + b.mass;
717  double mass_delta = a.mass - b.mass;
718 
719  Point vel_a = (Point(b.velocity) * (2 * b.mass) + Point(a.velocity) * mass_delta) * (1/mass_sum);
720  Point vel_b = (Point(a.velocity) * (2 * a.mass) - Point(b.velocity) * mass_delta) * (1/mass_sum);
721 
722  a.velocity = vel_a;
723  b.velocity = vel_b;
724 }
725 
726 // +--------------------------------------------------------------------+
727 
728 void
730 {
731  double mass_sum = a.mass + b.mass;
732 
733  Point vel_a = (Point(a.velocity) * a.mass + Point(b.velocity) * b.mass) * (1/mass_sum);
734 
735  a.velocity = vel_a;
736  b.velocity = vel_a;
737 }
738 
739 // +--------------------------------------------------------------------+
740 
741 void
743 {
744  double mass_sum = a.mass + b.mass;
745  double mass_delta = a.mass - b.mass;
746 
747  Point avel = a.Velocity();
748  Point bvel = b.Velocity();
749  Point dv = avel - bvel;
750 
751  // low delta-v: stick
752  if (dv.length() < 20) {
753  if (a.mass > b.mass) {
754  b.velocity = a.velocity;
755  }
756 
757  else {
758  a.velocity = b.velocity;
759  }
760  }
761 
762  // high delta-v: bounce
763  else {
764  Point Ve_a = (bvel * (2 * b.mass) + avel * mass_delta) * (1/mass_sum) * 0.65;
765  Point Ve_b = (avel * (2 * a.mass) - bvel * mass_delta) * (1/mass_sum) * 0.65;
766  Point Vi_ab = (avel * a.mass + bvel * b.mass) * (1/mass_sum) * 0.35;
767 
768  a.arcade_velocity = Point();
769  b.arcade_velocity = Point();
770 
771  a.velocity = Ve_a + Vi_ab;
772  b.velocity = Ve_b + Vi_ab;
773  }
774 }
775