DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

CollisionModel_trace.cpp (7817B)


      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 Trace through the spatial subdivision
     47 
     48 ===============================================================================
     49 */
     50 
     51 /*
     52 ================
     53 idCollisionModelManagerLocal::TraceTrmThroughNode
     54 ================
     55 */
     56 void idCollisionModelManagerLocal::TraceTrmThroughNode( cm_traceWork_t *tw, cm_node_t *node ) {
     57 	cm_polygonRef_t *pref;
     58 	cm_brushRef_t *bref;
     59 
     60 	// position test
     61 	if ( tw->positionTest ) {
     62 		// if already stuck in solid
     63 		if ( tw->trace.fraction == 0.0f ) {
     64 			return;
     65 		}
     66 		// test if any of the trm vertices is inside a brush
     67 		for ( bref = node->brushes; bref; bref = bref->next ) {
     68 			if ( idCollisionModelManagerLocal::TestTrmVertsInBrush( tw, bref->b ) ) {
     69 				return;
     70 			}
     71 		}
     72 		// if just testing a point we're done
     73 		if ( tw->pointTrace ) {
     74 			return;
     75 		}
     76 		// test if the trm is stuck in any polygons
     77 		for ( pref = node->polygons; pref; pref = pref->next ) {
     78 			if ( idCollisionModelManagerLocal::TestTrmInPolygon( tw, pref->p ) ) {
     79 				return;
     80 			}
     81 		}
     82 	}
     83 	else if ( tw->rotation ) {
     84 		// rotate through all polygons in this leaf
     85 		for ( pref = node->polygons; pref; pref = pref->next ) {
     86 			if ( idCollisionModelManagerLocal::RotateTrmThroughPolygon( tw, pref->p ) ) {
     87 				return;
     88 			}
     89 		}
     90 	}
     91 	else {
     92 		// trace through all polygons in this leaf
     93 		for ( pref = node->polygons; pref; pref = pref->next ) {
     94 			if ( idCollisionModelManagerLocal::TranslateTrmThroughPolygon( tw, pref->p ) ) {
     95 				return;
     96 			}
     97 		}
     98 	}
     99 }
    100 
    101 /*
    102 ================
    103 idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r
    104 ================
    105 */
    106 //#define NO_SPATIAL_SUBDIVISION
    107 
    108 void idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( cm_traceWork_t *tw, cm_node_t *node, float p1f, float p2f, idVec3 &p1, idVec3 &p2) {
    109 	float		t1, t2, offset;
    110 	float		frac, frac2;
    111 	float		idist;
    112 	idVec3		mid;
    113 	int			side;
    114 	float		midf;
    115 
    116 	if ( !node ) {
    117 		return;
    118 	}
    119 
    120 	if ( tw->quickExit ) {
    121 		return;		// stop immediately
    122 	}
    123 
    124 	if ( tw->trace.fraction <= p1f ) {
    125 		return;		// already hit something nearer
    126 	}
    127 
    128 	// if we need to test this node for collisions
    129 	if ( node->polygons || (tw->positionTest && node->brushes) ) {
    130 		// trace through node with collision data
    131 		idCollisionModelManagerLocal::TraceTrmThroughNode( tw, node );
    132 	}
    133 	// if already stuck in solid
    134 	if ( tw->positionTest && tw->trace.fraction == 0.0f ) {
    135 		return;
    136 	}
    137 	// if this is a leaf node
    138 	if ( node->planeType == -1 ) {
    139 		return;
    140 	}
    141 #ifdef NO_SPATIAL_SUBDIVISION
    142 	idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, node->children[0], p1f, p2f, p1, p2 );
    143 	idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, node->children[1], p1f, p2f, p1, p2 );
    144 	return;
    145 #endif
    146 	// distance from plane for trace start and end
    147 	t1 = p1[node->planeType] - node->planeDist;
    148 	t2 = p2[node->planeType] - node->planeDist;
    149 	// adjust the plane distance appropriately for mins/maxs
    150 	offset = tw->extents[node->planeType];
    151 	// see which sides we need to consider
    152 	if ( t1 >= offset && t2 >= offset ) {
    153 		idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, node->children[0], p1f, p2f, p1, p2 );
    154 		return;
    155 	}
    156 
    157 	if ( t1 < -offset && t2 < -offset ) {
    158 		idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, node->children[1], p1f, p2f, p1, p2 );
    159 		return;
    160 	}
    161 
    162 	if ( t1 < t2 ) {
    163 		idist = 1.0f / (t1-t2);
    164 		side = 1;
    165 		frac2 = (t1 + offset) * idist;
    166 		frac = (t1 - offset) * idist;
    167 	} else if (t1 > t2) {
    168 		idist = 1.0f / (t1-t2);
    169 		side = 0;
    170 		frac2 = (t1 - offset) * idist;
    171 		frac = (t1 + offset) * idist;
    172 	} else {
    173 		side = 0;
    174 		frac = 1.0f;
    175 		frac2 = 0.0f;
    176 	}
    177 
    178 	// move up to the node
    179 	if ( frac < 0.0f ) {
    180 		frac = 0.0f;
    181 	}
    182 	else if ( frac > 1.0f ) {
    183 		frac = 1.0f;
    184 	}
    185 
    186 	midf = p1f + (p2f - p1f)*frac;
    187 
    188 	mid[0] = p1[0] + frac*(p2[0] - p1[0]);
    189 	mid[1] = p1[1] + frac*(p2[1] - p1[1]);
    190 	mid[2] = p1[2] + frac*(p2[2] - p1[2]);
    191 
    192 	idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, node->children[side], p1f, midf, p1, mid );
    193 
    194 
    195 	// go past the node
    196 	if ( frac2 < 0.0f ) {
    197 		frac2 = 0.0f;
    198 	}
    199 	else if ( frac2 > 1.0f ) {
    200 		frac2 = 1.0f;
    201 	}
    202 		
    203 	midf = p1f + (p2f - p1f)*frac2;
    204 
    205 	mid[0] = p1[0] + frac2*(p2[0] - p1[0]);
    206 	mid[1] = p1[1] + frac2*(p2[1] - p1[1]);
    207 	mid[2] = p1[2] + frac2*(p2[2] - p1[2]);
    208 
    209 	idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, node->children[side^1], midf, p2f, mid, p2 );
    210 }
    211 
    212 /*
    213 ================
    214 idCollisionModelManagerLocal::TraceThroughModel
    215 ================
    216 */
    217 void idCollisionModelManagerLocal::TraceThroughModel( cm_traceWork_t *tw ) {
    218 	float d;
    219 	int i, numSteps;
    220 	idVec3 start, end;
    221 	idRotation rot;
    222 
    223 	if ( !tw->rotation ) {
    224 		// trace through spatial subdivision and then through leafs
    225 		idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, tw->model->node, 0, 1, tw->start, tw->end );
    226 	}
    227 	else {
    228 		// approximate the rotation with a series of straight line movements
    229 		// total length covered along circle
    230 		d = tw->radius * DEG2RAD( tw->angle );
    231 		// if more than one step
    232 		if ( d > CIRCLE_APPROXIMATION_LENGTH ) {
    233 			// number of steps for the approximation
    234 			numSteps = (int) (CIRCLE_APPROXIMATION_LENGTH / d);
    235 			// start of approximation
    236 			start = tw->start;
    237 			// trace circle approximation steps through the BSP tree
    238 			for ( i = 0; i < numSteps; i++ ) {
    239 				// calculate next point on approximated circle
    240 				rot.Set( tw->origin, tw->axis, tw->angle * ((float) (i+1) / numSteps) );
    241 				end = start * rot;
    242 				// trace through spatial subdivision and then through leafs
    243 				idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, tw->model->node, 0, 1, start, end );
    244 				// no need to continue if something was hit already
    245 				if ( tw->trace.fraction < 1.0f ) {
    246 					return;
    247 				}
    248 				start = end;
    249 			}
    250 		}
    251 		else {
    252 			start = tw->start;
    253 		}
    254 		// last step of the approximation
    255 		idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, tw->model->node, 0, 1, start, tw->end );
    256 	}
    257 }