-
Notifications
You must be signed in to change notification settings - Fork 266
/
Copy pathphysics.cpp
2590 lines (2062 loc) · 88.8 KB
/
physics.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cmath>
#include <cstdlib>
#include <limits>
#include "attach.h"
#include "collide.h"
#include "D3ForceFeedback.h"
#include "demofile.h"
#include "findintersection.h"
#include "fireball.h"
#include "game.h"
#include "log.h"
#include "multi.h"
#include "object.h"
#include "physics.h"
#include "player.h"
#include "polymodel.h"
#include "pserror.h"
#include "terrain.h"
#include "vecmat.h"
#include "viseffect.h"
#include "weapon.h"
// Global variables for physics system
uint8_t Default_player_terrain_leveling = 0;
uint8_t Default_player_room_leveling = 0;
// Maximum iterations thru the simulation loop NOTE: It is assumed that the player loops >= non-player loops
#define MAX_PLAYER_SIM_LOOPS 9
#define MAX_NON_PLAYER_SIM_LOOPS 5
#define CEILING_POWERUP_DELTA 30.0f
#define WIGGLE_FALLOFF_TIME 2.0
int Physics_normal_counter;
int Physics_normal_looping_counter;
int Physics_walking_counter;
int Physics_walking_looping_counter;
int Physics_vis_counter;
#ifdef _DEBUG
// This will allow us to debug physics in a better way.
struct sim_loop_info {
vector start_pos;
physics_info phys_info; // At beginnning of loop
fvi_info hit_info; // Hit information returned by FVI
};
sim_loop_info sim_loop_record[MAX_PLAYER_SIM_LOOPS];
#endif
#ifdef _DEBUG
int Physics_player_verbose = 0;
#endif
#define PHYSICS_GROUND_TOLERANCE 0.0001f
#define DAMP_ANG 1.0f // chrishack -- this is probably too tight and should be an int not a float
#define MAX_OBJECT_VEL 100000.0f
int Physics_cheat_flag = 0;
extern char BounceCheat;
int PhysicsLinkList[MAX_OBJECTS];
int Physics_NumLinked = 0;
// Current strength of gravity
float Gravity_strength = -32.2f; // Meters/second^2
void DoPhysLinkedFrame(object *obj) {
object *parent = ObjGet(obj->mtype.obj_link_info.parent_handle);
matrix new_orient;
vector new_pos;
if (parent && parent->type != OBJ_GHOST && parent->type != OBJ_DUMMY) {
poly_model *pm;
vector pnt;
vector fvec;
vector uvec;
matrix m;
int mn; // submodel number
float normalized_time[MAX_SUBOBJECTS];
pm = &Poly_models[parent->rtype.pobj_info.model_num];
ASSERT(pm->used);
SetNormalizedTimeObj(parent, normalized_time);
SetModelAnglesAndPos(pm, normalized_time);
pnt = obj->mtype.obj_link_info.pos;
fvec = obj->mtype.obj_link_info.fvec;
uvec = obj->mtype.obj_link_info.uvec;
mn = obj->mtype.obj_link_info.sobj_index;
if (mn < 0 || mn >= pm->n_models) {
LOG_WARNING << "Caught physics link bug!";
SetObjectDeadFlag(obj);
return;
}
// Instance up the tree for this gun
while (mn != -1) {
vector tpnt;
vm_AnglesToMatrix(&m, pm->submodel[mn].angs.p, pm->submodel[mn].angs.h, pm->submodel[mn].angs.b);
vm_TransposeMatrix(&m);
tpnt = pnt * m;
fvec = fvec * m;
uvec = uvec * m;
pnt = tpnt + pm->submodel[mn].offset + pm->submodel[mn].mod_pos;
mn = pm->submodel[mn].parent;
}
// now instance for the entire object
m = parent->orient;
vm_TransposeMatrix(&m);
new_pos = pnt * m;
fvec = fvec * m;
uvec = uvec * m;
vm_VectorToMatrix(&new_orient, &fvec, &uvec, NULL);
new_pos += parent->pos;
ObjSetPos(obj, &new_pos, parent->roomnum, &new_orient, false);
} else {
SetObjectDeadFlag(obj);
}
}
bool PhysCalcGround(vector *ground_point, vector *ground_normal, object *obj, int ground_num) {
poly_model *pm;
vector pnt;
vector normal;
matrix m;
int mn; // submodel number
float normalized_time[MAX_SUBOBJECTS];
bool f_good_gp = true;
if (obj->render_type != RT_POLYOBJ && obj->type != OBJ_PLAYER) {
// mprintf(0,"Object type %d is not a polyobj!\n",obj->type);
if (ground_point)
*ground_point = obj->pos;
if (ground_normal)
*ground_normal = obj->orient.fvec;
return false;
}
pm = &Poly_models[obj->rtype.pobj_info.model_num];
if (pm->n_ground == 0) {
LOG_WARNING.printf("Object %d with no weapons is firing.", ground_num);
if (ground_point)
*ground_point = obj->pos;
if (ground_normal)
*ground_normal = obj->orient.fvec;
return false;
}
SetNormalizedTimeObj(obj, normalized_time);
SetModelAnglesAndPos(pm, normalized_time);
if (ground_num < 0 || ground_num >= pm->n_ground) {
LOG_WARNING.printf("Bashing ground num %d to 0.", ground_num);
ground_num = 0;
f_good_gp = false;
}
pnt = pm->ground_slots[ground_num].pnt;
normal = pm->ground_slots[ground_num].norm;
mn = pm->ground_slots[ground_num].parent;
// Instance up the tree for this ground
while (mn != -1) {
vector tpnt;
vm_AnglesToMatrix(&m, pm->submodel[mn].angs.p, pm->submodel[mn].angs.h, pm->submodel[mn].angs.b);
vm_TransposeMatrix(&m);
tpnt = pnt * m;
normal = normal * m;
pnt = tpnt + pm->submodel[mn].offset + pm->submodel[mn].mod_pos;
mn = pm->submodel[mn].parent;
}
m = obj->orient;
vm_TransposeMatrix(&m);
if (ground_point)
*ground_point = pnt * m;
if (ground_normal)
*ground_normal = normal * m;
if (ground_point)
*ground_point += obj->pos;
return f_good_gp;
}
// Applies rotational thrust over at delta time
void PhysicsApplyConstRotForce(object &objp, const vector &rotforce, vector &rotvel, float deltaTime) {
const double drag = objp.mtype.phys_info.rotdrag;
const double mass = objp.mtype.phys_info.mass;
if (mass < std::numeric_limits<double>::epsilon() || drag < std::numeric_limits<double>::epsilon()) {
// Invalid mass/drag
rotvel += rotforce * deltaTime;
return;
}
// Standard angular motion with a linear air drag (drag is proportional to angular velocity)
const double oneOverDrag = 1.0 / drag;
const double rotForceOverDrag[3] = {double(rotforce.x) * oneOverDrag, double(rotforce.y) * oneOverDrag,
double(rotforce.z) * oneOverDrag};
const double dragOverMass = drag / mass;
const double expDoMDt = exp(-dragOverMass * double(deltaTime));
double newRotVel[3];
newRotVel[0] = (double(rotvel.x) - rotForceOverDrag[0]) * expDoMDt + rotForceOverDrag[0];
newRotVel[1] = (double(rotvel.y) - rotForceOverDrag[1]) * expDoMDt + rotForceOverDrag[1];
newRotVel[2] = (double(rotvel.z) - rotForceOverDrag[2]) * expDoMDt + rotForceOverDrag[2];
// set the new rotational velocity
rotvel.x = static_cast<float>(newRotVel[0]);
rotvel.y = static_cast<float>(newRotVel[1]);
rotvel.z = static_cast<float>(newRotVel[2]);
}
// Applies a linear force over time. --Like gravity or thrust
void PhysicsApplyConstantForce(const object &objp, vector &newPos, vector &newVel, vector &movementVec,
const vector &force, float deltaTime) {
const vector &pos = objp.pos;
const vector &vel = objp.mtype.phys_info.velocity;
const double drag = static_cast<double>(objp.mtype.phys_info.drag);
const double mass = static_cast<double>(objp.mtype.phys_info.mass);
if (mass < std::numeric_limits<double>::epsilon() || drag < std::numeric_limits<double>::epsilon()) {
// No Mass/Drag
movementVec = (vel * deltaTime) + (force * (0.5f * deltaTime * deltaTime));
newPos = pos + movementVec;
newVel = vel + force * deltaTime;
return;
}
// Standard motion with a linear air drag (drag is proportional to velocity)
const double dt = static_cast<double>(deltaTime);
const double oneOverDrag = 1.0 / drag;
const double massOverDrag = mass / drag;
const double forceOverDrag[3] = {force.x * oneOverDrag, force.y * oneOverDrag, force.z * oneOverDrag};
const double objVel[3] = {static_cast<double>(vel.x), static_cast<double>(vel.y), static_cast<double>(vel.z)};
const double expDoMDt = exp((-1.0 / massOverDrag) * dt);
newPos.x = static_cast<float>(static_cast<double>(pos.x) + forceOverDrag[0] * dt +
massOverDrag * (objVel[0] - forceOverDrag[0]) * (1.0 - expDoMDt));
newPos.y = static_cast<float>(static_cast<double>(pos.y) + forceOverDrag[1] * dt +
massOverDrag * (objVel[1] - forceOverDrag[1]) * (1.0 - expDoMDt));
newPos.z = static_cast<float>(static_cast<double>(pos.z) + forceOverDrag[2] * dt +
massOverDrag * (objVel[2] - forceOverDrag[2]) * (1.0 - expDoMDt));
movementVec = newPos - pos;
newVel.x = static_cast<float>((objVel[0] - forceOverDrag[0]) * expDoMDt + forceOverDrag[0]);
newVel.y = static_cast<float>((objVel[1] - forceOverDrag[1]) * expDoMDt + forceOverDrag[1]);
newVel.z = static_cast<float>((objVel[2] - forceOverDrag[2]) * expDoMDt + forceOverDrag[2]);
}
// Banks an object as it turns (we counteract this and then reapply it when we
// actually do the turn -- cool)
void set_object_turnroll(object *obj, vector *rotvel, angle *turnroll) {
float desired_bank;
angle desired_bank_angle;
desired_bank = -(rotvel->y) * obj->mtype.phys_info.turnroll_ratio;
// If desired bank > 32000, we will rotate the ship in weird ways. Should limit turn-roll to 32000 if this
// ever occurs. BTW This is where the limiter should be placed.
if (fabs(desired_bank) > 32000.0) {
if (desired_bank < 0.0)
desired_bank = -32000.0f;
else
desired_bank = 32000.0f;
}
if (desired_bank > -1.0) // accounts for -.9999 this is rounded to 0 in a float to int conversion
desired_bank_angle = desired_bank;
else
desired_bank_angle = 65535 + desired_bank;
if (*turnroll != desired_bank_angle) {
int delta_ang;
int max_roll;
max_roll = obj->mtype.phys_info.max_turnroll_rate * Frametime;
if (*turnroll > 32000)
delta_ang = desired_bank - (*turnroll - 65535);
else
delta_ang = desired_bank - *turnroll;
if (labs(delta_ang) > max_roll)
if (delta_ang > 0)
*turnroll += max_roll;
else
*turnroll -= max_roll;
else
*turnroll = desired_bank_angle;
} else
*turnroll = desired_bank_angle;
}
// Maximum number of objects that we already hit.
#define MAX_IGNORE_OBJS 100
// When leveling non-player objects - how many angles per sec. do they correct with.
#define MAX_LEVEL_ANGLES_PER_SEC 30000.0f
// Base scaler for pitch scaling of autoleveling
#define MAX_AUTOLEVEL_TILT_ANGLE 11000
// Multiplier for newbie autoleveling scaling of tilt angle
#define NEWBIE_AUTOLEVEL_TILT_ANGLE_SCALAR 1.25f
// How long a newbie sits there before the pitch leveling kicks in
#define NEWBIE_PITCH_LEVEL_TIME 1.5f
#define BANK_AUTOLEVEL_SPEED_SCALAR 2.0
// Rotational simulator the updates orient, rotvel, and turnroll
// (using these original values + frametime + rotthrust)
bool PhysicsDoSimRot(object *obj, float frame_time, matrix *orient, vector *rotthrust, vector *rotvel,
angle *turnroll) {
angvec tangles;
matrix rotmat;
physics_info *pi;
bool f_leveling = false;
float max_tilt_angle = 0;
bool f_newbie_leveling = false;
if (frame_time <= 0.0)
return false; // chrishack
pi = &obj->mtype.phys_info;
if (obj == Player_object && obj->control_type != CT_AI) {
max_tilt_angle = MAX_AUTOLEVEL_TILT_ANGLE;
if (OBJECT_OUTSIDE(obj)) {
if (Default_player_terrain_leveling) {
f_leveling = true;
if (Default_player_terrain_leveling > 1) {
max_tilt_angle *= NEWBIE_AUTOLEVEL_TILT_ANGLE_SCALAR;
f_newbie_leveling = true;
}
}
} else {
if (Default_player_room_leveling) {
f_leveling = true;
if (Default_player_room_leveling > 1) {
max_tilt_angle *= NEWBIE_AUTOLEVEL_TILT_ANGLE_SCALAR;
f_newbie_leveling = true;
}
}
}
} else {
if (pi->flags & PF_LEVELING)
f_leveling = true;
}
// Fixed rate rotaters
if (obj->mtype.phys_info.flags & PF_FIXED_ROT_VELOCITY) {
tangles.p = (int16_t)(rotvel->x * frame_time);
tangles.h = (int16_t)(rotvel->y * frame_time);
tangles.b = (int16_t)(rotvel->z * frame_time);
vm_AnglesToMatrix(&rotmat, tangles.p, tangles.h, tangles.b);
*orient = *orient * rotmat; // ObjSetOrient below
vm_Orthogonalize(orient); // Rest done after call
return true;
}
// now rotate object
// unrotate object for bank caused by turn
if (*turnroll != 0) {
tangles.p = tangles.h = 0;
tangles.b = -(*turnroll);
vm_AnglesToMatrix(&rotmat, tangles.p, tangles.h, tangles.b);
// Apply rotation matrix to the orientation matrix
*orient = *orient * rotmat; // ObjSetOrient is below
}
// Auto-leveling
if ((f_leveling) && (obj->type != OBJ_PLAYER)) {
if (fabs(rotthrust->z) < 100.0f) {
if ((fabs(rotvel->z) < 1500.0f) /*&& (pi->turnroll <= 10 || pi->turnroll >= 65535 - 10)*/) {
angvec ang;
vector fvec;
int bound;
// f_leveling = true;
// pi->turnroll = 0;
// extract angles from a matrix
vm_ExtractAnglesFromMatrix(&ang, orient);
if ((pi->flags & PF_TURNROLL) && *turnroll) {
bound = *turnroll;
if (bound > 32768) {
bound = 65535 - bound;
}
} else {
bound = 10;
}
if (ang.b > bound && ang.b < 65535 - bound) // About 1 degree
{
float scale;
// Scale on pitch
if (ang.p < 32768) {
scale = abs(16834 - (int)ang.p) / 16384.0f;
} else {
scale = abs(49152 - (int)ang.p) / 16384.0f;
}
// mprintf(0, "scale %f, ", scale);
if (ang.b < 32768) {
float al_rate = scale * MAX_LEVEL_ANGLES_PER_SEC * (1.0f - (abs(16834 - (int)ang.b) / 16384.0f));
if (al_rate < scale * 5000.0f) {
al_rate = scale * 5000.0f;
}
al_rate *= frame_time;
if (al_rate > ang.b)
al_rate = ang.b;
// mprintf(0, "L al_rate %f\n", al_rate);
ang.b -= al_rate;
} else {
float al_rate = scale * MAX_LEVEL_ANGLES_PER_SEC * (1.0f - (abs(16384 - ((int)ang.b - 32768)) / 16384.0f));
if (al_rate < scale * 5000.0f) {
al_rate = scale * 5000.0f;
}
al_rate *= frame_time;
if (al_rate > 65535 - ang.b)
al_rate = 65535 - ang.b;
// mprintf(0, "G al_rate %f\n", al_rate);
ang.b += al_rate;
}
fvec = orient->fvec;
vm_AnglesToMatrix(orient, ang.p, ang.h, ang.b);
}
}
}
} else if ((f_leveling) && (obj->type == OBJ_PLAYER)) {
bool f_pitch_leveled = false;
bool f_player_fired_recently = false;
int i;
for (i = 0; i < 21; i++) {
if (obj->dynamic_wb[i].last_fire_time + NEWBIE_PITCH_LEVEL_TIME > Gametime) {
f_player_fired_recently = true;
f_newbie_leveling = false;
break;
}
}
if (!f_player_fired_recently && f_newbie_leveling &&
(Players[obj->id].last_thrust_time + NEWBIE_PITCH_LEVEL_TIME < Gametime)) {
if (1) {
angvec ang;
int bound;
// f_leveling = true;
// pi->turnroll = 0;
// extract angles from a matrix
vm_ExtractAnglesFromMatrix(&ang, orient);
bound = 750;
// About 1 degree -- front or back
if ((ang.p > bound) && (ang.p < 65535 - bound) && // Forward
abs(ang.p - 32768) > bound) // Backward
{
float scale;
f_pitch_leveled = true;
if (obj->orient.uvec.y < 0.0f) {
ang.p += 16384;
}
scale = 1.05f - fabs(obj->orient.uvec.y);
// scale *= scale;
if (scale > 1.0)
scale = 1.0;
if (ang.p < 16834) {
rotthrust->x -= scale * obj->mtype.phys_info.full_rotthrust;
} else if (ang.p < 32768) {
rotthrust->x += scale * obj->mtype.phys_info.full_rotthrust;
} else if (ang.p < 49152) {
rotthrust->x -= scale * obj->mtype.phys_info.full_rotthrust;
} else {
rotthrust->x += scale * obj->mtype.phys_info.full_rotthrust;
}
}
}
}
if (!f_pitch_leveled && fabs(rotthrust->z) < 100.0f) {
angvec ang;
int bound;
// extract angles from a matrix
vm_ExtractAnglesFromMatrix(&ang, orient);
if ((ang.p < 32768 && (abs((int)ang.p - (int)16834) > max_tilt_angle)) ||
(ang.p >= 32768 && (abs((int)ang.p - (int)49152) > max_tilt_angle))) {
if ((pi->flags & PF_TURNROLL) && *turnroll) {
bound = *turnroll;
if (bound > 32768) {
bound = 65535 - bound;
}
} else {
bound = 10;
}
if (ang.b > bound && ang.b < 65535 - bound) // About 1 degree
{
float scale;
// Scale on pitch
if (ang.p < 32768) {
scale = (abs(16834 - (int)ang.p) - max_tilt_angle) / (16384.0f - max_tilt_angle);
} else {
scale = (abs(49152 - (int)ang.p) - max_tilt_angle) / (16384.0f - max_tilt_angle);
}
if (ang.b < 32768) {
float temp_scale;
if (ang.b > 28672) {
temp_scale = (1.04f - ((28672 - 16834) / 16384.0f));
} else {
temp_scale = (1.04f - (abs(16834 - (int)ang.b) / 16384.0f));
}
temp_scale *= temp_scale;
scale *= temp_scale;
if (f_player_fired_recently)
scale *= .25f;
rotthrust->z -= scale * BANK_AUTOLEVEL_SPEED_SCALAR * obj->mtype.phys_info.full_rotthrust;
} else {
float temp_scale;
if (ang.b < 36864) {
temp_scale = (1.04f - ((49152 - 36864) / 16384.0f));
} else {
temp_scale = (1.04f - (abs(49152 - (int)ang.b) / 16384.0f));
}
temp_scale *= temp_scale;
scale *= temp_scale;
if (f_player_fired_recently)
scale *= .25f;
rotthrust->z += scale * BANK_AUTOLEVEL_SPEED_SCALAR * obj->mtype.phys_info.full_rotthrust;
}
}
}
}
}
if (pi->rotdrag > 0.0f) {
if (!(pi->flags & PF_USES_THRUST)) {
*rotthrust = Zero_vector;
}
PhysicsApplyConstRotForce(*obj, *rotthrust, *rotvel, frame_time);
}
// Apply rotation to the "un-rollbanked" object
tangles.p = (int16_t)(rotvel->x * frame_time); // Casting to int16_t is required for aarch64 to avoid FCVTZU
// instruction which strips the negative sign
tangles.h = (int16_t)(rotvel->y * frame_time);
tangles.b = (int16_t)(rotvel->z * frame_time);
vm_AnglesToMatrix(&rotmat, tangles.p, tangles.h, tangles.b);
*orient = *orient * rotmat; // ObjSetOrient is below
// Determine the new turnroll from this amount of turning
if (pi->flags & PF_TURNROLL) {
set_object_turnroll(obj, rotvel, turnroll);
}
// re-rotate object for bank caused by turn
if (*turnroll != 0) {
tangles.p = tangles.h = 0;
tangles.b = *turnroll;
vm_AnglesToMatrix(&rotmat, tangles.p, tangles.h, tangles.b);
*orient = *orient * rotmat; // ObjSetOrient is below
}
// Make sure the new orientation is valid
vm_Orthogonalize(orient);
return true;
}
void PhysicsDoSimLinear(const object &obj, const vector &pos, const vector &force, vector &velocity,
vector &movementVec, vector &movementPos, float simTime, int count) {
// Determine velocity
if (obj.mtype.phys_info.flags & PF_FIXED_VELOCITY) {
movementVec = velocity * simTime;
movementPos = pos + movementVec;
return;
}
vector forceToUse = force;
if (!ROOMNUM_OUTSIDE(obj.roomnum) && !(obj.mtype.phys_info.flags & PF_LOCK_MASK) &&
Rooms[obj.roomnum].wind != Zero_vector && count == 0 && obj.mtype.phys_info.drag > 0.0f &&
!(obj.type == OBJ_POWERUP && Level_powerups_ignore_wind)) {
// Factor in outside wind
const vector &wind = Rooms[obj.roomnum].wind;
vector deltaForce = wind * obj.mtype.phys_info.drag * 16.0f;
forceToUse += deltaForce;
}
PhysicsApplyConstantForce(obj, movementPos, velocity, movementVec, forceToUse, simTime);
#ifdef _DEBUG
if (Physics_player_verbose && obj.type == OBJ_PLAYER) {
LOG_DEBUG.printf("Player Velocity = %f(%f)", vm_GetMagnitude(&velocity), vm_GetMagnitude(&movementVec) / simTime);
}
#endif
}
// -----------------------------------------------------------------------------------------------------------
// Simulate a physics object for this frame
void do_physics_sim(object *obj) {
// Since we might not call FVI, set this here
Fvi_num_recorded_faces = 0;
int n_ignore_objs = 0; // The number of ignored objects
int ignore_obj_list[MAX_IGNORE_OBJS + 1]; // List of ignored objects
int fate; // Collision type for response code
vector movement_vec; // Movement in this frame
vector movement_pos; // End point of the movement
int count = 0; // Simulation loop counter
int sim_loop_limit = (obj->type == OBJ_PLAYER) ? MAX_PLAYER_SIM_LOOPS : MAX_NON_PLAYER_SIM_LOOPS;
int objnum = OBJNUM(obj); // Simulated object's index
fvi_info hit_info; // Hit information
fvi_query fq; // Movement query
float sim_time_remaining = Frametime; // Amount of simulation time remaining (current iteration)
float old_sim_time_remaining = Frametime; // Amount of simulation time remaining (previous iteration)
vector init_pos = obj->pos; // Initial position
int init_room = obj->roomnum; // Initial room
vector start_pos; // Values at the start of current simulation loop
vector start_vel;
matrix start_orient;
vector start_rotvel;
angle start_turnroll;
int start_room;
float moved_time; // How long objected moved before hit something
physics_info *pi = &obj->mtype.phys_info; // Simulated object's physics information
int bounced = 0; // Did the object bounce?
vector total_force; // Constant force acting on an object
bool f_continue_sim; // Should we run another simulation loop
bool f_start_fvi_record = true; // Records the rooms that are passed thru
bool f_turn_gravity_on = false;
// Assert conditions
ASSERT(obj->type != OBJ_NONE);
ASSERT(obj->movement_type == MT_PHYSICS);
ASSERT(!(obj->mtype.phys_info.flags & PF_USES_THRUST) || obj->mtype.phys_info.drag != 0.0);
ASSERT(std::isfinite(obj->mtype.phys_info.velocity.x)); // Caller wants to go to infinity! -- Not FVI's fault.
ASSERT(std::isfinite(obj->mtype.phys_info.velocity.y)); // Caller wants to go to infinity! -- Not FVI's fault.
ASSERT(std::isfinite(obj->mtype.phys_info.velocity.z)); // Caller wants to go to infinity! -- Not FVI's fault.
#ifdef _DEBUG
if (!Game_do_flying_sim) {
return;
}
#endif
// Make powerups above the ceiling fall with gravity
if (obj->type == OBJ_POWERUP && ROOMNUM_OUTSIDE(obj->roomnum) &&
obj->pos.y > (CEILING_HEIGHT - obj->size - CEILING_POWERUP_DELTA) && !(obj->mtype.phys_info.flags & PF_GRAVITY)) {
obj->mtype.phys_info.flags |= PF_GRAVITY;
obj->flags |= OF_TEMP_GRAVITY;
} else if (obj->flags & OF_TEMP_GRAVITY) {
obj->mtype.phys_info.flags &= ~PF_GRAVITY;
}
// Instant bail cases
if ((obj->flags & (OF_DEAD | OF_ATTACHED)) || (Frametime <= 0.0) || (obj->type == OBJ_DUMMY)) {
return;
}
if (obj->type == OBJ_PLAYER &&
(obj->mtype.phys_info.thrust != Zero_vector || obj->mtype.phys_info.rotthrust != Zero_vector)) {
Players[obj->id].last_thrust_time = Gametime;
}
// mprintf(0, "%d Over terrain = %d\n", obj - Objects, obj->flags & OF_OVER_TERRAIN);
// If the object wiggles
if ((obj->mtype.phys_info.flags & PF_WIGGLE) && (Demo_flags != DF_PLAYBACK)) {
float swiggle;
vector w_pos;
if (vm_GetMagnitude(&obj->mtype.phys_info.thrust) < .1) {
obj->mtype.phys_info.last_still_time -= Frametime / WIGGLE_FALLOFF_TIME;
} else {
obj->mtype.phys_info.last_still_time += Frametime / WIGGLE_FALLOFF_TIME;
}
if (obj->mtype.phys_info.last_still_time < 0.0)
obj->mtype.phys_info.last_still_time = 0.0;
else if (obj->mtype.phys_info.last_still_time > 1.0)
obj->mtype.phys_info.last_still_time = 1.0;
// Ship always keeps 10% of its wiggle
// if(obj->mtype.phys_info.last_still_time < 1.0)
{
float scaler = 1.0f - obj->mtype.phys_info.last_still_time;
if (scaler < .1f)
scaler = .1f;
swiggle = obj->mtype.phys_info.wiggle_amplitude * (scaler) *
(FixSin((angle)((int)(Gametime * obj->mtype.phys_info.wiggles_per_sec * 65535) % 65535)) -
FixSin((angle)((int)((Gametime - Frametime) * obj->mtype.phys_info.wiggles_per_sec * 65535) % 65535)));
w_pos = obj->pos + obj->orient.uvec * (swiggle);
fq.p0 = &obj->pos;
fq.startroom = obj->roomnum;
fq.p1 = &w_pos;
fq.rad = obj->size;
fq.thisobjnum = objnum;
fq.ignore_obj_list = NULL;
fq.flags = FQ_CHECK_OBJS | FQ_RECORD | FQ_IGNORE_POWERUPS;
fq.flags |= FQ_NEW_RECORD_LIST;
fq.flags |= FQ_CHECK_CEILING;
// Need for weapons to go thru transparent areas of a texture
if (obj->type == OBJ_WEAPON)
fq.flags |= FQ_TRANSPOINT;
if (obj->flags & OF_NO_OBJECT_COLLISIONS)
fq.flags &= ~FQ_CHECK_OBJS;
fate = fvi_FindIntersection(&fq, &hit_info);
// Only wiggle on a non-hit case
if (fate == HIT_NONE) {
ASSERT(hit_info.hit_room != -1);
f_start_fvi_record = false;
// update object's position and segment number
ObjSetPos(obj, &hit_info.hit_pnt, hit_info.hit_room, NULL, false);
}
}
}
// If the object is effected by gravity (normal, lighter than air, and anti-gravity)
if (obj->mtype.phys_info.flags & PF_GRAVITY) {
total_force.x = total_force.z = 0.0;
total_force.y = Gravity_strength * obj->mtype.phys_info.mass;
} else if (obj->mtype.phys_info.flags & PF_REVERSE_GRAVITY) {
total_force.x = total_force.z = 0.0;
total_force.y = -Gravity_strength * obj->mtype.phys_info.mass;
} else {
total_force.x = total_force.y = total_force.z = 0.0;
}
obj->flags &= (~OF_STOPPED_THIS_FRAME);
// Do rotation velocity/accel stuff
bool f_rotated = false;
if (!(fabs(pi->velocity.x) > .000001 || fabs(pi->velocity.y) > .000001 || fabs(pi->velocity.z) > .000001 ||
fabs(pi->thrust.x) > .000001 || fabs(pi->thrust.y) > .000001 || fabs(pi->thrust.z) > .000001 ||
fabs(pi->rotvel.x) > .000001 || fabs(pi->rotvel.y) > .000001 || fabs(pi->rotvel.z) > .000001 ||
fabs(pi->rotthrust.x) > .000001 || fabs(pi->rotthrust.y) > .000001 || fabs(pi->rotthrust.z) > .000001 ||
(obj->mtype.phys_info.flags & PF_GRAVITY) ||
((!ROOMNUM_OUTSIDE(obj->roomnum)) && Rooms[obj->roomnum].wind != Zero_vector) ||
(obj->mtype.phys_info.flags & PF_WIGGLE) ||
((obj->ai_info != NULL) && ((obj->ai_info->flags & AIF_REPORT_NEW_ORIENT) != 0)))) {
if ((obj->flags & OF_MOVED_THIS_FRAME)) {
obj->flags &= (~OF_MOVED_THIS_FRAME);
obj->flags |= OF_STOPPED_THIS_FRAME;
}
return;
}
if (obj->ai_info)
obj->ai_info->flags &= ~AIF_REPORT_NEW_ORIENT;
obj->flags |= OF_MOVED_THIS_FRAME;
// This assumes that the thrust does not change within a frame. If it does, account for it
// there... like missiles bouncing off a wall and changing heading. --chrishack
if (obj->mtype.phys_info.drag != 0.0) {
if (obj->mtype.phys_info.flags & PF_USES_THRUST) {
total_force += obj->mtype.phys_info.thrust;
}
}
// mprintf(0, "PHYSICS: Current obj: %d, %9.2fx %9.2fy %9.2fz\n", OBJNUM(obj), XYZ(&obj->pos));
// mprintf(2,1,0,"x %9.2f\ny %9.2f\nz %9.2f", XYZ(&obj->mtype.phys_info.velocity));
// mprintf(0, "PHYSICS: Current velocity (%f, %f, %f)\n", XYZ(&obj->mtype.phys_info.velocity));
Physics_normal_counter++;
// Simulate movement until we are done (i.e. Frametime has passed or object is done moving)
do {
// Values at start of sim loop
start_pos = obj->pos;
start_vel = obj->mtype.phys_info.velocity;
start_orient = obj->orient;
start_rotvel = obj->mtype.phys_info.rotvel;
start_turnroll = obj->mtype.phys_info.turnroll;
start_room = obj->roomnum;
matrix end_orient = start_orient;
vector end_rotvel = start_rotvel;
angle end_turnroll = start_turnroll;
vector end_vel = start_vel;
#ifdef _DEBUG
// This records the sim.
sim_loop_record[count].phys_info = obj->mtype.phys_info;
#endif
// Initailly assume that this is the last sim cycle
f_continue_sim = false;
// Determines forces
if (count > 0) {
// If the object is effected by gravity (normal, lighter than air, and anti-gravity)
if (obj->mtype.phys_info.flags & PF_GRAVITY) {
total_force.x = total_force.z = 0.0;
total_force.y = Gravity_strength * obj->mtype.phys_info.mass;
} else if (obj->mtype.phys_info.flags & PF_REVERSE_GRAVITY) {
total_force.x = total_force.z = 0.0;
total_force.y = -Gravity_strength * obj->mtype.phys_info.mass;
} else if (!(obj->mtype.phys_info.flags & PF_BOUNCE)) {
// Player ship
} else {
total_force.x = total_force.y = total_force.z = 0.0;
}
}
// Compute rotational information
// Chrishack -- replace OBJ_CLUTTER with a BBOX_HIT_TYPE
bool f_set_orient;
if ((f_rotated == false && count == 0) || obj->type == OBJ_CLUTTER) {
f_rotated = true;
f_set_orient = true;
PhysicsDoSimRot(obj, sim_time_remaining, &end_orient, &obj->mtype.phys_info.rotthrust, &end_rotvel,
&end_turnroll);
ObjSetOrient(obj, &end_orient);
} else {
f_set_orient = false;
}
// Updates the thrust vector to the orientation for homing weapons
if ((obj->type == OBJ_WEAPON) && (pi->flags & PF_USES_THRUST) && (pi->flags & PF_HOMING)) {
pi->thrust = end_orient.fvec * pi->full_thrust;
movement_vec = obj->mtype.phys_info.velocity * Frametime;
movement_pos = obj->pos + movement_vec;
}
PhysicsDoSimLinear(*obj, obj->pos, total_force, end_vel, movement_vec, movement_pos, sim_time_remaining, count);
// Only do velocity until we've reached our destination position
// This is useful for multiplayer
if (obj->mtype.phys_info.flags & PF_DESTINATION_POS) {
vector sub1 = obj->pos - obj->mtype.phys_info.dest_pos;
vector sub2 = movement_pos - obj->mtype.phys_info.dest_pos;
if (vm_DotProduct(&sub1, &sub2) <= 0) {
obj->mtype.phys_info.flags &= ~PF_DESTINATION_POS;
vm_MakeZero(&obj->mtype.phys_info.velocity);
vm_MakeZero(&movement_vec);
movement_pos = obj->pos;
goto end_of_sim;
}
if ((vm_VectorDistanceQuick(&obj->mtype.phys_info.dest_pos, &obj->pos)) < 1) {
vm_MakeZero(&obj->mtype.phys_info.velocity);
obj->mtype.phys_info.flags &= ~PF_DESTINATION_POS;
goto end_of_sim;
}
}
// We are done, as there is no collision response with PF_NO_COLLIDE
if (obj->mtype.phys_info.flags & PF_NO_COLLIDE) {
obj->mtype.phys_info.rotvel = end_rotvel;
obj->mtype.phys_info.turnroll = end_turnroll;
obj->mtype.phys_info.velocity = end_vel;
// ObjSetOrient(obj,&end_orient);
ObjSetPos(obj, &movement_pos, obj->roomnum, NULL, false);
ObjSetAABB(obj);
obj->last_pos = init_pos;
goto end_of_sim;
}
Physics_normal_looping_counter++;
// Cap ignore list -- (Objects we already hit this frame)
ignore_obj_list[n_ignore_objs] = -1;
vector temp_vel = obj->mtype.phys_info.velocity;
obj->mtype.phys_info.velocity = (movement_pos - start_pos) / sim_time_remaining;
fq.p0 = &obj->pos;
fq.startroom = obj->roomnum;
fq.p1 = &movement_pos;
fq.rad = obj->size;
fq.thisobjnum = objnum;
fq.ignore_obj_list = ignore_obj_list;
fq.flags = FQ_CHECK_OBJS | FQ_RECORD;
if (f_start_fvi_record)
fq.flags |= FQ_NEW_RECORD_LIST;
fq.o_orient = &start_orient;
fq.o_rotvel = &start_rotvel;
fq.o_rotthrust = &obj->mtype.phys_info.rotthrust;
fq.o_turnroll = &start_turnroll;
fq.o_velocity = &start_vel;
fq.o_thrust = &total_force;
f_start_fvi_record = false;
// Need for weapons to go thru transparent areas of a texture
if (obj->type == OBJ_WEAPON)