CollisionModel_debug.cpp (15677B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition 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 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 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. 25 26 =========================================================================== 27 */ 28 29 /* 30 =============================================================================== 31 32 Trace model vs. polygonal model collision detection. 33 34 =============================================================================== 35 */ 36 37 #pragma hdrstop 38 #include "../idlib/precompiled.h" 39 40 41 #include "CollisionModel_local.h" 42 43 44 /* 45 =============================================================================== 46 47 Visualisation code 48 49 =============================================================================== 50 */ 51 52 const char *cm_contentsNameByIndex[] = { 53 "none", // 0 54 "solid", // 1 55 "opaque", // 2 56 "water", // 3 57 "playerclip", // 4 58 "monsterclip", // 5 59 "moveableclip", // 6 60 "ikclip", // 7 61 "blood", // 8 62 "body", // 9 63 "corpse", // 10 64 "trigger", // 11 65 "aas_solid", // 12 66 "aas_obstacle", // 13 67 "flashlight_trigger", // 14 68 NULL 69 }; 70 71 int cm_contentsFlagByIndex[] = { 72 -1, // 0 73 CONTENTS_SOLID, // 1 74 CONTENTS_OPAQUE, // 2 75 CONTENTS_WATER, // 3 76 CONTENTS_PLAYERCLIP, // 4 77 CONTENTS_MONSTERCLIP, // 5 78 CONTENTS_MOVEABLECLIP, // 6 79 CONTENTS_IKCLIP, // 7 80 CONTENTS_BLOOD, // 8 81 CONTENTS_BODY, // 9 82 CONTENTS_CORPSE, // 10 83 CONTENTS_TRIGGER, // 11 84 CONTENTS_AAS_SOLID, // 12 85 CONTENTS_AAS_OBSTACLE, // 13 86 CONTENTS_FLASHLIGHT_TRIGGER, // 14 87 0 88 }; 89 90 idCVar cm_drawMask( "cm_drawMask", "none", CVAR_GAME, "collision mask", cm_contentsNameByIndex, idCmdSystem::ArgCompletion_String<cm_contentsNameByIndex> ); 91 idCVar cm_drawColor( "cm_drawColor", "1 0 0 .5", CVAR_GAME, "color used to draw the collision models" ); 92 idCVar cm_drawFilled( "cm_drawFilled", "0", CVAR_GAME | CVAR_BOOL, "draw filled polygons" ); 93 idCVar cm_drawInternal( "cm_drawInternal", "1", CVAR_GAME | CVAR_BOOL, "draw internal edges green" ); 94 idCVar cm_drawNormals( "cm_drawNormals", "0", CVAR_GAME | CVAR_BOOL, "draw polygon and edge normals" ); 95 idCVar cm_backFaceCull( "cm_backFaceCull", "0", CVAR_GAME | CVAR_BOOL, "cull back facing polygons" ); 96 idCVar cm_debugCollision( "cm_debugCollision", "0", CVAR_GAME | CVAR_BOOL, "debug the collision detection" ); 97 98 static idVec4 cm_color; 99 100 /* 101 ================ 102 idCollisionModelManagerLocal::ContentsFromString 103 ================ 104 */ 105 int idCollisionModelManagerLocal::ContentsFromString( const char *string ) const { 106 int i, contents = 0; 107 idLexer src( string, idStr::Length( string ), "ContentsFromString" ); 108 idToken token; 109 110 while( src.ReadToken( &token ) ) { 111 if ( token == "," ) { 112 continue; 113 } 114 for ( i = 1; cm_contentsNameByIndex[i] != NULL; i++ ) { 115 if ( token.Icmp( cm_contentsNameByIndex[i] ) == 0 ) { 116 contents |= cm_contentsFlagByIndex[i]; 117 break; 118 } 119 } 120 } 121 122 return contents; 123 } 124 125 /* 126 ================ 127 idCollisionModelManagerLocal::StringFromContents 128 ================ 129 */ 130 const char *idCollisionModelManagerLocal::StringFromContents( const int contents ) const { 131 int i, length = 0; 132 static char contentsString[MAX_STRING_CHARS]; 133 134 contentsString[0] = '\0'; 135 136 for ( i = 1; cm_contentsFlagByIndex[i] != 0; i++ ) { 137 if ( contents & cm_contentsFlagByIndex[i] ) { 138 if ( length != 0 ) { 139 length += idStr::snPrintf( contentsString + length, sizeof( contentsString ) - length, "," ); 140 } 141 length += idStr::snPrintf( contentsString + length, sizeof( contentsString ) - length, cm_contentsNameByIndex[i] ); 142 } 143 } 144 145 return contentsString; 146 } 147 148 /* 149 ================ 150 idCollisionModelManagerLocal::DrawEdge 151 ================ 152 */ 153 void idCollisionModelManagerLocal::DrawEdge( cm_model_t *model, int edgeNum, const idVec3 &origin, const idMat3 &axis ) { 154 int side; 155 cm_edge_t *edge; 156 idVec3 start, end, mid; 157 bool isRotated; 158 159 isRotated = axis.IsRotated(); 160 161 edge = model->edges + abs(edgeNum); 162 side = edgeNum < 0; 163 164 start = model->vertices[edge->vertexNum[side]].p; 165 end = model->vertices[edge->vertexNum[!side]].p; 166 if ( isRotated ) { 167 start *= axis; 168 end *= axis; 169 } 170 start += origin; 171 end += origin; 172 173 if ( edge->internal ) { 174 if ( cm_drawInternal.GetBool() ) { 175 common->RW()->DebugArrow( colorGreen, start, end, 1 ); 176 } 177 } else { 178 if ( edge->numUsers > 2 ) { 179 common->RW()->DebugArrow( colorBlue, start, end, 1 ); 180 } else { 181 common->RW()->DebugArrow( cm_color, start, end, 1 ); 182 } 183 } 184 185 if ( cm_drawNormals.GetBool() ) { 186 mid = (start + end) * 0.5f; 187 if ( isRotated ) { 188 end = mid + 5 * (axis * edge->normal); 189 } else { 190 end = mid + 5 * edge->normal; 191 } 192 common->RW()->DebugArrow( colorCyan, mid, end, 1 ); 193 } 194 } 195 196 /* 197 ================ 198 idCollisionModelManagerLocal::DrawPolygon 199 ================ 200 */ 201 void idCollisionModelManagerLocal::DrawPolygon( cm_model_t *model, cm_polygon_t *p, const idVec3 &origin, const idMat3 &axis, const idVec3 &viewOrigin ) { 202 int i, edgeNum; 203 cm_edge_t *edge; 204 idVec3 center, end, dir; 205 206 if ( cm_backFaceCull.GetBool() ) { 207 edgeNum = p->edges[0]; 208 edge = model->edges + abs(edgeNum); 209 dir = model->vertices[edge->vertexNum[0]].p - viewOrigin; 210 if ( dir * p->plane.Normal() > 0.0f ) { 211 return; 212 } 213 } 214 215 if ( cm_drawNormals.GetBool() ) { 216 center = vec3_origin; 217 for ( i = 0; i < p->numEdges; i++ ) { 218 edgeNum = p->edges[i]; 219 edge = model->edges + abs(edgeNum); 220 center += model->vertices[edge->vertexNum[edgeNum < 0]].p; 221 } 222 center *= (1.0f / p->numEdges); 223 if ( axis.IsRotated() ) { 224 center = center * axis + origin; 225 end = center + 5 * (axis * p->plane.Normal()); 226 } else { 227 center += origin; 228 end = center + 5 * p->plane.Normal(); 229 } 230 common->RW()->DebugArrow( colorMagenta, center, end, 1 ); 231 } 232 233 if ( cm_drawFilled.GetBool() ) { 234 idFixedWinding winding; 235 for ( i = p->numEdges - 1; i >= 0; i-- ) { 236 edgeNum = p->edges[i]; 237 edge = model->edges + abs(edgeNum); 238 winding += origin + model->vertices[edge->vertexNum[INT32_SIGNBITSET(edgeNum)]].p * axis; 239 } 240 common->RW()->DebugPolygon( cm_color, winding ); 241 } else { 242 for ( i = 0; i < p->numEdges; i++ ) { 243 edgeNum = p->edges[i]; 244 edge = model->edges + abs(edgeNum); 245 if ( edge->checkcount == checkCount ) { 246 continue; 247 } 248 edge->checkcount = checkCount; 249 DrawEdge( model, edgeNum, origin, axis ); 250 } 251 } 252 } 253 254 /* 255 ================ 256 idCollisionModelManagerLocal::DrawNodePolygons 257 ================ 258 */ 259 void idCollisionModelManagerLocal::DrawNodePolygons( cm_model_t *model, cm_node_t *node, 260 const idVec3 &origin, const idMat3 &axis, 261 const idVec3 &viewOrigin, const float radius ) { 262 int i; 263 cm_polygon_t *p; 264 cm_polygonRef_t *pref; 265 266 while (1) { 267 for ( pref = node->polygons; pref; pref = pref->next ) { 268 p = pref->p; 269 if ( radius ) { 270 // polygon bounds should overlap with trace bounds 271 for ( i = 0; i < 3; i++ ) { 272 if ( p->bounds[0][i] > viewOrigin[i] + radius ) { 273 break; 274 } 275 if ( p->bounds[1][i] < viewOrigin[i] - radius ) { 276 break; 277 } 278 } 279 if ( i < 3 ) { 280 continue; 281 } 282 } 283 if ( p->checkcount == checkCount ) { 284 continue; 285 } 286 if ( !( p->contents & cm_contentsFlagByIndex[cm_drawMask.GetInteger()] ) ) { 287 continue; 288 } 289 290 DrawPolygon( model, p, origin, axis, viewOrigin ); 291 p->checkcount = checkCount; 292 } 293 if ( node->planeType == -1 ) { 294 break; 295 } 296 if ( radius && viewOrigin[node->planeType] > node->planeDist + radius ) { 297 node = node->children[0]; 298 } else if ( radius && viewOrigin[node->planeType] < node->planeDist - radius ) { 299 node = node->children[1]; 300 } else { 301 DrawNodePolygons( model, node->children[1], origin, axis, viewOrigin, radius ); 302 node = node->children[0]; 303 } 304 } 305 } 306 307 /* 308 ================ 309 idCollisionModelManagerLocal::DrawModel 310 ================ 311 */ 312 void idCollisionModelManagerLocal::DrawModel( cmHandle_t handle, const idVec3 &modelOrigin, const idMat3 &modelAxis, 313 const idVec3 &viewOrigin, const float radius ) { 314 315 cm_model_t *model; 316 idVec3 viewPos; 317 318 if ( handle < 0 && handle >= numModels ) { 319 return; 320 } 321 322 if ( cm_drawColor.IsModified() ) { 323 sscanf( cm_drawColor.GetString(), "%f %f %f %f", &cm_color.x, &cm_color.y, &cm_color.z, &cm_color.w ); 324 cm_drawColor.ClearModified(); 325 } 326 327 model = models[ handle ]; 328 viewPos = (viewOrigin - modelOrigin) * modelAxis.Transpose(); 329 checkCount++; 330 DrawNodePolygons( model, model->node, modelOrigin, modelAxis, viewPos, radius ); 331 } 332 333 /* 334 =============================================================================== 335 336 Speed test code 337 338 =============================================================================== 339 */ 340 341 static idCVar cm_testCollision( "cm_testCollision", "0", CVAR_GAME | CVAR_BOOL, "" ); 342 static idCVar cm_testRotation( "cm_testRotation", "1", CVAR_GAME | CVAR_BOOL, "" ); 343 static idCVar cm_testModel( "cm_testModel", "0", CVAR_GAME | CVAR_INTEGER, "" ); 344 static idCVar cm_testTimes( "cm_testTimes", "1000", CVAR_GAME | CVAR_INTEGER, "" ); 345 static idCVar cm_testRandomMany( "cm_testRandomMany", "0", CVAR_GAME | CVAR_BOOL, "" ); 346 static idCVar cm_testOrigin( "cm_testOrigin", "0 0 0", CVAR_GAME, "" ); 347 static idCVar cm_testReset( "cm_testReset", "0", CVAR_GAME | CVAR_BOOL, "" ); 348 static idCVar cm_testBox( "cm_testBox", "-16 -16 0 16 16 64", CVAR_GAME, "" ); 349 static idCVar cm_testBoxRotation( "cm_testBoxRotation", "0 0 0", CVAR_GAME, "" ); 350 static idCVar cm_testWalk( "cm_testWalk", "1", CVAR_GAME | CVAR_BOOL, "" ); 351 static idCVar cm_testLength( "cm_testLength", "1024", CVAR_GAME | CVAR_FLOAT, "" ); 352 static idCVar cm_testRadius( "cm_testRadius", "64", CVAR_GAME | CVAR_FLOAT, "" ); 353 static idCVar cm_testAngle( "cm_testAngle", "60", CVAR_GAME | CVAR_FLOAT, "" ); 354 355 static int total_translation; 356 static int min_translation = 999999; 357 static int max_translation = -999999; 358 static int num_translation = 0; 359 static int total_rotation; 360 static int min_rotation = 999999; 361 static int max_rotation = -999999; 362 static int num_rotation = 0; 363 static idVec3 start; 364 static idVec3 *testend; 365 366 #include "../sys/sys_public.h" 367 368 void idCollisionModelManagerLocal::DebugOutput( const idVec3 &origin ) { 369 int i, k, t; 370 char buf[128]; 371 idVec3 end; 372 idAngles boxAngles; 373 idMat3 modelAxis, boxAxis; 374 idBounds bounds; 375 trace_t trace; 376 377 if ( !cm_testCollision.GetBool() ) { 378 return; 379 } 380 381 testend = (idVec3 *) Mem_Alloc( cm_testTimes.GetInteger() * sizeof(idVec3), TAG_COLLISION ); 382 383 if ( cm_testReset.GetBool() || ( cm_testWalk.GetBool() && !start.Compare( start ) ) ) { 384 total_translation = total_rotation = 0; 385 min_translation = min_rotation = 999999; 386 max_translation = max_rotation = -999999; 387 num_translation = num_rotation = 0; 388 cm_testReset.SetBool( false ); 389 } 390 391 if ( cm_testWalk.GetBool() ) { 392 start = origin; 393 cm_testOrigin.SetString( va( "%1.2f %1.2f %1.2f", start[0], start[1], start[2] ) ); 394 } else { 395 sscanf( cm_testOrigin.GetString(), "%f %f %f", &start[0], &start[1], &start[2] ); 396 } 397 398 sscanf( cm_testBox.GetString(), "%f %f %f %f %f %f", &bounds[0][0], &bounds[0][1], &bounds[0][2], 399 &bounds[1][0], &bounds[1][1], &bounds[1][2] ); 400 sscanf( cm_testBoxRotation.GetString(), "%f %f %f", &boxAngles[0], &boxAngles[1], &boxAngles[2] ); 401 boxAxis = boxAngles.ToMat3(); 402 modelAxis.Identity(); 403 404 idTraceModel itm( bounds ); 405 idRandom random( 0 ); 406 idTimer timer; 407 408 if ( cm_testRandomMany.GetBool() ) { 409 // if many traces in one random direction 410 for ( i = 0; i < 3; i++ ) { 411 testend[0][i] = start[i] + random.CRandomFloat() * cm_testLength.GetFloat(); 412 } 413 for ( k = 1; k < cm_testTimes.GetInteger(); k++ ) { 414 testend[k] = testend[0]; 415 } 416 } else { 417 // many traces each in a different random direction 418 for ( k = 0; k < cm_testTimes.GetInteger(); k++ ) { 419 for ( i = 0; i < 3; i++ ) { 420 testend[k][i] = start[i] + random.CRandomFloat() * cm_testLength.GetFloat(); 421 } 422 } 423 } 424 425 // translational collision detection 426 timer.Clear(); 427 timer.Start(); 428 for ( i = 0; i < cm_testTimes.GetInteger(); i++ ) { 429 Translation( &trace, start, testend[i], &itm, boxAxis, CONTENTS_SOLID|CONTENTS_PLAYERCLIP, cm_testModel.GetInteger(), vec3_origin, modelAxis ); 430 } 431 timer.Stop(); 432 t = timer.Milliseconds(); 433 if ( t < min_translation ) min_translation = t; 434 if ( t > max_translation ) max_translation = t; 435 num_translation++; 436 total_translation += t; 437 if ( cm_testTimes.GetInteger() > 9999 ) { 438 sprintf( buf, "%3dK", (int ) ( cm_testTimes.GetInteger() / 1000 ) ); 439 } else { 440 sprintf( buf, "%4d", cm_testTimes.GetInteger() ); 441 } 442 common->Printf("%s translations: %4d milliseconds, (min = %d, max = %d, av = %1.1f)\n", buf, t, min_translation, max_translation, (float) total_translation / num_translation ); 443 444 if ( cm_testRandomMany.GetBool() ) { 445 // if many traces in one random direction 446 for ( i = 0; i < 3; i++ ) { 447 testend[0][i] = start[i] + random.CRandomFloat() * cm_testRadius.GetFloat(); 448 } 449 for ( k = 1; k < cm_testTimes.GetInteger(); k++ ) { 450 testend[k] = testend[0]; 451 } 452 } else { 453 // many traces each in a different random direction 454 for ( k = 0; k < cm_testTimes.GetInteger(); k++ ) { 455 for ( i = 0; i < 3; i++ ) { 456 testend[k][i] = start[i] + random.CRandomFloat() * cm_testRadius.GetFloat(); 457 } 458 } 459 } 460 461 if ( cm_testRotation.GetBool() ) { 462 // rotational collision detection 463 idVec3 vec( random.CRandomFloat(), random.CRandomFloat(), random.RandomFloat() ); 464 vec.Normalize(); 465 idRotation rotation( vec3_origin, vec, cm_testAngle.GetFloat() ); 466 467 timer.Clear(); 468 timer.Start(); 469 for ( i = 0; i < cm_testTimes.GetInteger(); i++ ) { 470 rotation.SetOrigin( testend[i] ); 471 Rotation( &trace, start, rotation, &itm, boxAxis, CONTENTS_SOLID|CONTENTS_PLAYERCLIP, cm_testModel.GetInteger(), vec3_origin, modelAxis ); 472 } 473 timer.Stop(); 474 t = timer.Milliseconds(); 475 if ( t < min_rotation ) min_rotation = t; 476 if ( t > max_rotation ) max_rotation = t; 477 num_rotation++; 478 total_rotation += t; 479 if ( cm_testTimes.GetInteger() > 9999 ) { 480 sprintf( buf, "%3dK", (int ) ( cm_testTimes.GetInteger() / 1000 ) ); 481 } else { 482 sprintf( buf, "%4d", cm_testTimes.GetInteger() ); 483 } 484 common->Printf("%s rotation: %4d milliseconds, (min = %d, max = %d, av = %1.1f)\n", buf, t, min_rotation, max_rotation, (float) total_rotation / num_rotation ); 485 } 486 487 Mem_Free( testend ); 488 testend = NULL; 489 }