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 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
|
/*
* Created by Ian "Goober5000" Warfield for the FreeSpace2 Source Code Project.
* You may not sell or otherwise commercially exploit the source or things you
* create based on the source.
*/
#include "math/bitarray.h"
#include "math/vecmat.h"
#include "mission/missionparse.h"
#include "object/object.h"
#include "object/objectdock.h"
#include "ship/ship.h"
// helper prototypes
void dock_evaluate_tree(object *objp, dock_function_info *infop, void (*function)(object *, dock_function_info *), ubyte *visited_bitstring);
void dock_move_docked_children_tree(object *objp, object *parent_objp);
void dock_count_total_docked_objects_helper(object *objp, dock_function_info *infop);
void dock_check_find_docked_object_helper(object *objp, dock_function_info *infop);
void dock_calc_docked_mins_maxs_helper(object *objp, dock_function_info *infop);
void dock_calc_docked_center_of_mass_helper(object *objp, dock_function_info *infop);
void dock_calc_total_docked_mass_helper(object *objp, dock_function_info *infop);
void dock_calc_max_cross_sectional_radius_squared_perpendicular_to_line_helper(object *objp, dock_function_info *infop);
void dock_calc_max_semilatus_rectum_squared_parallel_to_directrix_helper(object *objp, dock_function_info *infop);
void dock_find_max_speed_helper(object *objp, dock_function_info *infop);
void dock_find_max_fspeed_helper(object *objp, dock_function_info *infop);
void dock_calc_total_moi_helper(object* objp, dock_function_info* infop);
// management prototypes
bool dock_check_assume_hub();
object *dock_get_hub(object *objp);
void dock_add_instance(object *objp, int dockpoint, object *other_objp);
void dock_remove_instance(object *objp, object *other_objp);
dock_instance *dock_find_instance(object *objp, object *other_objp);
dock_instance *dock_find_instance(object *objp, int dockpoint);
int dock_count_instances(object *objp);
object *dock_get_first_docked_object(object *objp)
{
Assert(objp != NULL);
// are we docked?
if (!object_is_docked(objp))
return NULL;
return objp->dock_list->docked_objp;
}
bool dock_check_docked_one_on_one(object *objp)
{
Assert(objp != NULL);
// we must be docked
if (!object_is_docked(objp))
return false;
// our dock list must contain only one object
if (objp->dock_list->next != NULL)
return false;
// the other guy's dock list must contain only one object
if (dock_get_first_docked_object(objp)->dock_list->next != NULL)
return false;
// debug check to make sure that we're docked to each other
Assert(objp == dock_get_first_docked_object(objp)->dock_list->docked_objp);
// success
return true;
}
int dock_count_direct_docked_objects(object *objp)
{
Assert(objp != NULL);
return dock_count_instances(objp);
}
int dock_count_total_docked_objects(object *objp)
{
Assert(objp != NULL);
dock_function_info dfi;
dock_evaluate_all_docked_objects(objp, &dfi, dock_count_total_docked_objects_helper);
return dfi.maintained_variables.int_value;
}
bool dock_check_find_direct_docked_object(object *objp, object *other_objp)
{
Assert(objp != NULL);
Assert(other_objp != NULL);
return (dock_find_instance(objp, other_objp) != NULL);
}
bool dock_check_find_docked_object(object *objp, object *other_objp)
{
Assert(objp != nullptr);
Assert(objp->signature > 0);
Assert(other_objp != nullptr);
Assert(other_objp->signature > 0);
if (!(objp != nullptr && objp->signature > 0))
return false;
if (!(other_objp != nullptr && other_objp->signature > 0))
return false;
dock_function_info dfi;
dfi.parameter_variables.objp_value = other_objp;
dock_evaluate_all_docked_objects(objp, &dfi, dock_check_find_docked_object_helper);
return dfi.maintained_variables.bool_value;
}
object *dock_find_object_at_dockpoint(object *objp, int dockpoint)
{
Assert(objp != NULL);
dock_instance *result = dock_find_instance(objp, dockpoint);
if (result == NULL)
return NULL;
else
return result->docked_objp;
}
int dock_find_dockpoint_used_by_object(object *objp, object *other_objp)
{
Assert(objp != NULL);
Assert(other_objp != NULL);
dock_instance *result = dock_find_instance(objp, other_objp);
if (result == NULL)
return -1;
else
return result->dockpoint_used;
}
/**
* Get the offset of the actual center of the docked ship models for the purposes of warping (which may not be the specified center).
* Note, these are LOCAL coordinates in relation to objp, not world coordinates.
* See also ship_class_get_actual_center() in ship.cpp
*/
void dock_calc_docked_actual_center(vec3d *dest, object *objp)
{
Assert(dest != nullptr);
Assert(objp != nullptr);
vec3d overall_mins, overall_maxs;
dock_calc_docked_extents(&overall_mins, &overall_maxs, objp);
// c.f. ship_class_get_actual_center() in ship.cpp
dest->xyz.x = (overall_maxs.xyz.x + overall_mins.xyz.x) * 0.5f;
dest->xyz.y = (overall_maxs.xyz.y + overall_mins.xyz.y) * 0.5f;
dest->xyz.z = (overall_maxs.xyz.z + overall_mins.xyz.z) * 0.5f;
}
/**
* Get the mins and maxs of the entire assembly of docked ship models.
* Note, these are LOCAL coordinates in relation to objp, not world coordinates.
*/
void dock_calc_docked_extents(vec3d *mins, vec3d *maxs, object *objp)
{
Assert(mins != nullptr);
Assert(maxs != nullptr);
Assert(objp != nullptr);
*mins = vmd_zero_vector;
*maxs = vmd_zero_vector;
// Let's calculate all mins/maxes in relation to the orientation of the main object
// (which is expected to be the dock leader, but this technically isn't required).
// Since the vast majority of dockpoints are aligned with an axis, this should
// yield a much better fit of our docked bounding box.
dock_function_info dfi;
dfi.parameter_variables.objp_value = objp; // the reference object for our bounding box orientation
dfi.maintained_variables.vecp_value = mins; // mins
dfi.maintained_variables.vecp_value2 = maxs; // maxs
dock_evaluate_all_docked_objects(objp, &dfi, dock_calc_docked_mins_maxs_helper);
}
float dock_calc_docked_center_of_mass(vec3d *dest, object *objp)
{
Assertion(dest != nullptr, "dock_calc_docked_center_of_mass, invalid dest");
Assertion(objp != nullptr, "dock_calc_docked_center_of_mass, invalid objp");
vm_vec_zero(dest);
dock_function_info dfi;
dfi.maintained_variables.vecp_value = dest;
dock_evaluate_all_docked_objects(objp, &dfi, dock_calc_docked_center_of_mass_helper);
// overall center of mass = weighted sum of centers of mass divided by total mass
float total_mass = dfi.maintained_variables.float_value;
vm_vec_scale(dest, (1.0f / total_mass));
return total_mass;
}
float dock_calc_total_docked_mass(object *objp)
{
Assertion(objp != nullptr, "dock_calc_total_docked_mass, invalid argument");
dock_function_info dfi;
dock_evaluate_all_docked_objects(objp, &dfi, dock_calc_total_docked_mass_helper);
return dfi.maintained_variables.float_value;
}
float dock_calc_max_cross_sectional_radius_perpendicular_to_axis(object *objp, axis_type axis)
{
Assert(objp != NULL);
vec3d local_line_end;
vec3d *world_line_start, world_line_end;
dock_function_info dfi;
// to calculate the cross-sectional radius, we need a line that will be perpendicular to the cross-section
// the first endpoint is simply the position of the object
world_line_start = &objp->pos;
// the second endpoint extends in the axis direction
vm_vec_zero(&local_line_end);
switch(axis)
{
case X_AXIS:
local_line_end.xyz.x = 1.0f;
break;
case Y_AXIS:
local_line_end.xyz.y = 1.0f;
break;
case Z_AXIS:
local_line_end.xyz.z = 1.0f;
break;
default:
Int3();
return 0.0f;
}
// move the endpoint to go through the axis of the actual object
vm_vec_unrotate(&world_line_end, &local_line_end, &objp->orient);
vm_vec_add2(&world_line_end, &objp->pos);
// now we have a unit vector starting at the object's position and pointing along the chosen axis
// (although the length doesn't matter, as it's calculated as an endless line)
// now determine the cross-sectional radius
// set parameters and call function for the radius squared
dfi.parameter_variables.vecp_value = world_line_start;
dfi.parameter_variables.vecp_value2 = &world_line_end;
dock_evaluate_all_docked_objects(objp, &dfi, dock_calc_max_cross_sectional_radius_squared_perpendicular_to_line_helper);
// the radius is the square root of our result
return fl_sqrt(dfi.maintained_variables.float_value);
}
float dock_calc_max_semilatus_rectum_parallel_to_axis(object *objp, axis_type axis)
{
Assert(objp != NULL);
vec3d local_line_end;
vec3d *world_line_start, world_line_end;
dock_function_info dfi;
// to calculate the semilatus rectum, we need a directrix that will be parallel to the axis
// the first endpoint is simply the position of the object
world_line_start = &objp->pos;
// the second endpoint extends in the axis direction
vm_vec_zero(&local_line_end);
switch(axis)
{
case X_AXIS:
local_line_end.xyz.x = 1.0f;
break;
case Y_AXIS:
local_line_end.xyz.y = 1.0f;
break;
case Z_AXIS:
local_line_end.xyz.z = 1.0f;
break;
default:
Int3();
return 0.0f;
}
// move the endpoint to go through the axis of the actual object
vm_vec_unrotate(&world_line_end, &local_line_end, &objp->orient);
vm_vec_add2(&world_line_end, &objp->pos);
// now we have a unit vector starting at the object's position and pointing along the chosen axis
// (although the length doesn't matter, as it's calculated as an endless line)
// now determine the semilatus rectum
// set parameters and call function for the semilatus rectum squared
dfi.parameter_variables.vecp_value = world_line_start;
dfi.parameter_variables.vecp_value2 = &world_line_end;
dock_evaluate_all_docked_objects(objp, &dfi, dock_calc_max_semilatus_rectum_squared_parallel_to_directrix_helper);
// the semilatus rectum is the square root of our result
return fl_sqrt(dfi.maintained_variables.float_value);
}
float dock_calc_docked_fspeed(object *objp)
{
Assert(objp != NULL);
// *sigh*... the docked fspeed is simply the max fspeed of all docked objects
dock_function_info dfi;
dock_evaluate_all_docked_objects(objp, &dfi, dock_find_max_fspeed_helper);
return dfi.maintained_variables.float_value;
}
float dock_calc_docked_speed(object *objp)
{
Assert(objp != NULL);
// ditto with speed
dock_function_info dfi;
dock_evaluate_all_docked_objects(objp, &dfi, dock_find_max_speed_helper);
return dfi.maintained_variables.float_value;
}
// Calculates the total moi (NOT INVERTED) of a docked assembly
// dest => output matrix
// objp => one of the objects in the assembly
// center => center of mass of the assembly in world coords ( use dock_calc_docked_center_of_mass to find it )
// Returns whether or not was successful (in case some or all of the matrices were uninvertable or too close to it)
// If not successful, dest will have NaN or infinity, use at your own risk!
bool dock_calc_total_moi(matrix* dest, object* objp, vec3d *center)
{
Assertion((dest != nullptr) && (objp != nullptr) && (center != nullptr), "dock_calc_total_moi invalid argument(s)");
*dest = vmd_zero_matrix;
dock_function_info dfi;
dfi.parameter_variables.vecp_value = center;
dfi.maintained_variables.matrix_value = dest;
dock_evaluate_all_docked_objects(objp, &dfi, dock_calc_total_moi_helper);
return is_valid_matrix(dest);
}
// This ship is the only ship NOT moved by docking AI to keep everyone together
// All the other ships in the tree will update based on this one
// Since this is based on current speed don't expect it to remain consistent between frames
object* dock_find_dock_root(object *objp)
{
Assertion(objp != nullptr, "dock_find_dock_root invalid argument");
dock_function_info dfi;
object* fastest_objp;
dfi.maintained_variables.objp_value = nullptr;
// find the object with the highest speed
dock_evaluate_all_docked_objects(objp, &dfi, dock_find_max_speed_helper);
fastest_objp = dfi.maintained_variables.objp_value;
// if we have no max speed, just use the given one
if (fastest_objp == nullptr)
fastest_objp = objp;
return fastest_objp;
}
void dock_calculate_and_apply_whack_docked_object(vec3d* impulse, const vec3d* world_hit_pos, object* objp)
{
Assertion((objp != nullptr) && (impulse != nullptr) && (world_hit_pos != nullptr),
"dock_whack_docked_object invalid argument(s)");
// Detect null vector.
if (whack_below_limit(impulse))
return;
// calc overall world center-of-mass of all ships
vec3d world_center_of_mass;
float total_mass = dock_calc_docked_center_of_mass(&world_center_of_mass, objp);
vec3d hit_pos;
// the new hitpos is the vector from world center-of-mass to world hitpos
vm_vec_sub(&hit_pos, world_hit_pos, &world_center_of_mass);
matrix moi, inv_moi;
// calculate the effective inverse MOI for the docked composite object about its center of mass
if (dock_calc_total_moi(&moi, objp, &world_center_of_mass)) {
vm_inverse_matrix(&inv_moi, &moi);
}
else { // Just in case anything funky happened (usually due to some of the input matrices being non-invertable or too close to it)
inv_moi = vmd_zero_matrix;
}
// calculate the angular_impulse about the center of mass in world coords
vec3d angular_impulse;
vm_vec_cross(&angular_impulse, &hit_pos, impulse);
// calculate the change in rotvel caused by the whack in world coords
vec3d delta_rotvel;
vm_vec_rotate(&delta_rotvel, &angular_impulse, &inv_moi);
// get the total change in vel for the entire docked assembly
vec3d center_mass_delta_vel = *impulse * (1.0f / total_mass);
// get the root of the dock tree, so that updating this velocity will update the rest of the tree
object* root_objp;
root_objp = dock_find_dock_root(objp);
vec3d local_delta_rotvel;
// translate the rotvel change into the root's frame
vm_vec_rotate(&local_delta_rotvel, &delta_rotvel, &root_objp->orient);
// compute the root's linear vel as vel = center mass vel + world frame rotvel x relative pos
vec3d root_delta_vel;
vec3d rel_pos;
vm_vec_sub(&rel_pos, &root_objp->pos, &world_center_of_mass);
vm_vec_cross(&root_delta_vel, &delta_rotvel, &rel_pos);
vm_vec_add2(&root_delta_vel, ¢er_mass_delta_vel);
// whack it
physics_apply_whack(vm_vec_mag(impulse),
&root_objp->phys_info,
&local_delta_rotvel,
&root_delta_vel,
&root_objp->orient);
}
// functions to deal with all docked ships anywhere
// ---------------------------------------------------------------------------------------------------------------
// universal two functions
// -----------------------
// evaluate a certain function for all docked objects
// The_Force:: If changed please change in QtFred's ShipInitialStatusDialogModel also
void dock_evaluate_all_docked_objects(object *objp, dock_function_info *infop, void (*function)(object *, dock_function_info *))
{
Assertion((objp != nullptr) && (infop != nullptr) && (function != nullptr),
"dock_evaluate_all_docked_objects, invalid argument(s)");
// not docked?
if (!object_is_docked(objp))
{
// call the function for just the one object
function(objp, infop);
return;
}
// we only have two objects docked
if (dock_check_docked_one_on_one(objp))
{
// call the function for the first object, and return if instructed
function(objp, infop);
if (infop->early_return_condition) return;
// call the function for the second object, and return if instructed
function(objp->dock_list->docked_objp, infop);
if (infop->early_return_condition) return;
}
// we have multiple objects docked and we're treating them as a hub
else if (dock_check_assume_hub())
{
// get the hub
object *hub_objp = dock_get_hub(objp);
// call the function for the hub, and return if instructed
function(hub_objp, infop);
if (infop->early_return_condition) return;
// iterate through all docked objects
for (dock_instance *ptr = hub_objp->dock_list; ptr != NULL; ptr = ptr->next)
{
// call the function for this object, and return if instructed
function(ptr->docked_objp, infop);
if (infop->early_return_condition) return;
}
}
// we have multiple objects docked and we must treat them as a tree
else
{
// create a bit array to mark the objects we check
ubyte *visited_bitstring = (ubyte *) vm_malloc(calculate_num_bytes(MAX_OBJECTS));
// clear it
memset(visited_bitstring, 0, calculate_num_bytes(MAX_OBJECTS));
// start evaluating the tree
dock_evaluate_tree(objp, infop, function, visited_bitstring);
// destroy the bit array
vm_free(visited_bitstring);
visited_bitstring = NULL;
}
}
void dock_evaluate_tree(object *objp, dock_function_info *infop, void (*function)(object *, dock_function_info *), ubyte *visited_bitstring)
{
// make sure we haven't visited this object already
if (get_bit(visited_bitstring, OBJ_INDEX(objp)))
return;
// mark as visited
set_bit(visited_bitstring, OBJ_INDEX(objp));
// call the function for this object, and return if instructed
function(objp, infop);
if (infop->early_return_condition) return;
// iterate through all docked objects
for (dock_instance *ptr = objp->dock_list; ptr != NULL; ptr = ptr->next)
{
// start another tree with the docked object as the root, and return if instructed
dock_evaluate_tree(ptr->docked_objp, infop, function, visited_bitstring);
if (infop->early_return_condition) return;
}
}
// special-case functions
// ----------------------
void dock_move_docked_objects(object *objp)
{
Assert(objp != NULL);
if ((objp->type != OBJ_SHIP) && (objp->type != OBJ_START))
return;
if (!object_is_docked(objp))
return;
// has this object (by extension, this group of docked objects) been handled already?
if (objp->flags[Object::Object_Flags::Docked_already_handled])
return;
Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
dock_function_info dfi;
object *fastest_objp;
// in FRED, objp is the object everyone moves with
if (Fred_running)
{
fastest_objp = objp;
}
else
{
fastest_objp = dock_find_dock_root(objp);;
}
// start a tree with that object as the parent... do NOT use the überfunction for this,
// because we must use a tree for the parent ancestry to work correctly
// we don't need a bit array because OF_DOCKED_ALREADY_HANDLED takes care of it
// and must persist for the entire game frame
// start evaluating the tree, starting with the fastest object having no parent
dock_move_docked_children_tree(fastest_objp, NULL);
}
void dock_move_docked_children_tree(object *objp, object *parent_objp)
{
// has this object been handled already?
if (objp->flags[Object::Object_Flags::Docked_already_handled])
return;
// mark as handled
objp->flags.set(Object::Object_Flags::Docked_already_handled);
// if parent_objp exists
if (parent_objp != NULL)
{
// move this object to align with it
obj_move_one_docked_object(objp, parent_objp);
}
// iterate through all docked objects
for (dock_instance *ptr = objp->dock_list; ptr != NULL; ptr = ptr->next)
{
// start another tree with the docked object as the root and this object as the parent
dock_move_docked_children_tree(ptr->docked_objp, objp);
}
}
// helper functions
// ----------------
void dock_count_total_docked_objects_helper(object * /*objp*/, dock_function_info *infop)
{
// increment count
infop->maintained_variables.int_value++;
}
void dock_check_find_docked_object_helper(object *objp, dock_function_info *infop)
{
// if object found, set to true and break
if (infop->parameter_variables.objp_value == objp)
{
infop->maintained_variables.bool_value = true;
infop->early_return_condition = true;
}
}
void dock_calc_docked_mins_maxs_helper(object *objp, dock_function_info *infop)
{
polymodel *pm;
vec3d parent_relative_mins, parent_relative_maxs;
// find the model used by this object
int modelnum = object_get_model(objp);
Assert(modelnum >= 0);
pm = model_get(modelnum);
// special case: we are already in the correct frame of reference
if (objp == infop->parameter_variables.objp_value)
{
parent_relative_mins = pm->mins;
parent_relative_maxs = pm->maxs;
}
// we are not the parent object and need to do some gymnastics
else
{
// get mins and maxs in world coordinates
vec3d world_mins, world_maxs;
vm_vec_unrotate(&world_mins, &pm->mins, &objp->orient);
vm_vec_add2(&world_mins, &objp->pos);
vm_vec_unrotate(&world_maxs, &pm->maxs, &objp->orient);
vm_vec_add2(&world_maxs, &objp->pos);
// now adjust them to be local to the parent
vec3d temp_mins, temp_maxs;
vm_vec_sub(&temp_mins, &world_mins, &infop->parameter_variables.objp_value->pos);
vm_vec_rotate(&parent_relative_mins, &temp_mins, &infop->parameter_variables.objp_value->orient);
vm_vec_sub(&temp_maxs, &world_maxs, &infop->parameter_variables.objp_value->pos);
vm_vec_rotate(&parent_relative_maxs, &temp_maxs, &infop->parameter_variables.objp_value->orient);
}
// We test both points for both cases because they may have been flipped around. However, X is still comparable to X, Y to Y, Z to Z.
// test for overall min
for (int i = 0; i < 3; ++i)
{
if (parent_relative_mins.a1d[i] < infop->maintained_variables.vecp_value->a1d[i])
infop->maintained_variables.vecp_value->a1d[i] = parent_relative_mins.a1d[i];
if (parent_relative_maxs.a1d[i] < infop->maintained_variables.vecp_value->a1d[i])
infop->maintained_variables.vecp_value->a1d[i] = parent_relative_maxs.a1d[i];
}
// test for overall max
for (int i = 0; i < 3; ++i)
{
if (parent_relative_mins.a1d[i] > infop->maintained_variables.vecp_value2->a1d[i])
infop->maintained_variables.vecp_value2->a1d[i] = parent_relative_mins.a1d[i];
if (parent_relative_maxs.a1d[i] > infop->maintained_variables.vecp_value2->a1d[i])
infop->maintained_variables.vecp_value2->a1d[i] = parent_relative_maxs.a1d[i];
}
}
void dock_calc_docked_center_of_mass_helper(object *objp, dock_function_info *infop)
{
// add weighted object position and add mass
vm_vec_scale_add2(infop->maintained_variables.vecp_value, &objp->pos, objp->phys_info.mass);
infop->maintained_variables.float_value += objp->phys_info.mass;
}
void dock_calc_total_docked_mass_helper(object *objp, dock_function_info *infop)
{
// add mass
infop->maintained_variables.float_value += objp->phys_info.mass;
}
// What we're doing here is finding the distances between each extent of the object and the line, and then taking the
// maximum distance as the cross-sectional radius. We're actually maintaining the square of the distance rather than
// the actual distance, as it's faster to calculate and it gives the same result in a greater-than or less-than
// comparison. When we're done calculating everything for all objects (i.e. when we return to the parent function)
// we take the square root of the final value.
void dock_calc_max_cross_sectional_radius_squared_perpendicular_to_line_helper(object *objp, dock_function_info *infop)
{
vec3d world_point, local_point[6], nearest;
polymodel *pm;
int i;
float dist_squared;
// line parameters
vec3d *line_start = infop->parameter_variables.vecp_value;
vec3d *line_end = infop->parameter_variables.vecp_value2;
// We must find world coordinates for each of the six endpoints on the three axes of the object. I looked up
// which axis is front/back, left/right, and up/down, as well as which endpoint is which. It doesn't really
// matter, though, as all we need are the distances.
// grab our model
Assert(objp->type == OBJ_SHIP);
pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
// set up the points we want to check
memset(local_point, 0, sizeof(vec3d) * 6);
local_point[0].xyz.x = pm->maxs.xyz.x; // right point (max x)
local_point[1].xyz.x = pm->mins.xyz.x; // left point (min x)
local_point[2].xyz.y = pm->maxs.xyz.y; // top point (max y)
local_point[3].xyz.y = pm->mins.xyz.y; // bottom point (min y)
local_point[4].xyz.z = pm->maxs.xyz.z; // front point (max z)
local_point[5].xyz.z = pm->mins.xyz.z; // rear point (min z)
// check points
for (i = 0; i < 6; i++)
{
// calculate position of point
vm_vec_unrotate(&world_point, &local_point[i], &objp->orient);
vm_vec_add2(&world_point, &objp->pos);
// calculate square of distance to line
vm_vec_dist_squared_to_line(&world_point, line_start, line_end, &nearest, &dist_squared);
// update with farthest distance squared
if (dist_squared > infop->maintained_variables.float_value)
infop->maintained_variables.float_value = dist_squared;
}
}
// What we're doing here is projecting each object extent onto the directrix, calculating the distance between the
// projected point and the origin, and then taking the maximum distance as the semilatus rectum. We're actually
// maintaining the square of the distance rather than the actual distance, as it's faster to calculate and it gives
// the same result in a greater-than or less-than comparison. When we're done calculating everything for all
// objects (i.e. when we return to the parent function) we take the square root of the final value.
void dock_calc_max_semilatus_rectum_squared_parallel_to_directrix_helper(object *objp, dock_function_info *infop)
{
vec3d world_point, local_point[6], nearest;
polymodel *pm;
int i;
float temp, dist_squared;
// line parameters
vec3d *line_start = infop->parameter_variables.vecp_value;
vec3d *line_end = infop->parameter_variables.vecp_value2;
// We must find world coordinates for each of the six endpoints on the three axes of the object. I looked up
// which axis is front/back, left/right, and up/down, as well as which endpoint is which. It doesn't really
// matter, though, as all we need are the distances.
// grab our model
Assert(objp->type == OBJ_SHIP);
pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
// set up the points we want to check
memset(local_point, 0, sizeof(vec3d) * 6);
local_point[0].xyz.x = pm->maxs.xyz.x; // right point (max x)
local_point[1].xyz.x = pm->mins.xyz.x; // left point (min x)
local_point[2].xyz.y = pm->maxs.xyz.y; // top point (max y)
local_point[3].xyz.y = pm->mins.xyz.y; // bottom point (min y)
local_point[4].xyz.z = pm->maxs.xyz.z; // front point (max z)
local_point[5].xyz.z = pm->mins.xyz.z; // rear point (min z)
// check points
for (i = 0; i < 6; i++)
{
// calculate position of point
vm_vec_unrotate(&world_point, &local_point[i], &objp->orient);
vm_vec_add2(&world_point, &objp->pos);
// find the nearest point along the line
vm_vec_dist_squared_to_line(&world_point, line_start, line_end, &nearest, &temp);
// find the distance squared between the origin of the line and the point on the line
dist_squared = vm_vec_dist_squared(line_start, &nearest);
// update with farthest distance squared
if (dist_squared > infop->maintained_variables.float_value)
infop->maintained_variables.float_value = dist_squared;
}
}
void dock_find_max_fspeed_helper(object *objp, dock_function_info *infop)
{
// check our fspeed against the running maximum
if (objp->phys_info.fspeed > infop->maintained_variables.float_value)
{
infop->maintained_variables.float_value = objp->phys_info.fspeed;
infop->maintained_variables.objp_value = objp;
}
}
void dock_find_max_speed_helper(object *objp, dock_function_info *infop)
{
// check our speed against the running maximum
if (objp->phys_info.speed > infop->maintained_variables.float_value)
{
infop->maintained_variables.float_value = objp->phys_info.speed;
infop->maintained_variables.objp_value = objp;
}
}
void object_set_arriving_stage1_ndl_flag_helper(object *objp, dock_function_info * /*infop*/ )
{
if (! Ships[objp->instance].flags[Ship::Ship_Flags::Dock_leader])
Ships[objp->instance].flags.set(Ship::Ship_Flags::Arriving_stage_1_dock_follower);
}
void object_remove_arriving_stage1_ndl_flag_helper(object *objp, dock_function_info * /*infop*/ )
{
if (! Ships[objp->instance].flags[Ship::Ship_Flags::Dock_leader])
Ships[objp->instance].flags.remove(Ship::Ship_Flags::Arriving_stage_1_dock_follower);
}
void object_set_arriving_stage2_ndl_flag_helper(object *objp, dock_function_info * /*infop*/ )
{
if (! Ships[objp->instance].flags[Ship::Ship_Flags::Dock_leader])
Ships[objp->instance].flags.set(Ship::Ship_Flags::Arriving_stage_2_dock_follower);
}
void object_remove_arriving_stage2_ndl_flag_helper(object *objp, dock_function_info * /*infop*/ )
{
if (! Ships[objp->instance].flags[Ship::Ship_Flags::Dock_leader])
Ships[objp->instance].flags.remove(Ship::Ship_Flags::Arriving_stage_2_dock_follower);
}
void dock_calc_total_moi_helper(object* objp, dock_function_info* infop)
{
matrix local_moi, unorient, temp, world_moi;
// The MOI for a compound object is simply the sum of the MOI's of the parts, but
// they all have to be with respect to the same point (the center of mass, in this case).
// So for each part:
// We invert the inverse MOI to get an MOI in the local frame
if (!vm_inverse_matrix(&local_moi, &objp->phys_info.I_body_inv)) {
// This is done on purpose to indicate a zero inv_moi
infop->maintained_variables.matrix_value->a1d[0] = NAN;
return;
}
// We calculate the inverse of the orientation matrix (which is also the transpose)
vm_copy_transpose(&unorient, &objp->orient);
// We calculate the world space MOI using (world MOI) = O^-1 * (local MOI) * O
// where O is the orientation matrix (which translates between local space and world space).
// Note that because FS stores orientation matrices transposed, objp->orient is O^-1 in this formula.
vm_matrix_x_matrix(&temp, &objp->orient, &local_moi);
vm_matrix_x_matrix(&world_moi, &temp, &unorient);
// The world space MOI just calculated is about the center of mass of the part,
// so we need to translate it to the center of mass of the whole assembly.
// To do this we add a term corresponding to the MOI of a point mass whose position
// is the position of the part relative to the center of mass
vec3d* center = infop->parameter_variables.vecp_value;
vec3d pos = objp->pos - *center;
physics_add_point_mass_moi(&world_moi, objp->phys_info.mass, &pos);
// Finally we add the translated world space MOI for the part to the accumulated sum
*infop->maintained_variables.matrix_value += world_moi;
}
// ---------------------------------------------------------------------------------------------------------------
// end of über code block ----------------------------------------------------------------------------------------
// dock management functions -------------------------------------------------------------------------------------
void dock_dock_objects(object *objp1, int dockpoint1, object *objp2, int dockpoint2)
{
Assert(objp1 != NULL);
Assert(objp2 != NULL);
#ifndef NDEBUG
if ((dock_find_instance(objp1, objp2) != NULL) || (dock_find_instance(objp2, objp1) != NULL))
{
Error(LOCATION, "Trying to dock an object that's already docked!\n");
}
if ((dock_find_instance(objp1, dockpoint1) != NULL) || (dock_find_instance(objp2, dockpoint2) != NULL))
{
Error(LOCATION, "Trying to dock to a dockpoint that's in use!\n");
}
#endif
// put objects on each others' dock lists
dock_add_instance(objp1, dockpoint1, objp2);
dock_add_instance(objp2, dockpoint2, objp1);
}
void dock_undock_objects(object *objp1, object *objp2)
{
Assert(objp1 != NULL);
Assert(objp2 != NULL);
// remove objects from each others' dock lists
dock_remove_instance(objp1, objp2);
dock_remove_instance(objp2, objp1);
}
void dock_undock_all(object *objp)
{
Assert(objp != NULL);
while (object_is_docked(objp))
{
object* dockee = dock_get_first_docked_object(objp);
dock_undock_objects(objp, dockee);
}
}
// dock list functions -------------------------------------------------------------------------------------------
bool dock_check_assume_hub()
{
// There are several ways of handling ships docking to other ships. Level 1, the simplest, is the one-docker, one-dockee
// model used in retail FS2. Level 2 is the hub model, where we stipulate that any given set of docked ships
// includes one ship to which all other ships are docked. No ship except for the hub ship can be docked to more than
// one ship. Level 3 is the daisy-chain model, where you can string ships along and make a rooted tree.
//
// The new code can handle level 3 ship formations, but it requires more overhead than level 2 or level 1. (Whether
// the additional overhead is significant or not has not been determined.) In the vast majority of cases, level 3
// is not needed. So this function is provided to allow the code to optimize itself for level 2, should level 1
// evaluation fail.
// Determining this with a mission flag is a rather brittle design, and in any case this is not likely to be a large
// resource sink. Until a general-purpose tree detection routine is written, this function will always return false.
return false;
}
object *dock_get_hub(object *objp)
{
Assert(dock_check_assume_hub() && object_is_docked(objp));
// if our dock list contains only one object, it must be the hub
if (objp->dock_list->next == NULL)
{
return dock_get_first_docked_object(objp);
}
// otherwise we are the hub
else
{
return objp;
}
}
void dock_add_instance(object *objp, int dockpoint, object *other_objp)
{
dock_instance *item;
// create item
item = (dock_instance *) vm_malloc(sizeof(dock_instance));
item->dockpoint_used = dockpoint;
item->docked_objp = other_objp;
// prepend item to existing list
item->next = objp->dock_list;
objp->dock_list = item;
}
void dock_remove_instance(object *objp, object *other_objp)
{
int found = 0;
dock_instance *prev_ptr, *ptr;
prev_ptr = NULL;
ptr = objp->dock_list;
// iterate until item found
while (ptr != NULL)
{
// if found, exit loop
if (ptr->docked_objp == other_objp)
{
found = 1;
break;
}
// iterate
prev_ptr = ptr;
ptr = ptr->next;
}
// delete if found
if (found)
{
// special case... found at beginning of list
if (prev_ptr == NULL)
{
objp->dock_list = ptr->next;
}
// normal case
else
{
prev_ptr->next = ptr->next;
}
// delete it
vm_free(ptr);
}
else
{
// Trigger an assertion, we can recover from this one, thankfully.
UNREACHABLE("Tried to undock an object that isn't docked!\n");
}
}
// just free the list without worrying about undocking anything
void dock_free_dock_list(object *objp)
{
Assert(objp != NULL);
while (objp->dock_list != NULL)
{
dock_instance *ptr = objp->dock_list;
objp->dock_list = ptr->next;
vm_free(ptr);
}
}
dock_instance *dock_find_instance(object *objp, object *other_objp)
{
dock_instance *ptr = objp->dock_list;
// iterate until item found
while (ptr != NULL)
{
// if found, return it
if (ptr->docked_objp == other_objp)
return ptr;
// iterate
ptr = ptr->next;
}
// not found
return NULL;
}
dock_instance *dock_find_instance(object *objp, int dockpoint)
{
dock_instance *ptr = objp->dock_list;
// iterate until item found
while (ptr != NULL)
{
// if found, return it
if (ptr->dockpoint_used == dockpoint)
return ptr;
// iterate
ptr = ptr->next;
}
// not found
return NULL;
}
int dock_count_instances(object *objp)
{
int total_count = 0;
// count all instances in the list
dock_instance *ptr = objp->dock_list;
while (ptr != NULL)
{
// incrememnt for this object
total_count++;
// iterate
ptr = ptr->next;
}
// done
return total_count;
}
|