1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP 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 RTCW SP 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 RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP 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 RTCW SP 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 // tr_marks.c -- polygon projection on the world polygons
30 
31 #include "tr_local.h"
32 //#include "assert.h"
33 
34 #define MAX_VERTS_ON_POLY       64
35 
36 #define MARKER_OFFSET           0   // 1
37 
38 // Ridah, just make these global to prevent having to add more paramaters, which add overhead
39 static vec3_t bestnormal;
40 static float bestdist;
41 
42 /*
43 =============
44 R_ChopPolyBehindPlane
45 
46 Out must have space for two more vertexes than in
47 =============
48 */
49 #define SIDE_FRONT  0
50 #define SIDE_BACK   1
51 #define SIDE_ON     2
R_ChopPolyBehindPlane(int numInPoints,vec3_t inPoints[MAX_VERTS_ON_POLY],int * numOutPoints,vec3_t outPoints[MAX_VERTS_ON_POLY],vec3_t normal,vec_t dist,vec_t epsilon)52 static void R_ChopPolyBehindPlane( int numInPoints, vec3_t inPoints[MAX_VERTS_ON_POLY],
53 								   int *numOutPoints, vec3_t outPoints[MAX_VERTS_ON_POLY],
54 								   vec3_t normal, vec_t dist, vec_t epsilon ) {
55 	float dists[MAX_VERTS_ON_POLY + 4] = { 0 };
56 	int sides[MAX_VERTS_ON_POLY + 4] = { 0 };
57 	int counts[3];
58 	float dot;
59 	int i, j;
60 	float       *p1, *p2, *clip;
61 	float d;
62 
63 	// don't clip if it might overflow
64 	if ( numInPoints >= MAX_VERTS_ON_POLY - 2 ) {
65 		*numOutPoints = 0;
66 		return;
67 	}
68 
69 	counts[0] = counts[1] = counts[2] = 0;
70 
71 	// determine sides for each point
72 	for ( i = 0 ; i < numInPoints ; i++ ) {
73 		dot = DotProduct( inPoints[i], normal );
74 		dot -= dist;
75 		dists[i] = dot;
76 		if ( dot > epsilon ) {
77 			sides[i] = SIDE_FRONT;
78 		} else if ( dot < -epsilon ) {
79 			sides[i] = SIDE_BACK;
80 		} else {
81 			sides[i] = SIDE_ON;
82 		}
83 		counts[sides[i]]++;
84 	}
85 	sides[i] = sides[0];
86 	dists[i] = dists[0];
87 
88 	*numOutPoints = 0;
89 
90 	if ( !counts[0] ) {
91 		return;
92 	}
93 	if ( !counts[1] ) {
94 		*numOutPoints = numInPoints;
95 		memcpy( outPoints, inPoints, numInPoints * sizeof( vec3_t ) );
96 		return;
97 	}
98 
99 	for ( i = 0 ; i < numInPoints ; i++ ) {
100 		p1 = inPoints[i];
101 		clip = outPoints[ *numOutPoints ];
102 
103 		if ( sides[i] == SIDE_ON ) {
104 			VectorCopy( p1, clip );
105 			( *numOutPoints )++;
106 			continue;
107 		}
108 
109 		if ( sides[i] == SIDE_FRONT ) {
110 			VectorCopy( p1, clip );
111 			( *numOutPoints )++;
112 			clip = outPoints[ *numOutPoints ];
113 		}
114 
115 		if ( sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i] ) {
116 			continue;
117 		}
118 
119 		// generate a split point
120 		p2 = inPoints[ ( i + 1 ) % numInPoints ];
121 
122 		d = dists[i] - dists[i + 1];
123 		if ( d == 0 ) {
124 			dot = 0;
125 		} else {
126 			dot = dists[i] / d;
127 		}
128 
129 		// clip xyz
130 
131 		for ( j = 0 ; j < 3 ; j++ ) {
132 			clip[j] = p1[j] + dot * ( p2[j] - p1[j] );
133 		}
134 
135 		( *numOutPoints )++;
136 	}
137 }
138 
139 /*
140 =================
141 R_BoxSurfaces_r
142 
143 =================
144 */
R_BoxSurfaces_r(mnode_t * node,vec3_t mins,vec3_t maxs,surfaceType_t ** list,int listsize,int * listlength,vec3_t dir)145 void R_BoxSurfaces_r( mnode_t *node, vec3_t mins, vec3_t maxs, surfaceType_t **list, int listsize, int *listlength, vec3_t dir ) {
146 
147 	int s, c;
148 	msurface_t  *surf, **mark;
149 
150 	// RF, if this node hasn't been rendered recently, ignore it
151 	if ( node->visframe < tr.visCount - 2 ) { // allow us to be a few frames behind
152 		return;
153 	}
154 
155 	// do the tail recursion in a loop
156 	while ( node->contents == -1 ) {
157 		s = BoxOnPlaneSide( mins, maxs, node->plane );
158 		if ( s == 1 ) {
159 			node = node->children[0];
160 		} else if ( s == 2 ) {
161 			node = node->children[1];
162 		} else {
163 			R_BoxSurfaces_r( node->children[0], mins, maxs, list, listsize, listlength, dir );
164 			node = node->children[1];
165 		}
166 	}
167 
168 	// Ridah, don't mark alpha surfaces
169 	if ( node->contents & CONTENTS_TRANSLUCENT ) {
170 		return;
171 	}
172 
173 	// add the individual surfaces
174 	mark = node->firstmarksurface;
175 	c = node->nummarksurfaces;
176 	while ( c-- ) {
177 		//
178 		if ( *listlength >= listsize ) {
179 			break;
180 		}
181 		//
182 		surf = *mark;
183 		// check if the surface has NOIMPACT or NOMARKS set
184 		if ( ( surf->shader->surfaceFlags & ( SURF_NOIMPACT | SURF_NOMARKS ) )
185 			 || ( surf->shader->contentFlags & CONTENTS_FOG ) ) {
186 			surf->viewCount = tr.viewCount;
187 		}
188 		// extra check for surfaces to avoid list overflows
189 		else if ( *( surf->data ) == SF_FACE ) {
190 			// the face plane should go through the box
191 			s = BoxOnPlaneSide( mins, maxs, &( ( srfSurfaceFace_t * ) surf->data )->plane );
192 			if ( s == 1 || s == 2 ) {
193 				surf->viewCount = tr.viewCount;
194 			} else if ( DotProduct( ( ( srfSurfaceFace_t * ) surf->data )->plane.normal, dir ) < -0.5 )         {
195 				// don't add faces that make sharp angles with the projection direction
196 				surf->viewCount = tr.viewCount;
197 			}
198 		}
199 		else if (*(surfaceType_t *) (surf->data) != SF_GRID &&
200 			 *(surfaceType_t *) (surf->data) != SF_TRIANGLES)
201 			surf->viewCount = tr.viewCount;
202 		// check the viewCount because the surface may have
203 		// already been added if it spans multiple leafs
204 		if ( surf->viewCount != tr.viewCount ) {
205 			surf->viewCount = tr.viewCount;
206 			list[*listlength] = (surfaceType_t *) surf->data;
207 			( *listlength )++;
208 		}
209 		mark++;
210 	}
211 }
212 
213 /*
214 =================
215 R_AddMarkFragments
216 
217 =================
218 */
R_AddMarkFragments(int numClipPoints,vec3_t clipPoints[2][MAX_VERTS_ON_POLY],int numPlanes,vec3_t * normals,float * dists,int maxPoints,vec3_t pointBuffer,int maxFragments,markFragment_t * fragmentBuffer,int * returnedPoints,int * returnedFragments,vec3_t mins,vec3_t maxs)219 void R_AddMarkFragments( int numClipPoints, vec3_t clipPoints[2][MAX_VERTS_ON_POLY],
220 						 int numPlanes, vec3_t *normals, float *dists,
221 						 int maxPoints, vec3_t pointBuffer,
222 						 int maxFragments, markFragment_t *fragmentBuffer,
223 						 int *returnedPoints, int *returnedFragments,
224 						 vec3_t mins, vec3_t maxs ) {
225 	int pingPong, i;
226 	markFragment_t  *mf;
227 
228 	// chop the surface by all the bounding planes of the to be projected polygon
229 	pingPong = 0;
230 
231 	for ( i = 0 ; i < numPlanes ; i++ ) {
232 
233 		R_ChopPolyBehindPlane( numClipPoints, clipPoints[pingPong],
234 							   &numClipPoints, clipPoints[!pingPong],
235 							   normals[i], dists[i], 0.5 );
236 		pingPong ^= 1;
237 		if ( numClipPoints == 0 ) {
238 			break;
239 		}
240 	}
241 	// completely clipped away?
242 	if ( numClipPoints == 0 ) {
243 		return;
244 	}
245 
246 	// add this fragment to the returned list
247 	if ( numClipPoints + ( *returnedPoints ) > maxPoints ) {
248 		return; // not enough space for this polygon
249 	}
250 	/*
251 	// all the clip points should be within the bounding box
252 	for ( i = 0 ; i < numClipPoints ; i++ ) {
253 		int j;
254 		for ( j = 0 ; j < 3 ; j++ ) {
255 			if (clipPoints[pingPong][i][j] < mins[j] - 0.5) break;
256 			if (clipPoints[pingPong][i][j] > maxs[j] + 0.5) break;
257 		}
258 		if (j < 3) break;
259 	}
260 	if (i < numClipPoints) return;
261 	*/
262 
263 	mf = fragmentBuffer + ( *returnedFragments );
264 	mf->firstPoint = ( *returnedPoints );
265 	mf->numPoints = numClipPoints;
266 	//memcpy( pointBuffer + (*returnedPoints) * 3, clipPoints[pingPong], numClipPoints * sizeof(vec3_t) );
267 	for ( i = 0; i < numClipPoints; i++ ) {
268 		VectorCopy( clipPoints[pingPong][i], (float *)pointBuffer + 5 * ( *returnedPoints + i ) );
269 	}
270 
271 	( *returnedPoints ) += numClipPoints;
272 	( *returnedFragments )++;
273 }
274 
275 /*
276 =================
277 R_OldMarkFragments
278 
279 =================
280 */
R_OldMarkFragments(int numPoints,const vec3_t * points,const vec3_t projection,int maxPoints,vec3_t pointBuffer,int maxFragments,markFragment_t * fragmentBuffer)281 int R_OldMarkFragments( int numPoints, const vec3_t *points, const vec3_t projection,
282 						int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) {
283 	int numsurfaces, numPlanes;
284 	int i, j, k, m, n;
285 	surfaceType_t   *surfaces[64];
286 	vec3_t mins, maxs;
287 	int returnedFragments;
288 	int returnedPoints;
289 	vec3_t normals[MAX_VERTS_ON_POLY + 2];
290 	float dists[MAX_VERTS_ON_POLY + 2];
291 	vec3_t clipPoints[2][MAX_VERTS_ON_POLY];
292 	int numClipPoints;
293 	float           *v;
294 	srfGridMesh_t   *cv;
295 	drawVert_t      *dv;
296 	vec3_t normal;
297 	vec3_t projectionDir;
298 	vec3_t v1, v2;
299 	int             *indexes;
300 
301 	if (numPoints <= 0) {
302 		return 0;
303 	}
304 
305 	//increment view count for double check prevention
306 	tr.viewCount++;
307 
308 	//
309 	VectorNormalize2( projection, projectionDir );
310 	// find all the brushes that are to be considered
311 	ClearBounds( mins, maxs );
312 	for ( i = 0 ; i < numPoints ; i++ ) {
313 		vec3_t temp;
314 
315 		AddPointToBounds( points[i], mins, maxs );
316 		VectorAdd( points[i], projection, temp );
317 		AddPointToBounds( temp, mins, maxs );
318 		// make sure we get all the leafs (also the one(s) in front of the hit surface)
319 		VectorMA( points[i], -20, projectionDir, temp );
320 		AddPointToBounds( temp, mins, maxs );
321 	}
322 
323 	if ( numPoints > MAX_VERTS_ON_POLY ) {
324 		numPoints = MAX_VERTS_ON_POLY;
325 	}
326 	// create the bounding planes for the to be projected polygon
327 	for ( i = 0 ; i < numPoints ; i++ ) {
328 		VectorSubtract( points[( i + 1 ) % numPoints], points[i], v1 );
329 		VectorAdd( points[i], projection, v2 );
330 		VectorSubtract( points[i], v2, v2 );
331 		CrossProduct( v1, v2, normals[i] );
332 		VectorNormalizeFast( normals[i] );
333 		dists[i] = DotProduct( normals[i], points[i] );
334 	}
335 	// add near and far clipping planes for projection
336 	VectorCopy( projectionDir, normals[numPoints] );
337 	dists[numPoints] = DotProduct( normals[numPoints], points[0] ) - 32;
338 	VectorCopy( projectionDir, normals[numPoints + 1] );
339 	VectorInverse( normals[numPoints + 1] );
340 	dists[numPoints + 1] = DotProduct( normals[numPoints + 1], points[0] ) - 20;
341 	numPlanes = numPoints + 2;
342 
343 	numsurfaces = 0;
344 	R_BoxSurfaces_r( tr.world->nodes, mins, maxs, surfaces, 64, &numsurfaces, projectionDir );
345 	//assert(numsurfaces <= 64);
346 	//assert(numsurfaces != 64);
347 
348 	returnedPoints = 0;
349 	returnedFragments = 0;
350 
351 	for ( i = 0 ; i < numsurfaces ; i++ ) {
352 
353 		if ( *surfaces[i] == SF_GRID ) {
354 
355 			cv = (srfGridMesh_t *) surfaces[i];
356 			for ( m = 0 ; m < cv->height - 1 ; m++ ) {
357 				for ( n = 0 ; n < cv->width - 1 ; n++ ) {
358 					// We triangulate the grid and chop all triangles within
359 					// the bounding planes of the to be projected polygon.
360 					// LOD is not taken into account, not such a big deal though.
361 					//
362 					// It's probably much nicer to chop the grid itself and deal
363 					// with this grid as a normal SF_GRID surface so LOD will
364 					// be applied. However the LOD of that chopped grid must
365 					// be synced with the LOD of the original curve.
366 					// One way to do this; the chopped grid shares vertices with
367 					// the original curve. When LOD is applied to the original
368 					// curve the unused vertices are flagged. Now the chopped curve
369 					// should skip the flagged vertices. This still leaves the
370 					// problems with the vertices at the chopped grid edges.
371 					//
372 					// To avoid issues when LOD applied to "hollow curves" (like
373 					// the ones around many jump pads) we now just add a 2 unit
374 					// offset to the triangle vertices.
375 					// The offset is added in the vertex normal vector direction
376 					// so all triangles will still fit together.
377 					// The 2 unit offset should avoid pretty much all LOD problems.
378 
379 					numClipPoints = 3;
380 
381 					dv = cv->verts + m * cv->width + n;
382 
383 					VectorCopy( dv[0].xyz, clipPoints[0][0] );
384 					VectorMA( clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0] );
385 					VectorCopy( dv[cv->width].xyz, clipPoints[0][1] );
386 					VectorMA( clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1] );
387 					VectorCopy( dv[1].xyz, clipPoints[0][2] );
388 					VectorMA( clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2] );
389 					// check the normal of this triangle
390 					VectorSubtract( clipPoints[0][0], clipPoints[0][1], v1 );
391 					VectorSubtract( clipPoints[0][2], clipPoints[0][1], v2 );
392 					CrossProduct( v1, v2, normal );
393 					VectorNormalizeFast( normal );
394 					if ( DotProduct( normal, projectionDir ) < -0.1 ) {
395 						// add the fragments of this triangle
396 						R_AddMarkFragments( numClipPoints, clipPoints,
397 											numPlanes, normals, dists,
398 											maxPoints, pointBuffer,
399 											maxFragments, fragmentBuffer,
400 											&returnedPoints, &returnedFragments, mins, maxs );
401 
402 						if ( returnedFragments == maxFragments ) {
403 							return returnedFragments;   // not enough space for more fragments
404 						}
405 					}
406 
407 					VectorCopy( dv[1].xyz, clipPoints[0][0] );
408 					VectorMA( clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0] );
409 					VectorCopy( dv[cv->width].xyz, clipPoints[0][1] );
410 					VectorMA( clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1] );
411 					VectorCopy( dv[cv->width + 1].xyz, clipPoints[0][2] );
412 					VectorMA( clipPoints[0][2], MARKER_OFFSET, dv[cv->width + 1].normal, clipPoints[0][2] );
413 					// check the normal of this triangle
414 					VectorSubtract( clipPoints[0][0], clipPoints[0][1], v1 );
415 					VectorSubtract( clipPoints[0][2], clipPoints[0][1], v2 );
416 					CrossProduct( v1, v2, normal );
417 					VectorNormalizeFast( normal );
418 					if ( DotProduct( normal, projectionDir ) < -0.05 ) {
419 						// add the fragments of this triangle
420 						R_AddMarkFragments( numClipPoints, clipPoints,
421 											numPlanes, normals, dists,
422 											maxPoints, pointBuffer,
423 											maxFragments, fragmentBuffer,
424 											&returnedPoints, &returnedFragments, mins, maxs );
425 
426 						if ( returnedFragments == maxFragments ) {
427 							return returnedFragments;   // not enough space for more fragments
428 						}
429 					}
430 				}
431 			}
432 		} else if ( *surfaces[i] == SF_FACE )     {
433 
434 			srfSurfaceFace_t *surf = ( srfSurfaceFace_t * ) surfaces[i];
435 			// check the normal of this face
436 			if ( DotProduct( surf->plane.normal, projectionDir ) > -0.5 ) {
437 				continue;
438 			}
439 
440 			indexes = ( int * )( (byte *)surf + surf->ofsIndices );
441 			for ( k = 0 ; k < surf->numIndices ; k += 3 ) {
442 				for ( j = 0 ; j < 3 ; j++ ) {
443 					v = &surf->points[0][0] + VERTEXSIZE * indexes[k + j];
444 					VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] );
445 				}
446 				// add the fragments of this face
447 				R_AddMarkFragments( 3, clipPoints,
448 									numPlanes, normals, dists,
449 									maxPoints, pointBuffer,
450 									maxFragments, fragmentBuffer,
451 									&returnedPoints, &returnedFragments, mins, maxs );
452 				if ( returnedFragments == maxFragments ) {
453 					return returnedFragments;   // not enough space for more fragments
454 				}
455 			}
456 		}
457 		else if(*surfaces[i] == SF_TRIANGLES && r_marksOnTriangleMeshes->integer) {
458 
459 			srfTriangles_t *surf = (srfTriangles_t *) surfaces[i];
460 
461 			for (k = 0; k < surf->numIndexes; k += 3)
462 			{
463 				for(j = 0; j < 3; j++)
464 				{
465 					v = surf->verts[surf->indexes[k + j]].xyz;
466 					VectorMA(v, MARKER_OFFSET, surf->verts[surf->indexes[k + j]].normal, clipPoints[0][j]);
467 				}
468 
469 				// add the fragments of this face
470 				R_AddMarkFragments(3, clipPoints,
471 								   numPlanes, normals, dists,
472 								   maxPoints, pointBuffer,
473 								   maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs);
474 				if(returnedFragments == maxFragments)
475 				{
476 					return returnedFragments;	// not enough space for more fragments
477 				}
478 			}
479 		}
480 	}
481 	return returnedFragments;
482 }
483 
484 /*
485 =================
486 R_MarkFragments
487 
488 =================
489 */
R_MarkFragments(int orientation,const vec3_t * points,const vec3_t projection,int maxPoints,vec3_t pointBuffer,int maxFragments,markFragment_t * fragmentBuffer)490 int R_MarkFragments( int orientation, const vec3_t *points, const vec3_t projection,
491 					 int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) {
492 	int numsurfaces, numPlanes;
493 	int i, j, k, m, n;
494 	surfaceType_t   *surfaces[4096];
495 	vec3_t mins, maxs;
496 	int returnedFragments;
497 	int returnedPoints;
498 	vec3_t normals[MAX_VERTS_ON_POLY + 2];
499 	float dists[MAX_VERTS_ON_POLY + 2];
500 	vec3_t clipPoints[2][MAX_VERTS_ON_POLY];
501 	int numClipPoints;
502 	float           *v;
503 	srfGridMesh_t   *cv;
504 	drawVert_t      *dv;
505 	vec3_t normal;
506 	vec3_t projectionDir;
507 	vec3_t v1, v2;
508 	int             *indexes;
509 	float radius;
510 	vec3_t center;          // center of original mark
511 	int numPoints = 4;              // Ridah, we were only ever passing in 4, so I made this local and used the parameter for the orientation
512 	qboolean oldMapping = qfalse;
513 
514 	if (numPoints <= 0) {
515 		return 0;
516 	}
517 
518 	//increment view count for double check prevention
519 	tr.viewCount++;
520 
521 	// RF, negative maxFragments means we want original mapping
522 	if ( maxFragments < 0 ) {
523 		maxFragments = -maxFragments;
524 		oldMapping = qtrue;
525 	}
526 
527 	VectorClear( center );
528 	for ( i = 0 ; i < numPoints ; i++ ) {
529 		VectorAdd( points[i], center, center );
530 	}
531 	VectorScale( center, 1.0 / numPoints, center );
532 	//
533 	radius = VectorNormalize2( projection, projectionDir ) / 2.0;
534 	bestdist = 0;
535 	VectorNegate( projectionDir, bestnormal );
536 	// find all the brushes that are to be considered
537 	ClearBounds( mins, maxs );
538 	for ( i = 0 ; i < numPoints ; i++ ) {
539 		vec3_t temp;
540 
541 		AddPointToBounds( points[i], mins, maxs );
542 		VectorMA( points[i], 1 * ( 1 + oldMapping * radius * 4 ), projection, temp );
543 		AddPointToBounds( temp, mins, maxs );
544 		// make sure we get all the leafs (also the one(s) in front of the hit surface)
545 		VectorMA( points[i], -20 * ( 1.0 + (float)oldMapping * ( radius / 20.0 ) * 4 ), projectionDir, temp );
546 		AddPointToBounds( temp, mins, maxs );
547 	}
548 
549 	if ( numPoints > MAX_VERTS_ON_POLY ) {
550 		numPoints = MAX_VERTS_ON_POLY;
551 	}
552 	// create the bounding planes for the to be projected polygon
553 	for ( i = 0 ; i < numPoints ; i++ ) {
554 		VectorSubtract( points[( i + 1 ) % numPoints], points[i], v1 );
555 		VectorAdd( points[i], projection, v2 );
556 		VectorSubtract( points[i], v2, v2 );
557 		CrossProduct( v1, v2, normals[i] );
558 		VectorNormalize( normals[i] );
559 		dists[i] = DotProduct( normals[i], points[i] );
560 	}
561 	// add near and far clipping planes for projection
562 	VectorCopy( projectionDir, normals[numPoints] );
563 	dists[numPoints] = DotProduct( normals[numPoints], points[0] ) - radius * ( 1 + oldMapping * 10 );
564 	VectorCopy( projectionDir, normals[numPoints + 1] );
565 	VectorInverse( normals[numPoints + 1] );
566 	dists[numPoints + 1] = DotProduct( normals[numPoints + 1], points[0] ) - radius * ( 1 + oldMapping * 10 );
567 	numPlanes = numPoints + 2;
568 
569 	numsurfaces = 0;
570 	R_BoxSurfaces_r( tr.world->nodes, mins, maxs, surfaces, 4096, &numsurfaces, projectionDir );
571 
572 	returnedPoints = 0;
573 	returnedFragments = 0;
574 
575 	// find the closest surface to center the decal there, and wrap around other surfaces
576 	if ( !oldMapping ) {
577 		VectorNegate( bestnormal, bestnormal );
578 	}
579 
580 	for ( i = 0 ; i < numsurfaces ; i++ ) {
581 
582 		if ( *surfaces[i] == SF_GRID ) {
583 
584 			cv = (srfGridMesh_t *) surfaces[i];
585 			for ( m = 0 ; m < cv->height - 1 ; m++ ) {
586 				for ( n = 0 ; n < cv->width - 1 ; n++ ) {
587 					// We triangulate the grid and chop all triangles within
588 					// the bounding planes of the to be projected polygon.
589 					// LOD is not taken into account, not such a big deal though.
590 					//
591 					// It's probably much nicer to chop the grid itself and deal
592 					// with this grid as a normal SF_GRID surface so LOD will
593 					// be applied. However the LOD of that chopped grid must
594 					// be synced with the LOD of the original curve.
595 					// One way to do this; the chopped grid shares vertices with
596 					// the original curve. When LOD is applied to the original
597 					// curve the unused vertices are flagged. Now the chopped curve
598 					// should skip the flagged vertices. This still leaves the
599 					// problems with the vertices at the chopped grid edges.
600 					//
601 					// To avoid issues when LOD applied to "hollow curves" (like
602 					// the ones around many jump pads) we now just add a 2 unit
603 					// offset to the triangle vertices.
604 					// The offset is added in the vertex normal vector direction
605 					// so all triangles will still fit together.
606 					// The 2 unit offset should avoid pretty much all LOD problems.
607 
608 					numClipPoints = 3;
609 
610 					dv = cv->verts + m * cv->width + n;
611 
612 					VectorCopy( dv[0].xyz, clipPoints[0][0] );
613 					VectorMA( clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0] );
614 					VectorCopy( dv[cv->width].xyz, clipPoints[0][1] );
615 					VectorMA( clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1] );
616 					VectorCopy( dv[1].xyz, clipPoints[0][2] );
617 					VectorMA( clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2] );
618 					// check the normal of this triangle
619 					VectorSubtract( clipPoints[0][0], clipPoints[0][1], v1 );
620 					VectorSubtract( clipPoints[0][2], clipPoints[0][1], v2 );
621 					CrossProduct( v1, v2, normal );
622 					VectorNormalize( normal );
623 					if ( DotProduct( normal, projectionDir ) < -0.1 ) {
624 						// add the fragments of this triangle
625 						R_AddMarkFragments( numClipPoints, clipPoints,
626 											numPlanes, normals, dists,
627 											maxPoints, pointBuffer,
628 											maxFragments, fragmentBuffer,
629 											&returnedPoints, &returnedFragments, mins, maxs );
630 
631 						if ( returnedFragments == maxFragments ) {
632 							return returnedFragments;   // not enough space for more fragments
633 						}
634 					}
635 
636 					VectorCopy( dv[1].xyz, clipPoints[0][0] );
637 					VectorMA( clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0] );
638 					VectorCopy( dv[cv->width].xyz, clipPoints[0][1] );
639 					VectorMA( clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1] );
640 					VectorCopy( dv[cv->width + 1].xyz, clipPoints[0][2] );
641 					VectorMA( clipPoints[0][2], MARKER_OFFSET, dv[cv->width + 1].normal, clipPoints[0][2] );
642 					// check the normal of this triangle
643 					VectorSubtract( clipPoints[0][0], clipPoints[0][1], v1 );
644 					VectorSubtract( clipPoints[0][2], clipPoints[0][1], v2 );
645 					CrossProduct( v1, v2, normal );
646 					VectorNormalize( normal );
647 					if ( DotProduct( normal, projectionDir ) < -0.05 ) {
648 						// add the fragments of this triangle
649 						R_AddMarkFragments( numClipPoints, clipPoints,
650 											numPlanes, normals, dists,
651 											maxPoints, pointBuffer,
652 											maxFragments, fragmentBuffer,
653 											&returnedPoints, &returnedFragments, mins, maxs );
654 
655 						if ( returnedFragments == maxFragments ) {
656 							return returnedFragments;   // not enough space for more fragments
657 						}
658 					}
659 				}
660 			}
661 		} else if ( *surfaces[i] == SF_FACE )     {
662 			extern float VectorDistance( vec3_t v1, vec3_t v2 );
663 			vec3_t axis[3];
664 			float texCoordScale, dot;
665 			vec3_t originalPoints[4];
666 			vec3_t newCenter, delta;
667 			int oldNumPoints;
668 			float epsilon = 0.5;
669 			// duplicated so we don't mess with the original clips for the curved surfaces
670 			vec3_t lnormals[MAX_VERTS_ON_POLY + 2];
671 			float ldists[MAX_VERTS_ON_POLY + 2];
672 			vec3_t lmins, lmaxs;
673 
674 			srfSurfaceFace_t *surf = ( srfSurfaceFace_t * ) surfaces[i];
675 
676 			if ( !oldMapping ) {
677 
678 				// Ridah, create a new clip box such that this decal surface is mapped onto
679 				// the current surface without distortion. To find the center of the new clip box,
680 				// we project the center of the original impact center out along the projection vector,
681 				// onto the current surface
682 
683 				// find the center of the new decal
684 				dot = DotProduct( center, surf->plane.normal );
685 				dot -= surf->plane.dist;
686 				// check the normal of this face
687 				if ( dot < -epsilon && DotProduct( surf->plane.normal, projectionDir ) >= 0.01 ) {
688 					continue;
689 				} else if ( fabs( dot ) > radius ) {
690 					continue;
691 				}
692 				// if the impact point is behind the surface, subtract the projection, otherwise add it
693 				VectorMA( center, -dot, bestnormal, newCenter );
694 
695 				// recalc dot from the offset position
696 				dot = DotProduct( newCenter, surf->plane.normal );
697 				dot -= surf->plane.dist;
698 				VectorMA( newCenter, -dot, surf->plane.normal, newCenter );
699 
700 				VectorMA( newCenter, MARKER_OFFSET, surf->plane.normal, newCenter );
701 
702 				// create the texture axis
703 				VectorNormalize2( surf->plane.normal, axis[0] );
704 				PerpendicularVector( axis[1], axis[0] );
705 				RotatePointAroundVector( axis[2], axis[0], axis[1], (float)orientation );
706 				CrossProduct( axis[0], axis[2], axis[1] );
707 
708 				texCoordScale = 0.5 * 1.0 / radius;
709 
710 				// create the full polygon
711 				for ( j = 0 ; j < 3 ; j++ ) {
712 					originalPoints[0][j] = newCenter[j] - radius * axis[1][j] - radius * axis[2][j];
713 					originalPoints[1][j] = newCenter[j] + radius * axis[1][j] - radius * axis[2][j];
714 					originalPoints[2][j] = newCenter[j] + radius * axis[1][j] + radius * axis[2][j];
715 					originalPoints[3][j] = newCenter[j] - radius * axis[1][j] + radius * axis[2][j];
716 				}
717 
718 				ClearBounds( lmins, lmaxs );
719 
720 				// create the bounding planes for the to be projected polygon
721 				for ( j = 0 ; j < 4 ; j++ ) {
722 					AddPointToBounds( originalPoints[j], lmins, lmaxs );
723 
724 					VectorSubtract( originalPoints[( j + 1 ) % numPoints], originalPoints[j], v1 );
725 					VectorSubtract( originalPoints[j], surf->plane.normal, v2 );
726 					VectorSubtract( originalPoints[j], v2, v2 );
727 					CrossProduct( v1, v2, lnormals[j] );
728 					VectorNormalize( lnormals[j] );
729 					ldists[j] = DotProduct( lnormals[j], originalPoints[j] );
730 				}
731 				numPlanes = numPoints;
732 
733 				// done.
734 
735 				indexes = ( int * )( (byte *)surf + surf->ofsIndices );
736 				for ( k = 0 ; k < surf->numIndices ; k += 3 ) {
737 					for ( j = 0 ; j < 3 ; j++ ) {
738 						v = &surf->points[0][0] + VERTEXSIZE * indexes[k + j];
739 						VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] );
740 					}
741 
742 					oldNumPoints = returnedPoints;
743 
744 					// add the fragments of this face
745 					R_AddMarkFragments( 3, clipPoints,
746 										numPlanes, lnormals, ldists,
747 										maxPoints, pointBuffer,
748 										maxFragments, fragmentBuffer,
749 										&returnedPoints, &returnedFragments, lmins, lmaxs );
750 
751 					if ( oldNumPoints != returnedPoints ) {
752 						// flag this surface as already having computed ST's
753 						fragmentBuffer[returnedFragments - 1].numPoints *= -1;
754 
755 						// Ridah, calculate ST's
756 						for ( j = 0 ; j < ( returnedPoints - oldNumPoints ) ; j++ ) {
757 							VectorSubtract( (float *)pointBuffer + 5 * ( oldNumPoints + j ), newCenter, delta );
758 							*( (float *)pointBuffer + 5 * ( oldNumPoints + j ) + 3 ) = 0.5 + DotProduct( delta, axis[1] ) * texCoordScale;
759 							*( (float *)pointBuffer + 5 * ( oldNumPoints + j ) + 4 ) = 0.5 + DotProduct( delta, axis[2] ) * texCoordScale;
760 						}
761 					}
762 
763 					if ( returnedFragments == maxFragments ) {
764 						return returnedFragments;   // not enough space for more fragments
765 					}
766 				}
767 
768 			} else {    // old mapping
769 
770 				// check the normal of this face
771 				//if (DotProduct(surf->plane.normal, projectionDir) > 0.0) {
772 				//	continue;
773 				//}
774 
775 				indexes = ( int * )( (byte *)surf + surf->ofsIndices );
776 				for ( k = 0 ; k < surf->numIndices ; k += 3 ) {
777 					for ( j = 0 ; j < 3 ; j++ ) {
778 						v = &surf->points[0][0] + VERTEXSIZE * indexes[k + j];
779 						VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] );
780 					}
781 					// add the fragments of this face
782 					R_AddMarkFragments( 3, clipPoints,
783 										numPlanes, normals, dists,
784 										maxPoints, pointBuffer,
785 										maxFragments, fragmentBuffer,
786 										&returnedPoints, &returnedFragments, mins, maxs );
787 					if ( returnedFragments == maxFragments ) {
788 						return returnedFragments;   // not enough space for more fragments
789 					}
790 				}
791 
792 			}
793 
794 		}
795 		else if(*surfaces[i] == SF_TRIANGLES && r_marksOnTriangleMeshes->integer) {
796 
797 			srfTriangles_t *surf = (srfTriangles_t *) surfaces[i];
798 
799 			for (k = 0; k < surf->numIndexes; k += 3)
800 			{
801 				for(j = 0; j < 3; j++)
802 				{
803 					v = surf->verts[surf->indexes[k + j]].xyz;
804 					VectorMA(v, MARKER_OFFSET, surf->verts[surf->indexes[k + j]].normal, clipPoints[0][j]);
805 				}
806 
807 				// add the fragments of this face
808 				R_AddMarkFragments(3, clipPoints,
809 								   numPlanes, normals, dists,
810 								   maxPoints, pointBuffer,
811 								   maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs);
812 				if(returnedFragments == maxFragments)
813 				{
814 					return returnedFragments;	// not enough space for more fragments
815 				}
816 			}
817 		}
818 	}
819 	return returnedFragments;
820 }
821 
822