CollisionModel_rotate.cpp 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. /*
  21. ===============================================================================
  22. Trace model vs. polygonal model collision detection.
  23. ===============================================================================
  24. */
  25. #include "../idlib/precompiled.h"
  26. #pragma hdrstop
  27. #include "CollisionModel_local.h"
  28. /*
  29. ===============================================================================
  30. Collision detection for rotational motion
  31. ===============================================================================
  32. */
  33. // epsilon for round-off errors in epsilon calculations
  34. #define CM_PL_RANGE_EPSILON 1e-4f
  35. // if the collision point is this close to the rotation axis it is not considered a collision
  36. #define ROTATION_AXIS_EPSILON (CM_CLIP_EPSILON*0.25f)
  37. /*
  38. ================
  39. CM_RotatePoint
  40. rotates a point about an arbitrary axis using the tangent of half the rotation angle
  41. ================
  42. */
  43. void CM_RotatePoint( idVec3 &point, const idVec3 &origin, const idVec3 &axis, const float tanHalfAngle ) {
  44. double d, t, s, c;
  45. idVec3 proj, v1, v2;
  46. point -= origin;
  47. proj = axis * ( point * axis );
  48. v1 = point - proj;
  49. v2 = axis.Cross( v1 );
  50. // r = tan( a / 2 );
  51. // sin(a) = 2*r/(1+r*r);
  52. // cos(a) = (1-r*r)/(1+r*r);
  53. t = tanHalfAngle * tanHalfAngle;
  54. d = 1.0f / ( 1.0f + t );
  55. s = 2.0f * tanHalfAngle * d;
  56. c = ( 1.0f - t ) * d;
  57. point = v1 * c - v2 * s + proj + origin;
  58. }
  59. /*
  60. ================
  61. CM_RotateEdge
  62. rotates an edge about an arbitrary axis using the tangent of half the rotation angle
  63. ================
  64. */
  65. void CM_RotateEdge( idVec3 &start, idVec3 &end, const idVec3 &origin, const idVec3 &axis, const float tanHalfAngle ) {
  66. double d, t, s, c;
  67. idVec3 proj, v1, v2;
  68. // r = tan( a / 2 );
  69. // sin(a) = 2*r/(1+r*r);
  70. // cos(a) = (1-r*r)/(1+r*r);
  71. t = tanHalfAngle * tanHalfAngle;
  72. d = 1.0f / ( 1.0f + t );
  73. s = 2.0f * tanHalfAngle * d;
  74. c = ( 1.0f - t ) * d;
  75. start -= origin;
  76. proj = axis * ( start * axis );
  77. v1 = start - proj;
  78. v2 = axis.Cross( v1 );
  79. start = v1 * c - v2 * s + proj + origin;
  80. end -= origin;
  81. proj = axis * ( end * axis );
  82. v1 = end - proj;
  83. v2 = axis.Cross( v1 );
  84. end = v1 * c - v2 * s + proj + origin;
  85. }
  86. /*
  87. ================
  88. idCollisionModelManagerLocal::CollisionBetweenEdgeBounds
  89. verifies if the collision of two edges occurs between the edge bounds
  90. also calculates the collision point and collision plane normal if the collision occurs between the bounds
  91. ================
  92. */
  93. int idCollisionModelManagerLocal::CollisionBetweenEdgeBounds( cm_traceWork_t *tw, const idVec3 &va, const idVec3 &vb,
  94. const idVec3 &vc, const idVec3 &vd, float tanHalfAngle,
  95. idVec3 &collisionPoint, idVec3 &collisionNormal ) {
  96. float d1, d2, d;
  97. idVec3 at, bt, dir, dir1, dir2;
  98. idPluecker pl1, pl2;
  99. at = va;
  100. bt = vb;
  101. if ( tanHalfAngle != 0.0f ) {
  102. CM_RotateEdge( at, bt, tw->origin, tw->axis, tanHalfAngle );
  103. }
  104. dir1 = (at - tw->origin).Cross( tw->axis );
  105. dir2 = (bt - tw->origin).Cross( tw->axis );
  106. if ( dir1 * dir1 > dir2 * dir2 ) {
  107. dir = dir1;
  108. }
  109. else {
  110. dir = dir2;
  111. }
  112. if ( tw->angle < 0.0f ) {
  113. dir = -dir;
  114. }
  115. pl1.FromLine( at, bt );
  116. pl2.FromRay( vc, dir );
  117. d1 = pl1.PermutedInnerProduct( pl2 );
  118. pl2.FromRay( vd, dir );
  119. d2 = pl1.PermutedInnerProduct( pl2 );
  120. if ( ( d1 > 0.0f && d2 > 0.0f ) || ( d1 < 0.0f && d2 < 0.0f ) ) {
  121. return false;
  122. }
  123. pl1.FromLine( vc, vd );
  124. pl2.FromRay( at, dir );
  125. d1 = pl1.PermutedInnerProduct( pl2 );
  126. pl2.FromRay( bt, dir );
  127. d2 = pl1.PermutedInnerProduct( pl2 );
  128. if ( ( d1 > 0.0f && d2 > 0.0f ) || ( d1 < 0.0f && d2 < 0.0f ) ) {
  129. return false;
  130. }
  131. // collision point on the edge at-bt
  132. dir1 = (vd - vc).Cross( dir );
  133. d = dir1 * vc;
  134. d1 = dir1 * at - d;
  135. d2 = dir1 * bt - d;
  136. if ( d1 == d2 ) {
  137. return false;
  138. }
  139. collisionPoint = at + ( d1 / (d1 - d2) ) * ( bt - at );
  140. // normal is cross product of the rotated edge va-vb and the edge vc-vd
  141. collisionNormal.Cross( bt-at, vd-vc );
  142. return true;
  143. }
  144. /*
  145. ================
  146. idCollisionModelManagerLocal::RotateEdgeThroughEdge
  147. calculates the tangent of half the rotation angle at which the edges collide
  148. ================
  149. */
  150. int idCollisionModelManagerLocal::RotateEdgeThroughEdge( cm_traceWork_t *tw, const idPluecker &pl1,
  151. const idVec3 &vc, const idVec3 &vd,
  152. const float minTan, float &tanHalfAngle ) {
  153. double v0, v1, v2, a, b, c, d, sqrtd, q, frac1, frac2;
  154. idVec3 ct, dt;
  155. idPluecker pl2;
  156. /*
  157. a = start of line being rotated
  158. b = end of line being rotated
  159. pl1 = pluecker coordinate for line (a - b)
  160. pl2 = pluecker coordinate for edge we might collide with (c - d)
  161. t = rotation angle around the z-axis
  162. solve pluecker inner product for t of rotating line a-b and line l2
  163. // start point of rotated line during rotation
  164. an[0] = a[0] * cos(t) + a[1] * sin(t)
  165. an[1] = a[0] * -sin(t) + a[1] * cos(t)
  166. an[2] = a[2];
  167. // end point of rotated line during rotation
  168. bn[0] = b[0] * cos(t) + b[1] * sin(t)
  169. bn[1] = b[0] * -sin(t) + b[1] * cos(t)
  170. bn[2] = b[2];
  171. pl1[0] = a[0] * b[1] - b[0] * a[1];
  172. pl1[1] = a[0] * b[2] - b[0] * a[2];
  173. pl1[2] = a[0] - b[0];
  174. pl1[3] = a[1] * b[2] - b[1] * a[2];
  175. pl1[4] = a[2] - b[2];
  176. pl1[5] = b[1] - a[1];
  177. v[0] = (a[0] * cos(t) + a[1] * sin(t)) * (b[0] * -sin(t) + b[1] * cos(t)) - (b[0] * cos(t) + b[1] * sin(t)) * (a[0] * -sin(t) + a[1] * cos(t));
  178. v[1] = (a[0] * cos(t) + a[1] * sin(t)) * b[2] - (b[0] * cos(t) + b[1] * sin(t)) * a[2];
  179. v[2] = (a[0] * cos(t) + a[1] * sin(t)) - (b[0] * cos(t) + b[1] * sin(t));
  180. v[3] = (a[0] * -sin(t) + a[1] * cos(t)) * b[2] - (b[0] * -sin(t) + b[1] * cos(t)) * a[2];
  181. v[4] = a[2] - b[2];
  182. v[5] = (b[0] * -sin(t) + b[1] * cos(t)) - (a[0] * -sin(t) + a[1] * cos(t));
  183. pl2[0] * v[4] + pl2[1] * v[5] + pl2[2] * v[3] + pl2[4] * v[0] + pl2[5] * v[1] + pl2[3] * v[2] = 0;
  184. v[0] = (a[0] * cos(t) + a[1] * sin(t)) * (b[0] * -sin(t) + b[1] * cos(t)) - (b[0] * cos(t) + b[1] * sin(t)) * (a[0] * -sin(t) + a[1] * cos(t));
  185. v[0] = (a[1] * b[1] - a[0] * b[0]) * cos(t) * sin(t) + (a[0] * b[1] + a[1] * b[0] * cos(t)^2) - (a[1] * b[0]) - ((b[1] * a[1] - b[0] * a[0]) * cos(t) * sin(t) + (b[0] * a[1] + b[1] * a[0]) * cos(t)^2 - (b[1] * a[0]))
  186. v[0] = - (a[1] * b[0]) - ( - (b[1] * a[0]))
  187. v[0] = (b[1] * a[0]) - (a[1] * b[0])
  188. v[0] = (a[0]*b[1]) - (a[1]*b[0]);
  189. v[1] = (a[0]*b[2] - b[0]*a[2]) * cos(t) + (a[1]*b[2] - b[1]*a[2]) * sin(t);
  190. v[2] = (a[0]-b[0]) * cos(t) + (a[1]-b[1]) * sin(t);
  191. v[3] = (b[0]*a[2] - a[0]*b[2]) * sin(t) + (a[1]*b[2] - b[1]*a[2]) * cos(t);
  192. v[4] = a[2] - b[2];
  193. v[5] = (a[0]-b[0]) * sin(t) + (b[1]-a[1]) * cos(t);
  194. v[0] = (a[0]*b[1]) - (a[1]*b[0]);
  195. v[1] = (a[0]*b[2] - b[0]*a[2]) * cos(t) + (a[1]*b[2] - b[1]*a[2]) * sin(t);
  196. v[2] = (a[0]-b[0]) * cos(t) - (b[1]-a[1]) * sin(t);
  197. v[3] = (a[0]*b[2] - b[0]*a[2]) * -sin(t) + (a[1]*b[2] - b[1]*a[2]) * cos(t);
  198. v[4] = a[2] - b[2];
  199. v[5] = (a[0]-b[0]) * sin(t) + (b[1]-a[1]) * cos(t);
  200. v[0] = pl1[0];
  201. v[1] = pl1[1] * cos(t) + pl1[3] * sin(t);
  202. v[2] = pl1[2] * cos(t) - pl1[5] * sin(t);
  203. v[3] = pl1[3] * cos(t) - pl1[1] * sin(t);
  204. v[4] = pl1[4];
  205. v[5] = pl1[5] * cos(t) + pl1[2] * sin(t);
  206. pl2[0] * v[4] + pl2[1] * v[5] + pl2[2] * v[3] + pl2[4] * v[0] + pl2[5] * v[1] + pl2[3] * v[2] = 0;
  207. 0 = pl2[0] * pl1[4] +
  208. pl2[1] * (pl1[5] * cos(t) + pl1[2] * sin(t)) +
  209. pl2[2] * (pl1[3] * cos(t) - pl1[1] * sin(t)) +
  210. pl2[4] * pl1[0] +
  211. pl2[5] * (pl1[1] * cos(t) + pl1[3] * sin(t)) +
  212. pl2[3] * (pl1[2] * cos(t) - pl1[5] * sin(t));
  213. v2 * cos(t) + v1 * sin(t) + v0 = 0;
  214. // rotation about the z-axis
  215. v0 = pl2[0] * pl1[4] + pl2[4] * pl1[0];
  216. v1 = pl2[1] * pl1[2] - pl2[2] * pl1[1] + pl2[5] * pl1[3] - pl2[3] * pl1[5];
  217. v2 = pl2[1] * pl1[5] + pl2[2] * pl1[3] + pl2[5] * pl1[1] + pl2[3] * pl1[2];
  218. // rotation about the x-axis
  219. //v0 = pl2[3] * pl1[2] + pl2[2] * pl1[3];
  220. //v1 = -pl2[5] * pl1[0] + pl2[4] * pl1[1] - pl2[1] * pl1[4] + pl2[0] * pl1[5];
  221. //v2 = pl2[4] * pl1[0] + pl2[5] * pl1[1] + pl2[0] * pl1[4] + pl2[1] * pl1[5];
  222. r = tan(t / 2);
  223. sin(t) = 2*r/(1+r*r);
  224. cos(t) = (1-r*r)/(1+r*r);
  225. v1 * 2 * r / (1 + r*r) + v2 * (1 - r*r) / (1 + r*r) + v0 = 0
  226. (v1 * 2 * r + v2 * (1 - r*r)) / (1 + r*r) = -v0
  227. (v1 * 2 * r + v2 - v2 * r*r) / (1 + r*r) = -v0
  228. v1 * 2 * r + v2 - v2 * r*r = -v0 * (1 + r*r)
  229. v1 * 2 * r + v2 - v2 * r*r = -v0 + -v0 * r*r
  230. (v0 - v2) * r * r + (2 * v1) * r + (v0 + v2) = 0;
  231. MrE gives Pluecker a banana.. good monkey
  232. */
  233. tanHalfAngle = tw->maxTan;
  234. // transform rotation axis to z-axis
  235. ct = (vc - tw->origin) * tw->matrix;
  236. dt = (vd - tw->origin) * tw->matrix;
  237. pl2.FromLine( ct, dt );
  238. v0 = pl2[0] * pl1[4] + pl2[4] * pl1[0];
  239. v1 = pl2[1] * pl1[2] - pl2[2] * pl1[1] + pl2[5] * pl1[3] - pl2[3] * pl1[5];
  240. v2 = pl2[1] * pl1[5] + pl2[2] * pl1[3] + pl2[5] * pl1[1] + pl2[3] * pl1[2];
  241. a = v0 - v2;
  242. b = v1;
  243. c = v0 + v2;
  244. if ( a == 0.0f ) {
  245. if ( b == 0.0f ) {
  246. return false;
  247. }
  248. frac1 = -c / ( 2.0f * b );
  249. frac2 = 1e10; // = tan( idMath::HALF_PI )
  250. }
  251. else {
  252. d = b * b - c * a;
  253. if ( d <= 0.0f ) {
  254. return false;
  255. }
  256. sqrtd = sqrt( d );
  257. if ( b > 0.0f ) {
  258. q = - b + sqrtd;
  259. }
  260. else {
  261. q = - b - sqrtd;
  262. }
  263. frac1 = q / a;
  264. frac2 = c / q;
  265. }
  266. if ( tw->angle < 0.0f ) {
  267. frac1 = -frac1;
  268. frac2 = -frac2;
  269. }
  270. // get smallest tangent for which a collision occurs
  271. if ( frac1 >= minTan && frac1 < tanHalfAngle ) {
  272. tanHalfAngle = frac1;
  273. }
  274. if ( frac2 >= minTan && frac2 < tanHalfAngle ) {
  275. tanHalfAngle = frac2;
  276. }
  277. if ( tw->angle < 0.0f ) {
  278. tanHalfAngle = -tanHalfAngle;
  279. }
  280. return true;
  281. }
  282. /*
  283. ================
  284. idCollisionModelManagerLocal::EdgeFurthestFromEdge
  285. calculates the direction of motion at the initial position, where dir < 0 means the edges move towards each other
  286. if the edges move away from each other the tangent of half the rotation angle at which
  287. the edges are furthest apart is also calculated
  288. ================
  289. */
  290. int idCollisionModelManagerLocal::EdgeFurthestFromEdge( cm_traceWork_t *tw, const idPluecker &pl1,
  291. const idVec3 &vc, const idVec3 &vd,
  292. float &tanHalfAngle, float &dir ) {
  293. double v0, v1, v2, a, b, c, d, sqrtd, q, frac1, frac2;
  294. idVec3 ct, dt;
  295. idPluecker pl2;
  296. /*
  297. v2 * cos(t) + v1 * sin(t) + v0 = 0;
  298. // rotation about the z-axis
  299. v0 = pl2[0] * pl1[4] + pl2[4] * pl1[0];
  300. v1 = pl2[1] * pl1[2] - pl2[2] * pl1[1] + pl2[5] * pl1[3] - pl2[3] * pl1[5];
  301. v2 = pl2[1] * pl1[5] + pl2[2] * pl1[3] + pl2[5] * pl1[1] + pl2[3] * pl1[2];
  302. derivative:
  303. v1 * cos(t) - v2 * sin(t) = 0;
  304. r = tan(t / 2);
  305. sin(t) = 2*r/(1+r*r);
  306. cos(t) = (1-r*r)/(1+r*r);
  307. -v2 * 2 * r / (1 + r*r) + v1 * (1 - r*r)/(1+r*r);
  308. -v2 * 2 * r + v1 * (1 - r*r) / (1 + r*r) = 0;
  309. -v2 * 2 * r + v1 * (1 - r*r) = 0;
  310. (-v1) * r * r + (-2 * v2) * r + (v1) = 0;
  311. */
  312. tanHalfAngle = 0.0f;
  313. // transform rotation axis to z-axis
  314. ct = (vc - tw->origin) * tw->matrix;
  315. dt = (vd - tw->origin) * tw->matrix;
  316. pl2.FromLine( ct, dt );
  317. v0 = pl2[0] * pl1[4] + pl2[4] * pl1[0];
  318. v1 = pl2[1] * pl1[2] - pl2[2] * pl1[1] + pl2[5] * pl1[3] - pl2[3] * pl1[5];
  319. v2 = pl2[1] * pl1[5] + pl2[2] * pl1[3] + pl2[5] * pl1[1] + pl2[3] * pl1[2];
  320. // get the direction of motion at the initial position
  321. c = v0 + v2;
  322. if ( tw->angle > 0.0f ) {
  323. if ( c > 0.0f ) {
  324. dir = v1;
  325. }
  326. else {
  327. dir = -v1;
  328. }
  329. }
  330. else {
  331. if ( c > 0.0f ) {
  332. dir = -v1;
  333. }
  334. else {
  335. dir = v1;
  336. }
  337. }
  338. // negative direction means the edges move towards each other at the initial position
  339. if ( dir <= 0.0f ) {
  340. return true;
  341. }
  342. a = -v1;
  343. b = -v2;
  344. c = v1;
  345. if ( a == 0.0f ) {
  346. if ( b == 0.0f ) {
  347. return false;
  348. }
  349. frac1 = -c / ( 2.0f * b );
  350. frac2 = 1e10; // = tan( idMath::HALF_PI )
  351. }
  352. else {
  353. d = b * b - c * a;
  354. if ( d <= 0.0f ) {
  355. return false;
  356. }
  357. sqrtd = sqrt( d );
  358. if ( b > 0.0f ) {
  359. q = - b + sqrtd;
  360. }
  361. else {
  362. q = - b - sqrtd;
  363. }
  364. frac1 = q / a;
  365. frac2 = c / q;
  366. }
  367. if ( tw->angle < 0.0f ) {
  368. frac1 = -frac1;
  369. frac2 = -frac2;
  370. }
  371. if ( frac1 < 0.0f && frac2 < 0.0f ) {
  372. return false;
  373. }
  374. if ( frac1 > frac2 ) {
  375. tanHalfAngle = frac1;
  376. }
  377. else {
  378. tanHalfAngle = frac2;
  379. }
  380. if ( tw->angle < 0.0f ) {
  381. tanHalfAngle = -tanHalfAngle;
  382. }
  383. return true;
  384. }
  385. /*
  386. ================
  387. idCollisionModelManagerLocal::RotateTrmEdgeThroughPolygon
  388. ================
  389. */
  390. void idCollisionModelManagerLocal::RotateTrmEdgeThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmEdge_t *trmEdge ) {
  391. int i, j, edgeNum;
  392. float f1, f2, startTan, dir, tanHalfAngle;
  393. cm_edge_t *edge;
  394. cm_vertex_t *v1, *v2;
  395. idVec3 collisionPoint, collisionNormal, origin, epsDir;
  396. idPluecker epsPl;
  397. idBounds bounds;
  398. // if the trm is convex and the rotation axis intersects the trm
  399. if ( tw->isConvex && tw->axisIntersectsTrm ) {
  400. // if both points are behind the polygon the edge cannot collide within a 180 degrees rotation
  401. if ( tw->vertices[trmEdge->vertexNum[0]].polygonSide & tw->vertices[trmEdge->vertexNum[1]].polygonSide ) {
  402. return;
  403. }
  404. }
  405. // if the trace model edge rotation bounds do not intersect the polygon bounds
  406. if ( !trmEdge->rotationBounds.IntersectsBounds( poly->bounds ) ) {
  407. return;
  408. }
  409. // edge rotation bounds should cross polygon plane
  410. if ( trmEdge->rotationBounds.PlaneSide( poly->plane ) != SIDE_CROSS ) {
  411. return;
  412. }
  413. // check edges for a collision
  414. for ( i = 0; i < poly->numEdges; i++ ) {
  415. edgeNum = poly->edges[i];
  416. edge = tw->model->edges + abs(edgeNum);
  417. // if this edge is already checked
  418. if ( edge->checkcount == idCollisionModelManagerLocal::checkCount ) {
  419. continue;
  420. }
  421. // can never collide with internal edges
  422. if ( edge->internal ) {
  423. continue;
  424. }
  425. v1 = tw->model->vertices + edge->vertexNum[INTSIGNBITSET(edgeNum)];
  426. v2 = tw->model->vertices + edge->vertexNum[INTSIGNBITNOTSET(edgeNum)];
  427. // edge bounds
  428. for ( j = 0; j < 3; j++ ) {
  429. if ( v1->p[j] > v2->p[j] ) {
  430. bounds[0][j] = v2->p[j];
  431. bounds[1][j] = v1->p[j];
  432. }
  433. else {
  434. bounds[0][j] = v1->p[j];
  435. bounds[1][j] = v2->p[j];
  436. }
  437. }
  438. // if the trace model edge rotation bounds do not intersect the polygon edge bounds
  439. if ( !trmEdge->rotationBounds.IntersectsBounds( bounds ) ) {
  440. continue;
  441. }
  442. f1 = trmEdge->pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[i] );
  443. // pluecker coordinate for epsilon expanded edge
  444. epsDir = edge->normal * (CM_CLIP_EPSILON+CM_PL_RANGE_EPSILON);
  445. epsPl.FromLine( tw->model->vertices[edge->vertexNum[0]].p + epsDir,
  446. tw->model->vertices[edge->vertexNum[1]].p + epsDir );
  447. f2 = trmEdge->pl.PermutedInnerProduct( epsPl );
  448. // if the rotating edge is inbetween the polygon edge and the epsilon expanded edge
  449. if ( ( f1 < 0.0f && f2 > 0.0f ) || ( f1 > 0.0f && f2 < 0.0f ) ) {
  450. if ( !EdgeFurthestFromEdge( tw, trmEdge->plzaxis, v1->p, v2->p, startTan, dir ) ) {
  451. continue;
  452. }
  453. if ( dir <= 0.0f ) {
  454. // moving towards the polygon edge so stop immediately
  455. tanHalfAngle = 0.0f;
  456. }
  457. else if ( idMath::Fabs( startTan ) >= tw->maxTan ) {
  458. // never going to get beyond the start tangent during the current rotation
  459. continue;
  460. }
  461. else {
  462. // collide with the epsilon expanded edge
  463. if ( !RotateEdgeThroughEdge(tw, trmEdge->plzaxis, v1->p + epsDir, v2->p + epsDir, idMath::Fabs( startTan ), tanHalfAngle ) ) {
  464. tanHalfAngle = startTan;
  465. }
  466. }
  467. }
  468. else {
  469. // collide with the epsilon expanded edge
  470. epsDir = edge->normal * CM_CLIP_EPSILON;
  471. if ( !RotateEdgeThroughEdge(tw, trmEdge->plzaxis, v1->p + epsDir, v2->p + epsDir, 0.0f, tanHalfAngle ) ) {
  472. continue;
  473. }
  474. }
  475. if ( idMath::Fabs( tanHalfAngle ) >= tw->maxTan ) {
  476. continue;
  477. }
  478. // check if the collision is between the edge bounds
  479. if ( !CollisionBetweenEdgeBounds( tw, trmEdge->start, trmEdge->end, v1->p, v2->p,
  480. tanHalfAngle, collisionPoint, collisionNormal ) ) {
  481. continue;
  482. }
  483. // allow rotation if the rotation axis goes through the collisionPoint
  484. origin = tw->origin + tw->axis * ( tw->axis * ( collisionPoint - tw->origin ) );
  485. if ( ( collisionPoint - origin ).LengthSqr() < ROTATION_AXIS_EPSILON * ROTATION_AXIS_EPSILON ) {
  486. continue;
  487. }
  488. // fill in trace structure
  489. tw->maxTan = idMath::Fabs( tanHalfAngle );
  490. tw->trace.c.normal = collisionNormal;
  491. tw->trace.c.normal.Normalize();
  492. tw->trace.c.dist = tw->trace.c.normal * v1->p;
  493. // make sure the collision plane faces the trace model
  494. if ( (tw->trace.c.normal * trmEdge->start) - tw->trace.c.dist < 0 ) {
  495. tw->trace.c.normal = -tw->trace.c.normal;
  496. tw->trace.c.dist = -tw->trace.c.dist;
  497. }
  498. tw->trace.c.contents = poly->contents;
  499. tw->trace.c.material = poly->material;
  500. tw->trace.c.type = CONTACT_EDGE;
  501. tw->trace.c.modelFeature = edgeNum;
  502. tw->trace.c.trmFeature = trmEdge - tw->edges;
  503. tw->trace.c.point = collisionPoint;
  504. // if no collision can be closer
  505. if ( tw->maxTan == 0.0f ) {
  506. break;
  507. }
  508. }
  509. }
  510. /*
  511. ================
  512. idCollisionModelManagerLocal::RotatePointThroughPlane
  513. calculates the tangent of half the rotation angle at which the point collides with the plane
  514. ================
  515. */
  516. int idCollisionModelManagerLocal::RotatePointThroughPlane( const cm_traceWork_t *tw, const idVec3 &point, const idPlane &plane,
  517. const float angle, const float minTan, float &tanHalfAngle ) {
  518. double v0, v1, v2, a, b, c, d, sqrtd, q, frac1, frac2;
  519. idVec3 p, normal;
  520. /*
  521. p[0] = point[0] * cos(t) + point[1] * sin(t)
  522. p[1] = point[0] * -sin(t) + point[1] * cos(t)
  523. p[2] = point[2];
  524. normal[0] * (p[0] * cos(t) + p[1] * sin(t)) +
  525. normal[1] * (p[0] * -sin(t) + p[1] * cos(t)) +
  526. normal[2] * p[2] + dist = 0
  527. normal[0] * p[0] * cos(t) + normal[0] * p[1] * sin(t) +
  528. -normal[1] * p[0] * sin(t) + normal[1] * p[1] * cos(t) +
  529. normal[2] * p[2] + dist = 0
  530. v2 * cos(t) + v1 * sin(t) + v0
  531. // rotation about the z-axis
  532. v0 = normal[2] * p[2] + dist
  533. v1 = normal[0] * p[1] - normal[1] * p[0]
  534. v2 = normal[0] * p[0] + normal[1] * p[1]
  535. r = tan(t / 2);
  536. sin(t) = 2*r/(1+r*r);
  537. cos(t) = (1-r*r)/(1+r*r);
  538. v1 * 2 * r / (1 + r*r) + v2 * (1 - r*r) / (1 + r*r) + v0 = 0
  539. (v1 * 2 * r + v2 * (1 - r*r)) / (1 + r*r) = -v0
  540. (v1 * 2 * r + v2 - v2 * r*r) / (1 + r*r) = -v0
  541. v1 * 2 * r + v2 - v2 * r*r = -v0 * (1 + r*r)
  542. v1 * 2 * r + v2 - v2 * r*r = -v0 + -v0 * r*r
  543. (v0 - v2) * r * r + (2 * v1) * r + (v0 + v2) = 0;
  544. */
  545. tanHalfAngle = tw->maxTan;
  546. // transform rotation axis to z-axis
  547. p = (point - tw->origin) * tw->matrix;
  548. d = plane[3] + plane.Normal() * tw->origin;
  549. normal = plane.Normal() * tw->matrix;
  550. v0 = normal[2] * p[2] + d;
  551. v1 = normal[0] * p[1] - normal[1] * p[0];
  552. v2 = normal[0] * p[0] + normal[1] * p[1];
  553. a = v0 - v2;
  554. b = v1;
  555. c = v0 + v2;
  556. if ( a == 0.0f ) {
  557. if ( b == 0.0f ) {
  558. return false;
  559. }
  560. frac1 = -c / ( 2.0f * b );
  561. frac2 = 1e10; // = tan( idMath::HALF_PI )
  562. }
  563. else {
  564. d = b * b - c * a;
  565. if ( d <= 0.0f ) {
  566. return false;
  567. }
  568. sqrtd = sqrt( d );
  569. if ( b > 0.0f ) {
  570. q = - b + sqrtd;
  571. }
  572. else {
  573. q = - b - sqrtd;
  574. }
  575. frac1 = q / a;
  576. frac2 = c / q;
  577. }
  578. if ( angle < 0.0f ) {
  579. frac1 = -frac1;
  580. frac2 = -frac2;
  581. }
  582. // get smallest tangent for which a collision occurs
  583. if ( frac1 >= minTan && frac1 < tanHalfAngle ) {
  584. tanHalfAngle = frac1;
  585. }
  586. if ( frac2 >= minTan && frac2 < tanHalfAngle ) {
  587. tanHalfAngle = frac2;
  588. }
  589. if ( angle < 0.0f ) {
  590. tanHalfAngle = -tanHalfAngle;
  591. }
  592. return true;
  593. }
  594. /*
  595. ================
  596. idCollisionModelManagerLocal::PointFurthestFromPlane
  597. calculates the direction of motion at the initial position, where dir < 0 means the point moves towards the plane
  598. if the point moves away from the plane the tangent of half the rotation angle at which
  599. the point is furthest away from the plane is also calculated
  600. ================
  601. */
  602. int idCollisionModelManagerLocal::PointFurthestFromPlane( const cm_traceWork_t *tw, const idVec3 &point, const idPlane &plane,
  603. const float angle, float &tanHalfAngle, float &dir ) {
  604. double v1, v2, a, b, c, d, sqrtd, q, frac1, frac2;
  605. idVec3 p, normal;
  606. /*
  607. v2 * cos(t) + v1 * sin(t) + v0 = 0;
  608. // rotation about the z-axis
  609. v0 = normal[2] * p[2] + dist
  610. v1 = normal[0] * p[1] - normal[1] * p[0]
  611. v2 = normal[0] * p[0] + normal[1] * p[1]
  612. derivative:
  613. v1 * cos(t) - v2 * sin(t) = 0;
  614. r = tan(t / 2);
  615. sin(t) = 2*r/(1+r*r);
  616. cos(t) = (1-r*r)/(1+r*r);
  617. -v2 * 2 * r / (1 + r*r) + v1 * (1 - r*r)/(1+r*r);
  618. -v2 * 2 * r + v1 * (1 - r*r) / (1 + r*r) = 0;
  619. -v2 * 2 * r + v1 * (1 - r*r) = 0;
  620. (-v1) * r * r + (-2 * v2) * r + (v1) = 0;
  621. */
  622. tanHalfAngle = 0.0f;
  623. // transform rotation axis to z-axis
  624. p = (point - tw->origin) * tw->matrix;
  625. normal = plane.Normal() * tw->matrix;
  626. v1 = normal[0] * p[1] - normal[1] * p[0];
  627. v2 = normal[0] * p[0] + normal[1] * p[1];
  628. // the point will always start at the front of the plane, therefore v0 + v2 > 0 is always true
  629. if ( angle < 0.0f ) {
  630. dir = -v1;
  631. }
  632. else {
  633. dir = v1;
  634. }
  635. // negative direction means the point moves towards the plane at the initial position
  636. if ( dir <= 0.0f ) {
  637. return true;
  638. }
  639. a = -v1;
  640. b = -v2;
  641. c = v1;
  642. if ( a == 0.0f ) {
  643. if ( b == 0.0f ) {
  644. return false;
  645. }
  646. frac1 = -c / ( 2.0f * b );
  647. frac2 = 1e10; // = tan( idMath::HALF_PI )
  648. }
  649. else {
  650. d = b * b - c * a;
  651. if ( d <= 0.0f ) {
  652. return false;
  653. }
  654. sqrtd = sqrt( d );
  655. if ( b > 0.0f ) {
  656. q = - b + sqrtd;
  657. }
  658. else {
  659. q = - b - sqrtd;
  660. }
  661. frac1 = q / a;
  662. frac2 = c / q;
  663. }
  664. if ( angle < 0.0f ) {
  665. frac1 = -frac1;
  666. frac2 = -frac2;
  667. }
  668. if ( frac1 < 0.0f && frac2 < 0.0f ) {
  669. return false;
  670. }
  671. if ( frac1 > frac2 ) {
  672. tanHalfAngle = frac1;
  673. }
  674. else {
  675. tanHalfAngle = frac2;
  676. }
  677. if ( angle < 0.0f ) {
  678. tanHalfAngle = -tanHalfAngle;
  679. }
  680. return true;
  681. }
  682. /*
  683. ================
  684. idCollisionModelManagerLocal::RotatePointThroughEpsilonPlane
  685. ================
  686. */
  687. int idCollisionModelManagerLocal::RotatePointThroughEpsilonPlane( const cm_traceWork_t *tw, const idVec3 &point, const idVec3 &endPoint,
  688. const idPlane &plane, const float angle, const idVec3 &origin,
  689. float &tanHalfAngle, idVec3 &collisionPoint, idVec3 &endDir ) {
  690. float d, dir, startTan;
  691. idVec3 vec, startDir;
  692. idPlane epsPlane;
  693. // epsilon expanded plane
  694. epsPlane = plane;
  695. epsPlane.SetDist( epsPlane.Dist() + CM_CLIP_EPSILON );
  696. // if the rotation sphere at the rotation origin is too far away from the polygon plane
  697. d = epsPlane.Distance( origin );
  698. vec = point - origin;
  699. if ( d * d > vec * vec ) {
  700. return false;
  701. }
  702. // calculate direction of motion at vertex start position
  703. startDir = ( point - origin ).Cross( tw->axis );
  704. if ( angle < 0.0f ) {
  705. startDir = -startDir;
  706. }
  707. // if moving away from plane at start position
  708. if ( startDir * epsPlane.Normal() >= 0.0f ) {
  709. // if end position is outside epsilon range
  710. d = epsPlane.Distance( endPoint );
  711. if ( d >= 0.0f ) {
  712. return false; // no collision
  713. }
  714. // calculate direction of motion at vertex end position
  715. endDir = ( endPoint - origin ).Cross( tw->axis );
  716. if ( angle < 0.0f ) {
  717. endDir = -endDir;
  718. }
  719. // if also moving away from plane at end position
  720. if ( endDir * epsPlane.Normal() > 0.0f ) {
  721. return false; // no collision
  722. }
  723. }
  724. // if the start position is in the epsilon range
  725. d = epsPlane.Distance( point );
  726. if ( d <= CM_PL_RANGE_EPSILON ) {
  727. // calculate tangent of half the rotation for which the vertex is furthest away from the plane
  728. if ( !PointFurthestFromPlane( tw, point, plane, angle, startTan, dir ) ) {
  729. return false;
  730. }
  731. if ( dir <= 0.0f ) {
  732. // moving towards the polygon plane so stop immediately
  733. tanHalfAngle = 0.0f;
  734. }
  735. else if ( idMath::Fabs( startTan ) >= tw->maxTan ) {
  736. // never going to get beyond the start tangent during the current rotation
  737. return false;
  738. }
  739. else {
  740. // calculate collision with epsilon expanded plane
  741. if ( !RotatePointThroughPlane( tw, point, epsPlane, angle, idMath::Fabs( startTan ), tanHalfAngle ) ) {
  742. tanHalfAngle = startTan;
  743. }
  744. }
  745. }
  746. else {
  747. // calculate collision with epsilon expanded plane
  748. if ( !RotatePointThroughPlane( tw, point, epsPlane, angle, 0.0f, tanHalfAngle ) ) {
  749. return false;
  750. }
  751. }
  752. // calculate collision point
  753. collisionPoint = point;
  754. if ( tanHalfAngle != 0.0f ) {
  755. CM_RotatePoint( collisionPoint, tw->origin, tw->axis, tanHalfAngle );
  756. }
  757. // calculate direction of motion at collision point
  758. endDir = ( collisionPoint - origin ).Cross( tw->axis );
  759. if ( angle < 0.0f ) {
  760. endDir = -endDir;
  761. }
  762. return true;
  763. }
  764. /*
  765. ================
  766. idCollisionModelManagerLocal::RotateTrmVertexThroughPolygon
  767. ================
  768. */
  769. void idCollisionModelManagerLocal::RotateTrmVertexThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmVertex_t *v, int vertexNum ) {
  770. int i;
  771. float tanHalfAngle;
  772. idVec3 endDir, collisionPoint;
  773. idPluecker pl;
  774. // if the trm vertex is behind the polygon plane it cannot collide with the polygon within a 180 degrees rotation
  775. if ( tw->isConvex && tw->axisIntersectsTrm && v->polygonSide ) {
  776. return;
  777. }
  778. // if the trace model vertex rotation bounds do not intersect the polygon bounds
  779. if ( !v->rotationBounds.IntersectsBounds( poly->bounds ) ) {
  780. return;
  781. }
  782. // vertex rotation bounds should cross polygon plane
  783. if ( v->rotationBounds.PlaneSide( poly->plane ) != SIDE_CROSS ) {
  784. return;
  785. }
  786. // rotate the vertex through the epsilon plane
  787. if ( !RotatePointThroughEpsilonPlane( tw, v->p, v->endp, poly->plane, tw->angle, v->rotationOrigin,
  788. tanHalfAngle, collisionPoint, endDir ) ) {
  789. return;
  790. }
  791. if ( idMath::Fabs( tanHalfAngle ) < tw->maxTan ) {
  792. // verify if 'collisionPoint' moving along 'endDir' moves between polygon edges
  793. pl.FromRay( collisionPoint, endDir );
  794. for ( i = 0; i < poly->numEdges; i++ ) {
  795. if ( poly->edges[i] < 0 ) {
  796. if ( pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[i] ) > 0.0f ) {
  797. return;
  798. }
  799. }
  800. else {
  801. if ( pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[i] ) < 0.0f ) {
  802. return;
  803. }
  804. }
  805. }
  806. tw->maxTan = idMath::Fabs( tanHalfAngle );
  807. // collision plane is the polygon plane
  808. tw->trace.c.normal = poly->plane.Normal();
  809. tw->trace.c.dist = poly->plane.Dist();
  810. tw->trace.c.contents = poly->contents;
  811. tw->trace.c.material = poly->material;
  812. tw->trace.c.type = CONTACT_TRMVERTEX;
  813. tw->trace.c.modelFeature = *reinterpret_cast<int *>(&poly);
  814. tw->trace.c.trmFeature = v - tw->vertices;
  815. tw->trace.c.point = collisionPoint;
  816. }
  817. }
  818. /*
  819. ================
  820. idCollisionModelManagerLocal::RotateVertexThroughTrmPolygon
  821. ================
  822. */
  823. void idCollisionModelManagerLocal::RotateVertexThroughTrmPolygon( cm_traceWork_t *tw, cm_trmPolygon_t *trmpoly, cm_polygon_t *poly, cm_vertex_t *v, idVec3 &rotationOrigin ) {
  824. int i, edgeNum;
  825. float tanHalfAngle;
  826. idVec3 dir, endp, endDir, collisionPoint;
  827. idPluecker pl;
  828. cm_trmEdge_t *edge;
  829. // if the polygon vertex is behind the trm plane it cannot collide with the trm polygon within a 180 degrees rotation
  830. if ( tw->isConvex && tw->axisIntersectsTrm && trmpoly->plane.Distance( v->p ) < 0.0f ) {
  831. return;
  832. }
  833. // if the model vertex is outside the trm polygon rotation bounds
  834. if ( !trmpoly->rotationBounds.ContainsPoint( v->p ) ) {
  835. return;
  836. }
  837. // if the rotation axis goes through the polygon vertex
  838. dir = v->p - rotationOrigin;
  839. if ( dir * dir < ROTATION_AXIS_EPSILON * ROTATION_AXIS_EPSILON ) {
  840. return;
  841. }
  842. // calculate vertex end position
  843. endp = v->p;
  844. tw->modelVertexRotation.RotatePoint( endp );
  845. // rotate the vertex through the epsilon plane
  846. if ( !RotatePointThroughEpsilonPlane( tw, v->p, endp, trmpoly->plane, -tw->angle, rotationOrigin,
  847. tanHalfAngle, collisionPoint, endDir ) ) {
  848. return;
  849. }
  850. if ( idMath::Fabs( tanHalfAngle ) < tw->maxTan ) {
  851. // verify if 'collisionPoint' moving along 'endDir' moves between polygon edges
  852. pl.FromRay( collisionPoint, endDir );
  853. for ( i = 0; i < trmpoly->numEdges; i++ ) {
  854. edgeNum = trmpoly->edges[i];
  855. edge = tw->edges + abs(edgeNum);
  856. if ( edgeNum < 0 ) {
  857. if ( pl.PermutedInnerProduct( edge->pl ) > 0.0f ) {
  858. return;
  859. }
  860. }
  861. else {
  862. if ( pl.PermutedInnerProduct( edge->pl ) < 0.0f ) {
  863. return;
  864. }
  865. }
  866. }
  867. tw->maxTan = idMath::Fabs( tanHalfAngle );
  868. // collision plane is the flipped trm polygon plane
  869. tw->trace.c.normal = -trmpoly->plane.Normal();
  870. tw->trace.c.dist = tw->trace.c.normal * v->p;
  871. tw->trace.c.contents = poly->contents;
  872. tw->trace.c.material = poly->material;
  873. tw->trace.c.type = CONTACT_MODELVERTEX;
  874. tw->trace.c.modelFeature = v - tw->model->vertices;
  875. tw->trace.c.trmFeature = trmpoly - tw->polys;
  876. tw->trace.c.point = v->p;
  877. }
  878. }
  879. /*
  880. ================
  881. idCollisionModelManagerLocal::RotateTrmThroughPolygon
  882. returns true if the polygon blocks the complete rotation
  883. ================
  884. */
  885. bool idCollisionModelManagerLocal::RotateTrmThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *p ) {
  886. int i, j, k, edgeNum;
  887. float d;
  888. cm_trmVertex_t *bv;
  889. cm_trmEdge_t *be;
  890. cm_trmPolygon_t *bp;
  891. cm_vertex_t *v;
  892. cm_edge_t *e;
  893. idVec3 *rotationOrigin;
  894. // if already checked this polygon
  895. if ( p->checkcount == idCollisionModelManagerLocal::checkCount ) {
  896. return false;
  897. }
  898. p->checkcount = idCollisionModelManagerLocal::checkCount;
  899. // if this polygon does not have the right contents behind it
  900. if ( !(p->contents & tw->contents) ) {
  901. return false;
  902. }
  903. // if the the trace bounds do not intersect the polygon bounds
  904. if ( !tw->bounds.IntersectsBounds( p->bounds ) ) {
  905. return false;
  906. }
  907. // back face culling
  908. if ( tw->isConvex ) {
  909. // if the center of the convex trm is behind the polygon plane
  910. if ( p->plane.Distance( tw->start ) < 0.0f ) {
  911. // if the rotation axis intersects the trace model
  912. if ( tw->axisIntersectsTrm ) {
  913. return false;
  914. }
  915. else {
  916. // if the direction of motion at the start and end position of the
  917. // center of the trm both go towards or away from the polygon plane
  918. // or if the intersections of the rotation axis with the expanded heart planes
  919. // are both in front of the polygon plane
  920. }
  921. }
  922. }
  923. // if the polygon is too far from the first heart plane
  924. d = p->bounds.PlaneDistance( tw->heartPlane1 );
  925. if ( idMath::Fabs(d) > tw->maxDistFromHeartPlane1 ) {
  926. return false;
  927. }
  928. // rotation bounds should cross polygon plane
  929. switch( tw->bounds.PlaneSide( p->plane ) ) {
  930. case PLANESIDE_CROSS:
  931. break;
  932. case PLANESIDE_FRONT:
  933. if ( tw->model->isConvex ) {
  934. tw->quickExit = true;
  935. return true;
  936. }
  937. default:
  938. return false;
  939. }
  940. for ( i = 0; i < tw->numVerts; i++ ) {
  941. bv = tw->vertices + i;
  942. // calculate polygon side this vertex is on
  943. d = p->plane.Distance( bv->p );
  944. bv->polygonSide = FLOATSIGNBITSET( d );
  945. }
  946. for ( i = 0; i < p->numEdges; i++ ) {
  947. edgeNum = p->edges[i];
  948. e = tw->model->edges + abs(edgeNum);
  949. v = tw->model->vertices + e->vertexNum[INTSIGNBITSET(edgeNum)];
  950. // pluecker coordinate for edge
  951. tw->polygonEdgePlueckerCache[i].FromLine( tw->model->vertices[e->vertexNum[0]].p,
  952. tw->model->vertices[e->vertexNum[1]].p );
  953. // calculate rotation origin projected into rotation plane through the vertex
  954. tw->polygonRotationOriginCache[i] = tw->origin + tw->axis * ( tw->axis * ( v->p - tw->origin ) );
  955. }
  956. // copy first to last so we can easily cycle through
  957. tw->polygonRotationOriginCache[p->numEdges] = tw->polygonRotationOriginCache[0];
  958. // fast point rotation
  959. if ( tw->pointTrace ) {
  960. RotateTrmVertexThroughPolygon( tw, p, &tw->vertices[0], 0 );
  961. }
  962. else {
  963. // rotate trm vertices through polygon
  964. for ( i = 0; i < tw->numVerts; i++ ) {
  965. bv = tw->vertices + i;
  966. if ( bv->used ) {
  967. RotateTrmVertexThroughPolygon( tw, p, bv, i );
  968. }
  969. }
  970. // rotate trm edges through polygon
  971. for ( i = 1; i <= tw->numEdges; i++ ) {
  972. be = tw->edges + i;
  973. if ( be->used ) {
  974. RotateTrmEdgeThroughPolygon( tw, p, be );
  975. }
  976. }
  977. // rotate all polygon vertices through the trm
  978. for ( i = 0; i < p->numEdges; i++ ) {
  979. edgeNum = p->edges[i];
  980. e = tw->model->edges + abs(edgeNum);
  981. if ( e->checkcount == idCollisionModelManagerLocal::checkCount ) {
  982. continue;
  983. }
  984. // set edge check count
  985. e->checkcount = idCollisionModelManagerLocal::checkCount;
  986. // can never collide with internal edges
  987. if ( e->internal ) {
  988. continue;
  989. }
  990. // got to check both vertices because we skip internal edges
  991. for ( k = 0; k < 2; k++ ) {
  992. v = tw->model->vertices + e->vertexNum[k ^ INTSIGNBITSET(edgeNum)];
  993. // if this vertex is already checked
  994. if ( v->checkcount == idCollisionModelManagerLocal::checkCount ) {
  995. continue;
  996. }
  997. // set vertex check count
  998. v->checkcount = idCollisionModelManagerLocal::checkCount;
  999. // if the vertex is outside the trm rotation bounds
  1000. if ( !tw->bounds.ContainsPoint( v->p ) ) {
  1001. continue;
  1002. }
  1003. rotationOrigin = &tw->polygonRotationOriginCache[i+k];
  1004. for ( j = 0; j < tw->numPolys; j++ ) {
  1005. bp = tw->polys + j;
  1006. if ( bp->used ) {
  1007. RotateVertexThroughTrmPolygon( tw, bp, p, v, *rotationOrigin );
  1008. }
  1009. }
  1010. }
  1011. }
  1012. }
  1013. return ( tw->maxTan == 0.0f );
  1014. }
  1015. /*
  1016. ================
  1017. idCollisionModelManagerLocal::BoundsForRotation
  1018. only for rotations < 180 degrees
  1019. ================
  1020. */
  1021. void idCollisionModelManagerLocal::BoundsForRotation( const idVec3 &origin, const idVec3 &axis, const idVec3 &start, const idVec3 &end, idBounds &bounds ) {
  1022. int i;
  1023. float radiusSqr;
  1024. idVec3 v1, v2;
  1025. radiusSqr = ( start - origin ).LengthSqr();
  1026. v1 = ( start - origin ).Cross( axis );
  1027. v2 = ( end - origin ).Cross( axis );
  1028. for ( i = 0; i < 3; i++ ) {
  1029. // if the derivative changes sign along this axis during the rotation from start to end
  1030. if ( ( v1[i] > 0.0f && v2[i] < 0.0f ) || ( v1[i] < 0.0f && v2[i] > 0.0f ) ) {
  1031. if ( ( 0.5f * (start[i] + end[i]) - origin[i] ) > 0.0f ) {
  1032. bounds[0][i] = Min( start[i], end[i] );
  1033. bounds[1][i] = origin[i] + idMath::Sqrt( radiusSqr * ( 1.0f - axis[i] * axis[i] ) );
  1034. }
  1035. else {
  1036. bounds[0][i] = origin[i] - idMath::Sqrt( radiusSqr * ( 1.0f - axis[i] * axis[i] ) );
  1037. bounds[1][i] = Max( start[i], end[i] );
  1038. }
  1039. }
  1040. else if ( start[i] > end[i] ) {
  1041. bounds[0][i] = end[i];
  1042. bounds[1][i] = start[i];
  1043. }
  1044. else {
  1045. bounds[0][i] = start[i];
  1046. bounds[1][i] = end[i];
  1047. }
  1048. // expand for epsilons
  1049. bounds[0][i] -= CM_BOX_EPSILON;
  1050. bounds[1][i] += CM_BOX_EPSILON;
  1051. }
  1052. }
  1053. /*
  1054. ================
  1055. idCollisionModelManagerLocal::Rotation180
  1056. ================
  1057. */
  1058. void idCollisionModelManagerLocal::Rotation180( trace_t *results, const idVec3 &rorg, const idVec3 &axis,
  1059. const float startAngle, const float endAngle, const idVec3 &start,
  1060. const idTraceModel *trm, const idMat3 &trmAxis, int contentMask,
  1061. cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
  1062. int i, j, edgeNum;
  1063. float d, maxErr, initialTan;
  1064. bool model_rotated, trm_rotated;
  1065. idVec3 dir, dir1, dir2, tmp, vr, vup, org, at, bt;
  1066. idMat3 invModelAxis, endAxis, tmpAxis;
  1067. idRotation startRotation, endRotation;
  1068. idPluecker plaxis;
  1069. cm_trmPolygon_t *poly;
  1070. cm_trmEdge_t *edge;
  1071. cm_trmVertex_t *vert;
  1072. ALIGN16( static cm_traceWork_t tw );
  1073. if ( model < 0 || model > MAX_SUBMODELS || model > idCollisionModelManagerLocal::maxModels ) {
  1074. common->Printf("idCollisionModelManagerLocal::Rotation180: invalid model handle\n");
  1075. return;
  1076. }
  1077. if ( !idCollisionModelManagerLocal::models[model] ) {
  1078. common->Printf("idCollisionModelManagerLocal::Rotation180: invalid model\n");
  1079. return;
  1080. }
  1081. idCollisionModelManagerLocal::checkCount++;
  1082. tw.trace.fraction = 1.0f;
  1083. tw.trace.c.contents = 0;
  1084. tw.trace.c.type = CONTACT_NONE;
  1085. tw.contents = contentMask;
  1086. tw.isConvex = true;
  1087. tw.rotation = true;
  1088. tw.positionTest = false;
  1089. tw.axisIntersectsTrm = false;
  1090. tw.quickExit = false;
  1091. tw.angle = endAngle - startAngle;
  1092. assert( tw.angle > -180.0f && tw.angle < 180.0f );
  1093. tw.maxTan = initialTan = idMath::Fabs( tan( ( idMath::PI / 360.0f ) * tw.angle ) );
  1094. tw.model = idCollisionModelManagerLocal::models[model];
  1095. tw.start = start - modelOrigin;
  1096. // rotation axis, axis is assumed to be normalized
  1097. tw.axis = axis;
  1098. assert( tw.axis[0] * tw.axis[0] + tw.axis[1] * tw.axis[1] + tw.axis[2] * tw.axis[2] > 0.99f );
  1099. // rotation origin projected into rotation plane through tw.start
  1100. tw.origin = rorg - modelOrigin;
  1101. d = (tw.axis * tw.origin) - ( tw.axis * tw.start );
  1102. tw.origin = tw.origin - d * tw.axis;
  1103. // radius of rotation
  1104. tw.radius = ( tw.start - tw.origin ).Length();
  1105. // maximum error of the circle approximation traced through the axial BSP tree
  1106. d = tw.radius * tw.radius - (CIRCLE_APPROXIMATION_LENGTH*CIRCLE_APPROXIMATION_LENGTH*0.25f);
  1107. if ( d > 0.0f ) {
  1108. maxErr = tw.radius - idMath::Sqrt( d );
  1109. } else {
  1110. maxErr = tw.radius;
  1111. }
  1112. model_rotated = modelAxis.IsRotated();
  1113. if ( model_rotated ) {
  1114. invModelAxis = modelAxis.Transpose();
  1115. tw.axis *= invModelAxis;
  1116. tw.origin *= invModelAxis;
  1117. }
  1118. startRotation.Set( tw.origin, tw.axis, startAngle );
  1119. endRotation.Set( tw.origin, tw.axis, endAngle );
  1120. // create matrix which rotates the rotation axis to the z-axis
  1121. tw.axis.NormalVectors( vr, vup );
  1122. tw.matrix[0][0] = vr[0];
  1123. tw.matrix[1][0] = vr[1];
  1124. tw.matrix[2][0] = vr[2];
  1125. tw.matrix[0][1] = -vup[0];
  1126. tw.matrix[1][1] = -vup[1];
  1127. tw.matrix[2][1] = -vup[2];
  1128. tw.matrix[0][2] = tw.axis[0];
  1129. tw.matrix[1][2] = tw.axis[1];
  1130. tw.matrix[2][2] = tw.axis[2];
  1131. // if optimized point trace
  1132. if ( !trm || ( trm->bounds[1][0] - trm->bounds[0][0] <= 0.0f &&
  1133. trm->bounds[1][1] - trm->bounds[0][1] <= 0.0f &&
  1134. trm->bounds[1][2] - trm->bounds[0][2] <= 0.0f ) ) {
  1135. if ( model_rotated ) {
  1136. // rotate trace instead of model
  1137. tw.start *= invModelAxis;
  1138. }
  1139. tw.end = tw.start;
  1140. // if we start at a specific angle
  1141. if ( startAngle != 0.0f ) {
  1142. startRotation.RotatePoint( tw.start );
  1143. }
  1144. // calculate end position of rotation
  1145. endRotation.RotatePoint( tw.end );
  1146. // calculate rotation origin projected into rotation plane through the vertex
  1147. tw.numVerts = 1;
  1148. tw.vertices[0].p = tw.start;
  1149. tw.vertices[0].endp = tw.end;
  1150. tw.vertices[0].used = true;
  1151. tw.vertices[0].rotationOrigin = tw.origin + tw.axis * ( tw.axis * ( tw.vertices[0].p - tw.origin ) );
  1152. BoundsForRotation( tw.vertices[0].rotationOrigin, tw.axis, tw.start, tw.end, tw.vertices[0].rotationBounds );
  1153. // rotation bounds
  1154. tw.bounds = tw.vertices[0].rotationBounds;
  1155. tw.numEdges = tw.numPolys = 0;
  1156. // collision with single point
  1157. tw.pointTrace = true;
  1158. // extents is set to maximum error of the circle approximation traced through the axial BSP tree
  1159. tw.extents[0] = tw.extents[1] = tw.extents[2] = maxErr + CM_BOX_EPSILON;
  1160. // setup rotation heart plane
  1161. tw.heartPlane1.SetNormal( tw.axis );
  1162. tw.heartPlane1.FitThroughPoint( tw.start );
  1163. tw.maxDistFromHeartPlane1 = CM_BOX_EPSILON;
  1164. // trace through the model
  1165. idCollisionModelManagerLocal::TraceThroughModel( &tw );
  1166. // store results
  1167. *results = tw.trace;
  1168. results->endpos = start;
  1169. if ( tw.maxTan == initialTan ) {
  1170. results->fraction = 1.0f;
  1171. } else {
  1172. results->fraction = idMath::Fabs( atan( tw.maxTan ) * ( 2.0f * 180.0f / idMath::PI ) / tw.angle );
  1173. }
  1174. assert( results->fraction <= 1.0f );
  1175. endRotation.Set( rorg, axis, startAngle + (endAngle-startAngle) * results->fraction );
  1176. endRotation.RotatePoint( results->endpos );
  1177. results->endAxis.Identity();
  1178. if ( results->fraction < 1.0f ) {
  1179. // rotate trace plane normal if there was a collision with a rotated model
  1180. if ( model_rotated ) {
  1181. results->c.normal *= modelAxis;
  1182. results->c.point *= modelAxis;
  1183. }
  1184. results->c.point += modelOrigin;
  1185. results->c.dist += modelOrigin * results->c.normal;
  1186. }
  1187. return;
  1188. }
  1189. tw.pointTrace = false;
  1190. // setup trm structure
  1191. idCollisionModelManagerLocal::SetupTrm( &tw, trm );
  1192. trm_rotated = trmAxis.IsRotated();
  1193. // calculate vertex positions
  1194. if ( trm_rotated ) {
  1195. for ( i = 0; i < tw.numVerts; i++ ) {
  1196. // rotate trm around the start position
  1197. tw.vertices[i].p *= trmAxis;
  1198. }
  1199. }
  1200. for ( i = 0; i < tw.numVerts; i++ ) {
  1201. // set trm at start position
  1202. tw.vertices[i].p += tw.start;
  1203. }
  1204. if ( model_rotated ) {
  1205. for ( i = 0; i < tw.numVerts; i++ ) {
  1206. tw.vertices[i].p *= invModelAxis;
  1207. }
  1208. }
  1209. for ( i = 0; i < tw.numVerts; i++ ) {
  1210. tw.vertices[i].endp = tw.vertices[i].p;
  1211. }
  1212. // if we start at a specific angle
  1213. if ( startAngle != 0.0f ) {
  1214. for ( i = 0; i < tw.numVerts; i++ ) {
  1215. startRotation.RotatePoint( tw.vertices[i].p );
  1216. }
  1217. }
  1218. for ( i = 0; i < tw.numVerts; i++ ) {
  1219. // end position of vertex
  1220. endRotation.RotatePoint( tw.vertices[i].endp );
  1221. }
  1222. // add offset to start point
  1223. if ( trm_rotated ) {
  1224. tw.start += trm->offset * trmAxis;
  1225. } else {
  1226. tw.start += trm->offset;
  1227. }
  1228. // if the model is rotated
  1229. if ( model_rotated ) {
  1230. // rotate trace instead of model
  1231. tw.start *= invModelAxis;
  1232. }
  1233. tw.end = tw.start;
  1234. // if we start at a specific angle
  1235. if ( startAngle != 0.0f ) {
  1236. startRotation.RotatePoint( tw.start );
  1237. }
  1238. // calculate end position of rotation
  1239. endRotation.RotatePoint( tw.end );
  1240. // setup trm vertices
  1241. for ( vert = tw.vertices, i = 0; i < tw.numVerts; i++, vert++ ) {
  1242. // calculate rotation origin projected into rotation plane through the vertex
  1243. vert->rotationOrigin = tw.origin + tw.axis * ( tw.axis * ( vert->p - tw.origin ) );
  1244. // calculate rotation bounds for this vertex
  1245. BoundsForRotation( vert->rotationOrigin, tw.axis, vert->p, vert->endp, vert->rotationBounds );
  1246. // if the rotation axis goes through the vertex then the vertex is not used
  1247. d = ( vert->p - vert->rotationOrigin ).LengthSqr();
  1248. if ( d > ROTATION_AXIS_EPSILON * ROTATION_AXIS_EPSILON ) {
  1249. vert->used = true;
  1250. }
  1251. }
  1252. // setup trm edges
  1253. for ( edge = tw.edges + 1, i = 1; i <= tw.numEdges; i++, edge++ ) {
  1254. // if the rotation axis goes through both the edge vertices then the edge is not used
  1255. if ( tw.vertices[edge->vertexNum[0]].used | tw.vertices[edge->vertexNum[1]].used ) {
  1256. edge->used = true;
  1257. }
  1258. // edge start, end and pluecker coordinate
  1259. edge->start = tw.vertices[edge->vertexNum[0]].p;
  1260. edge->end = tw.vertices[edge->vertexNum[1]].p;
  1261. edge->pl.FromLine( edge->start, edge->end );
  1262. // pluecker coordinate for edge being rotated about the z-axis
  1263. at = ( edge->start - tw.origin ) * tw.matrix;
  1264. bt = ( edge->end - tw.origin ) * tw.matrix;
  1265. edge->plzaxis.FromLine( at, bt );
  1266. // get edge rotation bounds from the rotation bounds of both vertices
  1267. edge->rotationBounds = tw.vertices[edge->vertexNum[0]].rotationBounds;
  1268. edge->rotationBounds.AddBounds( tw.vertices[edge->vertexNum[1]].rotationBounds );
  1269. // used to calculate if the rotation axis intersects the trm
  1270. edge->bitNum = 0;
  1271. }
  1272. tw.bounds.Clear();
  1273. // rotate trm polygon planes
  1274. if ( trm_rotated & model_rotated ) {
  1275. tmpAxis = trmAxis * invModelAxis;
  1276. for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
  1277. poly->plane *= tmpAxis;
  1278. }
  1279. } else if ( trm_rotated ) {
  1280. for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
  1281. poly->plane *= trmAxis;
  1282. }
  1283. } else if ( model_rotated ) {
  1284. for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
  1285. poly->plane *= invModelAxis;
  1286. }
  1287. }
  1288. // setup trm polygons
  1289. for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
  1290. poly->used = true;
  1291. // set trm polygon plane distance
  1292. poly->plane.FitThroughPoint( tw.edges[abs(poly->edges[0])].start );
  1293. // get polygon bounds from edge bounds
  1294. poly->rotationBounds.Clear();
  1295. for ( j = 0; j < poly->numEdges; j++ ) {
  1296. // add edge rotation bounds to polygon rotation bounds
  1297. edge = &tw.edges[abs( poly->edges[j] )];
  1298. poly->rotationBounds.AddBounds( edge->rotationBounds );
  1299. }
  1300. // get trace bounds from polygon bounds
  1301. tw.bounds.AddBounds( poly->rotationBounds );
  1302. }
  1303. // extents including the maximum error of the circle approximation traced through the axial BSP tree
  1304. for ( i = 0; i < 3; i++ ) {
  1305. tw.size[0][i] = tw.bounds[0][i] - tw.start[i];
  1306. tw.size[1][i] = tw.bounds[1][i] - tw.start[i];
  1307. if ( idMath::Fabs( tw.size[0][i] ) > idMath::Fabs( tw.size[1][i] ) ) {
  1308. tw.extents[i] = idMath::Fabs( tw.size[0][i] ) + maxErr + CM_BOX_EPSILON;
  1309. } else {
  1310. tw.extents[i] = idMath::Fabs( tw.size[1][i] ) + maxErr + CM_BOX_EPSILON;
  1311. }
  1312. }
  1313. // for back-face culling
  1314. if ( tw.isConvex ) {
  1315. if ( tw.start == tw.origin ) {
  1316. tw.axisIntersectsTrm = true;
  1317. } else {
  1318. // determine if the rotation axis intersects the trm
  1319. plaxis.FromRay( tw.origin, tw.axis );
  1320. for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
  1321. // back face cull polygons
  1322. if ( poly->plane.Normal() * tw.axis > 0.0f ) {
  1323. continue;
  1324. }
  1325. // test if the axis goes between the polygon edges
  1326. for ( j = 0; j < poly->numEdges; j++ ) {
  1327. edgeNum = poly->edges[j];
  1328. edge = tw.edges + abs(edgeNum);
  1329. if ( !(edge->bitNum & 2) ) {
  1330. d = plaxis.PermutedInnerProduct( edge->pl );
  1331. edge->bitNum = FLOATSIGNBITSET( d ) | 2;
  1332. }
  1333. if ( ( edge->bitNum ^ INTSIGNBITSET( edgeNum ) ) & 1 ) {
  1334. break;
  1335. }
  1336. }
  1337. if ( j >= poly->numEdges ) {
  1338. tw.axisIntersectsTrm = true;
  1339. break;
  1340. }
  1341. }
  1342. }
  1343. }
  1344. // setup rotation heart plane
  1345. tw.heartPlane1.SetNormal( tw.axis );
  1346. tw.heartPlane1.FitThroughPoint( tw.start );
  1347. tw.maxDistFromHeartPlane1 = 0.0f;
  1348. for ( i = 0; i < tw.numVerts; i++ ) {
  1349. d = idMath::Fabs( tw.heartPlane1.Distance( tw.vertices[i].p ) );
  1350. if ( d > tw.maxDistFromHeartPlane1 ) {
  1351. tw.maxDistFromHeartPlane1 = d;
  1352. }
  1353. }
  1354. tw.maxDistFromHeartPlane1 += CM_BOX_EPSILON;
  1355. // inverse rotation to rotate model vertices towards trace model
  1356. tw.modelVertexRotation.Set( tw.origin, tw.axis, -tw.angle );
  1357. // trace through the model
  1358. idCollisionModelManagerLocal::TraceThroughModel( &tw );
  1359. // store results
  1360. *results = tw.trace;
  1361. results->endpos = start;
  1362. if ( tw.maxTan == initialTan ) {
  1363. results->fraction = 1.0f;
  1364. } else {
  1365. results->fraction = idMath::Fabs( atan( tw.maxTan ) * ( 2.0f * 180.0f / idMath::PI ) / tw.angle );
  1366. }
  1367. assert( results->fraction <= 1.0f );
  1368. endRotation.Set( rorg, axis, startAngle + (endAngle-startAngle) * results->fraction );
  1369. endRotation.RotatePoint( results->endpos );
  1370. results->endAxis = trmAxis * endRotation.ToMat3();
  1371. if ( results->fraction < 1.0f ) {
  1372. // rotate trace plane normal if there was a collision with a rotated model
  1373. if ( model_rotated ) {
  1374. results->c.normal *= modelAxis;
  1375. results->c.point *= modelAxis;
  1376. }
  1377. results->c.point += modelOrigin;
  1378. results->c.dist += modelOrigin * results->c.normal;
  1379. }
  1380. }
  1381. /*
  1382. ================
  1383. idCollisionModelManagerLocal::Rotation
  1384. ================
  1385. */
  1386. #ifdef _DEBUG
  1387. static int entered = 0;
  1388. #endif
  1389. void idCollisionModelManagerLocal::Rotation( trace_t *results, const idVec3 &start, const idRotation &rotation,
  1390. const idTraceModel *trm, const idMat3 &trmAxis, int contentMask,
  1391. cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
  1392. idVec3 tmp;
  1393. float maxa, stepa, a, lasta;
  1394. assert( ((byte *)&start) < ((byte *)results) || ((byte *)&start) > (((byte *)results) + sizeof( trace_t )) );
  1395. assert( ((byte *)&trmAxis) < ((byte *)results) || ((byte *)&trmAxis) > (((byte *)results) + sizeof( trace_t )) );
  1396. memset( results, 0, sizeof( *results ) );
  1397. // if special position test
  1398. if ( rotation.GetAngle() == 0.0f ) {
  1399. idCollisionModelManagerLocal::ContentsTrm( results, start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
  1400. return;
  1401. }
  1402. #ifdef _DEBUG
  1403. bool startsolid = false;
  1404. // test whether or not stuck to begin with
  1405. if ( cm_debugCollision.GetBool() ) {
  1406. if ( !entered ) {
  1407. entered = 1;
  1408. // if already messed up to begin with
  1409. if ( idCollisionModelManagerLocal::Contents( start, trm, trmAxis, -1, model, modelOrigin, modelAxis ) & contentMask ) {
  1410. startsolid = true;
  1411. }
  1412. entered = 0;
  1413. }
  1414. }
  1415. #endif
  1416. if ( rotation.GetAngle() >= 180.0f || rotation.GetAngle() <= -180.0f) {
  1417. if ( rotation.GetAngle() >= 360.0f ) {
  1418. maxa = 360.0f;
  1419. stepa = 120.0f; // three steps strictly < 180 degrees
  1420. } else if ( rotation.GetAngle() <= -360.0f ) {
  1421. maxa = -360.0f;
  1422. stepa = -120.0f; // three steps strictly < 180 degrees
  1423. } else {
  1424. maxa = rotation.GetAngle();
  1425. stepa = rotation.GetAngle() * 0.5f; // two steps strictly < 180 degrees
  1426. }
  1427. for ( lasta = 0.0f, a = stepa; fabs( a ) < fabs( maxa ) + 1.0f; lasta = a, a += stepa ) {
  1428. // partial rotation
  1429. idCollisionModelManagerLocal::Rotation180( results, rotation.GetOrigin(), rotation.GetVec(), lasta, a, start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
  1430. // if there is a collision
  1431. if ( results->fraction < 1.0f ) {
  1432. // fraction of total rotation
  1433. results->fraction = (lasta + stepa * results->fraction) / rotation.GetAngle();
  1434. return;
  1435. }
  1436. }
  1437. results->fraction = 1.0f;
  1438. return;
  1439. }
  1440. idCollisionModelManagerLocal::Rotation180( results, rotation.GetOrigin(), rotation.GetVec(), 0.0f, rotation.GetAngle(), start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
  1441. #ifdef _DEBUG
  1442. // test for missed collisions
  1443. if ( cm_debugCollision.GetBool() ) {
  1444. if ( !entered ) {
  1445. entered = 1;
  1446. // if the trm is stuck in the model
  1447. if ( idCollisionModelManagerLocal::Contents( results->endpos, trm, results->endAxis, -1, model, modelOrigin, modelAxis ) & contentMask ) {
  1448. trace_t tr;
  1449. // test where the trm is stuck in the model
  1450. idCollisionModelManagerLocal::Contents( results->endpos, trm, results->endAxis, -1, model, modelOrigin, modelAxis );
  1451. // re-run collision detection to find out where it failed
  1452. idCollisionModelManagerLocal::Rotation( &tr, start, rotation, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
  1453. }
  1454. entered = 0;
  1455. }
  1456. }
  1457. #endif
  1458. }