brush.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. /*
  2. ===========================================================================
  3. Copyright (C) 1999-2005 Id Software, Inc.
  4. This file is part of Quake III Arena source code.
  5. Quake III Arena source code is free software; you can redistribute it
  6. and/or modify it under the terms of the GNU General Public License as
  7. published by the Free Software Foundation; either version 2 of the License,
  8. or (at your option) any later version.
  9. Quake III Arena source code is distributed in the hope that it will be
  10. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Foobar; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. ===========================================================================
  17. */
  18. #include "qbsp.h"
  19. int c_active_brushes;
  20. int c_nodes;
  21. // if a brush just barely pokes onto the other side,
  22. // let it slide by without chopping
  23. #define PLANESIDE_EPSILON 0.001
  24. //0.1
  25. /*
  26. ================
  27. CountBrushList
  28. ================
  29. */
  30. int CountBrushList (bspbrush_t *brushes)
  31. {
  32. int c;
  33. c = 0;
  34. for ( ; brushes ; brushes = brushes->next)
  35. c++;
  36. return c;
  37. }
  38. /*
  39. ================
  40. AllocBrush
  41. ================
  42. */
  43. bspbrush_t *AllocBrush (int numsides)
  44. {
  45. bspbrush_t *bb;
  46. int c;
  47. c = (int)&(((bspbrush_t *)0)->sides[numsides]);
  48. bb = malloc(c);
  49. memset (bb, 0, c);
  50. if (numthreads == 1)
  51. c_active_brushes++;
  52. return bb;
  53. }
  54. /*
  55. ================
  56. FreeBrush
  57. ================
  58. */
  59. void FreeBrush (bspbrush_t *brushes)
  60. {
  61. int i;
  62. for (i=0 ; i<brushes->numsides ; i++)
  63. if (brushes->sides[i].winding)
  64. FreeWinding(brushes->sides[i].winding);
  65. free (brushes);
  66. if (numthreads == 1)
  67. c_active_brushes--;
  68. }
  69. /*
  70. ================
  71. FreeBrushList
  72. ================
  73. */
  74. void FreeBrushList (bspbrush_t *brushes)
  75. {
  76. bspbrush_t *next;
  77. for ( ; brushes ; brushes = next)
  78. {
  79. next = brushes->next;
  80. FreeBrush (brushes);
  81. }
  82. }
  83. /*
  84. ==================
  85. CopyBrush
  86. Duplicates the brush, the sides, and the windings
  87. ==================
  88. */
  89. bspbrush_t *CopyBrush (bspbrush_t *brush)
  90. {
  91. bspbrush_t *newbrush;
  92. int size;
  93. int i;
  94. size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);
  95. newbrush = AllocBrush (brush->numsides);
  96. memcpy (newbrush, brush, size);
  97. for (i=0 ; i<brush->numsides ; i++)
  98. {
  99. if (brush->sides[i].winding)
  100. newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
  101. }
  102. return newbrush;
  103. }
  104. /*
  105. ================
  106. DrawBrushList
  107. ================
  108. */
  109. void DrawBrushList (bspbrush_t *brush)
  110. {
  111. int i;
  112. side_t *s;
  113. GLS_BeginScene ();
  114. for ( ; brush ; brush=brush->next)
  115. {
  116. for (i=0 ; i<brush->numsides ; i++)
  117. {
  118. s = &brush->sides[i];
  119. if (!s->winding)
  120. continue;
  121. GLS_Winding (s->winding, 0);
  122. }
  123. }
  124. GLS_EndScene ();
  125. }
  126. /*
  127. ================
  128. WriteBrushList
  129. ================
  130. */
  131. void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
  132. {
  133. int i;
  134. side_t *s;
  135. FILE *f;
  136. qprintf ("writing %s\n", name);
  137. f = SafeOpenWrite (name);
  138. for ( ; brush ; brush=brush->next)
  139. {
  140. for (i=0 ; i<brush->numsides ; i++)
  141. {
  142. s = &brush->sides[i];
  143. if (!s->winding)
  144. continue;
  145. if (onlyvis && !s->visible)
  146. continue;
  147. OutputWinding (brush->sides[i].winding, f);
  148. }
  149. }
  150. fclose (f);
  151. }
  152. /*
  153. =============
  154. PrintBrush
  155. =============
  156. */
  157. void PrintBrush (bspbrush_t *brush)
  158. {
  159. int i;
  160. _printf ("brush: %p\n", brush);
  161. for (i=0;i<brush->numsides ; i++)
  162. {
  163. pw(brush->sides[i].winding);
  164. _printf ("\n");
  165. }
  166. }
  167. /*
  168. ==================
  169. BoundBrush
  170. Sets the mins/maxs based on the windings
  171. returns false if the brush doesn't enclose a valid volume
  172. ==================
  173. */
  174. qboolean BoundBrush (bspbrush_t *brush)
  175. {
  176. int i, j;
  177. winding_t *w;
  178. ClearBounds (brush->mins, brush->maxs);
  179. for (i=0 ; i<brush->numsides ; i++)
  180. {
  181. w = brush->sides[i].winding;
  182. if (!w)
  183. continue;
  184. for (j=0 ; j<w->numpoints ; j++)
  185. AddPointToBounds (w->p[j], brush->mins, brush->maxs);
  186. }
  187. for (i=0 ; i<3 ; i++) {
  188. if (brush->mins[i] < MIN_WORLD_COORD || brush->maxs[i] > MAX_WORLD_COORD
  189. || brush->mins[i] >= brush->maxs[i] ) {
  190. return qfalse;
  191. }
  192. }
  193. return qtrue;
  194. }
  195. /*
  196. ==================
  197. CreateBrushWindings
  198. makes basewindigs for sides and mins / maxs for the brush
  199. returns false if the brush doesn't enclose a valid volume
  200. ==================
  201. */
  202. qboolean CreateBrushWindings (bspbrush_t *brush)
  203. {
  204. int i, j;
  205. winding_t *w;
  206. side_t *side;
  207. plane_t *plane;
  208. for ( i = 0; i < brush->numsides; i++ )
  209. {
  210. side = &brush->sides[i];
  211. // don't create a winding for a bevel
  212. if ( side->bevel ) {
  213. continue;
  214. }
  215. plane = &mapplanes[side->planenum];
  216. w = BaseWindingForPlane (plane->normal, plane->dist);
  217. for ( j = 0; j < brush->numsides && w; j++ )
  218. {
  219. if (i == j)
  220. continue;
  221. if ( brush->sides[j].planenum == ( brush->sides[i].planenum ^ 1 ) )
  222. continue; // back side clipaway
  223. if (brush->sides[j].bevel)
  224. continue;
  225. if (brush->sides[j].backSide)
  226. continue;
  227. plane = &mapplanes[brush->sides[j].planenum^1];
  228. ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
  229. }
  230. // free any existing winding
  231. if ( side->winding ) {
  232. FreeWinding( side->winding );
  233. }
  234. side->winding = w;
  235. }
  236. return BoundBrush (brush);
  237. }
  238. /*
  239. ==================
  240. BrushFromBounds
  241. Creates a new axial brush
  242. ==================
  243. */
  244. bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs)
  245. {
  246. bspbrush_t *b;
  247. int i;
  248. vec3_t normal;
  249. vec_t dist;
  250. b = AllocBrush (6);
  251. b->numsides = 6;
  252. for (i=0 ; i<3 ; i++)
  253. {
  254. VectorClear (normal);
  255. normal[i] = 1;
  256. dist = maxs[i];
  257. b->sides[i].planenum = FindFloatPlane (normal, dist);
  258. normal[i] = -1;
  259. dist = -mins[i];
  260. b->sides[3+i].planenum = FindFloatPlane (normal, dist);
  261. }
  262. CreateBrushWindings (b);
  263. return b;
  264. }
  265. /*
  266. ==================
  267. BrushVolume
  268. ==================
  269. */
  270. vec_t BrushVolume (bspbrush_t *brush)
  271. {
  272. int i;
  273. winding_t *w;
  274. vec3_t corner;
  275. vec_t d, area, volume;
  276. plane_t *plane;
  277. if (!brush)
  278. return 0;
  279. // grab the first valid point as the corner
  280. w = NULL;
  281. for (i=0 ; i<brush->numsides ; i++)
  282. {
  283. w = brush->sides[i].winding;
  284. if (w)
  285. break;
  286. }
  287. if (!w)
  288. return 0;
  289. VectorCopy (w->p[0], corner);
  290. // make tetrahedrons to all other faces
  291. volume = 0;
  292. for ( ; i<brush->numsides ; i++)
  293. {
  294. w = brush->sides[i].winding;
  295. if (!w)
  296. continue;
  297. plane = &mapplanes[brush->sides[i].planenum];
  298. d = -(DotProduct (corner, plane->normal) - plane->dist);
  299. area = WindingArea (w);
  300. volume += d*area;
  301. }
  302. volume /= 3;
  303. return volume;
  304. }
  305. /*
  306. ==================
  307. WriteBspBrushMap
  308. ==================
  309. */
  310. void WriteBspBrushMap (char *name, bspbrush_t *list)
  311. {
  312. FILE *f;
  313. side_t *s;
  314. int i;
  315. winding_t *w;
  316. _printf ("writing %s\n", name);
  317. f = fopen (name, "wb");
  318. if (!f)
  319. Error ("Can't write %s\b", name);
  320. fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
  321. for ( ; list ; list=list->next )
  322. {
  323. fprintf (f, "{\n");
  324. for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
  325. {
  326. w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
  327. fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
  328. fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
  329. fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
  330. fprintf (f, "notexture 0 0 0 1 1\n" );
  331. FreeWinding (w);
  332. }
  333. fprintf (f, "}\n");
  334. }
  335. fprintf (f, "}\n");
  336. fclose (f);
  337. }
  338. //=====================================================================================
  339. /*
  340. ====================
  341. FilterBrushIntoTree_r
  342. ====================
  343. */
  344. int FilterBrushIntoTree_r( bspbrush_t *b, node_t *node ) {
  345. bspbrush_t *front, *back;
  346. int c;
  347. if ( !b ) {
  348. return 0;
  349. }
  350. // add it to the leaf list
  351. if ( node->planenum == PLANENUM_LEAF ) {
  352. b->next = node->brushlist;
  353. node->brushlist = b;
  354. // classify the leaf by the structural brush
  355. if ( !b->detail ) {
  356. if ( b->opaque ) {
  357. node->opaque = qtrue;
  358. node->areaportal = qfalse;
  359. } else if ( b->contents & CONTENTS_AREAPORTAL ) {
  360. if ( !node->opaque ) {
  361. node->areaportal = qtrue;
  362. }
  363. }
  364. }
  365. return 1;
  366. }
  367. // split it by the node plane
  368. SplitBrush ( b, node->planenum, &front, &back );
  369. FreeBrush( b );
  370. c = 0;
  371. c += FilterBrushIntoTree_r( front, node->children[0] );
  372. c += FilterBrushIntoTree_r( back, node->children[1] );
  373. return c;
  374. }
  375. /*
  376. =====================
  377. FilterDetailBrushesIntoTree
  378. Fragment all the detail brushes into the structural leafs
  379. =====================
  380. */
  381. void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree ) {
  382. bspbrush_t *b, *newb;
  383. int r;
  384. int c_unique, c_clusters;
  385. int i;
  386. qprintf( "----- FilterDetailBrushesIntoTree -----\n");
  387. c_unique = 0;
  388. c_clusters = 0;
  389. for ( b = e->brushes ; b ; b = b->next ) {
  390. if ( !b->detail ) {
  391. continue;
  392. }
  393. c_unique++;
  394. newb = CopyBrush( b );
  395. r = FilterBrushIntoTree_r( newb, tree->headnode );
  396. c_clusters += r;
  397. // mark all sides as visible so drawsurfs are created
  398. if ( r ) {
  399. for ( i = 0 ; i < b->numsides ; i++ ) {
  400. if ( b->sides[i].winding ) {
  401. b->sides[i].visible = qtrue;
  402. }
  403. }
  404. }
  405. }
  406. qprintf( "%5i detail brushes\n", c_unique );
  407. qprintf( "%5i cluster references\n", c_clusters );
  408. }
  409. /*
  410. =====================
  411. FilterStructuralBrushesIntoTree
  412. Mark the leafs as opaque and areaportals
  413. =====================
  414. */
  415. void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree ) {
  416. bspbrush_t *b, *newb;
  417. int r;
  418. int c_unique, c_clusters;
  419. int i;
  420. qprintf( "----- FilterStructuralBrushesIntoTree -----\n");
  421. c_unique = 0;
  422. c_clusters = 0;
  423. for ( b = e->brushes ; b ; b = b->next ) {
  424. if ( b->detail ) {
  425. continue;
  426. }
  427. c_unique++;
  428. newb = CopyBrush( b );
  429. r = FilterBrushIntoTree_r( newb, tree->headnode );
  430. c_clusters += r;
  431. // mark all sides as visible so drawsurfs are created
  432. if ( r ) {
  433. for ( i = 0 ; i < b->numsides ; i++ ) {
  434. if ( b->sides[i].winding ) {
  435. b->sides[i].visible = qtrue;
  436. }
  437. }
  438. }
  439. }
  440. qprintf( "%5i structural brushes\n", c_unique );
  441. qprintf( "%5i cluster references\n", c_clusters );
  442. }
  443. /*
  444. ================
  445. AllocTree
  446. ================
  447. */
  448. tree_t *AllocTree (void)
  449. {
  450. tree_t *tree;
  451. tree = malloc(sizeof(*tree));
  452. memset (tree, 0, sizeof(*tree));
  453. ClearBounds (tree->mins, tree->maxs);
  454. return tree;
  455. }
  456. /*
  457. ================
  458. AllocNode
  459. ================
  460. */
  461. node_t *AllocNode (void)
  462. {
  463. node_t *node;
  464. node = malloc(sizeof(*node));
  465. memset (node, 0, sizeof(*node));
  466. return node;
  467. }
  468. /*
  469. ================
  470. WindingIsTiny
  471. Returns true if the winding would be crunched out of
  472. existance by the vertex snapping.
  473. ================
  474. */
  475. #define EDGE_LENGTH 0.2
  476. qboolean WindingIsTiny (winding_t *w)
  477. {
  478. /*
  479. if (WindingArea (w) < 1)
  480. return qtrue;
  481. return qfalse;
  482. */
  483. int i, j;
  484. vec_t len;
  485. vec3_t delta;
  486. int edges;
  487. edges = 0;
  488. for (i=0 ; i<w->numpoints ; i++)
  489. {
  490. j = i == w->numpoints - 1 ? 0 : i+1;
  491. VectorSubtract (w->p[j], w->p[i], delta);
  492. len = VectorLength (delta);
  493. if (len > EDGE_LENGTH)
  494. {
  495. if (++edges == 3)
  496. return qfalse;
  497. }
  498. }
  499. return qtrue;
  500. }
  501. /*
  502. ================
  503. WindingIsHuge
  504. Returns true if the winding still has one of the points
  505. from basewinding for plane
  506. ================
  507. */
  508. qboolean WindingIsHuge (winding_t *w)
  509. {
  510. int i, j;
  511. for (i=0 ; i<w->numpoints ; i++)
  512. {
  513. for (j=0 ; j<3 ; j++)
  514. if (w->p[i][j] <= MIN_WORLD_COORD || w->p[i][j] >= MAX_WORLD_COORD)
  515. return qtrue;
  516. }
  517. return qfalse;
  518. }
  519. //============================================================
  520. /*
  521. ==================
  522. BrushMostlyOnSide
  523. ==================
  524. */
  525. int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
  526. {
  527. int i, j;
  528. winding_t *w;
  529. vec_t d, max;
  530. int side;
  531. max = 0;
  532. side = PSIDE_FRONT;
  533. for (i=0 ; i<brush->numsides ; i++)
  534. {
  535. w = brush->sides[i].winding;
  536. if (!w)
  537. continue;
  538. for (j=0 ; j<w->numpoints ; j++)
  539. {
  540. d = DotProduct (w->p[j], plane->normal) - plane->dist;
  541. if (d > max)
  542. {
  543. max = d;
  544. side = PSIDE_FRONT;
  545. }
  546. if (-d > max)
  547. {
  548. max = -d;
  549. side = PSIDE_BACK;
  550. }
  551. }
  552. }
  553. return side;
  554. }
  555. /*
  556. ================
  557. SplitBrush
  558. Generates two new brushes, leaving the original
  559. unchanged
  560. ================
  561. */
  562. void SplitBrush (bspbrush_t *brush, int planenum,
  563. bspbrush_t **front, bspbrush_t **back)
  564. {
  565. bspbrush_t *b[2];
  566. int i, j;
  567. winding_t *w, *cw[2], *midwinding;
  568. plane_t *plane, *plane2;
  569. side_t *s, *cs;
  570. float d, d_front, d_back;
  571. *front = *back = NULL;
  572. plane = &mapplanes[planenum];
  573. // check all points
  574. d_front = d_back = 0;
  575. for (i=0 ; i<brush->numsides ; i++)
  576. {
  577. w = brush->sides[i].winding;
  578. if (!w)
  579. continue;
  580. for (j=0 ; j<w->numpoints ; j++)
  581. {
  582. d = DotProduct (w->p[j], plane->normal) - plane->dist;
  583. if (d > 0 && d > d_front)
  584. d_front = d;
  585. if (d < 0 && d < d_back)
  586. d_back = d;
  587. }
  588. }
  589. if (d_front < 0.1) // PLANESIDE_EPSILON)
  590. { // only on back
  591. *back = CopyBrush (brush);
  592. return;
  593. }
  594. if (d_back > -0.1) // PLANESIDE_EPSILON)
  595. { // only on front
  596. *front = CopyBrush (brush);
  597. return;
  598. }
  599. // create a new winding from the split plane
  600. w = BaseWindingForPlane (plane->normal, plane->dist);
  601. for (i=0 ; i<brush->numsides && w ; i++)
  602. {
  603. if ( brush->sides[i].backSide ) {
  604. continue; // fake back-sided polygons never split
  605. }
  606. plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
  607. ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
  608. }
  609. if (!w || WindingIsTiny (w) )
  610. { // the brush isn't really split
  611. int side;
  612. side = BrushMostlyOnSide (brush, plane);
  613. if (side == PSIDE_FRONT)
  614. *front = CopyBrush (brush);
  615. if (side == PSIDE_BACK)
  616. *back = CopyBrush (brush);
  617. return;
  618. }
  619. if (WindingIsHuge (w))
  620. {
  621. qprintf ("WARNING: huge winding\n");
  622. }
  623. midwinding = w;
  624. // split it for real
  625. for (i=0 ; i<2 ; i++)
  626. {
  627. b[i] = AllocBrush (brush->numsides+1);
  628. memcpy( b[i], brush, sizeof( bspbrush_t ) - sizeof( brush->sides ) );
  629. b[i]->numsides = 0;
  630. b[i]->next = NULL;
  631. b[i]->original = brush->original;
  632. }
  633. // split all the current windings
  634. for (i=0 ; i<brush->numsides ; i++)
  635. {
  636. s = &brush->sides[i];
  637. w = s->winding;
  638. if (!w)
  639. continue;
  640. ClipWindingEpsilon (w, plane->normal, plane->dist,
  641. 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
  642. for (j=0 ; j<2 ; j++)
  643. {
  644. if (!cw[j])
  645. continue;
  646. /*
  647. if (WindingIsTiny (cw[j]))
  648. {
  649. FreeWinding (cw[j]);
  650. continue;
  651. }
  652. */
  653. cs = &b[j]->sides[b[j]->numsides];
  654. b[j]->numsides++;
  655. *cs = *s;
  656. cs->winding = cw[j];
  657. }
  658. }
  659. // see if we have valid polygons on both sides
  660. for (i=0 ; i<2 ; i++)
  661. {
  662. BoundBrush (b[i]);
  663. for (j=0 ; j<3 ; j++)
  664. {
  665. if (b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD)
  666. {
  667. qprintf ("bogus brush after clip\n");
  668. break;
  669. }
  670. }
  671. if (b[i]->numsides < 3 || j < 3)
  672. {
  673. FreeBrush (b[i]);
  674. b[i] = NULL;
  675. }
  676. }
  677. if ( !(b[0] && b[1]) )
  678. {
  679. if (!b[0] && !b[1])
  680. qprintf ("split removed brush\n");
  681. else
  682. qprintf ("split not on both sides\n");
  683. if (b[0])
  684. {
  685. FreeBrush (b[0]);
  686. *front = CopyBrush (brush);
  687. }
  688. if (b[1])
  689. {
  690. FreeBrush (b[1]);
  691. *back = CopyBrush (brush);
  692. }
  693. return;
  694. }
  695. // add the midwinding to both sides
  696. for (i=0 ; i<2 ; i++)
  697. {
  698. cs = &b[i]->sides[b[i]->numsides];
  699. b[i]->numsides++;
  700. cs->planenum = planenum^i^1;
  701. cs->shaderInfo = NULL;
  702. if (i==0)
  703. cs->winding = CopyWinding (midwinding);
  704. else
  705. cs->winding = midwinding;
  706. }
  707. {
  708. vec_t v1;
  709. int i;
  710. for (i=0 ; i<2 ; i++)
  711. {
  712. v1 = BrushVolume (b[i]);
  713. if (v1 < 1.0)
  714. {
  715. FreeBrush (b[i]);
  716. b[i] = NULL;
  717. // qprintf ("tiny volume after clip\n");
  718. }
  719. }
  720. }
  721. *front = b[0];
  722. *back = b[1];
  723. }