1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 
20 //
21 // rf_decal.c
22 // FIXME TODO:
23 // - Clean up CG_SpawnDecal parms
24 // - could be re-worked so that decals are only added to the list when their owner surface is
25 // - r_decal_maxFragments r_decal_maxVerts?
26 //
27 
28 #include "rf_local.h"
29 
30 #define MAX_DECAL_VERTS		512
31 #define MAX_DECAL_FRAGMENTS	384
32 
33 typedef struct refFragment_s {
34 	int					firstVert;
35 	int					numVerts;
36 
37 	vec3_t				normal;
38 
39 	mBspSurface_t		*surf;
40 } refFragment_t;
41 
42 static mesh_t		r_decalMesh;
43 
44 /*
45 ==============================================================================
46 
47 	REFRESH FUNCTIONS
48 
49 ==============================================================================
50 */
51 
52 /*
53 ===============
54 R_AddDecalsToList
55 ===============
56 */
R_AddDecalsToList(void)57 void R_AddDecalsToList (void)
58 {
59 	refDecal_t		*d;
60 	mQ3BspFog_t		*fog;
61 	mBspSurface_t	*surf;
62 	uint32			i, j;
63 
64 	if (!r_drawDecals->intVal)
65 		return;
66 
67 	// Add decal meshes to list
68 	for (i=0 ; i<ri.scn.numDecals ; i++) {
69 		d = ri.scn.decalList[i];
70 
71 		// Check the surface visibility
72 		fog = NULL;
73 		if (d->numSurfaces) {
74 			for (j=0 ; j<d->numSurfaces ; j++) {
75 				surf = d->surfaces[j];
76 				if (!R_CullSurface (surf)) {
77 					if (!fog && surf->q3_fog)
78 						fog = surf->q3_fog;	// FIXME: test me!
79 					break;
80 				}
81 			}
82 			if (j == d->numSurfaces)
83 				continue;
84 		}
85 
86 		// Frustum cull
87 		if (R_CullSphere (d->origin, d->radius, 31))
88 			continue;
89 
90 		// Add to the list
91 		R_AddMeshToList (d->poly.shader, d->poly.shaderTime, NULL, fog, MBT_DECAL, d);
92 		ri.scn.drawnDecals++;
93 	}
94 }
95 
96 
97 /*
98 ================
99 R_PushDecal
100 ================
101 */
R_PushDecal(meshBuffer_t * mb,meshFeatures_t features)102 void R_PushDecal (meshBuffer_t *mb, meshFeatures_t features)
103 {
104 	refDecal_t	*d;
105 
106 	d = (refDecal_t *)mb->mesh;
107 	if (d->poly.numVerts > RB_MAX_VERTS)
108 		return;
109 
110 	r_decalMesh.numIndexes = d->numIndexes;
111 	r_decalMesh.indexArray = d->indexes;
112 
113 	r_decalMesh.numVerts = d->poly.numVerts;
114 	r_decalMesh.colorArray = d->poly.colors;
115 	r_decalMesh.coordArray = (vec2_t *)d->poly.texCoords;
116 	r_decalMesh.normalsArray = (vec3_t *)d->normals;
117 	r_decalMesh.vertexArray = (vec3_t *)d->poly.vertices;
118 
119 	RB_PushMesh (&r_decalMesh, features);
120 }
121 
122 
123 /*
124 ================
125 R_DecalOverflow
126 ================
127 */
R_DecalOverflow(meshBuffer_t * mb)128 qBool R_DecalOverflow (meshBuffer_t *mb)
129 {
130 	refDecal_t	*d;
131 
132 	d = (refDecal_t *)mb->mesh;
133 	return RB_BackendOverflow (d->poly.numVerts, d->numIndexes);
134 }
135 
136 
137 /*
138 ================
139 R_DecalInit
140 ================
141 */
R_DecalInit(void)142 void R_DecalInit (void)
143 {
144 	r_decalMesh.lmCoordArray = NULL;
145 	r_decalMesh.sVectorsArray = NULL;
146 	r_decalMesh.tVectorsArray = NULL;
147 	r_decalMesh.trNeighborsArray = NULL;
148 	r_decalMesh.trNormalsArray = NULL;
149 }
150 
151 /*
152 ==============================================================================
153 
154 	FRAGMENT CLIPPING
155 
156 ==============================================================================
157 */
158 
159 static uint32			r_numFragmentVerts;
160 static vec3_t			r_fragmentVerts[MAX_DECAL_VERTS];
161 static vec3_t			r_fragmentNormals[MAX_DECAL_VERTS];
162 
163 static uint32			r_numClippedFragments;
164 static refFragment_t	r_clippedFragments[MAX_DECAL_FRAGMENTS];
165 
166 static uint32			r_fragmentFrame = 0;
167 static cBspPlane_t		r_fragmentPlanes[6];
168 
169 static vec3_t			r_decalOrigin;
170 static vec3_t			r_decalNormal;
171 static float			r_decalRadius;
172 
173 /*
174 ==============================================================================
175 
176 	QUAKE II FRAGMENT CLIPPING
177 
178 ==============================================================================
179 */
180 
181 /*
182 =================
183 R_Q2BSP_ClipPoly
184 =================
185 */
R_Q2BSP_ClipPoly(int nump,vec4_t vecs,int stage,refFragment_t * fr)186 static void R_Q2BSP_ClipPoly (int nump, vec4_t vecs, int stage, refFragment_t *fr)
187 {
188 	cBspPlane_t	*plane;
189 	qBool		front, back;
190 	vec4_t		newv[MAX_DECAL_VERTS];
191 	int			sides[MAX_DECAL_VERTS];
192 	float		dists[MAX_DECAL_VERTS];
193 	float		*v, d;
194 	int			newc, i, j;
195 
196 	if (nump > MAX_DECAL_VERTS - 2) {
197 		Com_Printf (PRNT_ERROR, "R_Q2BSP_ClipPoly: nump > MAX_DECAL_VERTS - 2");
198 		return;
199 	}
200 
201 	if (stage == 6) {
202 		// Fully clipped
203 		if (nump > 2) {
204 			fr->numVerts = nump;
205 			fr->firstVert = r_numFragmentVerts;
206 
207 			if (r_numFragmentVerts+nump >= MAX_DECAL_VERTS)
208 				nump = MAX_DECAL_VERTS - r_numFragmentVerts;
209 
210 			for (i=0, v=vecs ; i<nump ; i++, v+=4) {
211 				Vec3Copy (fr->normal, r_fragmentNormals[r_numFragmentVerts + i]);
212 				Vec3Copy (v, r_fragmentVerts[r_numFragmentVerts + i]);
213 			}
214 
215 			r_numFragmentVerts += nump;
216 		}
217 
218 		return;
219 	}
220 
221 	front = back = qFalse;
222 	plane = &r_fragmentPlanes[stage];
223 	for (i=0, v=vecs ; i<nump ; i++ , v+=4) {
224 		d = PlaneDiff (v, plane);
225 		if (d > LARGE_EPSILON) {
226 			front = qTrue;
227 			sides[i] = SIDE_FRONT;
228 		}
229 		else if (d < -LARGE_EPSILON) {
230 			back = qTrue;
231 			sides[i] = SIDE_BACK;
232 		}
233 		else
234 			sides[i] = SIDE_ON;
235 
236 		dists[i] = d;
237 	}
238 
239 	if (!front)
240 		return;
241 
242 	// Clip it
243 	sides[i] = sides[0];
244 	dists[i] = dists[0];
245 	Vec3Copy (vecs, (vecs + (i * 4)));
246 	newc = 0;
247 
248 	for (i=0, v=vecs ; i<nump ; i++, v+=4) {
249 		switch (sides[i]) {
250 		case SIDE_FRONT:
251 			Vec3Copy (v, newv[newc]);
252 			newc++;
253 			break;
254 		case SIDE_BACK:
255 			break;
256 		case SIDE_ON:
257 			Vec3Copy (v, newv[newc]);
258 			newc++;
259 			break;
260 		}
261 
262 		if (sides[i] == SIDE_ON
263 		|| sides[i+1] == SIDE_ON
264 		|| sides[i+1] == sides[i])
265 			continue;
266 
267 		d = dists[i] / (dists[i] - dists[i+1]);
268 		for (j=0 ; j<3 ; j++)
269 			newv[newc][j] = v[j] + d * (v[j+4] - v[j]);
270 		newc++;
271 	}
272 
273 	// Continue
274 	R_Q2BSP_ClipPoly (newc, newv[0], stage+1, fr);
275 }
276 
277 
278 /*
279 =================
280 R_Q2BSP_PlanarClipFragment
281 =================
282 */
R_Q2BSP_PlanarClipFragment(mBspNode_t * node,mBspSurface_t * surf)283 static void R_Q2BSP_PlanarClipFragment (mBspNode_t *node, mBspSurface_t *surf)
284 {
285 	int				i;
286 	float			*v, *v2, *v3;
287 	refFragment_t	*fr;
288 	vec4_t			verts[MAX_DECAL_VERTS];
289 
290 	v = surf->mesh->vertexArray[0];
291 
292 	// Copy vertex data and clip to each triangle
293 	for (i=0; i<surf->mesh->numVerts-2 ; i++) {
294 		fr = &r_clippedFragments[r_numClippedFragments];
295 		fr->numVerts = 0;
296 		fr->surf = surf;
297 		Vec3Copy (surf->mesh->normalsArray[i], fr->normal);
298 
299 		v2 = surf->mesh->vertexArray[0] + (i+1) * 3;
300 		v3 = surf->mesh->vertexArray[0] + (i+2) * 3;
301 
302 		Vec3Copy (v , verts[0]);
303 		Vec3Copy (v2, verts[1]);
304 		Vec3Copy (v3, verts[2]);
305 
306 		R_Q2BSP_ClipPoly (3, verts[0], 0, fr);
307 		if (fr->numVerts && (r_numFragmentVerts >= MAX_DECAL_VERTS || ++r_numClippedFragments >= MAX_DECAL_FRAGMENTS))
308 			return;
309 	}
310 }
311 
312 
313 /*
314 =================
315 R_Q2BSP_FragmentNode
316 =================
317 */
R_Q2BSP_FragmentNode(mBspNode_t * node)318 static void R_Q2BSP_FragmentNode (mBspNode_t *node)
319 {
320 	float			dist;
321 	mBspLeaf_t		*leaf;
322 	mBspSurface_t	*surf, **mark;
323 
324 mark0:
325 	if (r_numFragmentVerts >= MAX_DECAL_VERTS || r_numClippedFragments >= MAX_DECAL_FRAGMENTS)
326 		return;	// Already reached the limit somewhere else
327 
328 	if (node->c.q2_contents != -1) {
329 		if (node->c.q2_contents == CONTENTS_SOLID)
330 			return;
331 
332 		// Leaf
333 		leaf = (mBspLeaf_t *)node;
334 		if (!leaf->q2_firstDecalSurface)
335 			return;
336 
337 		mark = leaf->q2_firstDecalSurface;
338 		do {
339 			if (r_numFragmentVerts >= MAX_DECAL_VERTS || r_numClippedFragments >= MAX_DECAL_FRAGMENTS)
340 				return;
341 
342 			surf = *mark++;
343 			if (!surf)
344 				continue;
345 
346 			if (surf->fragmentFrame == r_fragmentFrame)
347 				continue;		// Already touched
348 			surf->fragmentFrame = r_fragmentFrame;
349 
350 			if (surf->q2_numEdges < 3)
351 				continue;		// Bogus face
352 
353 			if (surf->q2_flags & SURF_PLANEBACK) {
354 				if (DotProduct(r_decalNormal, surf->q2_plane->normal) > -0.5f)
355 					continue;	// Greater than 60 degrees
356 			}
357 			else {
358 				if (DotProduct(r_decalNormal, surf->q2_plane->normal) < 0.5f)
359 					continue;	// Greater than 60 degrees
360 			}
361 
362 			// Clip
363 			R_Q2BSP_PlanarClipFragment (node, surf);
364 		} while (*mark);
365 
366 		return;
367 	}
368 
369 	dist = PlaneDiff (r_decalOrigin, node->c.plane);
370 	if (dist > r_decalRadius) {
371 		node = node->children[0];
372 		goto mark0;
373 	}
374 	if (dist < -r_decalRadius) {
375 		node = node->children[1];
376 		goto mark0;
377 	}
378 
379 	R_Q2BSP_FragmentNode (node->children[0]);
380 	R_Q2BSP_FragmentNode (node->children[1]);
381 }
382 
383 /*
384 ==============================================================================
385 
386 	QUAKE III FRAGMENT CLIPPING
387 
388 ==============================================================================
389 */
390 
391 /*
392 =================
393 R_Q3BSP_WindingClipFragment
394 
395 This function operates on windings (convex polygons without
396 any points inside) like triangles, quads, etc. The output is
397 a convex fragment (polygon, trifan) which the result of clipping
398 the input winding by six fragment planes.
399 =================
400 */
R_Q3BSP_WindingClipFragment(vec3_t * wVerts,int numVerts,refFragment_t * fr)401 static void R_Q3BSP_WindingClipFragment (vec3_t *wVerts, int numVerts, refFragment_t *fr)
402 {
403 	int			i, j;
404 	int			stage, newc, numv;
405 	cBspPlane_t	*plane;
406 	qBool		front;
407 	float		*v, *nextv, d;
408 	float		dists[MAX_DECAL_VERTS+1];
409 	int			sides[MAX_DECAL_VERTS+1];
410 	vec3_t		*verts, *newverts, newv[2][MAX_DECAL_VERTS];
411 
412 	numv = numVerts;
413 	verts = wVerts;
414 
415 	for (stage=0, plane=r_fragmentPlanes ; stage<6 ; stage++, plane++) {
416 		for (i=0, v=verts[0], front=qFalse ; i<numv ; i++, v+=3) {
417 			d = PlaneDiff (v, plane);
418 
419 			if (d > LARGE_EPSILON) {
420 				front = qTrue;
421 				sides[i] = SIDE_FRONT;
422 			}
423 			else if (d < -LARGE_EPSILON) {
424 				sides[i] = SIDE_BACK;
425 			}
426 			else {
427 				front = qTrue;
428 				sides[i] = SIDE_ON;
429 			}
430 			dists[i] = d;
431 		}
432 
433 		if (!front)
434 			return;
435 
436 		// Clip it
437 		sides[i] = sides[0];
438 		dists[i] = dists[0];
439 
440 		newc = 0;
441 		newverts = newv[stage & 1];
442 
443 		for (i=0, v=verts[0] ; i<numv ; i++, v+=3) {
444 			switch (sides[i]) {
445 			case SIDE_FRONT:
446 				if (newc == MAX_DECAL_VERTS)
447 					return;
448 				Vec3Copy (v, newverts[newc]);
449 				newc++;
450 				break;
451 
452 			case SIDE_BACK:
453 				break;
454 
455 			case SIDE_ON:
456 				if (newc == MAX_DECAL_VERTS)
457 					return;
458 				Vec3Copy (v, newverts[newc]);
459 				newc++;
460 				break;
461 			}
462 
463 			if (sides[i] == SIDE_ON
464 			|| sides[i+1] == SIDE_ON
465 			|| sides[i+1] == sides[i])
466 				continue;
467 			if (newc == MAX_DECAL_VERTS)
468 				return;
469 
470 			d = dists[i] / (dists[i] - dists[i+1]);
471 			nextv = (i == numv - 1) ? verts[0] : v + 3;
472 			for (j=0 ; j<3 ; j++)
473 				newverts[newc][j] = v[j] + d * (nextv[j] - v[j]);
474 
475 			newc++;
476 		}
477 
478 		if (newc <= 2)
479 			return;
480 
481 		// Continue with new verts
482 		numv = newc;
483 		verts = newverts;
484 	}
485 
486 	// Fully clipped
487 	if (r_numFragmentVerts + numv > MAX_DECAL_VERTS)
488 		return;
489 
490 	fr->numVerts = numv;
491 	fr->firstVert = r_numFragmentVerts;
492 
493 	for (i=0, v=verts[0] ; i<numv ; i++, v+=3) {
494 		Vec3Copy (fr->normal, r_fragmentNormals[r_numFragmentVerts + i]);
495 		Vec3Copy (v, r_fragmentVerts[r_numFragmentVerts + i]);
496 	}
497 	r_numFragmentVerts += numv;
498 }
499 
500 
501 /*
502 =================
503 R_Q3BSP_PlanarSurfClipFragment
504 
505 NOTE: one might want to combine this function with
506 R_Q3BSP_WindingClipFragment for special cases like trifans (q1 and
507 q2 polys) or tristrips for ultra-fast clipping, providing there's
508 enough stack space (depending on MAX_DECAL_VERTS value).
509 =================
510 */
R_Q3BSP_PlanarSurfClipFragment(mBspSurface_t * surf,mBspNode_t * node)511 static void R_Q3BSP_PlanarSurfClipFragment (mBspSurface_t *surf, mBspNode_t *node)
512 {
513 	int				i;
514 	mesh_t			*mesh;
515 	index_t			*index;
516 	vec3_t			*normals, *verts, tri[3];
517 	refFragment_t	*fr;
518 
519 	if (DotProduct (r_decalNormal, surf->q3_origin) < 0.5)
520 		return;		// Greater than 60 degrees
521 
522 	mesh = surf->mesh;
523 
524 	// Clip each triangle individually
525 	index = mesh->indexArray;
526 	normals = mesh->normalsArray;
527 	verts = mesh->vertexArray;
528 	for (i=0 ; i<mesh->numIndexes ; i+=3, index+=3) {
529 		fr = &r_clippedFragments[r_numClippedFragments];
530 		fr->numVerts = 0;
531 		fr->surf = surf;
532 
533 		Vec3Copy (normals[index[0]], fr->normal);
534 
535 		Vec3Copy (verts[index[0]], tri[0]);
536 		Vec3Copy (verts[index[1]], tri[1]);
537 		Vec3Copy (verts[index[2]], tri[2]);
538 
539 		R_Q3BSP_WindingClipFragment (tri, 3, fr);
540 		if (fr->numVerts && (r_numFragmentVerts == MAX_DECAL_VERTS || ++r_numClippedFragments == MAX_DECAL_FRAGMENTS))
541 			return;
542 	}
543 }
544 
545 
546 /*
547 =================
548 R_Q3BSP_PatchSurfClipFragment
549 =================
550 */
R_Q3BSP_PatchSurfClipFragment(mBspSurface_t * surf,mBspNode_t * node)551 static void R_Q3BSP_PatchSurfClipFragment (mBspSurface_t *surf, mBspNode_t *node)
552 {
553 	int				i;
554 	mesh_t			*mesh;
555 	index_t			*index;
556 	vec3_t			*normals, *verts, tri[3];
557 	vec3_t			dir1, dir2, snorm;
558 	refFragment_t	*fr;
559 
560 	mesh = surf->mesh;
561 
562 	// Clip each triangle individually
563 	index = mesh->indexArray;
564 	normals = mesh->normalsArray;
565 	verts = mesh->vertexArray;
566 	for (i=0 ; i<mesh->numIndexes ; i+=3, index+=3) {
567 		fr = &r_clippedFragments[r_numClippedFragments];
568 		fr->numVerts = 0;
569 		fr->surf = surf;
570 
571 		Vec3Copy (normals[index[0]], fr->normal);
572 
573 		Vec3Copy (verts[index[0]], tri[0]);
574 		Vec3Copy (verts[index[1]], tri[1]);
575 		Vec3Copy (verts[index[2]], tri[2]);
576 
577 		// Calculate two mostly perpendicular edge directions
578 		Vec3Subtract (tri[0], tri[1], dir1);
579 		Vec3Subtract (tri[2], tri[1], dir2);
580 
581 		// We have two edge directions, we can calculate a third vector from
582 		// them, which is the direction of the triangle normal
583 		CrossProduct (dir1, dir2, snorm);
584 
585 		// We multiply 0.5 by length of snorm to avoid normalizing
586 		if (DotProduct(r_decalNormal, snorm) < 0.5f * Vec3Length(snorm))
587 			continue;	// Greater than 60 degrees
588 
589 		R_Q3BSP_WindingClipFragment (tri, 3, fr);
590 		if (fr->numVerts && (r_numFragmentVerts == MAX_DECAL_VERTS || ++r_numClippedFragments == MAX_DECAL_FRAGMENTS))
591 			return;
592 	}
593 }
594 
595 
596 /*
597 =================
598 R_Q3BSP_FragmentNode
599 =================
600 */
R_Q3BSP_FragmentNode(void)601 static void R_Q3BSP_FragmentNode (void)
602 {
603 	int				stackdepth = 0;
604 	float			dist;
605 	mBspNode_t		*node, *localStack[2048];
606 	mBspLeaf_t		*leaf;
607 	mBspSurface_t	*surf, **mark;
608 
609 	node = ri.scn.worldModel->bspModel.nodes;
610 	for (stackdepth=0 ; ; ) {
611 		if (node->c.plane == NULL) {
612 			leaf = (mBspLeaf_t *)node;
613 			if (!leaf->q3_firstFragmentSurface)
614 				goto nextNodeOnStack;
615 
616 			mark = leaf->q3_firstFragmentSurface;
617 			do {
618 				if (r_numFragmentVerts == MAX_DECAL_VERTS || r_numClippedFragments == MAX_DECAL_FRAGMENTS)
619 					return;		// Already reached the limit
620 
621 				surf = *mark++;
622 				if (surf->fragmentFrame == r_fragmentFrame)
623 					continue;
624 				surf->fragmentFrame = r_fragmentFrame;
625 
626 				if (surf->q3_faceType == FACETYPE_PLANAR)
627 					R_Q3BSP_PlanarSurfClipFragment (surf, node);
628 				else
629 					R_Q3BSP_PatchSurfClipFragment (surf, node);
630 			} while (*mark);
631 
632 			if (r_numFragmentVerts == MAX_DECAL_VERTS || r_numClippedFragments == MAX_DECAL_FRAGMENTS)
633 				return;		// Already reached the limit
634 
635 nextNodeOnStack:
636 			if (!stackdepth)
637 				break;
638 			node = localStack[--stackdepth];
639 			continue;
640 		}
641 
642 		dist = PlaneDiff (r_decalOrigin, node->c.plane);
643 		if (dist > r_decalRadius) {
644 			node = node->children[0];
645 			continue;
646 		}
647 
648 		if (dist >= -r_decalRadius && (stackdepth < sizeof (localStack) / sizeof (mBspNode_t *)))
649 			localStack[stackdepth++] = node->children[0];
650 		node = node->children[1];
651 	}
652 }
653 
654 // ===========================================================================
655 
656 /*
657 =================
658 R_GetClippedFragments
659 =================
660 */
R_GetClippedFragments(vec3_t origin,float radius,vec3_t axis[3])661 static uint32 R_GetClippedFragments (vec3_t origin, float radius, vec3_t axis[3])
662 {
663 	int		i;
664 	float	d;
665 
666 	if (ri.def.rdFlags & RDF_NOWORLDMODEL)
667 		return 0;
668 	if (!ri.scn.worldModel->bspModel.nodes)
669 		return 0;
670 
671 	r_fragmentFrame++;
672 
673 	// Store data
674 	Vec3Copy (origin, r_decalOrigin);
675 	Vec3Copy (axis[0], r_decalNormal);
676 	r_decalRadius = radius;
677 
678 	// Initialize fragments
679 	r_numFragmentVerts = 0;
680 	r_numClippedFragments = 0;
681 
682 	// Calculate clipping planes
683 	for (i=0 ; i<3; i++) {
684 		d = DotProduct (origin, axis[i]);
685 
686 		Vec3Copy (axis[i], r_fragmentPlanes[i*2].normal);
687 		r_fragmentPlanes[i*2].dist = d - radius;
688 		r_fragmentPlanes[i*2].type = PlaneTypeForNormal (r_fragmentPlanes[i*2].normal);
689 
690 		Vec3Negate (axis[i], r_fragmentPlanes[i*2+1].normal);
691 		r_fragmentPlanes[i*2+1].dist = -d - radius;
692 		r_fragmentPlanes[i*2+1].type = PlaneTypeForNormal (r_fragmentPlanes[i*2+1].normal);
693 	}
694 
695 	if (ri.scn.worldModel->type == MODEL_Q3BSP)
696 		R_Q3BSP_FragmentNode ();
697 	else
698 		R_Q2BSP_FragmentNode (ri.scn.worldModel->bspModel.nodes);
699 
700 	return r_numClippedFragments;
701 }
702 
703 /*
704 ==============================================================================
705 
706 	EXPORT FUNCTIONS
707 
708 ==============================================================================
709 */
710 
711 /*
712 ===============
713 R_CreateDecal
714 ===============
715 */
R_CreateDecal(refDecal_t * d,vec3_t origin,vec3_t direction,float angle,float size)716 qBool R_CreateDecal (refDecal_t *d, vec3_t origin, vec3_t direction, float angle, float size)
717 {
718 	vec3_t			*clipNormals, *clipVerts;
719 	refFragment_t	*fr, *clipFragments;
720 	vec3_t			axis[3];
721 	uint32			numFragments, i, k;
722 	byte			*buffer;
723 	uint32			totalIndexes;
724 	index_t			*outIndexes;
725 	uint32			totalVerts;
726 	float			*outNormals;
727 	float			*outVerts;
728 	float			*outCoords;
729 	uint32			totalSurfaces;
730 	mBspSurface_t	**outSurfs;
731 	vec3_t			mins, maxs;
732 	vec3_t			temp;
733 	int				j;
734 
735 	if (!d)
736 		return qFalse;
737 
738 	// See if there's room and it's valid
739 	if (!size || Vec3Compare (direction, vec3Origin)) {
740 		Com_DevPrintf (PRNT_WARNING, "WARNING: attempted to create a decal with an invalid %s\n", !size ? "size" : "direction");
741 		return qFalse;
742 	}
743 
744 	// Negativity check
745 	if (size < 0)
746 		size *= -1;
747 
748 	// Calculate orientation matrix
749 	VectorNormalizef (direction, axis[0]);
750 	PerpendicularVector (axis[0], axis[1]);
751 	RotatePointAroundVector (axis[2], axis[0], axis[1], angle);
752 	CrossProduct (axis[0], axis[2], axis[1]);
753 
754 	// Clip it
755 	clipNormals = r_fragmentNormals;
756 	clipVerts = r_fragmentVerts;
757 	clipFragments = r_clippedFragments;
758 	numFragments = R_GetClippedFragments (origin, size, axis);
759 	if (!numFragments)
760 		return qFalse;	// No valid fragments
761 
762 	// Find the total allocation size
763 	totalIndexes = 0;
764 	totalVerts = 0;
765 	totalSurfaces = 0;
766 	for (i=0, fr=clipFragments ; i<numFragments ; fr++, i++) {
767 		totalIndexes += (fr->numVerts - 2) * 3;
768 		totalVerts += fr->numVerts;
769 
770 		// NULL out duplicate surfaces to save redundant cull attempts
771 		if (fr->surf) {
772 			for (k=i+1 ; k<numFragments ; k++) {
773 				if (clipFragments[k].surf == fr->surf)
774 					clipFragments[k].surf = NULL;
775 			}
776 
777 			totalSurfaces++;
778 		}
779 	}
780 	assert (totalIndexes && totalVerts);
781 
782 	// Store values
783 	Vec3Copy (origin, d->origin);
784 	d->numIndexes = totalIndexes;
785 	d->poly.numVerts = totalVerts;
786 	d->numSurfaces = totalSurfaces;
787 
788 	// Allocate space
789 	buffer = Mem_PoolAlloc ((d->poly.numVerts * sizeof (vec3_t) * 2)
790 							+ (d->numIndexes * sizeof (index_t))
791 							+ (d->poly.numVerts * sizeof (vec2_t))
792 							+ (d->poly.numVerts * sizeof (bvec4_t))
793 							+ (d->numSurfaces * sizeof (struct mBspSurface_s *)), ri.decalSysPool, 0);
794 	outVerts = (float *)buffer;
795 	d->poly.vertices = (vec3_t *)buffer;
796 
797 	buffer += d->poly.numVerts * sizeof (vec3_t);
798 	outNormals = (float *)buffer;
799 	d->normals = (vec3_t *)buffer;
800 
801 	buffer += d->poly.numVerts * sizeof (vec3_t);
802 	outIndexes = d->indexes = (int *)buffer;
803 
804 	buffer += d->numIndexes * sizeof (index_t);
805 	outCoords = (float *)buffer;
806 	d->poly.texCoords = (vec2_t *)buffer;
807 
808 	buffer += d->poly.numVerts * sizeof (vec2_t);
809 	d->poly.colors = (bvec4_t *)buffer;
810 
811 	buffer += d->poly.numVerts * sizeof (bvec4_t);
812 	d->surfaces = outSurfs = (struct mBspSurface_s **)buffer;
813 
814 	// Store vertex data
815 	ClearBounds (mins, maxs);
816 	totalVerts = 0;
817 	totalIndexes = 0;
818 
819 	size = 0.5f / size;
820 	Vec3Scale (axis[1], size, axis[1]);
821 	Vec3Scale (axis[2], size, axis[2]);
822 	for (i=0, fr=clipFragments ; i<numFragments ; fr++, i++) {
823 		if (fr->surf)
824 			*outSurfs++ = fr->surf;
825 
826 		// Indexes
827 		outIndexes = d->indexes + totalIndexes;
828 		totalIndexes += (fr->numVerts - 2) * 3;
829 		for (j=2 ; j<fr->numVerts ; j++) {
830 			outIndexes[0] = totalVerts;
831 			outIndexes[1] = totalVerts + j - 1;
832 			outIndexes[2] = totalVerts + j;
833 
834 			outIndexes += 3;
835 		}
836 
837 		for (j=0 ; j<fr->numVerts ; j++) {
838 			// Vertices
839 			outVerts[0] = clipVerts[fr->firstVert+j][0];
840 			outVerts[1] = clipVerts[fr->firstVert+j][1];
841 			outVerts[2] = clipVerts[fr->firstVert+j][2];
842 
843 			// Normals
844 			outNormals[0] = clipNormals[fr->firstVert+j][0];
845 			outNormals[1] = clipNormals[fr->firstVert+j][1];
846 			outNormals[2] = clipNormals[fr->firstVert+j][2];
847 
848 			// Bounds
849 			AddPointToBounds (outVerts, mins, maxs);
850 
851 			// Coords
852 			Vec3Subtract (outVerts, origin, temp);
853 			outCoords[0] = DotProduct (temp, axis[1]) + 0.5f;
854 			outCoords[1] = DotProduct (temp, axis[2]) + 0.5f;
855 
856 			outVerts += 3;
857 			outNormals += 3;
858 			outCoords += 2;
859 		}
860 
861 		totalVerts += fr->numVerts;
862 	}
863 
864 	// Calculate radius
865 	d->radius = RadiusFromBounds (mins, maxs);
866 	assert (d->radius);
867 	return qTrue;
868 }
869 
870 
871 /*
872 ===============
873 R_FreeDecal
874 
875 Releases decal memory for index and vertex data.
876 ===============
877 */
R_FreeDecal(refDecal_t * d)878 qBool R_FreeDecal (refDecal_t *d)
879 {
880 	if (!d || !d->poly.vertices)
881 		return qFalse;
882 
883 	Mem_Free (d->poly.vertices);
884 	d->poly.vertices = NULL;
885 	return qTrue;
886 }
887