]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/pmesh.cpp
apply patch from Martin Gerhardy - more quake2 related modules compiled and misc...
[xonotic/netradiant.git] / radiant / pmesh.cpp
1 /*
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 // 
23 // Preliminary patch stuff
24 //
25 // 
26
27 #include "stdafx.h"
28 #include "gtkmisc.h"
29
30 #include "gtkr_list.h"
31
32 // externs
33 extern void MemFile_fprintf(MemStream* pMemFile, const char* pText, ...);
34 extern face_t *Face_Alloc( void );
35 extern void DrawAlternatePoint(vec3_t v, float scale);
36
37 void _Write3DMatrix (FILE *f, int y, int x, int z, float *m);
38 void _Write3DMatrix (MemStream *f, int y, int x, int z, float *m);
39
40 void Patch_InitialiseLODPointers(patchMesh_t *p)
41 {
42         int i;
43         int rowcount = ((MAX_PATCH_WIDTH-1)/2) * MAX_PATCH_HEIGHT;
44         for (i=0; i<rowcount; i++)
45                 p->rowLOD[i] = NULL;
46         int colcount = ((MAX_PATCH_HEIGHT-1)/2) * MAX_PATCH_WIDTH;
47         for (i=0; i<colcount; i++)
48                 p->colLOD[i] = NULL;
49 }
50
51 patchMesh_t* Patch_Alloc()
52 {
53   patchMesh_t *pPatch = (patchMesh_t *)malloc(sizeof(patchMesh_t));
54   pPatch->pShader = NULL;
55   pPatch->pSymbiot = NULL; // Hydra: added missing initialiser.
56         // spog - initialise patch LOD pointers
57   Patch_InitialiseLODPointers(pPatch);
58   pPatch->drawLists = NULL;
59   pPatch->bDirty = true;
60   pPatch->nListID = -1;
61   pPatch->bSelected = false;
62   pPatch->bOverlay = false;
63   pPatch->bDirty = true;
64   pPatch->LODUpdated = false;
65
66   int i;
67   for (i=0; i<(((MAX_PATCH_WIDTH-1)-1)/2); i++)
68     pPatch->rowDirty[i] = false;
69   for (i=0; i<(((MAX_PATCH_HEIGHT-1)-1)/2); i++)
70     pPatch->colDirty[i] = false;
71
72   return pPatch;
73 }
74
75 patchMesh_t* MakeNewPatch()
76 {
77   patchMesh_t *pm = reinterpret_cast<patchMesh_t*>(qmalloc(sizeof(patchMesh_t)));
78
79   // spog - initialise patch LOD pointers
80   Patch_InitialiseLODPointers(pm);
81   pm->drawLists = NULL;
82   pm->bDirty = true;
83
84   return pm;
85 }
86
87 // FIXME: this needs to be dynamic
88 //#define       MAX_PATCH_MESHES        4096
89 //patchMesh_t           patchMeshes[MAX_PATCH_MESHES];
90 //int numPatchMeshes = 0;
91
92 // used for a save spot
93 patchMesh_t patchSave;
94
95 // Tracks the selected patch for point manipulation/update. FIXME: Need to revert back to a generalized 
96 // brush approach
97 //--int  g_nSelectedPatch = -1;  
98
99 // HACK: for tracking which view generated the click
100 // as we dont want to deselect a point on a same point
101 // click if it is from a different view
102 int  g_nPatchClickedView = -1;
103 bool g_bSameView = false;
104
105 //typedef enum XFormType { TRANSLATE, SCALE, ROTATE };
106
107
108 // globals
109 bool g_bPatchShowBounds = true;
110 bool g_bPatchWireFrame = false;
111 bool g_bPatchWeld = true;
112 bool g_bPatchDrillDown = true;
113 //bool g_bPatchInsertMode = false;
114 bool g_bPatchBendMode = false;
115 int  g_nPatchBendState = -1;
116 int  g_nPatchInsertState = -1;
117 int  g_nBendOriginIndex = 0;
118 vec3_t g_vBendOrigin;
119
120 bool g_bPatchAxisOnRow = true;
121 int  g_nPatchAxisIndex = 0;
122 bool g_bPatchLowerEdge = true;
123
124 vec3_t g_vCycleCapNormal;
125 // cycles when we use Patch_CycleCapSelected
126 VIEWTYPE g_nCycleCapIndex = XY;
127
128 // BEND states
129 enum
130 {
131   BEND_SELECT_ROTATION = 0,
132   BEND_SELECT_ORIGIN,
133   BEND_SELECT_EDGE,
134   BEND_BENDIT,
135   BEND_STATE_COUNT
136 };
137
138 const char *g_pBendStateMsg[] =
139 {
140   "Use TAB to cycle through available bend axis. Press ENTER when the desired one is highlighted.",
141   "Use TAB to cycle through available rotation axis. This will LOCK around that point. You may also use Shift + Middle Click to select an arbitrary point. Press ENTER when the desired one is highlighted",
142   "Use TAB to choose which side to bend. Press ENTER when the desired one is highlighted.",
143   "Use the MOUSE to bend the patch. It uses the same ui rules as Free Rotation. Press ENTER to accept the bend, press ESC to abandon it and exit Bend mode",
144   ""
145 };
146
147 // INSERT states
148 enum
149 {
150   INSERT_SELECT_EDGE = 0,
151   INSERT_STATE_COUNT
152 };
153
154 const char* g_pInsertStateMsg[] =
155 {
156   "Use TAB to cycle through available rows/columns for insertion/deletion. Press INS to insert at the highlight, DEL to remove the pair"
157 };
158
159
160 float *g_InversePoints[1024];
161
162 const float fFullBright = 1.0;
163 const float fLowerLimit = .50;
164 const float fDec = .05f;
165 void _SetColor(face_t* f, float fColor[3])
166 {
167   return;
168   fColor[0] = f->d_color[0];
169   fColor[1] = f->d_color[1];
170   fColor[2] = f->d_color[2];
171   qglColor3fv(fColor);
172 }
173
174
175 void _DecColor(float fColor[3])
176 {
177   return;
178   fColor[0] -= fDec;
179   fColor[1] -= fDec ;
180   fColor[2] -= fDec;
181   for (int i = 0; i < 3; i++)
182   {
183     if (fColor[i] <= fLowerLimit)
184     {
185       fColor[0] = fFullBright;
186       fColor[1] = fFullBright;
187       fColor[2] = fFullBright;
188       break;
189     }
190   }
191         qglColor3fv(fColor);
192 }
193
194 vec_t __VectorNormalize (vec3_t in, vec3_t out)
195 {
196         vec_t   length, ilength;
197
198         length = sqrt (in[0]*in[0] + in[1]*in[1] + in[2]*in[2]);
199         if (length == 0)
200         {
201                 VectorClear (out);
202                 return 0;
203         }
204
205         ilength = 1.0/length;
206         out[0] = in[0]*ilength;
207         out[1] = in[1]*ilength;
208         out[2] = in[2]*ilength;
209
210         return length;
211 }
212
213
214 void Patch_SetType(patchMesh_t *p, int nType)
215 {
216   p->type = (p->type & PATCH_STYLEMASK) | nType;
217 }
218
219 void Patch_SetStyle(patchMesh_t *p, int nStyle)
220 {
221   p->type = (p->type & PATCH_TYPEMASK) | nStyle;
222 }
223
224 /*
225 ==================
226 Patch_MemorySize
227 ==================
228 */
229 int Patch_MemorySize(patchMesh_t *p)
230 {
231   //    return _msize(p);
232   return 0;
233 }
234
235
236 /*
237 ===============
238 InterpolateInteriorPoints
239 ===============
240 */
241 void InterpolateInteriorPoints( patchMesh_t *p ) 
242 {
243         int             i, j, k;
244         int             next, prev;
245
246         for ( i = 0 ; i < p->width ; i += 2 ) 
247   {
248
249     next = ( i == p->width - 1 ) ? 1 : ( i + 1 ) % p->width;
250     prev = ( i == 0 ) ? p->width - 2 : i - 1;
251
252 #if 0
253                 if ( i == 0 ) 
254     {
255                         next = ( i + 1 ) % p->width;
256                         prev = p->width - 2;                  // joined wrap case
257                 } 
258     else if ( i == p->width - 1 ) 
259     {
260                         next = 1;
261                         prev = i - 1;
262                 } 
263     else 
264     {
265                         next = ( i + 1 ) % p->width;
266                         prev = i - 1;
267                 }
268 #endif
269
270                 for ( j = 0 ; j < p->height ; j++ ) 
271     {
272                         for ( k = 0 ; k < 3 ; k++ ) 
273       {
274                                 p->ctrl[i][j].xyz[k] = ( p->ctrl[next][j].xyz[k] + p->ctrl[prev][j].xyz[k] ) * 0.5;
275                         }
276                 }
277         }
278 }
279
280 /*
281 =================
282 MakeMeshNormals
283
284 =================
285 */
286 int     neighbors[8][2] = {
287         {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}
288 };
289
290 void Patch_MeshNormals(patchMesh_t *in ) 
291 {
292         int             i, j, k, dist;
293         vec3_t  normal;
294         vec3_t  sum;
295         int             count;
296         vec3_t  base;
297         vec3_t  delta;
298         int             x, y;
299         drawVert_t      *dv;
300         vec3_t          around[8], temp;
301         qboolean        good[8];
302         qboolean        wrapWidth, wrapHeight;
303         float           len;
304
305         wrapWidth = false;
306         for ( i = 0 ; i < in->height ; i++ ) 
307         {
308
309                 VectorSubtract( in->ctrl[0][i].xyz, 
310                                       in->ctrl[in->width-1][i].xyz, delta );
311                 len = VectorLength( delta );
312                 if ( len > 1.0 ) 
313                 {
314                         break;
315                 }
316         }
317         if ( i == in->height ) 
318         {
319                 wrapWidth = true;
320         }
321
322         wrapHeight = false;
323         for ( i = 0 ; i < in->width ; i++ ) 
324         {
325                 VectorSubtract( in->ctrl[i][0].xyz, 
326                                       in->ctrl[i][in->height-1].xyz, delta );
327                 len = VectorLength( delta );
328                 if ( len > 1.0 ) 
329                 {
330                         break;
331                 }
332         }
333         if ( i == in->width) 
334         {
335                 wrapHeight = true;
336         }
337
338
339         for ( i = 0 ; i < in->width ; i++ ) 
340         {
341                 for ( j = 0 ; j < in->height ; j++ ) 
342                 {
343                         count = 0;
344                         //--dv = reinterpret_cast<drawVert_t*>(in.ctrl[j*in.width+i]);
345                         dv = &in->ctrl[i][j];
346                         VectorCopy( dv->xyz, base );
347                         for ( k = 0 ; k < 8 ; k++ ) 
348                         {
349                                 VectorClear( around[k] );
350                                 good[k] = false;
351
352                                 for ( dist = 1 ; dist <= 3 ; dist++ ) 
353                                 {
354                                         x = i + neighbors[k][0] * dist;
355                                         y = j + neighbors[k][1] * dist;
356                                         if ( wrapWidth ) 
357                                         {
358                                                 if ( x < 0 ) 
359                                                 {
360                                                         x = in->width - 1 + x;
361                                                 } 
362                                                 else if ( x >= in->width ) 
363                                                 {
364                                                         x = 1 + x - in->width;
365                                                 }
366                                         }
367                                         if ( wrapHeight ) 
368                                         {
369                                                 if ( y < 0 ) 
370                                                 {
371                                                         y = in->height - 1 + y;
372                                                 } 
373                                                 else if ( y >= in->height ) 
374                                                 {
375                                                         y = 1 + y - in->height;
376                                                 }
377                                         }
378
379                                         if ( x < 0 || x >= in->width || y < 0 || y >= in->height ) 
380                                         {
381                                                 break;                                  // edge of patch
382                                         }
383                                         //--VectorSubtract( in.ctrl[y*in.width+x]->xyz, base, temp );
384                                         VectorSubtract( in->ctrl[x][y].xyz, base, temp );
385                                         if ( __VectorNormalize( temp, temp ) == 0 ) 
386                                         {
387                                                 continue;                               // degenerate edge, get more dist
388                                         } 
389                                         else                 
390                                         {
391                                                 good[k] = true;
392                                                 VectorCopy( temp, around[k] );
393                                                 break;                                  // good edge
394                                         }
395                                 }
396                         }
397
398                         VectorClear( sum );
399                         for ( k = 0 ; k < 8 ; k++ ) 
400                         {
401                                 if ( !good[k] || !good[(k+1)&7] ) 
402                                 {
403                                         continue;       // didn't get two points
404                                 }
405                                 CrossProduct( around[(k+1)&7], around[k], normal );
406                                 if ( __VectorNormalize( normal, normal ) == 0 ) 
407                                 {
408                                         continue;
409                                 }
410                                 VectorAdd( normal, sum, sum );
411                                 count++;
412                         }
413                         if ( count == 0 ) 
414                         {
415         //printf("bad normal\n");
416                                 count = 1;
417         //continue;
418                         }
419                         __VectorNormalize( sum, dv->normal );
420                 }
421         }
422 }
423
424
425
426
427 /*
428 ==================
429 Patch_CalcBounds
430 ==================
431 */
432 void Patch_CalcBounds(patchMesh_t *p, vec3_t& vMin, vec3_t& vMax)
433 {
434   vMin[0] = vMin[1] = vMin[2] = 99999;
435   vMax[0] = vMax[1] = vMax[2] = -99999;
436
437   p->bDirty = true;
438   for (int w = 0; w < p->width; w++)
439   {
440     for (int h = 0; h < p->height; h++)
441     {
442       for (int j = 0; j < 3; j++)
443       {
444         float f = p->ctrl[w][h].xyz[j];
445         if (f < vMin[j])
446           vMin[j] = f;
447         if (f > vMax[j])
448           vMax[j] = f;
449       }
450     }
451   }
452 }
453
454 /*
455 ==================
456 Brush_RebuildBrush
457 ==================
458 */
459 void Brush_RebuildBrush(brush_t *b, vec3_t vMins, vec3_t vMaxs)
460 {
461   //
462   // Total hack job 
463   // Rebuilds a brush
464         int             i, j;
465         face_t  *f, *next;
466         vec3_t  pts[4][2];
467   texdef_t      texdef;
468         // free faces
469
470   for (j = 0; j < 3; j++)
471   {
472     if ((int)vMins[j] == (int)vMaxs[j])
473     {
474       vMins[j] -= 4;
475       vMaxs[j] += 4;
476     }
477   }
478
479   
480   for (f=b->brush_faces ; f ; f=next)
481         {
482                 next = f->next;
483     if (f)
484       texdef = f->texdef;
485     Face_Free( f );
486         }
487
488   b->brush_faces = NULL;
489
490   // left the last face so we can use its texdef
491
492         for (i=0 ; i<3 ; i++)
493                 if (vMaxs[i] < vMins[i])
494                         Error ("Brush_RebuildBrush: backwards");
495
496         pts[0][0][0] = vMins[0];
497         pts[0][0][1] = vMins[1];
498         
499         pts[1][0][0] = vMins[0];
500         pts[1][0][1] = vMaxs[1];
501         
502         pts[2][0][0] = vMaxs[0];
503         pts[2][0][1] = vMaxs[1];
504         
505         pts[3][0][0] = vMaxs[0];
506         pts[3][0][1] = vMins[1];
507         
508         for (i=0 ; i<4 ; i++)
509         {
510                 pts[i][0][2] = vMins[2];
511                 pts[i][1][0] = pts[i][0][0];
512                 pts[i][1][1] = pts[i][0][1];
513                 pts[i][1][2] = vMaxs[2];
514         }
515
516         for (i=0 ; i<4 ; i++)
517         {
518                 f = Face_Alloc();
519                 f->texdef = texdef;
520                 f->texdef.flags &= ~SURF_KEEP;
521                 f->texdef.contents &= ~CONTENTS_KEEP;
522 //              f->texdef.flags |= SURF_PATCH; 
523                 f->next = b->brush_faces;
524                 b->brush_faces = f;
525                 j = (i+1)%4;
526
527                 VectorCopy (pts[j][1], f->planepts[0]);
528                 VectorCopy (pts[i][1], f->planepts[1]);
529                 VectorCopy (pts[i][0], f->planepts[2]);
530         }
531         
532         f = Face_Alloc();
533         f->texdef = texdef;
534   f->texdef.flags &= ~SURF_KEEP;
535         f->texdef.contents &= ~CONTENTS_KEEP;
536 //  f->texdef.flags |= SURF_PATCH; 
537         f->next = b->brush_faces;
538         b->brush_faces = f;
539
540         VectorCopy (pts[0][1], f->planepts[0]);
541         VectorCopy (pts[1][1], f->planepts[1]);
542         VectorCopy (pts[2][1], f->planepts[2]);
543
544         f = Face_Alloc();
545         f->texdef = texdef;
546   f->texdef.flags &= ~SURF_KEEP;
547         f->texdef.contents &= ~CONTENTS_KEEP;
548 //  f->texdef.flags |= SURF_PATCH; 
549         f->next = b->brush_faces;
550         b->brush_faces = f;
551
552         VectorCopy (pts[2][0], f->planepts[0]);
553         VectorCopy (pts[1][0], f->planepts[1]);
554         VectorCopy (pts[0][0], f->planepts[2]);
555
556   Brush_Build(b);
557 }
558
559 void WINAPI Patch_Rebuild(patchMesh_t *p)
560 {
561   vec3_t vMin, vMax;
562   Patch_CalcBounds(p, vMin, vMax);
563   Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
564   p->bDirty = true;
565 }
566
567 /*
568 ==================
569 AddBrushForPatch
570 ==================
571  adds a patch brush and ties it to this patch id
572 */
573 brush_t* AddBrushForPatch(patchMesh_t *pm, bool bLinkToWorld )
574 {
575   // find the farthest points in x,y,z
576   vec3_t vMin, vMax;
577   Patch_CalcBounds(pm, vMin, vMax);
578
579   for (int j = 0; j < 3; j++)
580   {
581     if (vMin[j] == vMax[j])
582     {
583       vMin[j] -= 4;
584       vMax[j] += 4;
585     }
586   }
587
588   brush_t *b = Brush_Create(vMin, vMax, &g_qeglobals.d_texturewin.texdef);
589
590   // FIXME: this entire type of linkage needs to be fixed
591   b->patchBrush = true;
592   b->pPatch = pm;
593   pm->pSymbiot = b;
594   pm->bSelected = false;
595   pm->bOverlay = false;
596   pm->bDirty = true;
597   pm->nListID = -1;
598
599   if (bLinkToWorld)
600   {
601     Brush_AddToList (b, &active_brushes);
602           Entity_LinkBrush (world_entity, b);
603     Brush_Build(b);
604   }
605
606   return b;
607 }
608
609 void Patch_SetPointIntensities(int n)
610 {
611 #if 0
612         patchMesh_t     *p = patchMeshes[n];
613   for (int i = 0; i < p->width; i++)
614   {
615     for (int j = 0; j < p->height; j++)
616     {
617
618     }
619   }
620 #endif
621 }
622
623 // very approximate widths and heights
624
625 /*
626 ==================
627 Patch_Width
628 ==================
629 */
630 float Patch_Width(patchMesh_t *p)
631 {
632   float f = 0;
633   for (int i = 0; i < p->width-1; i++)
634   {
635     vec3_t vTemp;
636     VectorSubtract(p->ctrl[i][0].xyz, p->ctrl[i+1][0].xyz, vTemp);
637     f += VectorLength(vTemp);
638   }
639   return f;
640 }
641
642 float Patch_WidthDistanceTo(patchMesh_t *p, int j)
643 {
644   float f = 0;
645   for (int i = 0; i < j; i++)
646   {
647     vec3_t vTemp;
648     VectorSubtract(p->ctrl[i][0].xyz, p->ctrl[i+1][0].xyz, vTemp);
649     f += VectorLength(vTemp);
650   }
651   return f;
652 }
653
654
655
656 /*
657 ==================
658 Patch_Height
659 ==================
660 */
661 float Patch_Height(patchMesh_t *p)
662 {
663   float f = 0;
664   for (int i = 0; i < p->height-1; i++)
665   {
666     vec3_t vTemp;
667     VectorSubtract(p->ctrl[0][i].xyz, p->ctrl[0][i+1].xyz, vTemp);
668     f += VectorLength(vTemp);
669   }
670   return f;
671 }
672
673 float Patch_HeightDistanceTo(patchMesh_t *p, int j)
674 {
675   float f = 0;
676   for (int i = p->height-1; i > j; i--)
677   {
678     vec3_t vTemp;
679     VectorSubtract(p->ctrl[0][i].xyz, p->ctrl[0][i-1].xyz, vTemp); // reverse order for T coords
680     f += VectorLength(vTemp);
681   }
682   return f;
683 }
684
685
686
687 /*
688 ==================
689 Patch_Naturalize
690 ==================
691 texture = TotalTexture * LengthToThisControlPoint / TotalControlPointLength
692
693 dist( this control point to first control point ) / dist ( last control pt to first)
694 */
695 void WINAPI Patch_Naturalize(patchMesh_t *p)
696 {
697   int nWidth = (int)(p->d_texture->width * g_pGameDescription->mTextureDefaultScale);
698   int nHeight = (int)(p->d_texture->height * g_pGameDescription->mTextureDefaultScale);
699   float fPWidth = Patch_Width(p);
700   float fPHeight = Patch_Height(p);
701   float xAccum = 0.0f;
702
703   for ( int i = 0; i < p->width ; i++ ) 
704   {
705         float yAccum = 0.0f;
706         for ( int j = p->height-1; j >= 0 ; j-- ) 
707         {
708           p->ctrl[i][j].st[0] = (fPWidth / nWidth) * xAccum / fPWidth;
709           p->ctrl[i][j].st[1] = (fPHeight / nHeight) * yAccum / fPHeight;
710            yAccum = Patch_HeightDistanceTo(p,j-1);
711           //p->ctrl[i][j][3] = (fPWidth / nWidth) * (float)i / (p->width - 1);
712           //p->ctrl[i][j][4] = (fPHeight/ nHeight) * (float)j / (p->height - 1);
713         }
714         xAccum = Patch_WidthDistanceTo(p,i+1);
715   }
716   p->bDirty = true;
717 }
718
719 /*
720   if (bIBevel)
721   {
722     VectorCopy(p->ctrl[1][0], p->ctrl[1][1]);
723   }
724
725   if (bIEndcap)
726   {
727     VectorCopy(p->ctrl[3][0], p->ctrl[4][1]);
728     VectorCopy(p->ctrl[2][0], p->ctrl[3][1]);
729     VectorCopy(p->ctrl[2][0], p->ctrl[2][1]);
730     VectorCopy(p->ctrl[2][0], p->ctrl[1][1]);
731     VectorCopy(p->ctrl[1][0], p->ctrl[0][1]);
732     VectorCopy(p->ctrl[1][0], p->ctrl[0][2]);
733     VectorCopy(p->ctrl[1][0], p->ctrl[1][2]);
734     VectorCopy(p->ctrl[2][0], p->ctrl[2][2]);
735     VectorCopy(p->ctrl[3][0], p->ctrl[3][2]);
736     VectorCopy(p->ctrl[3][0], p->ctrl[4][2]);
737   }
738 */
739
740 int Index3By[][2] =
741 {
742   {0,0},
743   {1,0},
744   {2,0},
745   {2,1},
746   {2,2},
747   {1,2},
748   {0,2},
749   {0,1},
750   {0,0},
751   {0,0},
752   {0,0},
753   {0,0},
754   {0,0},
755   {0,0},
756   {0,0}
757 };
758
759 int Index5By[][2] =
760 {
761   {0,0},
762   {1,0},
763   {2,0},
764   {3,0},
765   {4,0},
766   {4,1},
767   {4,2},
768   {4,3},
769   {4,4},
770   {3,4},
771   {2,4},
772   {1,4},
773   {0,4},
774   {0,3},
775   {0,2},
776   {0,1}
777 };
778
779
780
781 int Interior3By[][2] =
782 {
783   {1,1}
784 };
785
786 int Interior5By[][2] =
787 {
788   {1,1},
789   {2,1},
790   {3,1},
791   {1,2},
792   {2,2},
793   {3,2},
794   {1,3},
795   {2,3},
796   {3,3}
797 };
798
799 int Interior3ByCount = sizeof(Interior3By) / sizeof(int[2]);
800 int Interior5ByCount = sizeof(Interior5By) / sizeof(int[2]);
801
802 extern int Plane_FromPoints(vec3_t p1, vec3_t p2, vec3_t p3, plane_t *plane);
803 // the bFaceCycle only means we are going through a patch cycling loop
804 // then we rely on g_vCycleCapNormal to compute the cap
805
806 void Patch_CapTexture(patchMesh_t *p, bool bFaceCycle = false)
807 {
808         vec3_t vProjection, vX, vY;
809         qtexture_t *texture = p->pShader->getTexture();
810         plane_t Plane1, Plane2, Plane3;
811         bool bThing=true;
812
813         if (bFaceCycle)
814                 VectorCopy (g_vCycleCapNormal, vProjection);
815
816         else
817         {
818                 VectorClear ( vProjection );
819
820                 // find normal for plane from first 3 corner points
821                 if (!Plane_FromPoints(p->ctrl[0][0].xyz,p->ctrl[0][p->height-1].xyz,p->ctrl[p->width-1][p->height-1].xyz,&Plane1))
822                 {
823                         VectorClear ( Plane3.normal );
824                         bThing = false;
825                 }
826                 
827                 // find normal for plane from next 3 corner points
828                 if (!Plane_FromPoints(p->ctrl[p->width-1][p->height-1].xyz,p->ctrl[p->width-1][0].xyz,p->ctrl[0][0].xyz,&Plane2))
829                 {
830                         if (bThing)
831                         {       
832                                 VectorCopy ( Plane1.normal, Plane3.normal );
833                                 Plane3.dist = Plane1.dist;
834                         }
835                 }
836
837                 else
838                 {
839                         if (bThing)
840                                 // find average plane for all 4 corner points
841                         {
842                                 for (int n = 0; n <= 2; n++)
843                                 {
844                                         Plane3.normal[n] = (Plane1.normal[n] + Plane2.normal[n]) / 2;
845                                 }
846                                 Plane3.dist = (Plane1.dist + Plane2.dist) / 2;
847                         }
848                         else
849                         {
850                                 VectorCopy ( Plane2.normal, Plane3.normal );
851                                 Plane3.dist = Plane2.dist;
852                         }
853                 }
854
855                 // get best axis for projection from average plane
856                 //Sys_Printf("surface normal1: (%f,%f,%f)\n",Plane1.normal[0],Plane1.normal[1],Plane1.normal[0]);
857                 //Sys_Printf("surface normal2: (%f,%f,%f)\n",Plane2.normal[0],Plane2.normal[1],Plane2.normal[0]);
858                 //Sys_Printf("surface normal3: (%f,%f,%f)\n",Plane3.normal[0],Plane3.normal[1],Plane3.normal[0]);
859                 TextureAxisFromPlane(&Plane3, vX, vY);
860         }
861
862         for (int w = 0; w < p->width; w++)
863         {
864                 for (int h = 0; h < p->height; h++)
865                 {
866                         if (vProjection[2] == 1.0f || (vX[0] == 1.0f && vY[1] == -1.0f))
867                         {
868                                 p->ctrl[w][h].st[0] = p->ctrl[w][h].xyz[0] / (texture->width * g_pGameDescription->mTextureDefaultScale);
869                                 p->ctrl[w][h].st[1] = p->ctrl[w][h].xyz[1] / (texture->height * g_pGameDescription->mTextureDefaultScale) * -1;
870                         }
871                         else if (vProjection[0] == 1.0f || (vX[1] == 1.0f && vY[2] == -1.0f))
872                         {
873                                 p->ctrl[w][h].st[0] = p->ctrl[w][h].xyz[1] / (texture->width * g_pGameDescription->mTextureDefaultScale);
874                                 p->ctrl[w][h].st[1] = p->ctrl[w][h].xyz[2] / (texture->height * g_pGameDescription->mTextureDefaultScale) * -1;
875                         }
876                         else if (vProjection[1] == 1.0f || (vX[0] == 1.0f && vY[2] == -1.0f))
877                         {
878                                 p->ctrl[w][h].st[0] = p->ctrl[w][h].xyz[0] / (texture->width * g_pGameDescription->mTextureDefaultScale);
879                                 p->ctrl[w][h].st[1] = p->ctrl[w][h].xyz[2] / (texture->height * g_pGameDescription->mTextureDefaultScale) * -1;
880                         }
881                         //Sys_Printf("(%i,%i) (%f,%f,%f) (%f,%f) %f\n",w,h,
882                         //      p->ctrl[w][h].xyz[0],p->ctrl[w][h].xyz[1],p->ctrl[w][h].xyz[2],
883                         //      p->ctrl[w][h].st[0],p->ctrl[w][h].st[1],p->ctrl[w][h].normal);
884                 }
885         }
886         // make sure it will rebuild
887         p->bDirty = true;
888 }
889
890 void FillPatch(patchMesh_t *p, vec3_t v)
891 {
892   for (int i = 0; i < p->width; i++)
893   {
894     for (int j = 0; j < p->height; j++)
895     {
896       VectorCopy(v, p->ctrl[i][j].xyz);
897     }
898   }
899 }
900
901 // temporarily moved function to allow use in Cap() and CapSpecial()
902 void patchInvert(patchMesh_t *p)
903 {
904   drawVert_t vertTemp;
905   p->bDirty = true;
906         for ( int i = 0 ; i < p->width ; i++ ) 
907   {
908     for (int j = 0; j < p->height / 2; j++)
909     {
910       memcpy(&vertTemp, &p->ctrl[i][p->height - 1- j], sizeof (drawVert_t));
911       memcpy(&p->ctrl[i][p->height - 1 - j], &p->ctrl[i][j], sizeof(drawVert_t));
912       memcpy(&p->ctrl[i][j], &vertTemp, sizeof(drawVert_t));
913                 }
914         }
915 }
916
917 brush_t* Cap(patchMesh_t *pParent, bool bByColumn, bool bFirst)
918 {
919   brush_t *b;
920   patchMesh_t *p;
921   vec3_t vMin, vMax;
922   int i, j;
923
924   bool bSmall = true;
925   // make a generic patch
926   if (pParent->width <= 9)
927   {
928     b = Patch_GenericMesh(3, 3, 2, false);
929   }
930   else
931   {
932     b = Patch_GenericMesh(5, 5, 2, false);
933     bSmall = false;
934   }
935
936   if (!b)
937   {
938     Sys_Printf("Unable to cap. You may need to ungroup the patch.\n");
939     return NULL;
940   }
941
942   p = b->pPatch;
943   p->type |= PATCH_CAP;
944
945   vMin[0] = vMin[1] = vMin[2] = 9999;
946   vMax[0] = vMax[1] = vMax[2] = -9999;
947
948   // we seam the column edge, FIXME: this might need to be able to seem either edge
949   // 
950   int nSize = (bByColumn) ? pParent->width : pParent->height;
951   int nIndex = (bFirst) ? 0 : (bByColumn) ? pParent->height-1 : pParent->width-1;
952
953   FillPatch(p, pParent->ctrl[0][nIndex].xyz);
954
955   for (i = 0; i < nSize; i++)
956   {
957     if (bByColumn)
958     {
959       if (bSmall)
960       {
961         VectorCopy(pParent->ctrl[i][nIndex].xyz, p->ctrl[Index3By[i][0]][Index3By[i][1]].xyz);
962       }
963       else
964       {
965         VectorCopy(pParent->ctrl[i][nIndex].xyz, p->ctrl[Index5By[i][0]][Index5By[i][1]].xyz);
966       }
967     }
968     else
969     {
970       if (bSmall)
971       {
972         VectorCopy(pParent->ctrl[nIndex][i].xyz, p->ctrl[Index3By[i][0]][Index3By[i][1]].xyz);
973       }
974       else
975       {
976         VectorCopy(pParent->ctrl[nIndex][i].xyz, p->ctrl[Index5By[i][0]][Index5By[i][1]].xyz);
977       }
978     }
979   
980     for (j = 0; j < 3; j++)
981     {
982       float f = (bSmall) ? p->ctrl[Index3By[i][0]][Index3By[i][1]].xyz[j] : p->ctrl[Index5By[i][0]][Index5By[i][1]].xyz[j];
983       if (f < vMin[j])
984         vMin[j] = f;
985       if (f > vMax[j])
986         vMax[j] = f;
987     }
988   }
989
990   vec3_t vTemp;
991   for (j = 0; j < 3; j++)
992   {
993     vTemp[j] = vMin[j] + fabs((vMax[j] - vMin[j]) * 0.5);
994   }
995   int nCount = (bSmall) ? Interior3ByCount : Interior5ByCount;
996   for (j = 0; j < nCount; j++)
997   {
998     if (bSmall)
999     {
1000       VectorCopy(vTemp, p->ctrl[Interior3By[j][0]][Interior3By[j][1]].xyz);
1001     }
1002     else
1003     {
1004       VectorCopy(vTemp, p->ctrl[Interior5By[j][0]][Interior5By[j][1]].xyz);
1005     }
1006   }
1007
1008   if (bFirst)
1009           patchInvert(p);
1010   /*
1011   {
1012     drawVert_t vertTemp;
1013     for (i = 0; i < p->width; i++)
1014     {
1015       for (j = 0; j < p->height / 2; j++)
1016       {
1017         memcpy(&vertTemp, &p->ctrl[i][p->height - 1- j], sizeof (drawVert_t));
1018         memcpy(&p->ctrl[i][p->height - 1 - j], &p->ctrl[i][j], sizeof(drawVert_t));
1019         memcpy(&p->ctrl[i][j], &vertTemp, sizeof(drawVert_t));
1020       }
1021     }
1022   }
1023   */
1024
1025   Patch_Rebuild(p);
1026   Patch_CapTexture(p);
1027   return p->pSymbiot;
1028 }
1029
1030 brush_t* CapSpecial(patchMesh_t *pParent, int nType, bool bFirst)
1031 {
1032
1033   brush_t *b;
1034   patchMesh_t *p;
1035   vec3_t vMin, vMax, vTemp;
1036   int i, j;
1037
1038   if (nType == IENDCAP)
1039     b = Patch_GenericMesh(5, 3, 2, false);
1040   else
1041     b = Patch_GenericMesh(3, 3, 2, false);
1042
1043   if (!b)
1044   {
1045     Sys_Printf("Unable to cap. Make sure you ungroup before re-capping.");
1046     return NULL;
1047   }
1048
1049   p = b->pPatch;
1050   p->type |= PATCH_CAP;
1051
1052   vMin[0] = vMin[1] = vMin[2] = 9999;
1053   vMax[0] = vMax[1] = vMax[2] = -9999;
1054
1055   //  int nSize = pParent->width;
1056   int nIndex = (bFirst) ? 0 : pParent->height-1;
1057
1058   // parent bounds are used for some things
1059   Patch_CalcBounds(pParent, vMin, vMax);
1060
1061   for (j = 0; j < 3; j++)
1062   {
1063     vTemp[j] = vMin[j] + fabs((vMax[j] - vMin[j]) * 0.5);
1064   }
1065   
1066   if (nType == IBEVEL)
1067   {
1068     VectorCopy(pParent->ctrl[0][nIndex].xyz, p->ctrl[0][0].xyz);
1069     VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[0][2].xyz);
1070     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[0][1].xyz);
1071     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[2][2].xyz);
1072     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[1][0].xyz);
1073     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[1][1].xyz);
1074     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[1][2].xyz);
1075     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[2][0].xyz);
1076     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[2][1].xyz);
1077   }
1078   else if (nType == BEVEL)
1079   {
1080     vec3_t p1, p2, p3, p4; //, temp, dir;
1081
1082     VectorCopy(pParent->ctrl[0][nIndex].xyz, p3);
1083     VectorCopy(pParent->ctrl[1][nIndex].xyz, p1);
1084     VectorCopy(pParent->ctrl[2][nIndex].xyz, p2);
1085         
1086         //Sys_Printf("CapSpecial() p1: %f %f %f\n",p1[0],p1[1],p1[2]);
1087         //Sys_Printf("CapSpecial() p2: %f %f %f\n",p2[0],p2[1],p2[2]);
1088         //Sys_Printf("CapSpecial() p3: %f %f %f\n",p3[0],p3[1],p3[2]);
1089         
1090         VectorSubtract(p2, p1, p4);
1091         VectorAdd(p3, p4, p4);
1092         // spog - use opposite-point-on-parallelogram to find p4
1093         /*
1094     VectorSubtract(p3, p2, dir);
1095     VectorNormalize(dir);
1096     VectorSubtract(p1, p2, temp);
1097     vec_t dist = _DotProduct(temp, dir);
1098     VectorScale(dir, dist, temp);
1099     VectorAdd(p2, temp, temp);
1100     VectorSubtract(temp, p1, temp);
1101     VectorScale(temp, 2, temp);
1102     VectorAdd(p1, temp, p4);
1103         */
1104
1105         //Sys_Printf("CapSpecial() p1: %f %f %f\n",p1[0],p1[1],p1[2]);
1106         //Sys_Printf("CapSpecial() p2: %f %f %f\n",p2[0],p2[1],p2[2]);
1107         //Sys_Printf("CapSpecial() p3: %f %f %f\n",p3[0],p3[1],p3[2]);
1108         //Sys_Printf("CapSpecial() p4: %f %f %f\n",p4[0],p4[1],p4[2]);
1109
1110     VectorCopy(p4, p->ctrl[0][0].xyz);
1111     VectorCopy(p4, p->ctrl[1][0].xyz);
1112     VectorCopy(p4, p->ctrl[0][1].xyz);
1113     VectorCopy(p4, p->ctrl[1][1].xyz);
1114     VectorCopy(p4, p->ctrl[0][2].xyz);
1115     VectorCopy(p4, p->ctrl[1][2].xyz);
1116     VectorCopy(p2, p->ctrl[2][0].xyz);
1117     VectorCopy(p1, p->ctrl[2][1].xyz);
1118     VectorCopy(p3, p->ctrl[2][2].xyz);
1119
1120   }
1121   else if (nType == ENDCAP)
1122   {
1123     VectorAdd(pParent->ctrl[4][nIndex].xyz, pParent->ctrl[0][nIndex].xyz, vTemp);
1124     VectorScale(vTemp, 0.5, vTemp);
1125     VectorCopy(pParent->ctrl[0][nIndex].xyz, p->ctrl[0][0].xyz);
1126                                                    VectorCopy(vTemp, p->ctrl[1][0].xyz);
1127     VectorCopy(pParent->ctrl[4][nIndex].xyz, p->ctrl[2][0].xyz);
1128
1129     VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[0][2].xyz);
1130     VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[1][2].xyz);
1131     VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[2][2].xyz);
1132     VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[1][1].xyz);
1133     
1134     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[0][1].xyz);
1135     VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[2][1].xyz);
1136   }
1137   else
1138   {
1139     VectorCopy(pParent->ctrl[4][nIndex].xyz, p->ctrl[0][0].xyz);
1140     VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[1][0].xyz);
1141     VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[2][0].xyz);
1142     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[3][0].xyz);
1143     VectorCopy(pParent->ctrl[0][nIndex].xyz, p->ctrl[4][0].xyz);
1144     
1145     VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[0][1].xyz);
1146     VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[1][1].xyz);
1147     VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[2][1].xyz);
1148     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[3][1].xyz);
1149     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[4][1].xyz);
1150
1151     VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[0][2].xyz);
1152     VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[1][2].xyz);
1153     VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[2][2].xyz);
1154     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[3][2].xyz);
1155     VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[4][2].xyz);
1156   }
1157
1158
1159   if (!bFirst)
1160   {
1161     drawVert_t vertTemp;
1162     for (i = 0; i < p->width; i++)
1163     {
1164       for (j = 0; j < p->height / 2; j++)
1165       {
1166         memcpy(&vertTemp, &p->ctrl[i][p->height - 1- j], sizeof (drawVert_t));
1167         memcpy(&p->ctrl[i][p->height - 1 - j], &p->ctrl[i][j], sizeof(drawVert_t));
1168         memcpy(&p->ctrl[i][j], &vertTemp, sizeof(drawVert_t));
1169       }
1170     }
1171   }
1172
1173   //--Patch_CalcBounds(p, vMin, vMax);
1174   //--Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
1175   Patch_Rebuild(p);
1176   Patch_CapTexture(p);
1177   return p->pSymbiot;
1178 }
1179
1180 void Patch_CapCurrent()
1181 {
1182   patchMesh_t *pParent = NULL;
1183   brush_t *b[4];
1184   brush_t *pCap = NULL;
1185   b[0] = b[1] = b[2] = b[3] = NULL;
1186   int nIndex = 0;
1187   bool b_GroupResult = TRUE;
1188   
1189   if (!QE_SingleBrush(true))
1190   {
1191     Sys_Printf("Patch_CapCurrent: you must have a single patch selected\n");
1192     return;
1193   }
1194   
1195   
1196   for (brush_t *pb = selected_brushes.next ; pb != NULL && pb != &selected_brushes ; pb = pb->next)
1197   {
1198     if (pb->patchBrush)
1199     {
1200       pParent = pb->pPatch;
1201       // decide which if any ends we are going to cap
1202       // if any of these compares hit, it is a closed patch and as such
1203       // the generic capping will work.. if we do not find a closed edge 
1204       // then we need to ask which kind of cap to add
1205       if (VectorCompare(pParent->ctrl[0][0].xyz, pParent->ctrl[pParent->width-1][0].xyz))
1206       {
1207         pCap = Cap(pParent, true, false);
1208         if (pCap != NULL)
1209         {
1210           b[nIndex++] = pCap;
1211         }
1212       }
1213       if (VectorCompare(pParent->ctrl[0][pParent->height-1].xyz, pParent->ctrl[pParent->width-1][pParent->height-1].xyz))
1214       {
1215         pCap = Cap(pParent, true, true);
1216         if (pCap != NULL)
1217         {
1218           b[nIndex++] = pCap;
1219         }
1220       }
1221       if (VectorCompare(pParent->ctrl[0][0].xyz, pParent->ctrl[0][pParent->height-1].xyz))
1222       {
1223         pCap = Cap(pParent, false, false);
1224         if (pCap != NULL)
1225         {
1226           b[nIndex++] = pCap;
1227         }
1228       }
1229       if (VectorCompare(pParent->ctrl[pParent->width-1][0].xyz, pParent->ctrl[pParent->width-1][pParent->height-1].xyz))
1230       {
1231         pCap = Cap(pParent, false, true);
1232         if (pCap != NULL)
1233         {
1234           b[nIndex++] = pCap;
1235         }
1236       }
1237     }
1238   }
1239   
1240   if (pParent)
1241   {
1242     // if we did not cap anything with the above tests
1243     if (nIndex == 0)
1244     {
1245       int type;
1246       
1247       if (DoCapDlg (&type, &b_GroupResult) == IDOK)
1248       {
1249         b[nIndex++] = CapSpecial(pParent, type, false);
1250         b[nIndex++] = CapSpecial(pParent, type, true);
1251       }
1252     }
1253     
1254     if (nIndex > 0)
1255     {
1256       while (nIndex > 0)
1257       {
1258         nIndex--;
1259         if (b[nIndex])
1260         {
1261           Select_Brush(b[nIndex]);
1262         }
1263       }
1264       // Gef: Added toggle for capped patch func_group
1265       if(b_GroupResult) {
1266         entity_t *e = Entity_Alloc();
1267         SetKeyValue(e, "classname", "func_group");
1268         SetKeyValue(e, "type", "patchCapped");
1269         Select_GroupEntity(e);
1270         Entity_AddToList(e, &entities);
1271       }
1272     }
1273   }
1274 }
1275
1276 /*
1277 ===============
1278 BrushToPatchMesh
1279 ===============
1280 */
1281 void Patch_BrushToMesh(bool bCone, bool bBevel, bool bEndcap, bool bSquare, int nHeight)
1282 {
1283         brush_t         *b;
1284         patchMesh_t     *p;
1285         int                     i,j;
1286
1287         if (!QE_SingleBrush())
1288                 return;
1289
1290         b = selected_brushes.next;
1291
1292         p = MakeNewPatch();
1293
1294         p->d_texture = b->brush_faces->d_texture;
1295         p->pShader = b->brush_faces->pShader;
1296
1297   p->height = nHeight;
1298
1299   p->type = PATCH_CYLINDER;
1300   if (bBevel & !bSquare)
1301   {
1302     p->type = PATCH_BEVEL;
1303     p->width = 3;
1304     int nStep = (int)((b->maxs[2] - b->mins[2]) / (p->height-1));
1305     int nStart = (int)(b->mins[2]);
1306     for (i = 0; i < p->height; i++)
1307     {
1308             p->ctrl[0][i].xyz[0] =  b->mins[0];
1309             p->ctrl[0][i].xyz[1] =  b->mins[1];
1310       p->ctrl[0][i].xyz[2] = nStart;
1311
1312             p->ctrl[1][i].xyz[0] =  b->maxs[0];
1313             p->ctrl[1][i].xyz[1] =  b->mins[1];
1314       p->ctrl[1][i].xyz[2] = nStart;
1315
1316             p->ctrl[2][i].xyz[0] =  b->maxs[0];
1317             p->ctrl[2][i].xyz[1] =  b->maxs[1];
1318       p->ctrl[2][i].xyz[2] = nStart;
1319       nStart += nStep;
1320     }
1321   }
1322   else if (bEndcap & !bSquare)
1323   {
1324     p->type = PATCH_ENDCAP;
1325     p->width = 5;
1326     int nStep = (int)((b->maxs[2] - b->mins[2]) / (p->height-1));
1327     int nStart = (int)(b->mins[2]);
1328     for (i = 0; i < p->height; i++)
1329     {
1330       p->ctrl[0][i].xyz[0] =  b->mins[0];
1331       p->ctrl[0][i].xyz[1] =  b->mins[1];
1332       p->ctrl[0][i].xyz[2] = nStart;
1333
1334       p->ctrl[1][i].xyz[0] =  b->mins[0];
1335       p->ctrl[1][i].xyz[1] =  b->maxs[1];
1336       p->ctrl[1][i].xyz[2] = nStart;
1337
1338       p->ctrl[2][i].xyz[0] =  b->mins[0] + ((b->maxs[0] - b->mins[0]) * 0.5);
1339       p->ctrl[2][i].xyz[1] =  b->maxs[1];
1340       p->ctrl[2][i].xyz[2] = nStart;
1341            
1342       p->ctrl[3][i].xyz[0] =  b->maxs[0];
1343       p->ctrl[3][i].xyz[1] =  b->maxs[1];
1344       p->ctrl[3][i].xyz[2] = nStart;
1345
1346       p->ctrl[4][i].xyz[0] =  b->maxs[0];
1347       p->ctrl[4][i].xyz[1] =  b->mins[1];
1348       p->ctrl[4][i].xyz[2] = nStart;
1349       nStart += nStep;
1350     }
1351   }
1352   else
1353   {
1354     p->width = 9;
1355     p->ctrl[1][0].xyz[0] =  b->mins[0];
1356     p->ctrl[1][0].xyz[1] =  b->mins[1];
1357
1358     p->ctrl[3][0].xyz[0] =  b->maxs[0];
1359     p->ctrl[3][0].xyz[1] =  b->mins[1];
1360
1361     p->ctrl[5][0].xyz[0] =  b->maxs[0];
1362     p->ctrl[5][0].xyz[1] =  b->maxs[1];
1363
1364     p->ctrl[7][0].xyz[0] =  b->mins[0];
1365     p->ctrl[7][0].xyz[1] =  b->maxs[1];
1366
1367     for ( i = 1 ; i < p->width - 1 ; i += 2 ) 
1368     {
1369
1370       p->ctrl[i][0].xyz[2] =  b->mins[2];
1371
1372                   VectorCopy( p->ctrl[i][0].xyz, p->ctrl[i][2].xyz );
1373
1374                   p->ctrl[i][2].xyz[2] =  b->maxs[2];
1375
1376                   p->ctrl[i][1].xyz[0] = ( p->ctrl[i][0].xyz[0] + p->ctrl[i][2].xyz[0] ) * 0.5;
1377                   p->ctrl[i][1].xyz[1] = ( p->ctrl[i][0].xyz[1] + p->ctrl[i][2].xyz[1] ) * 0.5;
1378                   p->ctrl[i][1].xyz[2] = ( p->ctrl[i][0].xyz[2] + p->ctrl[i][2].xyz[2] ) * 0.5;
1379           }
1380           InterpolateInteriorPoints( p );
1381
1382     if (bSquare)
1383     {
1384       if (bBevel || bEndcap)
1385       {
1386         if (bBevel)
1387         {
1388           for (i = 0; i < p->height; i++)
1389           {
1390             VectorCopy(p->ctrl[1][i].xyz, p->ctrl[2][i].xyz);
1391             VectorCopy(p->ctrl[7][i].xyz, p->ctrl[6][i].xyz);
1392           }
1393         }
1394         else
1395         {
1396           for (i = 0; i < p->height; i++)
1397           {
1398             VectorCopy(p->ctrl[5][i].xyz, p->ctrl[4][i].xyz);
1399             VectorCopy(p->ctrl[1][i].xyz, p->ctrl[2][i].xyz);
1400             VectorCopy(p->ctrl[7][i].xyz, p->ctrl[6][i].xyz);
1401             VectorCopy(p->ctrl[8][i].xyz, p->ctrl[7][i].xyz);
1402           }
1403         }
1404       }
1405       else
1406       {
1407         for (i = 0; i < p->width-1; i ++)
1408         {
1409           for (j = 0; j < p->height; j++)
1410           {
1411             VectorCopy(p->ctrl[i+1][j].xyz, p->ctrl[i][j].xyz);
1412           }
1413         }
1414         for (j = 0; j < p->height; j++)
1415         {
1416           VectorCopy(p->ctrl[0][j].xyz, p->ctrl[8][j].xyz);
1417         }
1418       }
1419     }
1420   }
1421
1422
1423   Patch_Naturalize(p);
1424
1425   if (bCone)
1426   {
1427     p->type = PATCH_CONE;
1428     float xc = (b->maxs[0] + b->mins[0]) * 0.5; 
1429     float yc = (b->maxs[1] + b->mins[1]) * 0.5; 
1430
1431     for ( i = 0 ; i < p->width ; i ++)
1432     {
1433       p->ctrl[i][2].xyz[0] = xc;
1434       p->ctrl[i][2].xyz[1] = yc;
1435     }
1436   }
1437   
1438   b = AddBrushForPatch(p);
1439
1440   Select_Delete();
1441   Select_Brush(b);
1442
1443 }
1444
1445 /*
1446 ==================
1447 Patch_GenericMesh
1448 ==================
1449 */
1450 brush_t* Patch_GenericMesh(int nWidth, int nHeight, int nOrientation, bool bDeleteSource, bool bOverride)
1451 {
1452   int i,j;
1453
1454   if (nHeight < 3 || nHeight > 15 || nWidth < 3 || nWidth > 15)
1455   {
1456     Sys_Printf("Invalid patch width or height.\n");
1457     return NULL;
1458   }
1459
1460         if (! bOverride && !QE_SingleBrush())
1461   {
1462     Sys_Printf("Error: you must have a single brush selected\n");
1463                 return NULL;
1464   }
1465
1466   patchMesh_t* p = MakeNewPatch();
1467   p->pShader = g_qeglobals.d_texturewin.pShader;
1468   p->d_texture = g_qeglobals.d_texturewin.pShader->getTexture();
1469
1470         p->width = nWidth;
1471         p->height = nHeight;
1472   p->type = PATCH_GENERIC;
1473
1474   int nFirst = 0;
1475   int nSecond = 1;
1476   if (nOrientation == 0)
1477   {
1478     nFirst = 1;
1479     nSecond = 2;
1480   }
1481   else if (nOrientation == 1)
1482   {
1483     nSecond = 2;
1484   }
1485
1486         brush_t *b = selected_brushes.next;
1487         // set the workzone to this brush, use it later to create the patch points
1488         UpdateWorkzone_ForBrush( b );
1489
1490   int xStep = (int)(b->mins[nFirst]);
1491   float xAdj = fabs((b->maxs[nFirst] - b->mins[nFirst]) / (nWidth - 1));
1492   float yAdj = fabs((b->maxs[nSecond] - b->mins[nSecond]) / (nHeight - 1));
1493
1494   for (i = 0; i < nWidth; i++)
1495   {
1496     int yStep = (int)(b->mins[nSecond]);
1497     for (j = 0; j < nHeight; j++)
1498     {
1499       p->ctrl[i][j].xyz[nFirst] = xStep;
1500       p->ctrl[i][j].xyz[nSecond] = yStep;
1501       // create patch based on workzone
1502       p->ctrl[i][j].xyz[nOrientation] = g_qeglobals.d_work_max[nOrientation];
1503       yStep += (int)yAdj;
1504     }
1505     xStep += (int)xAdj;
1506   }
1507
1508   Patch_Naturalize(p);
1509
1510   b = AddBrushForPatch(p);
1511   if (bDeleteSource)
1512   {
1513     Select_Delete();
1514     Select_Brush(b);
1515   }
1516
1517   return b;
1518   //g_qeglobals.d_select_mode = sel_curvepoint;
1519 }
1520
1521 /*
1522 ==================
1523 PointInMoveList
1524 ==================
1525 */
1526 int PointInMoveList(float *pf)
1527 {
1528   for (int i = 0; i < g_qeglobals.d_num_move_points; i++)
1529   {
1530     if (pf == &g_qeglobals.d_move_points[i][0])
1531       return i;
1532   }
1533   return -1;
1534 }
1535
1536 /*
1537 ==================
1538 PointValueInMoveList
1539 ==================
1540 */
1541 int PointValueInMoveList(vec3_t v)
1542 {
1543   for (int i = 0; i < g_qeglobals.d_num_move_points; i++)
1544   {
1545     if (VectorCompare(v, g_qeglobals.d_move_points[i]))
1546       return i;
1547   }
1548   return -1;
1549 }
1550
1551
1552 /*
1553 ==================
1554 RemovePointFromMoveList
1555 ==================
1556 */
1557 void RemovePointFromMoveList(vec3_t v)
1558 {
1559   int n;
1560   while ( (n = PointValueInMoveList(v)) >= 0)
1561   {
1562     for (int i = n; i < g_qeglobals.d_num_move_points-1; i++)
1563     {
1564       g_qeglobals.d_move_points[i] = g_qeglobals.d_move_points[i+1];
1565     }
1566     g_qeglobals.d_num_move_points--;
1567   }
1568 }
1569
1570 /*
1571 ==================
1572 ColumnSelected
1573 ==================
1574 */
1575 bool ColumnSelected(patchMesh_t* p, int nCol)
1576 {
1577   for (int i = 0; i < p->height; i++)
1578   {
1579     if (PointInMoveList(p->ctrl[nCol][i].xyz) == -1)
1580       return false;
1581   }
1582   return true;
1583 }
1584
1585 /*
1586 ==================
1587 AddPoint
1588 ==================
1589 */
1590 void AddPoint(patchMesh_t* p, vec3_t v, bool bWeldOrDrill = true)
1591 {
1592   int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0;
1593   int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2;
1594   g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = v;
1595   if ((g_bPatchWeld || g_bPatchDrillDown) && bWeldOrDrill)
1596   {
1597           for ( int i = 0 ; i < p->width ; i++ ) 
1598     {
1599                   for ( int j = 0 ; j < p->height ; j++ ) 
1600       {
1601         if (g_bPatchWeld)
1602         {
1603           if ( VectorCompare(v, p->ctrl[i][j].xyz)
1604             && PointInMoveList(p->ctrl[i][j].xyz) == -1)
1605           {
1606             g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl[i][j].xyz;
1607             continue;
1608           }
1609         }
1610         if (g_bPatchDrillDown && g_nPatchClickedView != W_CAMERA)
1611         {
1612           if ( (fabs(v[nDim1] - p->ctrl[i][j].xyz[nDim1]) <= EQUAL_EPSILON) 
1613              &&(fabs(v[nDim2] - p->ctrl[i][j].xyz[nDim2]) <= EQUAL_EPSILON)) 
1614           {
1615             if (PointInMoveList(p->ctrl[i][j].xyz) == -1)
1616             {
1617               g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl[i][j].xyz;
1618               continue;
1619             }
1620           }
1621         }
1622       }
1623     }
1624   }
1625 }
1626
1627 /*
1628 ==================
1629 SelectRow
1630 ==================
1631 */
1632 void SelectRow(patchMesh_t* p, int nRow, bool bMulti)
1633 {
1634   if (!bMulti)
1635     g_qeglobals.d_num_move_points = 0;
1636   for (int i = 0; i < p->width; i++)
1637   {
1638     AddPoint(p, p->ctrl[i][nRow].xyz, false);
1639   }
1640   //Sys_Printf("Selected Row %d\n", nRow);
1641 }
1642
1643 /*
1644 ==================
1645 SelectColumn
1646 ==================
1647 */
1648 void SelectColumn(patchMesh_t* p, int nCol, bool bMulti)
1649 {
1650   if (!bMulti)
1651     g_qeglobals.d_num_move_points = 0;
1652   for (int i = 0; i < p->height; i++)
1653   {
1654     AddPoint(p, p->ctrl[nCol][i].xyz, false);
1655   }
1656   //Sys_Printf("Selected Col %d\n", nCol);
1657 }
1658
1659
1660 /*
1661 ==================
1662 AddPatchMovePoint
1663 ==================
1664 */
1665 void AddPatchMovePoint(vec3_t v, bool bMulti, bool bFull)
1666 {
1667   if (!g_bSameView && !bMulti && !bFull)
1668   {
1669     g_bSameView = true;
1670     //return; // was causing odd behaviour on patch vertex selection
1671   }
1672
1673         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
1674         {
1675     if (pb->patchBrush)
1676     {
1677             patchMesh_t* p = pb->pPatch;
1678             for ( int i = 0 ; i < p->width ; i++ ) 
1679       {
1680                     for ( int j = 0 ; j < p->height ; j++ ) 
1681         {
1682           if (VectorCompare(v, p->ctrl[i][j].xyz))
1683           {
1684             if (PointInMoveList(p->ctrl[i][j].xyz) == -1)
1685             {
1686               if (bFull)        // if we want the full row/col this is on
1687               {
1688                 SelectColumn(p, i, bMulti);
1689               }
1690               else
1691               {
1692                 if (!bMulti)
1693                   g_qeglobals.d_num_move_points = 0;
1694                 AddPoint(p, p->ctrl[i][j].xyz);
1695                 //Sys_Printf("Selected col:row %d:%d\n", i, j);
1696               }
1697               //--if (!bMulti)
1698               return;
1699             }
1700             else
1701             {
1702               if (bFull)
1703               {
1704                 if (ColumnSelected(p, i))
1705                 {
1706                   SelectRow(p, j, bMulti);
1707                 }
1708                 else
1709                 {
1710                   SelectColumn(p, i, bMulti);
1711                 }
1712                 return;
1713               }
1714                           //if (!bMulti)
1715                           //{
1716               //    g_qeglobals.d_num_move_points = 0;
1717               //    AddPoint(p, p->ctrl[i][j].xyz);
1718                           //}
1719               if (bMulti)// if (g_bSameView) // this is not having desired effect
1720               {
1721                 RemovePointFromMoveList(v);
1722                 return;
1723               }
1724             }
1725           }
1726                     }
1727             }
1728     }
1729   }
1730 }
1731
1732 /*
1733 ==================
1734 Patch_UpdateSelected
1735 ==================
1736 */
1737 void Patch_UpdateSelected(vec3_t vMove)
1738 {
1739         int i;//, j;
1740         for (i=0 ; i < g_qeglobals.d_num_move_points ; i++)
1741         {
1742                 VectorAdd (g_qeglobals.d_move_points[i], vMove, g_qeglobals.d_move_points[i]);
1743                 if (g_qeglobals.d_num_move_points == 1)
1744                 {
1745                 }
1746         }
1747         
1748         //--patchMesh_t* p = &patchMeshes[g_nSelectedPatch];
1749         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
1750         {
1751                 if (pb->patchBrush)
1752                 {
1753                         patchMesh_t* p = pb->pPatch;
1754                         
1755 #if 0 //moving to SelectCurvePointByRay
1756                         g_qeglobals.d_numpoints = 0;
1757                         for (i = 0 ; i < p->width ; i++ ) 
1758                         {
1759                                 for ( j = 0 ; j < p->height ; j++ ) 
1760                                 {
1761                                         VectorCopy (p->ctrl[i][j].xyz, g_qeglobals.d_points[g_qeglobals.d_numpoints]);
1762                                         if (g_qeglobals.d_numpoints < MAX_POINTS-1)
1763                                         {
1764                                                 g_qeglobals.d_numpoints++;
1765                                         }
1766                                 }
1767                         }
1768 #endif                  
1769                         vec3_t vMin, vMax;
1770                         Patch_CalcBounds(p, vMin, vMax);
1771                         Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
1772                 }
1773         }
1774   //Brush_Free(p->pSymbiot);
1775   //Select_Brush(AddBrushForPatch(g_nSelectedPatch));
1776 }
1777
1778
1779
1780 /*
1781 ===============
1782 SampleSinglePatch
1783 ===============
1784 */
1785 void SampleSinglePatch (float ctrl[3][3][5], float u, float v, float out[5]) {
1786         float   vCtrl[3][5];
1787         int             vPoint;
1788         int             axis;
1789
1790         // find the control points for the v coordinate
1791         for (vPoint = 0 ; vPoint < 3 ; vPoint++) 
1792         {
1793                 for (axis = 0 ; axis < 5 ; axis++) 
1794                 {
1795                         float   a, b, c;
1796                         float   qA, qB, qC;
1797
1798                         a = ctrl[0][vPoint][axis];
1799                         b = ctrl[1][vPoint][axis];
1800                         c = ctrl[2][vPoint][axis];
1801                         qA = a - 2 * b + c;
1802                         qB = 2 * b - 2 * a;
1803                         qC = a;
1804
1805                         vCtrl[vPoint][axis] = qA * u * u + qB * u + qC;
1806                 }
1807         }
1808
1809         // interpolate the v value
1810         for (axis = 0 ; axis < 5 ; axis++) 
1811         {
1812                 float   a, b, c;
1813                 float   qA, qB, qC;
1814
1815                 a = vCtrl[0][axis];
1816                 b = vCtrl[1][axis];
1817                 c = vCtrl[2][axis];
1818                 qA = a - 2 * b + c;
1819                 qB = 2 * b - 2 * a;
1820                 qC = a;
1821
1822                 out[axis] = qA * v * v + qB * v + qC;
1823         }
1824 }
1825
1826 //spog - Curve LOD stuff starts
1827
1828 float ShadeForNormal(vec3_t normal)
1829 {
1830   float f;
1831
1832         vec3_t L;
1833         L[0] = 1.0f;
1834         L[1] = 1.0f;
1835         L[2] = 1.0f;
1836
1837   
1838         // quick diffuse shading
1839   f = DotProduct(L, normal);
1840
1841   // range 0.5 to 1.0
1842         f = (f+1)/4.0f;
1843   //if (f < 0.0f) f = 0.0f;
1844
1845         f += 0.5f;
1846         
1847   return f;
1848 }
1849
1850 void ShadeVertex (drawVert_t &p)
1851 {
1852         p.lightmap[0] = ShadeForNormal(p.normal);
1853 }
1854
1855
1856 void Patch_DrawNormals(patchMesh_t *patch)
1857 {
1858         int row, col;
1859         vec3_t vNormal;
1860
1861         qglBegin (GL_LINES);
1862         for (col=0; col<patch->width; col++)
1863         {
1864                 for (row=0; row<patch->height; row++)
1865                 {
1866                         VectorAdd(patch->ctrl[col][row].xyz, patch->ctrl[col][row].normal, vNormal);
1867                         qglVertex3fv (patch->ctrl[col][row].xyz);
1868                         qglVertex3fv (vNormal);
1869                 }
1870         }
1871         qglEnd ();
1872 }
1873
1874
1875 // take an array of three drawVerts, and the addresses of three more drawVerts
1876 // interpolate new XYZST values from the three drawVerts, these are:
1877 // the left sub-control-point, the right sub-control-point and the midpoint of the curve respectively
1878 // store these values in the drawVerts passed to the function
1879 void Patch_CurveSplit(drawVert_t *vCurve[3], drawVert_t &pLeft, drawVert_t &pRight, drawVert_t &pMid, float u)
1880 {
1881         int i;
1882         //float u = 0.5f;
1883 //      float a, b;
1884         drawVert_t v1, v2, v3;
1885 //      vec3_t v4;
1886         
1887         for (i=0; i<3; i++)
1888         {
1889                  // xyz
1890                 v1.xyz[i] = vCurve[1]->xyz[i] - vCurve[0]->xyz[i];
1891                 v2.xyz[i] = vCurve[2]->xyz[i] - vCurve[1]->xyz[i];
1892                 v1.xyz[i] *= u;
1893                 v2.xyz[i] *= u;
1894                 pLeft.xyz[i] = vCurve[0]->xyz[i] + v1.xyz[i];
1895                 pRight.xyz[i] = vCurve[1]->xyz[i] + v2.xyz[i];
1896
1897                 v3.xyz[i] = pRight.xyz[i] - pLeft.xyz[i];
1898                 v3.xyz[i] *= u;
1899                 pMid.xyz[i] = pLeft.xyz[i] + v3.xyz[i];
1900
1901                 // normal (weighted average) // no, that's b0rked
1902                 //a = 1 / u; // total
1903                 //b = u * a; // component 2
1904                 //a = u - b; // component 1
1905                 //pMid.normal[i] = u * ((vCurve[0]->normal[i] * b) + (vCurve[2]->normal[i] * a));
1906
1907                 if (i==2) continue;
1908
1909                 // st
1910                 v1.st[i] = vCurve[1]->st[i] - vCurve[0]->st[i];
1911                 v2.st[i] = vCurve[2]->st[i] - vCurve[1]->st[i];
1912                 v1.st[i] *= u;
1913                 v2.st[i] *= u;
1914                 pLeft.st[i] = vCurve[0]->st[i] + v1.st[i];
1915                 pRight.st[i] = vCurve[1]->st[i] + v2.st[i];
1916
1917                 v3.st[i] = pRight.st[i] - pLeft.st[i];
1918                 v3.st[i] *= u;
1919                 pMid.st[i] = pLeft.st[i] + v3.st[i];
1920         }
1921 }
1922
1923 // take an array of three points, return an index representing the curvature of those three points
1924 // return zero if the curve is a straight line, unless the midpoint is not between the endpoints
1925 float Patch_CurveIndex(vec3_t vCurve[])
1926 {
1927         vec3_t vTemp, v1, v2, v3, vClear;
1928 //      int i;
1929         float width, angle;
1930         float index, dot;
1931
1932         VectorClear(vClear);
1933                 
1934         VectorSubtract(vCurve[2], vCurve[0], vTemp);
1935         VectorSubtract(vCurve[1], vCurve[0], v1);
1936         VectorSubtract(vCurve[2], vCurve[1], v2);
1937
1938         if (VectorCompare(v1, vClear) || VectorCompare(vTemp, v1)) // return 0 if 1->2 == 0 or 1->2 == 1->3
1939                 return 0.0f;
1940
1941         VectorNormalize(v1, v1);
1942         VectorNormalize(v2, v2);
1943         if (VectorCompare(v1, v2))
1944                 return 0.0f;
1945         
1946         VectorCopy(vTemp, v3);
1947         width = VectorNormalize(v3, v3);
1948
1949         if (VectorCompare(v1, v3) && VectorCompare(v2, v3))
1950                 return 0.0f;
1951         
1952         dot = DotProduct(v1, v2);
1953
1954         angle = acos(dot) / Q_PI;
1955
1956         index = width * angle;
1957
1958         return index;
1959 }
1960
1961
1962 // create a new tree root, give it the coordinate values of the drawVert
1963 // return a pointer to the new tree root
1964 BTNode_t *BTree_Create(drawVert_t info)
1965 {
1966         BTNode_t *BTree = new BTNode_t;
1967         BTree->left = BTree->right = NULL;
1968         VectorCopy(info.xyz, BTree->info.xyz);
1969         VectorCopy(info.xyz, BTree->vMid.xyz);
1970         for (int i=0; i<2; i++)
1971         {
1972                 BTree->info.st[i] = info.st[i];
1973                 BTree->vMid.st[i] = info.st[i];
1974         }
1975         return BTree;
1976 }
1977
1978 // take ownership of the subtree
1979 // delete the entire subtree
1980 // return a NULL pointer
1981 BTNode_t *BTree_Delete(BTNode_t *pBT)
1982 {
1983         if (pBT != NULL)
1984         {
1985                 BTree_Delete(pBT->left);
1986                 BTree_Delete(pBT->right);
1987                 delete pBT;
1988         }
1989         return NULL;
1990 }
1991
1992 // NOT currently used
1993 BTNode_t *BTree_Clear(BTNode_t *pBT, bool bFirst = true)
1994 {
1995         if (pBT != NULL)
1996         {
1997                 BTree_Clear(pBT->left, false);
1998                 BTree_Clear(pBT->right, false);
1999                 if (!bFirst) delete pBT;
2000         }
2001         return pBT;
2002 }
2003
2004 // take a pointer to the last item added to the list (this can also be a NULL pointer)
2005 // take a pointer to the root of a subtree, and the patch points to the left and right of it
2006 // add a new item to the subtree list, and add the subtree and its adjacent points to the new item
2007 // return a pointer to the last item added to the subtree list
2008 BTreeList_t *BTree_AddToList(BTreeList_t *pBTList, BTNode_t *pBT, drawVert_t &pLeft, drawVert_t &pRight)
2009 {
2010         BTreeList_t *newBTList = new BTreeList_t;
2011         newBTList->next = pBTList;
2012         newBTList->pBT = pBT;
2013         VectorCopy(pLeft.xyz, newBTList->vLeft.xyz);
2014         VectorCopy(pRight.xyz, newBTList->vRight.xyz);
2015         VectorCopy(pLeft.normal, newBTList->vLeft.normal);
2016         VectorCopy(pRight.normal, newBTList->vRight.normal);
2017         for (int i=0; i<2; i++)
2018         {
2019                 newBTList->vLeft.st[i] = pLeft.st[i];
2020                 newBTList->vRight.st[i] = pRight.st[i];
2021         }
2022         return newBTList;
2023 }
2024
2025 // NOT currently used, subtrees are now stored on the patch
2026 // take ownership of the subtree list
2027 // delete the entire list and the subtrees it points to
2028 // return a NULL pointer
2029 BTreeList_t *BTree_DeleteList(BTreeList_t *pBTList)
2030 {
2031         if (pBTList != NULL)
2032         {
2033                 BTree_DeleteList(pBTList->next);
2034                 pBTList->pBT = BTree_Delete(pBTList->pBT);
2035                 delete pBTList;
2036         }
2037         return NULL;
2038 }
2039
2040 // take ownership of the subtree list
2041 // delete the entire subtree list, but not the subtrees themselves
2042 // return a NULL pointer
2043 BTreeList_t *BTree_DeletePointerList(BTreeList_t *pBTList)
2044 {
2045         if (pBTList != NULL)
2046         {
2047                 BTree_DeletePointerList(pBTList->next);
2048                 delete pBTList;
2049         }
2050         return NULL;
2051 }
2052
2053 // take a pointer to the last item added to the list of subtree lists
2054 // add a subtree list to the list
2055 // return a pointer to the last item added
2056 BTListList_t *BTree_AddListToList(BTListList_t *pBTListList, BTreeList_t *pBTList)
2057 {
2058         BTListList_t *newBTListList = new BTListList_t;
2059         newBTListList->next = pBTListList;
2060         newBTListList->list = pBTList;
2061         return newBTListList;
2062 }
2063
2064
2065 // take ownership of the list of subtree lists
2066 // delete the entire list of lists, but not the subtrees themselves
2067 // return a NULL pointer
2068 BTListList_t *BTree_DeleteListFromList(BTListList_t *pBTListList)
2069 {
2070         if (pBTListList != NULL)
2071         {
2072                 BTree_DeleteListFromList(pBTListList->next);
2073                 pBTListList->list = BTree_DeletePointerList(pBTListList->list);
2074                 delete pBTListList;
2075         }
2076         return NULL;
2077 }
2078
2079 // take a pointer to the last item in the list
2080 // add a NULL linker subtree to the list, setting the "flipped" flag using the left curvepoint normal .. er.. hacky?
2081 BTreeList_t *BTree_AddLinkToList(BTreeList_t *pBTList, bool bFlipped = false)
2082 {
2083         BTreeList_t *linkBTList = new BTreeList_t;
2084         linkBTList->pBT = NULL;
2085         linkBTList->next = pBTList;
2086         linkBTList->vLeft.normal[0] = (bFlipped) ? 1.0f : 0.0f;
2087         return linkBTList;
2088 }
2089
2090
2091 // take an array of three points and the address of a vector
2092 // store midpoint of the bezier curve formed by the three points, in the vector
2093 void Patch_BezierInterpolate(vec3_t vCurve[], vec3_t &pMid)
2094 {
2095         vec3_t vTemp;
2096         int i;
2097         VectorSubtract(vCurve[2], vCurve[0], vTemp); // Start->End
2098         for (i=0; i<3; i++)
2099                 vTemp[i] /= 2;
2100         VectorAdd(vCurve[0], vTemp, vTemp); // midpoint of Start->End
2101
2102         VectorSubtract(vTemp, vCurve[1], vTemp); // Mid->(midpoint of Start->End)
2103         for (i=0; i<3; i++)
2104                 vTemp[i] /= 2;
2105         VectorAdd(vCurve[1], vTemp, pMid); // midpoint of Mid->(midpoint of Start->End)
2106 }
2107
2108
2109 // take a pointer to the list of subtrees, and a threshold value
2110 // generate REAL surface curvature for the subtree curves, using bezier interpolation
2111 // if any of the real curves has an index greater than the threshold, return true 
2112 bool Patch_MostCurvedRow(BTreeList_t *pBTList, int threshold)
2113 {
2114         BTreeList_t *p;
2115         float index;//, bestindex = 0;
2116         vec3_t vCurve[3];
2117         vec3_t vRow[3];
2118 //      int i;
2119
2120         for (p = pBTList; p != NULL; p = p->next->next)
2121         {
2122                 // this row
2123                 VectorCopy(p->vLeft.xyz, vCurve[0]);
2124                 VectorCopy(p->pBT->info.xyz, vCurve[1]);
2125                 VectorCopy(p->vRight.xyz, vCurve[2]);
2126                 
2127                 index = Patch_CurveIndex(vCurve);
2128                 if (index > threshold)
2129                         return true;
2130
2131                 if (p->next == NULL)
2132                         break;
2133
2134                 if (p->next->pBT == NULL) continue;
2135
2136                 VectorCopy(p->vLeft.xyz, vCurve[0]);
2137                 VectorCopy(p->next->vLeft.xyz, vCurve[1]);
2138                 VectorCopy(p->next->next->vLeft.xyz, vCurve[2]);
2139                 Patch_BezierInterpolate(vCurve, vRow[0]);
2140
2141                 VectorCopy(p->pBT->info.xyz, vCurve[0]);
2142                 VectorCopy(p->next->pBT->info.xyz, vCurve[1]);
2143                 VectorCopy(p->next->next->pBT->info.xyz, vCurve[2]);
2144                 Patch_BezierInterpolate(vCurve, vRow[1]);
2145
2146                 VectorCopy(p->vRight.xyz, vCurve[0]);
2147                 VectorCopy(p->next->vRight.xyz, vCurve[1]);
2148                 VectorCopy(p->next->next->vRight.xyz, vCurve[2]);
2149                 Patch_BezierInterpolate(vCurve, vRow[2]);
2150
2151                 index = Patch_CurveIndex(vRow);
2152                 if (index > threshold)
2153                         return true;
2154         }
2155         return false;
2156 }
2157
2158
2159 // take a pointer to a list of subtrees.. each subtree in the list is a 3-point bezier curve formed by two endpoints owned by the list, and a midpoint subtree node owned by a patch.
2160 // if any of the subtrees are curved above a threshold, create a left and right subsubtree for each subtree in the list.
2161 // if a NULL linker subtree is found, check for an orientation flip - ie. an inverted LOD-match - and create a NULL subsubtree with the same orientation flip
2162 // this effectively generates trees for multiple patches at the same time.. the subtrees are always owned by their respective patches though
2163 void BTree_ListCurveRecurse(BTreeList_t *pBTList)
2164 {
2165         BTreeList_t *p;
2166         BTreeList_t *leftBTList, *rightBTList;
2167         //drawVert_t pLeft, pRight, pMid;
2168         drawVert_t *vCurve[3];
2169         int threshold;
2170         //int i;
2171         bool bFlipped = false;
2172
2173   if (g_PrefsDlg.m_nSubdivisions >= 1)
2174     threshold = g_PrefsDlg.m_nSubdivisions;
2175         
2176         leftBTList = rightBTList = NULL;
2177         
2178         if (Patch_MostCurvedRow(pBTList, threshold)) // split all subtrees in list if any subtree is above threshold
2179         {
2180                 //Sys_Printf("| ");
2181                 // traverse nodes in list
2182                 for (p = pBTList; p != NULL; p=p->next)
2183                 {
2184                         if (p->pBT == NULL) 
2185                         {
2186                                 leftBTList = BTree_AddLinkToList(leftBTList, (p->vLeft.normal[0] == 1.0f));
2187                                 rightBTList = BTree_AddLinkToList(rightBTList, (p->vLeft.normal[0] == 1.0f));
2188                                 if (p->vLeft.normal[0] == 1.0f) bFlipped = (!bFlipped) ? true : false; // switch bFlipped if true
2189                                 continue;
2190                         }
2191
2192                         // create left node for this subtree
2193                         BTNode_t *newLeft = new BTNode_t;
2194                         p->pBT->left = newLeft;
2195                         newLeft->left = newLeft->right = NULL;
2196                         
2197                         // create right node for this subtree
2198                         BTNode_t *newRight = new BTNode_t;
2199                         p->pBT->right = newRight;
2200                         newRight->left = newRight->right = NULL;
2201
2202                         // split this node
2203                         vCurve[0] = &p->vLeft;
2204                         vCurve[1] = &p->pBT->info;
2205                         vCurve[2] = &p->vRight;
2206                         Patch_CurveSplit(vCurve, newLeft->info, newRight->info, p->pBT->vMid, 0.5);
2207
2208                         memcpy(&newLeft->vMid, &newLeft->info, sizeof(drawVert_t));
2209                         memcpy(&newRight->vMid, &newRight->info, sizeof(drawVert_t));
2210                         
2211                         
2212                         if (!bFlipped)
2213                         {
2214                                 // add new left subtree to left subtree list
2215                                 leftBTList = BTree_AddToList(leftBTList, newLeft, p->vLeft, p->pBT->vMid);
2216                                 
2217                                 // add new right subtree to right subtree list
2218                                 rightBTList = BTree_AddToList(rightBTList, newRight, p->pBT->vMid, p->vRight);
2219                         }
2220                         else
2221                         {
2222                                 // add new left subtree to right subtree list
2223                                 rightBTList = BTree_AddToList(rightBTList, newLeft, p->vLeft, p->pBT->vMid);
2224                                 
2225                                 // add new right subtree to left subtree list
2226                                 leftBTList = BTree_AddToList(leftBTList, newRight, p->pBT->vMid, p->vRight);
2227                         }
2228                 }
2229
2230                 // continue tree left
2231                 BTree_ListCurveRecurse(leftBTList);
2232                 leftBTList = BTree_DeletePointerList(leftBTList);
2233
2234                 // continue tree right
2235                 BTree_ListCurveRecurse(rightBTList);
2236                 rightBTList = BTree_DeletePointerList(rightBTList);
2237         }
2238 }
2239
2240 // take mins and maxs values from two brushes
2241 // return true if they intersect on every axis
2242 bool TouchingAABBs(vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2)
2243 {
2244         //bool xyz[3];
2245         vec3_t v1, v2, p1, p2, T;
2246         for (int i=0; i<3; i++)
2247         {
2248                 v1[i] = maxs1[i] - mins1[i];
2249                 v2[i] = maxs2[i] - mins2[i];
2250                 v1[i] /=2;
2251                 v2[i] /=2;
2252                 p1[i] = mins1[i] + v1[i];
2253                 p2[i] = mins2[i] + v2[i];
2254                 // p1 == origin of aabb1
2255                 // p2 == origin of aabb1
2256                 // v1 == displacement of aabb1
2257                 // v1 == displacement of aabb2
2258                 T[i] = p2[i] - p1[i]; // T == vector from aabb1 to aabb2
2259                 if ( fabs(T[i]) > (fabs(v1[i]) + fabs(v2[i])) )
2260                         return false;
2261         }
2262         return true;
2263 }
2264
2265 // take a pointer to the last item added to pBTList, a pointer to the patch, a row index (start) and a column index
2266 // generate a row of row-curve tree roots, owned by the patch and add the entire column of row-curves to the list, using the row index to decide the order to add
2267 // return a pointer to the last item added to the list
2268 BTreeList_t *Patch_CreateBTListForRows(BTreeList_t *pBTList, patchMesh_t *patch, int start, int col)
2269 {
2270         int row, pos;
2271         patch->colDirty[(col-1)/2] = true;
2272                                                 
2273         if (start == 0)
2274         {
2275                 for (row=0; row<patch->height; row++)
2276                 {
2277                         pos = (((col-1)/2)*patch->height)+row;
2278                         patch->rowLOD[pos] = BTree_Delete(patch->rowLOD[pos]);
2279                         patch->rowLOD[pos] = BTree_Create(patch->ctrl[col][row]);
2280                         pBTList = BTree_AddToList(pBTList, patch->rowLOD[pos], patch->ctrl[col-1][row], patch->ctrl[col+1][row]);
2281                 }
2282         }
2283         else
2284         {
2285                 for (row=patch->height-1; row>=0; row--)
2286                 {
2287                         pos = (((col-1)/2)*patch->height)+row;
2288                         patch->rowLOD[pos] = BTree_Delete(patch->rowLOD[pos]);
2289                         patch->rowLOD[pos] = BTree_Create(patch->ctrl[col][row]);
2290                         pBTList = BTree_AddToList(pBTList, patch->rowLOD[pos], patch->ctrl[col-1][row], patch->ctrl[col+1][row]);
2291                 }
2292         }
2293         return pBTList;
2294 }
2295
2296 // take a pointer to the last item added to pBTList, a pointer to the patch, a row index and a column index (start)
2297 // generate a row of column-curve tree roots, owned by the patch and add the entire row of column-curves to the list, using the column index to decide the order to add
2298 // return a pointer to the last item added to the list
2299 BTreeList_t *Patch_CreateBTListForCols(BTreeList_t *pBTList, patchMesh_t *patch, int row, int start)
2300 {
2301         int col, pos;
2302         patch->rowDirty[(row-1)/2] = true;
2303
2304         if (start == 0)
2305         {
2306                 for (col=0; col<patch->width; col++)
2307                 {
2308                         pos = (((row-1)/2)*patch->width)+col;
2309                         patch->colLOD[pos] = BTree_Delete(patch->colLOD[pos]);
2310                         patch->colLOD[pos] = BTree_Create(patch->ctrl[col][row]);
2311                         pBTList = BTree_AddToList(pBTList, patch->colLOD[pos], patch->ctrl[col][row-1], patch->ctrl[col][row+1]);
2312                 }
2313         }
2314         else
2315         {
2316                 for (col=patch->width-1; col>=0; col--)
2317                 {
2318                         pos = (((row-1)/2)*patch->width)+col;
2319                         patch->colLOD[pos] = BTree_Delete(patch->colLOD[pos]);
2320                         patch->colLOD[pos] = BTree_Create(patch->ctrl[col][row]);
2321                         pBTList = BTree_AddToList(pBTList, patch->colLOD[pos], patch->ctrl[col][row-1], patch->ctrl[col][row+1]);
2322                 }
2323
2324         }
2325         return pBTList;
2326 }
2327
2328 bool BTree_IsInList(BTreeList_t *pBTList, BTNode_t *pBT)
2329 {
2330         BTreeList_t *p;
2331         if (pBTList == NULL) return false;
2332
2333         for (p=pBTList; p != NULL; p=p->next)
2334         {
2335                 if (p->pBT != NULL)
2336                 {
2337                         if (p->pBT == pBT)
2338         return true;
2339                 }
2340         }
2341         return false;
2342 }
2343
2344 int Patch_DegenCurve(vec3_t &start, vec3_t &mid, vec3_t &end)
2345 {
2346   if (VectorCompare(start, mid) || VectorCompare(end, mid))
2347   {
2348     if (VectorCompare(start, end)) return 2;
2349     else return 1;
2350   }
2351   else return 0;
2352 }
2353
2354 // take a pointer to the last item added to the list, and a pointer to a patch (this patch is the owner of the three drawverts)
2355 // take the addresses of three drawVerts, and compare them with the edges of all patches that touch the patch
2356 // if they match an edge, add the tree roots for that section of the matched patch to the list, and recurse for the opposite edge of that patch section. Also, set the matched patch Dirty, so that its drawlists will be rebuilt
2357 // return a pointer to the last item added
2358 BTreeList_t *Patch_FindLODMatches(patchMesh_t *patch, BTreeList_t *pBTList, drawVert_t &pMid, drawVert_t &pLeft, drawVert_t &pRight)
2359 {
2360         brush_t *pb, *brushlist;
2361         int row, col, i;//, pos;
2362         vec3_t vTemp, v1, v2;//, vClear;
2363   bool bAlreadyAdded;
2364
2365         //Sys_Printf("Patch_FindLODMatches: called\n");
2366
2367         if (VectorCompare(pMid.xyz, pLeft.xyz) && VectorCompare(pMid.xyz, pRight.xyz))
2368                 return pBTList;
2369
2370         //VectorClear(vClear);
2371         VectorSubtract(pRight.xyz, pLeft.xyz, vTemp);
2372         VectorSubtract(pMid.xyz, pLeft.xyz, v1);
2373         VectorSubtract(pRight.xyz, pMid.xyz, v2);
2374
2375         //if (VectorCompare(v1, vClear) || VectorCompare(vTemp, v1)) // return null if 1->2 == 0 or 1->2 == 1->3
2376         //      return pBTList;
2377
2378         VectorNormalize(v1, v1);
2379         VectorNormalize(v2, v2);
2380         if (VectorCompare(v1, v2))
2381                 return pBTList;
2382
2383         VectorNormalize(vTemp, vTemp);
2384         if (VectorCompare(v1, vTemp) && VectorCompare(v2, vTemp))
2385                 return pBTList;
2386
2387         brushlist = &active_brushes;
2388         for (i=0; i<2; i++)
2389         {
2390                 for (pb = brushlist->next; pb != brushlist; pb=pb->next)
2391                 {
2392                         if (!pb->patchBrush || pb->pPatch == patch)
2393                                 continue;
2394
2395                         // ignore this patch if its AABB does not touch the subject patch
2396                         if (!TouchingAABBs(patch->pSymbiot->maxs, patch->pSymbiot->mins, pb->maxs, pb->mins))
2397                                 continue;
2398
2399       // all columns of curves
2400                         for (col=1; col<pb->pPatch->width; col+=2)
2401                         {
2402                                 if (pb->pPatch->colDirty[(col-1)/2]) continue;
2403
2404                                 bAlreadyAdded = false;
2405
2406                           // top and bottom curves of this column
2407                                 for (row=0; row<pb->pPatch->height; row+=pb->pPatch->height-1)
2408                                 {
2409           if (bAlreadyAdded)
2410             continue;
2411           //if (!BTree_IsInList(pBTList, pb->pPatch->rowLOD[(((col-1)/2)*patch->height)+row]))
2412           //  continue;
2413                                         // ignore this curve if it shares no mid ctrl point with the test curve
2414                                         if (!VectorCompare (pb->pPatch->ctrl[col][row].xyz, pMid.xyz))
2415                                                 continue;
2416                                         // ignore this curve if it is degenerate
2417                                         if (VectorCompare (pb->pPatch->ctrl[col][row].xyz, pb->pPatch->ctrl[col-1][row].xyz) || VectorCompare (pb->pPatch->ctrl[col][row].xyz, pb->pPatch->ctrl[col+1][row].xyz))
2418                                                 continue;
2419                                         // if curve matches the test curve directly
2420                                         if (VectorCompare (pb->pPatch->ctrl[col-1][row].xyz, pLeft.xyz) && VectorCompare (pb->pPatch->ctrl[col+1][row].xyz, pRight.xyz))
2421                                         {
2422                                                 // add a blank link as separator
2423                                                 pBTList = BTree_AddLinkToList(pBTList);
2424                                                 // add this entire column, if top, top-to-bottom, else bottom to top
2425                                                 pBTList = Patch_CreateBTListForRows(pBTList, pb->pPatch, row, col);
2426                                                 // continue checking from last curve added to list
2427                                           pBTList = Patch_FindLODMatches(pb->pPatch, pBTList, pBTList->pBT->info, pBTList->vLeft, pBTList->vRight);
2428                                                 // set flag
2429                                                 pb->pPatch->LODUpdated = true;
2430             bAlreadyAdded = true;
2431                                         }
2432                                         // if curve matches test curve but flipped
2433                                         else if (VectorCompare (pb->pPatch->ctrl[col-1][row].xyz, pRight.xyz) && VectorCompare (pb->pPatch->ctrl[col+1][row].xyz, pLeft.xyz))
2434                                         {
2435                                                 pBTList = BTree_AddLinkToList(pBTList, true); // flip
2436                                                 pBTList = Patch_CreateBTListForRows(pBTList, pb->pPatch, row, col);
2437                                                 pBTList = Patch_FindLODMatches(pb->pPatch, pBTList, pBTList->pBT->info, pBTList->vLeft, pBTList->vRight);                               
2438                                                 pb->pPatch->LODUpdated = true;
2439             bAlreadyAdded = true;
2440                                         }
2441                                 }
2442                         }
2443
2444                         // all rows of curves
2445                         for (row=1; row<pb->pPatch->height; row+=2)
2446                         {
2447                                 if (pb->pPatch->rowDirty[(row-1)/2]) continue;
2448
2449                                 bAlreadyAdded = false;
2450
2451                           for (col=0; col<pb->pPatch->width; col+=pb->pPatch->width-1)
2452                                 {
2453           if (bAlreadyAdded)
2454             continue;
2455           //if (BTree_IsInList(pBTList, pb->pPatch->colLOD[(((row-1)/2)*patch->width)+col]))
2456           //  continue;
2457                                         if (!VectorCompare (pb->pPatch->ctrl[col][row].xyz, pMid.xyz))
2458                                                 continue;
2459                                         if (VectorCompare (pb->pPatch->ctrl[col][row].xyz, pb->pPatch->ctrl[col][row-1].xyz) || VectorCompare (pb->pPatch->ctrl[col][row].xyz, pb->pPatch->ctrl[col][row+1].xyz))
2460                                                 continue;
2461                                         if (VectorCompare (pb->pPatch->ctrl[col][row-1].xyz, pLeft.xyz) && VectorCompare (pb->pPatch->ctrl[col][row+1].xyz, pRight.xyz))
2462                                         {
2463                                                 pBTList = BTree_AddLinkToList(pBTList);
2464                                                 pBTList = Patch_CreateBTListForCols(pBTList, pb->pPatch, row, col);
2465                                                 pBTList = Patch_FindLODMatches(pb->pPatch, pBTList, pBTList->pBT->info, pBTList->vLeft, pBTList->vRight);
2466                                                 pb->pPatch->LODUpdated = true;
2467             bAlreadyAdded = true;
2468                                         }
2469                                         else if (VectorCompare (pb->pPatch->ctrl[col][row-1].xyz, pRight.xyz) && VectorCompare (pb->pPatch->ctrl[col][row+1].xyz, pLeft.xyz))
2470                                         {
2471                                                 pBTList = BTree_AddLinkToList(pBTList, true); // flip
2472                                                 pBTList = Patch_CreateBTListForCols(pBTList, pb->pPatch, row, col);
2473                                                 pBTList = Patch_FindLODMatches(pb->pPatch, pBTList, pBTList->pBT->info, pBTList->vLeft, pBTList->vRight);
2474             pb->pPatch->LODUpdated = true;
2475             bAlreadyAdded = true;
2476                                         }
2477                                 }
2478                         }
2479                 }
2480                 brushlist = &selected_brushes;
2481         }
2482         return pBTList;
2483 }
2484
2485 // take a pointer to a patch
2486 // create tree roots for all the rows and columns of curves in the patch, the patch takes ownership of these new tree roots
2487 // generate lists of pointers to all the trees in all the patches in the map which need to match the LOD of trees owned by this patch
2488 // store all the lists in a list of lists
2489 // recursively generate the rest of every tree in each list in the list
2490 void Patch_CreateLODTrees(patchMesh_t *patch)
2491 {
2492         BTreeList_t *pBTList;
2493         int col, row, pos;//, rowcount, colcount;
2494         BTListList_t *pLists;
2495
2496         //Sys_Printf("Patch_CreateMatchedLODTrees: called\n");
2497
2498         BTListList_t *LODLists;
2499         LODLists = NULL;
2500         
2501         pBTList = NULL;
2502
2503         patch->bDirty = false;
2504         patch->LODUpdated = true;
2505         
2506         for(col=1; col<patch->width; col+=2)
2507         {
2508                 if (patch->colDirty[(col-1)/2]) continue;
2509                 else patch->colDirty[(col-1)/2] = true;
2510
2511                 // create list for rows of current patch
2512                 for(row=0; row<patch->height; row++)
2513                 {
2514                         pos = (((col-1)/2)*patch->height)+row;
2515                         patch->rowLOD[pos] = BTree_Delete(patch->rowLOD[pos]);
2516                         patch->rowLOD[pos] = BTree_Create(patch->ctrl[col][row]);
2517                         pBTList = BTree_AddToList(pBTList, patch->rowLOD[pos], patch->ctrl[col-1][row], patch->ctrl[col+1][row]);
2518                 }
2519         
2520                 //create connection list for first row
2521                 pBTList = Patch_FindLODMatches(patch, pBTList, patch->ctrl[col][0], patch->ctrl[col-1][0], patch->ctrl[col+1][0]);
2522                 //create connection list for last row
2523                 pBTList = Patch_FindLODMatches(patch, pBTList, patch->ctrl[col][row-1], patch->ctrl[col-1][row-1], patch->ctrl[col+1][row-1]);
2524
2525                 LODLists = BTree_AddListToList(LODLists, pBTList);
2526                 pBTList = NULL;
2527         }
2528
2529         pBTList = NULL;
2530         for(row=1; row<patch->height; row+=2)
2531         {
2532                 if (patch->rowDirty[(row-1)/2]) continue;
2533                 else patch->rowDirty[(row-1)/2] = true;
2534
2535                 // create list for cols of current patch
2536                 for(col=0; col<patch->width; col++)
2537                 {
2538                         pos = (((row-1)/2)*patch->width)+col;
2539                         patch->colLOD[pos] = BTree_Delete(patch->colLOD[pos]);
2540                         patch->colLOD[pos] = BTree_Create(patch->ctrl[col][row]);
2541                         pBTList = BTree_AddToList(pBTList, patch->colLOD[pos], patch->ctrl[col][row-1], patch->ctrl[col][row+1]);
2542                 }
2543
2544                 //create connection list for first col
2545                 pBTList = Patch_FindLODMatches(patch, pBTList, patch->ctrl[0][row], patch->ctrl[0][row-1], patch->ctrl[0][row+1]);
2546                 //create connection list for last col
2547                 pBTList = Patch_FindLODMatches(patch, pBTList, patch->ctrl[col-1][row], patch->ctrl[col-1][row-1], patch->ctrl[col-1][row+1]);
2548
2549                 LODLists = BTree_AddListToList(LODLists, pBTList);
2550                 pBTList = NULL;
2551         }
2552
2553         for (pLists = LODLists; pLists != NULL; pLists=pLists->next)
2554                 BTree_ListCurveRecurse(pLists->list);
2555         LODLists = BTree_DeleteListFromList(LODLists);
2556 }
2557
2558 int Patch_GetCVTangent(vec3_t &v1, vec3_t &p1, vec3_t &p2, vec3_t &p3)
2559 {
2560         if (VectorCompare(p1, p2))
2561         {
2562                 if (VectorCompare(p1, p3))
2563                 {
2564                         return 2;
2565                 }
2566                 else VectorSubtract(p3, p1, v1);
2567     return 1;
2568         }
2569         else VectorSubtract(p2, p1, v1);
2570   return 0;
2571 }
2572
2573 void Patch_CVNormal(vec3_t ctrl[3][3], vec3_t &normal)
2574 {
2575         vec3_t v1, v2, vTemp1, vTemp2;
2576   int a, b;
2577         
2578   a = Patch_GetCVTangent(v1, ctrl[0][0], ctrl[1][0], ctrl[2][0]);
2579         b = Patch_GetCVTangent(v2, ctrl[0][0], ctrl[0][1], ctrl[0][2]);
2580
2581         //Sys_Printf("p1: (%1.1f %1.1f %1.1f) p2: (%1.1f %1.1f %1.1f) p2: (%1.1f %1.1f %1.1f)\n",
2582         //      ctrl[0][0][0], ctrl[0][0][1], ctrl[0][0][2], ctrl[0][2][0], ctrl[0][2][1], ctrl[0][2][2], ctrl[2][0][0], ctrl[2][0][1], ctrl[2][0][2]);
2583         
2584   if (a == 2)
2585   {
2586     a = Patch_GetCVTangent(v1, ctrl[0][0], ctrl[1][1], ctrl[1][2]);
2587   }
2588   if (b == 2)
2589   {
2590     b = Patch_GetCVTangent(v2, ctrl[0][0], ctrl[1][1], ctrl[2][1]);
2591   }
2592  
2593   if (a == 2)
2594   {
2595     a = Patch_GetCVTangent(v1, ctrl[0][0], ctrl[2][1], ctrl[2][2]);
2596   }
2597   if (b == 2)
2598   {
2599     b = Patch_GetCVTangent(v2, ctrl[0][0], ctrl[1][2], ctrl[2][2]);
2600   }  
2601
2602         CrossProduct(v1, v2, normal);
2603
2604   
2605   if (normal[0] == 0.0f && normal[1] == 0.0f && normal[2] == 0.0f)
2606   {
2607     // more degenerate cases
2608     vec3_t pMid;
2609     vec3_t vCurve[3];
2610     /*
2611     if (VectorCompare(ctrl[0][0], ctrl[2][0])) // endcap left
2612     {
2613       if (VectorCompare(ctrl[0][2], ctrl[1][2]))
2614       {
2615         VectorSubtract(ctrl[2][2], ctrl[0][0], v2);
2616       }
2617       else if (VectorCompare(ctrl[1][2], ctrl[2][2]))
2618       {
2619         VectorSubtract(ctrl[0][2], ctrl[0][0], v2);
2620       }
2621       else
2622       a = Patch_DegenCurve(ctrl[0][2], ctrl[1][2], ctrl[2][2]);
2623       if (a == 0)
2624       {
2625         VectorCopy(ctrl[0][2], vCurve[0]);
2626         VectorCopy(ctrl[1][2], vCurve[1]);
2627         VectorCopy(ctrl[2][2], vCurve[2]);
2628         Patch_BezierInterpolate(vCurve, pMid);
2629                           VectorSubtract(pMid, ctrl[0][0], v1);
2630       }
2631       
2632       
2633                 }
2634           else if (VectorCompare(ctrl[0][0], ctrl[0][2])) // endcap right
2635                 {
2636       
2637       if (VectorCompare(ctrl[2][0], ctrl[2][1]))
2638       {
2639         VectorSubtract(ctrl[2][2], ctrl[0][0], v2);
2640       }
2641       else if (VectorCompare(ctrl[2][1], ctrl[2][2]))
2642       {
2643         VectorSubtract(ctrl[2][0], ctrl[0][0], v2);
2644       }
2645       else
2646       
2647       b = Patch_DegenCurve(ctrl[2][0], ctrl[2][1], ctrl[2][2]);
2648       if (b == 0)
2649       {
2650                     VectorCopy(ctrl[2][0], vCurve[0]);
2651         VectorCopy(ctrl[2][1], vCurve[1]);
2652         VectorCopy(ctrl[2][2], vCurve[2]);
2653         Patch_BezierInterpolate(vCurve, pMid);
2654                           VectorSubtract(pMid, ctrl[0][0], v2);
2655       }
2656       
2657                 }
2658     */
2659     if (VectorCompare(ctrl[0][0], ctrl[2][0])) // bottom degen
2660     {
2661       Patch_GetCVTangent(v1, ctrl[0][0], ctrl[2][1], ctrl[2][2]);
2662     }
2663     else if (VectorCompare(ctrl[0][0], ctrl[0][2])) // left degen
2664     {
2665       Patch_GetCVTangent(v2, ctrl[0][0], ctrl[1][2], ctrl[2][2]);
2666     }
2667     else if (VectorCompare(ctrl[0][2], ctrl[2][2])) // top degen
2668     {
2669       VectorSubtract(ctrl[2][0], ctrl[0][0], v1);
2670     }
2671     else if (VectorCompare(ctrl[2][0], ctrl[2][2])) // right degen
2672     {
2673       VectorSubtract(ctrl[0][2], ctrl[0][0], v2);
2674     }
2675     else // tangents parallel
2676     {
2677       VectorCopy(v1, vTemp1);
2678       VectorCopy(v2, vTemp2);
2679       VectorNormalize(vTemp1, vTemp1);
2680       VectorNormalize(vTemp2, vTemp2);
2681       if (VectorCompare(vTemp1, vTemp2)) // parallel same way
2682       {
2683         VectorSubtract(ctrl[2][0], ctrl[0][0], vTemp1);
2684         VectorNormalize(vTemp1, vTemp1);
2685         if (VectorCompare(vTemp1, vTemp2))
2686         {
2687           VectorSubtract(ctrl[0][2], ctrl[0][0], v2);
2688         }
2689         else
2690         {
2691           VectorCopy(vTemp1, v1);
2692         }
2693       }
2694       else // parallel opposite way
2695       {
2696                     VectorCopy(ctrl[2][0], vCurve[0]);
2697         VectorCopy(ctrl[1][1], vCurve[1]);
2698         VectorCopy(ctrl[0][2], vCurve[2]);
2699         Patch_BezierInterpolate(vCurve, pMid);
2700                           VectorSubtract(pMid, ctrl[0][0], v2);
2701                   }
2702     }
2703
2704     CrossProduct(v1, v2, normal);
2705   }
2706 }
2707
2708 void Patch_CalcCVNormals(patchMesh_t *patch)
2709 {
2710         int row, col, i, j, n;
2711         vec3_t ctrl[3][3];
2712         vec3_t normals[4];
2713
2714         for (col=0; col<patch->width; col+=2)
2715         {
2716                 for (row=0; row<patch->height; row+=2)
2717                 {
2718                         n=0;
2719                         if (col+1 != patch->width && row+1 != patch->height)
2720                         {
2721                                 for (i=0; i<3; i++)
2722                                         for (j=0; j<3; j++)
2723                                                 VectorCopy (patch->ctrl[col+i][row+j].xyz, ctrl[i][j]);
2724
2725                                 Patch_CVNormal(ctrl, normals[n]);
2726         VectorNormalize(normals[n], normals[n]);
2727                                 n++;
2728                         }
2729
2730                         if (col-1 >= 0 && row-1 >= 0)
2731                         {
2732                                 for (i=0; i<3; i++)
2733                                         for (j=0; j<3; j++)
2734                                                 VectorCopy (patch->ctrl[col-i][row-j].xyz, ctrl[i][j]);
2735
2736                                 Patch_CVNormal(ctrl, normals[n]);
2737         VectorNormalize(normals[n], normals[n]);
2738                                 n++;
2739                         }
2740                         if (col-1 >= 0 && row+1 != patch->height)
2741                         {
2742                                 for (i=0; i<3; i++)
2743                                         for (j=0; j<3; j++)
2744                                                 VectorCopy (patch->ctrl[col-i][row+j].xyz, ctrl[j][i]);
2745
2746                                 Patch_CVNormal(ctrl, normals[n]);
2747         VectorNormalize(normals[n], normals[n]);
2748                                 n++;
2749                         }
2750                         if (col+1 != patch->width && row-1 >= 0)
2751                         {
2752                                 for (i=0; i<3; i++)
2753                                         for (j=0; j<3; j++)
2754                                                 VectorCopy (patch->ctrl[col+i][row-j].xyz, ctrl[j][i]);
2755
2756                                 Patch_CVNormal(ctrl, normals[n]);
2757         VectorNormalize(normals[n], normals[n]);
2758                                 n++;
2759                         }
2760
2761                         for (i=0; i<3; i++)
2762                         {
2763                                 if (n == 1) patch->ctrl[col][row].normal[i] = normals[0][i];
2764                                 if (n == 2) patch->ctrl[col][row].normal[i] = (normals[0][i] + normals[1][i]) / n;
2765                                 //if (n == 3) patch->ctrl[col][row].normal[i] = (normals[0][i] + normals[1][i] + normals[2][i]) / n;
2766                                 if (n == 4) patch->ctrl[col][row].normal[i] = (normals[0][i] + normals[1][i] + normals[2][i] + normals[3][i]) / n;
2767                         }
2768                         VectorNormalize(patch->ctrl[col][row].normal, patch->ctrl[col][row].normal);
2769                         //if (!g_PrefsDlg.m_bGLLighting)
2770                         //      ShadeVertex(patch->ctrl[col][row]);
2771                 }
2772         }
2773 }
2774
2775
2776 void BTree_SetNormals(BTNode_t *pBT, vec3_t &normal)
2777 {
2778         if (pBT != NULL)
2779         {
2780                 if (pBT->left != NULL && pBT->right != NULL)
2781                 {
2782                         VectorCopy(normal, pBT->vMid.normal);
2783                         //if (!g_PrefsDlg.m_bGLLighting)
2784                         //      ShadeVertex(pBT->vMid);
2785                 }
2786                 BTree_SetNormals(pBT->left, normal);
2787                 BTree_SetNormals(pBT->right, normal);
2788         }
2789 }
2790
2791
2792 void NormalFromPoints(vec3_t p1, vec3_t p2, vec3_t p3, vec3_t &normal, bool flip = false)
2793 {
2794   vec3_t v1, v2;
2795
2796   if (flip)
2797   {
2798     VectorSubtract(p2, p3, v1); //p3->p2
2799           VectorSubtract(p1, p2, v2); //p2->p1
2800   }
2801   else
2802   {
2803     VectorSubtract(p2, p1, v1); //p1->p2
2804           VectorSubtract(p3, p2, v2); //p2->p3
2805   }
2806         CrossProduct(v1, v2, normal);
2807 }
2808
2809
2810 void BTree_GenerateNormals(BTNode_t *pBTMid, BTNode_t *pBTLeft, BTNode_t *pBTRight, bool avg, bool flat, bool nomid, bool noleft, bool noright, /*bool endcap, vec3_t &n1, vec3_t &n2,*/ bool flip)
2811 {
2812         if (pBTMid != NULL)
2813         {
2814                 if (pBTMid->left != NULL && pBTMid->right != NULL)
2815                 {
2816                         vec3_t normal;
2817      
2818       if (noleft) // left curve is degenerate
2819       {
2820         if (nomid) // mid curve is degenerate
2821         {
2822           NormalFromPoints(pBTRight->right->info.xyz, pBTRight->vMid.xyz, pBTMid->vMid.xyz, normal, flip);
2823                 NormalFromPoints(pBTRight->right->info.xyz, pBTRight->vMid.xyz, pBTMid->vMid.xyz, pBTRight->vMid.normal, flip);
2824         }
2825         //else if (endcap)
2826         //{
2827         //  VectorCopy(n1, normal);
2828         //  NormalFromPoints(pBTRight->right->info.xyz, pBTRight->vMid.xyz, pBTMid->vMid.xyz, pBTRight->vMid.normal, flip);
2829         //}
2830         else
2831         {
2832           NormalFromPoints(pBTMid->left->info.xyz, pBTLeft->vMid.xyz, pBTMid->vMid.xyz, normal, flip);
2833                 NormalFromPoints(pBTRight->right->info.xyz, pBTRight->vMid.xyz, pBTMid->vMid.xyz, pBTRight->vMid.normal, flip);
2834         }
2835       }
2836       else if (noright) // right curve is degenerate
2837       {
2838         if (nomid) // mid curve is degenerate
2839         {
2840           NormalFromPoints(pBTLeft->left->info.xyz, pBTLeft->vMid.xyz, pBTMid->vMid.xyz, normal, flip);
2841                 NormalFromPoints(pBTLeft->left->info.xyz, pBTLeft->vMid.xyz, pBTMid->vMid.xyz, pBTRight->vMid.normal, flip);
2842         }
2843         //else if (endcap)
2844         //{
2845         //  NormalFromPoints(pBTLeft->left->info.xyz, pBTLeft->vMid.xyz, pBTMid->vMid.xyz, normal, flip);
2846         //  VectorCopy(n2, pBTRight->vMid.normal);
2847         //}
2848         else
2849         {
2850           NormalFromPoints(pBTLeft->left->info.xyz, pBTLeft->vMid.xyz, pBTMid->vMid.xyz, normal, flip);
2851                 NormalFromPoints(pBTMid->right->info.xyz, pBTRight->vMid.xyz, pBTMid->vMid.xyz, pBTRight->vMid.normal, flip);
2852         }
2853       }
2854       else
2855       {
2856         if (flat) // all curves are semi-degenerate (flat) or degenerate
2857         {
2858           NormalFromPoints(pBTLeft->left->info.xyz, pBTLeft->vMid.xyz, pBTRight->vMid.xyz, normal, flip);
2859                 NormalFromPoints(pBTRight->right->info.xyz, pBTRight->vMid.xyz, pBTLeft->vMid.xyz, pBTRight->vMid.normal, flip);
2860         }
2861         else
2862         {
2863                       NormalFromPoints(pBTLeft->left->info.xyz, pBTLeft->vMid.xyz, pBTMid->vMid.xyz, normal, flip);
2864                 NormalFromPoints(pBTRight->right->info.xyz, pBTRight->vMid.xyz, pBTMid->vMid.xyz, pBTRight->vMid.normal, flip);
2865         }
2866       }
2867
2868       VectorNormalize(normal, normal);
2869                         if (avg)
2870                                 for (int i=0; i<3; i++)
2871                                         pBTLeft->vMid.normal[i] = (normal[i] + pBTLeft->vMid.normal[i]) / 2.0f;
2872                         else VectorCopy(normal, pBTLeft->vMid.normal);
2873      
2874                         VectorNormalize(pBTLeft->vMid.normal, pBTLeft->vMid.normal);
2875       VectorNormalize(pBTRight->vMid.normal, pBTRight->vMid.normal);
2876
2877                 }
2878                 BTree_GenerateNormals(pBTMid->left, pBTLeft->left, pBTRight->left, avg, flat, nomid, noleft, noright, /*endcap, n1, n2,*/ flip);
2879                 BTree_GenerateNormals(pBTMid->right, pBTLeft->right, pBTRight->right, avg, flat, nomid, noleft, noright, /*endcap, n1, n2,*/ flip);
2880         }
2881 }
2882
2883
2884
2885 void Patch_GenerateLODNormals(patchMesh_t *patch)
2886 {
2887   int col, row, rowpos, colpos, i;
2888   BTNode_t *tree[2][3];
2889   int degen[2][3];
2890   bool rowAvg, colAvg;
2891
2892   for(col=0; col+2<patch->width; col+=2)
2893         {
2894                 for(row=0; row+2<patch->height; row+=2)
2895                 {
2896       if (!patch->colDirty[col/2] && !patch->rowDirty[row/2]) continue;  
2897  
2898                 rowpos = ((col/2)*patch->height)+row;
2899       colpos = ((row/2)*patch->width)+col;
2900       
2901       if (row==0) rowAvg = false;
2902       else rowAvg = true;
2903       if (col==0) colAvg = false;
2904       else colAvg = true;
2905
2906       for (i=0; i<3; i++)
2907       {
2908         tree[0][i] = patch->rowLOD[rowpos+i];
2909         tree[1][i] = patch->colLOD[colpos+i];
2910         
2911         degen[0][i] = Patch_DegenCurve(patch->ctrl[col][row+i].xyz, patch->ctrl[col+1][row+i].xyz, patch->ctrl[col+2][row+i].xyz);
2912         degen[1][i] = Patch_DegenCurve(patch->ctrl[col+i][row].xyz, patch->ctrl[col+i][row+1].xyz, patch->ctrl[col+i][row+2].xyz);
2913       }
2914
2915       BTree_GenerateNormals(tree[0][1], tree[0][0], tree[0][2], rowAvg, (degen[1][0] && degen[1][1] && degen[1][2]), degen[0][1] == 2, degen[0][0] == 2, degen[0][2] == 2, /*degen[1][1], patch->ctrl[col][row].normal, patch->ctrl[col][row+2].normal,*/ false);
2916       BTree_GenerateNormals(tree[1][1], tree[1][0], tree[1][2], colAvg, (degen[0][0] && degen[0][1] && degen[0][2]), degen[1][1] == 2, degen[1][0] == 2, degen[1][2] == 2, /*degen[0][1], patch->ctrl[col][row].normal, patch->ctrl[col+2][row].normal,*/ true);
2917     }
2918   }
2919 }
2920
2921
2922 void Patch_ClearLODFlags(patchMesh_t *p)
2923 {
2924         int i;
2925
2926         for (i=0;i<(p->width-1)/2; i++)
2927                 p->colDirty[i] = false;
2928
2929         for (i=0;i<(p->height-1)/2; i++)
2930                 p->rowDirty[i] = false;
2931 }
2932
2933 // reset the lodDirty flags owned by all patches in the map
2934 // create new LOD trees for all dirty patches, matched with all other patches in the map
2935 void Patch_LODMatchAll()
2936 {
2937         brush_t *pb, *brushlist;
2938         int i;
2939
2940         // create LOD tree roots and LOD tree lists for all patches that are dirty
2941
2942         brushlist = &active_brushes;
2943         for (i=0; i<2; i++)
2944         {
2945                 for (pb = brushlist->next; pb && (pb != brushlist); pb=pb->next)
2946                 {
2947       // create lod for selected patches when patches are filtered
2948                         if (pb->bFiltered && (pb->patchBrush && !pb->pPatch->bSelected))
2949                                 continue;
2950                         if (!pb->patchBrush)
2951                                 continue;
2952                         if (!pb->pPatch->bDirty)
2953                                 continue;
2954
2955       Patch_CalcCVNormals(pb->pPatch);
2956                         Patch_CreateLODTrees(pb->pPatch);
2957                 }
2958                 brushlist = &selected_brushes;
2959         }
2960
2961         brushlist = &active_brushes;
2962         for (i=0; i<2; i++)
2963         {
2964                 for (pb = brushlist->next; pb && (pb != brushlist); pb=pb->next)
2965                 {
2966                         if (!pb->patchBrush)
2967                                 continue;
2968
2969       if (pb->pPatch->LODUpdated)
2970                           Patch_GenerateLODNormals(pb->pPatch);
2971
2972       Patch_ClearLODFlags(pb->pPatch);
2973     }
2974                 brushlist = &selected_brushes;
2975         }
2976
2977 }
2978
2979 void Vertex_TransformTexture(drawVert_t *pVert, float fx, float fy, transformtype xform)
2980 {
2981   switch(xform)
2982   {
2983   case TRANSLATE:
2984     pVert->st[0] += fx;
2985     pVert->st[1] += fy;
2986     break;
2987   case SCALE:
2988     pVert->st[0] *= fx;
2989     pVert->st[1] *= fy;
2990     break;
2991   case ROTATE:
2992     float x = pVert->st[0];
2993     float y = pVert->st[1];
2994     pVert->st[0] = x * fx - y * fy;
2995     pVert->st[1] = y * fx + x * fy;
2996   }
2997 }
2998
2999 void BTree_TransformTexture(BTNode_t *pBT, float fx, float fy, transformtype xform)
3000 {
3001         if (pBT != NULL)
3002         {       // PreOrder traversal
3003                 Vertex_TransformTexture(&pBT->info, fx, fy, xform);
3004     Vertex_TransformTexture(&pBT->vMid, fx, fy, xform);
3005                 BTree_TransformTexture(pBT->left, fx, fy, xform);
3006                 BTree_TransformTexture(pBT->right, fx, fy, xform);
3007         }
3008 }
3009
3010 void Patch_TransformLODTexture(patchMesh_t *p, float fx, float fy, transformtype xform)
3011 {
3012         int col, row;
3013
3014         for(col=1; col<p->width; col+=2)
3015                 for(row=0; row<p->height; row++)
3016                         BTree_TransformTexture(p->rowLOD[(((col-1)/2)*p->height)+row], fx, fy, xform);
3017
3018         for(row=1; row<p->height; row+=2)
3019                 for(col=0; col<p->width; col++)
3020                         BTree_TransformTexture(p->colLOD[(((row-1)/2)*p->width)+col], fx, fy, xform);
3021 }
3022
3023 void Patch_AddBTreeToDrawListInOrder(list<drawVert_t> *drawList, BTNode_t *pBT)
3024 {
3025         if (pBT != NULL) //traverse InOrder
3026         {
3027                 Patch_AddBTreeToDrawListInOrder(drawList, pBT->left);
3028                 if (pBT->left != NULL && pBT->right != NULL)
3029                         drawList->push_back(pBT->vMid);
3030                 Patch_AddBTreeToDrawListInOrder(drawList, pBT->right);
3031         }
3032 }
3033
3034 void Patch_InterpolateListFromRowBT(list<drawVert_t> *drawList, BTNode_t *rowBT, BTNode_t *rowBTLeft, drawVert_t *vCurve[], float u, float n, float v)
3035 {
3036         if (rowBT != NULL)
3037         {
3038                 Patch_InterpolateListFromRowBT(drawList, rowBT->left, rowBTLeft->left, vCurve, u-n, n*0.5f, v);
3039                 if (rowBT->left != NULL && rowBT->right != NULL)
3040                 {
3041                         vec3_t v1, v2;
3042                         drawVert_t newVert, vTemp1, vTemp2;
3043                         Patch_CurveSplit(vCurve, vTemp1, vTemp2, newVert, u);
3044                         for (int i=0; i<3; i++)
3045                         {
3046                                 v1[i] = rowBT->vMid.xyz[i] - rowBTLeft->vMid.xyz[i]; // left -> mid
3047                                 v1[i] = rowBTLeft->vMid.xyz[i] + (v1[i] * v);
3048                                 v1[i] = newVert.xyz[i] - v1[i];
3049                         }
3050                         VectorSubtract(vTemp1.xyz, newVert.xyz, v2);
3051                         CrossProduct(v1, v2, newVert.normal);
3052                         VectorNormalize(newVert.normal, newVert.normal);
3053                         //if (!g_PrefsDlg.m_bGLLighting)
3054                         //      ShadeVertex(newVert);
3055                         drawList->push_back(newVert);
3056                 }
3057                 Patch_InterpolateListFromRowBT(drawList, rowBT->right, rowBTLeft->right, vCurve, u+n, n*0.5f, v);
3058         }
3059 }
3060
3061 void Patch_TraverseColBTInOrder(list<list<drawVert_t>*>::iterator& iter, BTNode_t *colBTLeft, BTNode_t *colBT, BTNode_t *colBTRight, BTNode_t *rowBT, BTNode_t *rowBTLeft, float v, float n)
3062 {
3063         if (colBT != NULL)
3064         {
3065                  //traverse subtree In Order
3066                 Patch_TraverseColBTInOrder(iter, colBTLeft->left, colBT->left, colBTRight->left, rowBT, rowBTLeft, v-n, n*0.5f);
3067                 if (colBT->left != NULL && colBT->right != NULL)
3068                 {
3069                         drawVert_t *vCurve[3];
3070                         vCurve[0] = &colBTLeft->vMid;
3071                         vCurve[1] = &colBT->vMid;
3072                         vCurve[2] = &colBTRight->vMid;
3073                         Patch_InterpolateListFromRowBT((*iter), rowBT, rowBTLeft, vCurve, 0.5f, 0.25f, v);
3074
3075                         (*iter)->push_back(colBTRight->vMid);
3076                         iter++;
3077                 }
3078                 Patch_TraverseColBTInOrder(iter, colBTLeft->right, colBT->right, colBTRight->right, rowBT, rowBTLeft, v+n, n*0.5f);
3079         }
3080 }
3081
3082
3083 void Patch_StartDrawLists(list<list<drawVert_t>*> *drawLists, BTNode_t *colBT)
3084 {
3085         if (colBT != NULL)
3086         {
3087                  //traverse subtree In Order
3088                 Patch_StartDrawLists(drawLists, colBT->left);
3089                 if (colBT->left != NULL && colBT->right != NULL)
3090                 {
3091                         list<drawVert_t> *newList = new list<drawVert_t>;
3092                         drawLists->push_back(newList); // add empty list to back
3093                         drawLists->back()->push_back(colBT->vMid);
3094                 }
3095                 Patch_StartDrawLists(drawLists, colBT->right);
3096         }
3097 }
3098
3099 typedef list<drawVert_t> drawList_t;
3100 typedef list<list<drawVert_t>*> drawLists_t;
3101
3102 void Patch_CreateDrawLists(patchMesh_t *patch)
3103 {
3104   int col, row, colpos, rowpos;
3105
3106         drawLists_t *drawLists = new drawLists_t;
3107
3108         drawLists_t::iterator iter1, iter2;
3109
3110         for (row=0; row<patch->height; row+=2)
3111         {
3112                 colpos = (row/2)*patch->width;
3113                 drawList_t *newList = new drawList_t;
3114                 drawLists->push_back(newList); // add a new empty list to back
3115                 drawLists->back()->push_back(patch->ctrl[0][row]); // fill list at back
3116
3117                 if (row+1 == patch->height)
3118                         continue;
3119                 Patch_StartDrawLists(drawLists, patch->colLOD[colpos]);
3120         }
3121
3122         iter1 = drawLists->begin();
3123         for (row=0; row<patch->height; row+=2)
3124         {
3125                 iter2 = iter1;
3126                 for (col=0; col+1<patch->width; col+=2)
3127                 {
3128                         iter1 = iter2;
3129                         colpos = ((row/2)*patch->width)+col;
3130                         rowpos = ((col/2)*patch->height)+row;
3131
3132       Patch_AddBTreeToDrawListInOrder((*iter1), patch->rowLOD[rowpos]);
3133                         (*iter1)->push_back(patch->ctrl[col+2][row]);
3134                         
3135                         if (row+1 == patch->height)
3136                                 continue;
3137                 
3138                         iter1++;        
3139
3140       Patch_TraverseColBTInOrder(iter1, patch->colLOD[colpos], patch->colLOD[colpos+1], patch->colLOD[colpos+2], patch->rowLOD[rowpos+1], patch->rowLOD[rowpos], 0.5, 0.25);
3141                 }
3142         }
3143
3144   patch->drawLists = drawLists;
3145 }
3146
3147
3148 void Patch_DeleteDrawLists(patchMesh_t *patch)
3149 {
3150   drawLists_t *drawLists;
3151   drawLists_t::iterator iter;
3152
3153   if (patch->drawLists == NULL)
3154     return;
3155
3156   drawLists = (drawLists_t *)patch->drawLists;
3157
3158         for (iter=drawLists->begin(); iter != drawLists->end(); iter++)
3159         {
3160                 delete (*iter);
3161         }
3162
3163   delete drawLists;
3164   patch->drawLists = NULL;
3165 }
3166
3167
3168 void Patch_DrawLODPatchMesh(patchMesh_t *patch)
3169 {
3170         drawLists_t *drawLists;
3171
3172         drawLists_t::iterator iterLists, iterListsNext;
3173         drawList_t::iterator iterList, iterListNext;
3174
3175   //int nGLState = g_pParentWnd->GetCamera()->Camera()->draw_glstate;
3176
3177   if (patch->drawLists == NULL)
3178     return;
3179
3180   drawLists = (drawLists_t *)patch->drawLists;
3181
3182         iterListsNext=drawLists->begin();
3183         iterListsNext++;
3184         for (iterLists=drawLists->begin(); iterLists != drawLists->end() && iterListsNext != drawLists->end(); iterLists++, iterListsNext++)
3185   {
3186                 // traverse two drawlists at once to draw a strip
3187     //if (nGLState & DRAW_GL_LINE)
3188                   qglBegin(GL_QUAD_STRIP);
3189     //else
3190     //  qglBegin(GL_TRIANGLE_STRIP);
3191                 for (iterList=(*iterLists)->begin(), iterListNext=(*iterListsNext)->begin(); iterList != (*iterLists)->end() && iterListNext != (*iterListsNext)->end(); iterList++, iterListNext++)
3192                 {
3193                         //if (g_PrefsDlg.m_bGLLighting)
3194                                 qglNormal3fv((*iterList).normal);
3195                         //else if (bShade && !g_PrefsDlg.m_bDisplayLists)
3196                         //      qglColor3f((*iterList).lightmap[0], (*iterList).lightmap[0], (*iterList).lightmap[0]);
3197
3198                         qglTexCoord2fv((*iterList).st);
3199                         qglVertex3fv((*iterList).xyz);
3200
3201                   //if (g_PrefsDlg.m_bGLLighting)
3202                                 qglNormal3fv((*iterListNext).normal);
3203                         //else if (bShade && !g_PrefsDlg.m_bDisplayLists)
3204                         //      qglColor3f((*iterListNext).lightmap[0], (*iterListNext).lightmap[0], (*iterListNext).lightmap[0]);
3205
3206                         qglTexCoord2fv((*iterListNext).st);
3207                         qglVertex3fv((*iterListNext).xyz);
3208     }
3209                 qglEnd();
3210   }
3211 /*
3212 #ifdef _DEBUG
3213   vec3_t vNormal;
3214   for (iterLists=drawLists->begin(); iterLists != drawLists->end(); iterLists++)
3215   {
3216                 qglBegin (GL_LINES); // draw normals
3217                 //qglColor3f(1,1,1);
3218                 for (iterList=(*iterLists)->begin(); iterList != (*iterLists)->end(); iterList++)
3219                 {
3220                         VectorAdd((*iterList).xyz, (*iterList).normal, vNormal);
3221                         qglVertex3fv ((*iterList).xyz);
3222                         qglVertex3fv (vNormal);
3223                 }
3224                 qglEnd ();
3225   }
3226
3227         Patch_DrawNormals(patch);
3228
3229 #endif
3230   */
3231 }
3232
3233 /*
3234 // fast memory-efficient ray-triangle intersection - MollerTrumbore97 
3235
3236 #define EPSILON 0.000001
3237 #define CROSS(dest,v1,v2) {dest[0]=v1[1]*v2[2]-v1[2]*v2[1];dest[1]=v1[2]*v2[0]-v1[0]*v2[2];dest[2]=v1[0]*v2[1]-v1[1]*v2[0];}
3238 #define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2])
3239 #define SUB(dest,v1,v2) {dest[0]=v1[0]-v2[0];dest[1]=v1[1]-v2[1];dest[2]=v1[2]-v2[2];}
3240
3241 int intersect_triangle(float orig[3], float dir[3],
3242                    float vert0[3], float vert1[3], float vert2[3],
3243                    double *t, double *u, double *v)
3244 {
3245    double edge1[3], edge2[3], tvec[3], pvec[3], qvec[3];
3246    double det,inv_det;
3247
3248    // find vectors for two edges sharing vert0
3249    SUB(edge1, vert1, vert0);
3250    SUB(edge2, vert2, vert0);
3251
3252    // begin calculating determinant - also used to calculate U parameter 
3253    CROSS(pvec, dir, edge2);
3254
3255    // if determinant is near zero, ray lies in plane of triangle
3256    det = DOT(edge1, pvec);
3257
3258 #ifdef TEST_CULL           // define TEST_CULL if culling is desired
3259    if (det < EPSILON)
3260       return 0;
3261
3262    // calculate distance from vert0 to ray origin
3263    SUB(tvec, orig, vert0);
3264
3265    // calculate U parameter and test bounds
3266    *u = DOT(tvec, pvec);
3267    if (*u < 0.0 || *u > det)
3268       return 0;
3269
3270    // prepare to test V parameter
3271    CROSS(qvec, tvec, edge1);
3272
3273     // calculate V parameter and test bounds
3274    *v = DOT(dir, qvec);
3275    if (*v < 0.0 || *u + *v > det)
3276       return 0;
3277
3278    // calculate t, scale parameters, ray intersects triangle
3279    *t = DOT(edge2, qvec);
3280    inv_det = 1.0 / det;
3281    *t *= inv_det;
3282    *u *= inv_det;
3283    *v *= inv_det;
3284 #else                    // the non-culling branch
3285    if (det > -EPSILON && det < EPSILON)
3286      return 0;
3287    inv_det = 1.0 / det;
3288
3289    // calculate distance from vert0 to ray origin
3290    SUB(tvec, orig, vert0);
3291
3292    // calculate U parameter and test bounds
3293    *u = DOT(tvec, pvec) * inv_det;
3294    if (*u < 0.0 || *u > 1.0)
3295      return 0;
3296
3297    // prepare to test V parameter
3298    CROSS(qvec, tvec, edge1);
3299
3300    // calculate V parameter and test bounds
3301    *v = DOT(dir, qvec) * inv_det;
3302    if (*v < 0.0 || *u + *v > 1.0)
3303      return 0;
3304
3305    // calculate t, ray intersects triangle
3306    *t = DOT(edge2, qvec) * inv_det;
3307 #endif
3308    return 1;
3309 }
3310 */
3311
3312 int Triangle_Ray(float orig[3], float dir[3], bool bCullBack,
3313                  float vert0[3], float vert1[3], float vert2[3],
3314                  double *t, double *u, double *v)
3315 {
3316   float edge1[3], edge2[3], tvec[3], pvec[3], qvec[3];
3317   double det,inv_det;
3318   
3319   /* find vectors for two edges sharing vert0 */
3320   VectorSubtract(vert1, vert0, edge1);
3321   VectorSubtract(vert2, vert0, edge2);
3322   
3323   /* begin calculating determinant - also used to calculate U parameter */
3324   CrossProduct(dir, edge2, pvec);
3325   
3326   /* if determinant is near zero, ray lies in plane of triangle */
3327   det = DotProduct(edge1, pvec);
3328   
3329   if (bCullBack)
3330   {
3331     if (det < 0.000001)
3332       return 0;
3333     
3334     // calculate distance from vert0 to ray origin
3335     VectorSubtract(orig, vert0, tvec);
3336     
3337     // calculate U parameter and test bounds
3338     *u = DotProduct(tvec, pvec);
3339     if (*u < 0.0 || *u > det)
3340       return 0;
3341     
3342     // prepare to test V parameter
3343     CrossProduct(tvec, edge1, qvec);
3344     
3345     // calculate V parameter and test bounds
3346     *v = DotProduct(dir, qvec);
3347     if (*v < 0.0 || *u + *v > det)
3348       return 0;
3349     
3350     // calculate t, scale parameters, ray intersects triangle
3351     *t = DotProduct(edge2, qvec);
3352     inv_det = 1.0 / det;
3353     *t *= inv_det;
3354     *u *= inv_det;
3355     *v *= inv_det;
3356   }
3357   else
3358   {
3359     /* the non-culling branch */
3360     if (det > -0.000001 && det < 0.000001)
3361       return 0;
3362     inv_det = 1.0 / det;
3363     
3364     /* calculate distance from vert0 to ray origin */
3365     VectorSubtract(orig, vert0, tvec);
3366     
3367     /* calculate U parameter and test bounds */
3368     *u = DotProduct(tvec, pvec) * inv_det;
3369     if (*u < 0.0 || *u > 1.0)
3370       return 0;
3371     
3372     /* prepare to test V parameter */
3373     CrossProduct(tvec, edge1, qvec);
3374     
3375     /* calculate V parameter and test bounds */
3376     *v = DotProduct(dir, qvec) * inv_det;
3377     if (*v < 0.0 || *u + *v > 1.0)
3378       return 0;
3379     
3380     /* calculate t, ray intersects triangle */
3381     *t = DotProduct(edge2, qvec) * inv_det;
3382   }
3383   return 1;
3384 }
3385
3386 bool Patch_Ray(patchMesh_t *patch, vec3_t origin, vec3_t dir, double *t, double *u, double *v)
3387 {
3388         drawLists_t *drawLists;
3389
3390         drawLists_t::iterator iterLists, iterListsNext;
3391         drawList_t::iterator i1, i2, i3, i4;
3392
3393 //  vec3_t tris[2][3];
3394   bool bIntersect = false;
3395   float tBest = FLT_MAX;
3396
3397   if (patch->drawLists == NULL)
3398     return false;
3399
3400   drawLists = (drawLists_t *)patch->drawLists;
3401
3402         iterListsNext=drawLists->begin();
3403         iterListsNext++;
3404         for (iterLists=drawLists->begin(); iterLists != drawLists->end() && iterListsNext != drawLists->end(); iterLists++, iterListsNext++)
3405   {
3406                 // traverse two drawlists at once with two iterators each to triangulate
3407     i1 = i3 = (*iterLists)->begin();
3408     i2 = i4 = (*iterListsNext)->begin();
3409     i3++;
3410     i4++;
3411                 while (i3 != (*iterLists)->end() && i4 != (*iterListsNext)->end())
3412                 {
3413       if (Triangle_Ray(origin, dir, false, (*i1).xyz, (*i2).xyz, (*i3).xyz, t, u, v))
3414       {
3415         bIntersect = true;
3416         if (*t < tBest)
3417           tBest = *t;
3418       }      
3419       if (Triangle_Ray(origin, dir, false, (*i3).xyz, (*i4).xyz, (*i2).xyz, t, u, v))
3420       {
3421         bIntersect = true;
3422         if (*t < tBest)
3423           tBest = *t;
3424       }
3425       i1++;
3426       i2++;
3427       i3++;
3428       i4++;
3429                 }
3430   }
3431   if (bIntersect)
3432   {
3433     *t = tBest;
3434     return true;
3435   }
3436   else
3437   {
3438     *t = 0;
3439     return false;
3440   }
3441 }
3442
3443 // spog - curve LOD stuff ends
3444
3445 /*
3446 =================
3447 DrawPatchMesh
3448 =================
3449 */
3450 void DrawPatchMesh(patchMesh_t *pm)
3451 {
3452   if (g_PrefsDlg.m_bDisplayLists)
3453   {
3454     if (pm->bDirty || pm->nListID <= 0 || pm->LODUpdated)
3455     {
3456       if (pm->nListID <= 0)
3457         pm->nListID = qglGenLists(1);
3458       if (pm->nListID > 0)
3459       {
3460         qglNewList(pm->nListID, GL_COMPILE_AND_EXECUTE);
3461       }
3462
3463       Patch_DeleteDrawLists(pm);
3464       Patch_CreateDrawLists(pm);
3465         
3466                   Patch_DrawLODPatchMesh(pm);
3467
3468       if (pm->nListID > 0)
3469       {
3470         qglEndList();
3471             }
3472
3473             pm->bDirty = false;
3474             pm->LODUpdated = false;
3475     }
3476     else
3477     {
3478       qglCallList(pm->nListID);
3479     }
3480   }
3481   else
3482   {
3483     if (pm->bDirty || pm->LODUpdated)
3484     {
3485       Patch_DeleteDrawLists(pm);
3486       Patch_CreateDrawLists(pm);
3487       pm->bDirty = false;
3488       pm->LODUpdated = false;
3489     }
3490           Patch_DrawLODPatchMesh(pm);
3491   }
3492 }
3493
3494 /*
3495 =================
3496 DrawPatchControls
3497 =================
3498 */
3499 void DrawPatchControls(patchMesh_t *pm)
3500 {
3501   int i, j;
3502   bool bSelectedPoints[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT]; 
3503  
3504   bool bOverlay = pm->bOverlay;
3505
3506   // bending
3507   if (g_bPatchBendMode)
3508   {
3509     qglPointSize(6);
3510     if (g_bPatchAxisOnRow)
3511     {
3512       qglColor3f(1, 0, 1);
3513       if(!g_PrefsDlg.m_bGlPtWorkaround)
3514       {
3515         qglBegin(GL_POINTS);
3516         for (i = 0; i < pm->width; i++)
3517         {
3518           qglVertex3fv(pm->ctrl[i][g_nPatchAxisIndex].xyz);
3519         }
3520         qglEnd();
3521       }
3522       else
3523       {
3524         qglLineWidth(2.0);
3525         qglBegin(GL_LINES);
3526         for(i = 0; i < pm->width; i++)
3527         {
3528           DrawAlternatePoint(pm->ctrl[i][g_nPatchAxisIndex].xyz, 0);
3529         }
3530         qglEnd();
3531         qglLineWidth(1.0);
3532       }                         
3533       
3534       if (g_nPatchBendState == BEND_SELECT_EDGE || g_nPatchBendState == BEND_BENDIT || g_nPatchBendState == BEND_SELECT_ORIGIN)
3535       {
3536         if(!g_PrefsDlg.m_bGlPtWorkaround)
3537         {
3538           qglColor3f(0, 0, 1);
3539           qglBegin(GL_POINTS);
3540           if (g_nPatchBendState == BEND_SELECT_ORIGIN)          
3541           {
3542             qglVertex3fv(g_vBendOrigin);
3543           }
3544           else          
3545           {
3546             for (i = 0; i < pm->width; i++)             
3547             {
3548               if (g_bPatchLowerEdge)                    
3549               {
3550                 for (j = 0; j < g_nPatchAxisIndex; j++)
3551                   qglVertex3fv(pm->ctrl[i][j].xyz);
3552               }
3553               else              
3554               {
3555                 for (j = pm->height-1; j > g_nPatchAxisIndex; j--)
3556                   qglVertex3fv(pm->ctrl[i][j].xyz);
3557               }
3558             }           
3559           }
3560           qglEnd();
3561         }
3562         else {
3563           qglColor3f(0, 0, 1);
3564           qglLineWidth(2.0);
3565           qglBegin(GL_LINES);
3566           if(g_nPatchBendState == BEND_SELECT_ORIGIN)                                   
3567           {
3568             DrawAlternatePoint(g_vBendOrigin, 0);
3569           }
3570           else                                  
3571           {
3572             for(i = 0; i < pm->width; i++)                                              
3573             {
3574               if(g_bPatchLowerEdge)                                                     
3575               {
3576                 for(j = 0; j < g_nPatchAxisIndex; j++)
3577                 {
3578                   DrawAlternatePoint(pm->ctrl[i][j].xyz, 0);
3579                 }                                                       
3580               }
3581               else              
3582               {
3583                 for (j = pm->height-1; j > g_nPatchAxisIndex; j--)                      
3584                 {
3585                   DrawAlternatePoint(pm->ctrl[i][j].xyz, 0);
3586                 }               
3587               }                                         
3588             }           
3589           }
3590           qglEnd();
3591           qglLineWidth(1.0);
3592         }
3593       }
3594     }
3595     else
3596     {
3597       if(!g_PrefsDlg.m_bGlPtWorkaround)
3598       {
3599         qglColor3f(1, 0, 1);
3600         qglBegin(GL_POINTS);
3601         for (i = 0; i < pm->height; i++)
3602         {
3603           qglVertex3fv(pm->ctrl[g_nPatchAxisIndex][i].xyz);
3604         }
3605         qglEnd();
3606       }
3607       else {
3608         qglColor3f(1, 0, 1);
3609         qglLineWidth(2.0);
3610         qglBegin(GL_LINES);
3611         for(i = 0; i < pm->height; i++)
3612         {
3613           DrawAlternatePoint(pm->ctrl[g_nPatchAxisIndex][i].xyz, 0);
3614         }
3615         qglEnd();
3616         qglLineWidth(1.0);
3617       }
3618       
3619       if (g_nPatchBendState == BEND_SELECT_EDGE || g_nPatchBendState == BEND_BENDIT || g_nPatchBendState == BEND_SELECT_ORIGIN)
3620       {
3621         if(!g_PrefsDlg.m_bGlPtWorkaround)
3622         {
3623           qglColor3f(0, 0, 1);
3624           qglBegin(GL_POINTS);
3625           for (i = 0; i < pm->height; i++)
3626           {
3627             if (g_nPatchBendState == BEND_SELECT_ORIGIN)
3628             {
3629               qglVertex3fv(pm->ctrl[g_nBendOriginIndex][i].xyz);
3630             }
3631             else
3632             {
3633               if (g_bPatchLowerEdge)
3634               {
3635                 for (j = 0; j < g_nPatchAxisIndex; j++)
3636                   qglVertex3fv(pm->ctrl[j][i].xyz);
3637               }
3638               else
3639               {
3640                 for (j = pm->width-1; j > g_nPatchAxisIndex; j--)
3641                   qglVertex3fv(pm->ctrl[j][i].xyz);
3642               }
3643             }
3644           }
3645           qglEnd();
3646         }
3647         else {
3648           qglColor3f(0, 0, 1);
3649           qglLineWidth(2.0);
3650           qglBegin(GL_LINES);
3651           for(i = 0; i < pm->height; i++)
3652           {
3653             if(g_nPatchBendState == BEND_SELECT_ORIGIN)
3654             {
3655               DrawAlternatePoint(pm->ctrl[g_nBendOriginIndex][i].xyz, 0);
3656             }
3657             else
3658             {
3659               if(g_bPatchLowerEdge)
3660               {
3661                 for(j = 0; j < g_nPatchAxisIndex; j++)
3662                 {
3663                   DrawAlternatePoint(pm->ctrl[j][i].xyz, 0);
3664                 }
3665               }
3666               else
3667               {
3668                 for(j = pm->width-1; j > g_nPatchAxisIndex; j--)
3669                 {
3670                   DrawAlternatePoint(pm->ctrl[j][i].xyz, 0);
3671                 }
3672               }
3673             }
3674           }
3675           qglEnd();
3676           qglLineWidth(1.0);
3677         }
3678       }
3679     }
3680   }
3681   else 
3682   {
3683     //qglDisable(GL_TEXTURE_2D); // stops point colours being multiplied by texture colour..
3684     //draw CV lattice - could be made optional
3685     //qglDisable( GL_CULL_FACE );
3686     //    qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
3687     qglEnable (GL_POLYGON_OFFSET_LINE);
3688     if (g_PrefsDlg.m_bNoStipple == FALSE)
3689       qglDisable (GL_LINE_STIPPLE);
3690     qglLineWidth (1);
3691     qglColor3f(1.0f, 0.75f, 0.0f);
3692     for ( i = 0 ; i+1 < pm->width ; i++ )
3693     {
3694       qglBegin(GL_QUAD_STRIP);
3695       for ( j = 0 ; j < pm->height ; j++ ) 
3696       {
3697         qglVertex3fv(pm->ctrl[i][j].xyz);
3698         qglVertex3fv(pm->ctrl[i+1][j].xyz);
3699       }
3700       qglEnd();
3701     }
3702     qglDisable (GL_POLYGON_OFFSET_LINE);
3703     //if (g_PrefsDlg.m_bNoStipple == FALSE)
3704     //  qglEnable (GL_LINE_STIPPLE);
3705     
3706     // draw selection handles
3707     if(!g_PrefsDlg.m_bGlPtWorkaround)
3708     {
3709       qglPointSize(6);
3710       qglBegin(GL_POINTS);
3711       for ( i = 0 ; i < pm->width ; i++ )
3712       {
3713         for ( j = 0 ; j < pm->height ; j++ ) 
3714         {
3715           if (PointInMoveList(pm->ctrl[i][j].xyz) != -1)
3716           {
3717             bSelectedPoints[i][j] = true;
3718           }
3719           else
3720           {     
3721             bSelectedPoints[i][j] = false;
3722             if (i & 0x01 || j & 0x01)
3723               qglColor3f(1, 0, 1);
3724             else
3725               qglColor3f(0, 1, 0);
3726             
3727             qglVertex3fv(pm->ctrl[i][j].xyz);
3728           }
3729         }
3730       }
3731       qglColor3f(0, 0, 1);
3732       for ( i = 0 ; i < pm->width ; i++ )
3733       {
3734         for ( j = 0 ; j < pm->height ; j++ ) 
3735         {
3736           if (bSelectedPoints[i][j])
3737             qglVertex3fv(pm->ctrl[i][j].xyz);
3738         }
3739       }
3740       qglEnd();
3741     }
3742     else
3743     {
3744       qglLineWidth(2.0);
3745       qglBegin(GL_LINES);
3746       for(i = 0; i < pm->width; i++)
3747       {
3748         for(j = 0; j < pm->height; j++)
3749         {
3750           if(PointInMoveList(pm->ctrl[i][j].xyz) != -1)
3751           {
3752             bSelectedPoints[i][j] = true;
3753           }
3754           else
3755           {
3756             bSelectedPoints[i][j] = false;
3757             if(i & 0x01 || j & 0x01)
3758               qglColor3f(1, 0, 1);
3759             else
3760               qglColor3f(0, 1, 0);
3761             
3762             // draw verts
3763             DrawAlternatePoint(pm->ctrl[i][j].xyz, 0);
3764           }
3765         }
3766       }
3767       qglColor3f(0, 0, 1);
3768       for(i = 0; i < pm->width; i++)
3769       {
3770         for(j = 0; j < pm->height; j++)
3771         {
3772           if(bSelectedPoints[i][j])
3773           {
3774             // draw verts
3775             DrawAlternatePoint(pm->ctrl[i][j].xyz, 0);
3776           }
3777         }
3778       }
3779       qglEnd(); 
3780       qglLineWidth(1.0);
3781     }
3782   }
3783   if (bOverlay)
3784   {
3785     if(!g_PrefsDlg.m_bGlPtWorkaround)
3786     {
3787       qglPointSize(6);
3788       qglBegin(GL_POINTS);
3789       for ( i = 0 ; i < pm->width ; i++ )
3790       {
3791         for ( j = 0 ; j < pm->height ; j++ ) 
3792         {
3793           if (i & 0x01 || j & 0x01)
3794             qglColor3f(1, 0, 1);
3795           else
3796             qglColor3f(0, 1, 0);
3797           qglVertex3fv(pm->ctrl[i][j].xyz);
3798         }
3799       }
3800       qglEnd();
3801     }
3802     else
3803     {
3804       qglLineWidth(2.0);
3805       qglBegin(GL_LINES);
3806       for ( i = 0 ; i < pm->width ; i++ )
3807       {
3808         for ( j = 0 ; j < pm->height ; j++ ) 
3809         {
3810           if (i & 0x01 || j & 0x01)
3811             qglColor3f(1, 0, 1);
3812           else
3813             qglColor3f(0, 1, 0);
3814           // draw verts
3815           DrawAlternatePoint(pm->ctrl[i][j].xyz, 0);
3816         }
3817       }
3818       qglEnd();
3819       qglLineWidth(1.0);
3820     }
3821   }
3822   //qglPopAttrib();
3823 }
3824
3825 /*
3826 ==================
3827 Patch_DrawXY
3828 ==================
3829 */
3830 void Patch_DrawXY(patchMesh_t *pm)
3831 {
3832   qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
3833
3834   if (pm->bSelected)
3835   {
3836     qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES]);
3837     if (g_PrefsDlg.m_bNoStipple == FALSE)
3838       qglEnable (GL_LINE_STIPPLE);
3839     qglLineWidth (2);
3840   }
3841
3842   DrawPatchMesh(pm);
3843
3844   if ( (pm->bSelected && (g_qeglobals.d_select_mode == sel_curvepoint
3845     || g_qeglobals.d_select_mode == sel_area
3846     || g_bPatchBendMode))
3847     || pm->bOverlay )
3848     DrawPatchControls(pm);
3849 }
3850
3851 /*
3852 ==================
3853 Patch_DrawCam
3854 ==================
3855 */
3856 void Patch_DrawCam(patchMesh_t *pm)
3857 {
3858         qglPushAttrib(GL_ALL_ATTRIB_BITS); // save the current state
3859
3860   if (g_bPatchWireFrame)
3861   {
3862     qglDisable( GL_CULL_FACE );
3863     qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
3864     qglDisable(GL_TEXTURE_2D);
3865     if (g_PrefsDlg.m_bGLLighting)
3866       qglDisable(GL_LIGHTING);
3867
3868     DrawPatchMesh(pm);
3869
3870     //if (g_PrefsDlg.m_bGLLighting)
3871     //  qglEnable(GL_LIGHTING);
3872     //qglEnable( GL_CULL_FACE );
3873   }
3874   else
3875   {
3876     qglDisable(GL_CULL_FACE);
3877     qglBindTexture (GL_TEXTURE_2D, pm->d_texture->texture_number);
3878     qglPolygonMode (GL_FRONT, GL_FILL);
3879     qglPolygonMode (GL_BACK, GL_LINE);
3880
3881     if (pm->pShader->getTrans() < 1.0f)
3882     {
3883       qglEnable(GL_BLEND);
3884       qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3885       qglColor4f(pm->d_texture->color[0], pm->d_texture->color[1], pm->d_texture->color[2], pm->pShader->getTrans());
3886     }
3887     
3888     DrawPatchMesh(pm); // both sides
3889   }
3890
3891   qglPopAttrib(); // restore saved state
3892 }
3893
3894 void ConvexHullForSection( float section[2][4][7] ) {
3895 }
3896
3897 void BrushesForSection( float section[2][4][7] ) {
3898 }
3899
3900 /*
3901 ================
3902 Patch_BuildPoints
3903 ================
3904 */
3905 void Patch_BuildPoints (brush_t *b) 
3906 {
3907         face_t          *f;
3908         b->patchBrush = false;
3909         for (f=b->brush_faces ; f ; f=f->next) 
3910   {
3911                 if (f->texdef.flags & SURF_PATCH) 
3912     {
3913                         b->patchBrush = true;
3914       //vec3_t vMin, vMax;
3915       //Patch_CalcBounds(&patchMeshes[b->nPatchID], vMin, vMax);
3916       //VectorCopy(vMin, b->mins);
3917       //VectorCopy(vMax, b->maxs);
3918                         break;
3919                 }
3920         }
3921 }
3922
3923 /*
3924 ==================
3925 Patch_Move
3926 ==================
3927 */
3928 void Patch_Move(patchMesh_t *pm, const vec3_t vMove, bool bRebuild)
3929 {
3930   pm->bDirty = true;
3931   for (int w = 0; w < pm->width; w++)
3932   {
3933     for (int h = 0; h < pm->height; h++)
3934     {
3935       VectorAdd(pm->ctrl[w][h].xyz, vMove, pm->ctrl[w][h].xyz);
3936     }
3937   }
3938   // bRebuild is never true
3939   if (bRebuild)
3940   {
3941     vec3_t vMin, vMax;
3942     Patch_CalcBounds(pm, vMin, vMax);
3943     //Brush_RebuildBrush(patchMeshes[n].pSymbiot, vMin, vMax);
3944   }
3945   UpdatePatchInspector();
3946
3947 }
3948
3949 /*
3950 ==================
3951 Patch_ApplyMatrix
3952 ==================
3953 */
3954 void Patch_ApplyMatrix(patchMesh_t *p, const vec3_t vOrigin, const vec3_t vMatrix[3], bool bSnap)
3955 {
3956         vec3_t vTemp;
3957         
3958         for (int w = 0; w < p->width; w++)
3959         {
3960                 for (int h = 0; h < p->height; h++)
3961                 {
3962                         if (((g_qeglobals.d_select_mode == sel_curvepoint && g_qeglobals.d_num_move_points != 0) || g_bPatchBendMode)
3963                                 && PointInMoveList(p->ctrl[w][h].xyz) == -1) // snap selected points only, if selected
3964                                 continue;
3965                         VectorSubtract (p->ctrl[w][h].xyz, vOrigin, vTemp);
3966                         for (int j = 0; j < 3; j++)
3967                         {
3968                                 p->ctrl[w][h].xyz[j] = DotProduct(vTemp, vMatrix[j]) + vOrigin[j];
3969                                 if (bSnap)
3970                                 {
3971                                         p->ctrl[w][h].xyz[j] = floor(p->ctrl[w][h].xyz[j] + 0.5);
3972                                 }
3973                         }
3974                 }
3975         }
3976         vec3_t vMin, vMax;
3977         Patch_CalcBounds(p, vMin, vMax);
3978         Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
3979 }
3980
3981 /*
3982 ==================
3983 Patch_EditPatch
3984 ==================
3985 */
3986 void Patch_EditPatch()
3987 {
3988   //--patchMesh_t* p = &patchMeshes[n];
3989   g_qeglobals.d_numpoints = 0;
3990   g_qeglobals.d_num_move_points = 0;
3991         
3992   for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
3993         {
3994     if (pb->patchBrush)
3995     {
3996             patchMesh_t* p = pb->pPatch;
3997             for ( int i = 0 ; i < p->width ; i++ ) 
3998       {
3999                     for ( int j = 0 ; j < p->height ; j++ ) 
4000         {
4001                 VectorCopy (p->ctrl[i][j].xyz, g_qeglobals.d_points[g_qeglobals.d_numpoints]);
4002           if (g_qeglobals.d_numpoints < MAX_POINTS-1)
4003           {
4004                   g_qeglobals.d_numpoints++;
4005           }
4006                     }
4007       }
4008     }
4009   }
4010   g_qeglobals.d_select_mode = sel_curvepoint;
4011   //--g_nSelectedPatch = n;
4012 }
4013
4014
4015
4016 /*
4017 ==================
4018 Patch_Deselect
4019 ==================
4020 */
4021 //FIXME: need all sorts of asserts throughout a lot of this crap
4022 void Patch_Deselect()
4023 {
4024   //--g_nSelectedPatch = -1;
4025   g_qeglobals.d_select_mode = sel_brush;
4026
4027         for (brush_t *b = selected_brushes.next ; b != &selected_brushes ; b=b->next)
4028   {
4029     if (b->patchBrush)
4030     {
4031       b->pPatch->bSelected = false;
4032     }
4033   }
4034
4035   //for (int i = 0; i < numPatchMeshes; i++)
4036   //  patchMeshes[i].bSelected = false;
4037
4038   if (g_bPatchBendMode)
4039     Patch_BendToggle();
4040 //  if (g_bPatchInsertMode)
4041 //    Patch_InsDelToggle();
4042 }
4043
4044
4045 /*
4046 ==================
4047 Patch_Select
4048 ==================
4049 */
4050 void Patch_Select(patchMesh_t *p)
4051 {
4052   // maintained for point manip.. which i need to fix as this 
4053   // is pf error prone
4054   //--g_nSelectedPatch = n;
4055   p->bSelected = true;
4056 }
4057
4058
4059 /*
4060 ==================
4061 Patch_Deselect
4062 ==================
4063 */
4064 void Patch_Deselect(patchMesh_t *p)
4065 {
4066   p->bSelected = false;
4067 }
4068
4069
4070 /*
4071 ==================
4072 Patch_Delete
4073 ==================
4074 */
4075 extern BTNode_t *BTree_Delete(BTNode_t *pBT);
4076 extern BTListList_t *BTree_DeleteListFromList(BTListList_t *pBTListList);
4077
4078 void Patch_Delete(patchMesh_t *p)
4079 {
4080   if (p->pSymbiot) // Hydra - added a check to prevent access violations.
4081   {
4082   p->pSymbiot->pPatch = NULL;
4083   p->pSymbiot->patchBrush = false;
4084   }
4085
4086   // spog - free dynamically allocated memory used by LODs
4087   int rowcount = ((MAX_PATCH_WIDTH-1)/2) * MAX_PATCH_HEIGHT;
4088         int colcount = ((MAX_PATCH_HEIGHT-1)/2) * MAX_PATCH_WIDTH;
4089         int i;
4090         for (i=0; i<rowcount; i++)
4091                 p->rowLOD[i] = BTree_Delete(p->rowLOD[i]);
4092         for (i=0; i<colcount; i++)
4093                 p->colLOD[i] = BTree_Delete(p->colLOD[i]);
4094
4095   // delete display list associated with patch
4096   if (p->nListID != -1)
4097     qglDeleteLists (p->nListID, 1); // list#, number of lists
4098   
4099   // delete LOD drawLists
4100   Patch_DeleteDrawLists(p);
4101
4102
4103   free(p);
4104   p = NULL;
4105
4106   
4107   UpdatePatchInspector();
4108 }
4109
4110
4111 /*
4112 ==================
4113 Patch_Scale
4114 ==================
4115 */
4116 void Patch_Scale(patchMesh_t *p, const vec3_t vOrigin, const vec3_t vAmt, bool bRebuild)
4117 {
4118
4119   for (int w = 0; w < p->width; w++)
4120   {
4121     for (int h = 0; h < p->height; h++)
4122     {
4123       if (g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(p->ctrl[w][h].xyz) == -1)
4124         continue;
4125                         for (int i=0 ; i<3 ; i++)
4126                         {
4127         p->ctrl[w][h].xyz[i] -= vOrigin[i];
4128         p->ctrl[w][h].xyz[i] *= vAmt[i];
4129         p->ctrl[w][h].xyz[i] += vOrigin[i];
4130       }
4131     }
4132   }
4133   if (bRebuild)
4134   {
4135     vec3_t vMin, vMax;
4136     Patch_CalcBounds(p, vMin, vMax);
4137     Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
4138   }
4139   UpdatePatchInspector();
4140 }
4141
4142
4143 /*
4144 ==================
4145 Patch_SetView
4146 ==================
4147 */
4148 void Patch_SetView(int n)
4149 {
4150   g_bSameView = (n == g_nPatchClickedView);
4151   g_nPatchClickedView = n;
4152 }
4153
4154
4155 /*
4156 ==================
4157 Patch_SetTexture
4158 ==================
4159 */
4160 // FIXME: need array validation throughout
4161 void Patch_SetTexture(patchMesh_t *p, texdef_t *tex_def, IPluginTexdef* pPlugTexdef)
4162 {
4163   // NOTE: I don't know for sure if this happens
4164   if (p->pShader)
4165     p->pShader->DecRef();
4166   p->pShader = QERApp_Shader_ForName(tex_def->GetName());
4167   p->pShader->IncRef();
4168   p->d_texture = p->pShader->getTexture();
4169
4170   UpdatePatchInspector();
4171 }
4172
4173
4174 /*
4175 ==================
4176 Patch_DragScale
4177 ==================
4178 */
4179 bool Patch_DragScale(patchMesh_t *p, vec3_t vAmt, vec3_t vMove)
4180 {
4181   vec3_t vMin, vMax, vScale, vTemp, vMid;
4182   int i;
4183
4184   Patch_CalcBounds(p, vMin, vMax);
4185
4186   VectorSubtract(vMax, vMin, vTemp);
4187
4188   // if we are scaling in the same dimension the patch has no depth
4189   for (i = 0; i < 3; i ++)
4190   {
4191     if (vTemp[i] == 0 && vMove[i] != 0)
4192     {
4193       //Patch_Move(n, vMove, true);
4194       return false;
4195     }
4196   }
4197   
4198   for (i=0 ; i<3 ; i++)
4199     vMid[i] = (vMin[i] + ((vMax[i] - vMin[i]) / 2));
4200
4201   for (i = 0; i < 3; i++)
4202   {
4203     if (vAmt[i] != 0)
4204     {
4205       vScale[i] = 1.0 + vAmt[i] / vTemp[i];
4206     }
4207     else
4208     {
4209       vScale[i] = 1.0;
4210     }
4211   }
4212
4213   Patch_Scale(p, vMid, vScale, false);
4214
4215   VectorSubtract(vMax, vMin, vTemp);
4216
4217   Patch_CalcBounds(p, vMin, vMax);
4218   
4219   VectorSubtract(vMax, vMin, vMid);
4220
4221   VectorSubtract(vMid, vTemp, vTemp);
4222
4223   VectorScale(vTemp, 0.5, vTemp);
4224
4225   // abs of both should always be equal
4226   if (!VectorCompare(vMove, vAmt))
4227   {
4228     for (i = 0; i < 3; i++)
4229     {
4230       if (vMove[i] != vAmt[i])
4231         vTemp[i] = -(vTemp[i]);
4232     }
4233   }
4234
4235   Patch_Move(p, vTemp);
4236   return true;
4237 }
4238
4239 /*
4240 ==================
4241 Patch_InsertColumn
4242 ==================
4243 */
4244 void Patch_InsertColumn(patchMesh_t *p, bool bAdd)
4245 {
4246         int w, h, i, width;
4247         vec3_t vTemp;
4248         float stTemp[2];
4249   
4250         if (p->width + 2 >= MAX_PATCH_WIDTH)
4251                 return;
4252
4253         // check for selected column points
4254         for (h = 0; h < p->height; h++)
4255         {
4256                 for (w = 1; w < p->width; w+=2)
4257                         if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4258                                 break;
4259                 if (w < p->width)
4260                         break;
4261                 for (w = 0; w < p->width; w+=2)
4262                         if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4263                                 break;
4264                 if (w < p->width)
4265                         break;
4266         }
4267
4268         if (w >= p->width)
4269         {
4270                 if (bAdd) w=p->width-1;
4271                 else w=2;
4272         }
4273         else if (w==0) w=2;
4274         else if (w%2) w++;
4275
4276         // add columns at w
4277         for (h = 0; h < p->height; h++)
4278         {
4279                 for (width = p->width-1; width > w; width--)
4280                         memcpy(&p->ctrl[width+2][h],&p->ctrl[width][h], sizeof(drawVert_t));
4281                 
4282                 // set two new column points
4283                 memcpy(&p->ctrl[w+2][h],&p->ctrl[w][h], sizeof(drawVert_t));
4284                 memcpy(&p->ctrl[w+1][h],&p->ctrl[w-1][h], sizeof(drawVert_t));
4285                 
4286                 for (i=0; i<3; i++) // xyz
4287                 {
4288                         vTemp[i] = p->ctrl[w][h].xyz[i] - p->ctrl[w-1][h].xyz[i];
4289                         p->ctrl[w+1][h].xyz[i] = p->ctrl[w+1][h].xyz[i] + (vTemp[i] / 2);
4290                         
4291                         vTemp[i] = p->ctrl[w-2][h].xyz[i] - p->ctrl[w-1][h].xyz[i];
4292                         p->ctrl[w-1][h].xyz[i] = p->ctrl[w-1][h].xyz[i] + (vTemp[i] / 2);
4293                         
4294                         vTemp[i] = p->ctrl[w+1][h].xyz[i] - p->ctrl[w-1][h].xyz[i];
4295                         p->ctrl[w][h].xyz[i] = p->ctrl[w-1][h].xyz[i] + (vTemp[i] / 2);
4296                 }
4297                 for (i=0; i<2; i++) // st
4298                 {
4299                         stTemp[i] = p->ctrl[w][h].st[i] - p->ctrl[w-1][h].st[i];
4300                         p->ctrl[w+1][h].st[i] = p->ctrl[w+1][h].st[i] + (stTemp[i] / 2);
4301                         
4302                         stTemp[i] = p->ctrl[w-2][h].st[i] - p->ctrl[w-1][h].st[i];
4303                         p->ctrl[w-1][h].st[i] = p->ctrl[w-1][h].st[i] + (stTemp[i] / 2);
4304                         
4305                         stTemp[i] = p->ctrl[w+1][h].st[i] - p->ctrl[w-1][h].st[i];
4306                         p->ctrl[w][h].st[i] = p->ctrl[w-1][h].st[i] + (stTemp[i] / 2);
4307                 }
4308         }
4309
4310         p->width += 2;
4311         // deselect all points to keep things neat
4312         if (g_qeglobals.d_select_mode == sel_curvepoint)
4313                 Patch_EditPatch();
4314
4315         UpdatePatchInspector();
4316 }
4317
4318 /*
4319 ==================
4320 Patch_InsertRow
4321 ==================
4322 */
4323
4324 void Patch_InsertRow(patchMesh_t *p, bool bAdd)
4325 {
4326         int h, w, i, height;
4327         vec3_t vTemp;
4328         float stTemp[2];
4329   
4330         if (p->height + 2 >= MAX_PATCH_HEIGHT)
4331                 return;
4332
4333         // check for selected row points
4334         for (w = 0; w < p->width; w++)
4335         {
4336                 for (h = 1; h < p->height; h+=2)
4337                         if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4338                                 break;
4339                 if (h < p->height)
4340                         break;
4341                 for (h = 0; h < p->height; h+=2)
4342                         if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4343                                 break;
4344                 if (h < p->height)
4345                         break;
4346         }
4347         if (h >= p->height)
4348         {
4349                 if (bAdd) h=p->height-1;
4350                 else h=2;
4351         }
4352         else if (h==0) h=2;
4353         else if (h%2) h++;
4354
4355         // add rows at h
4356         for (w = 0; w < p->width; w++)
4357         {
4358                 for (height = p->height-1; height > h; height--)
4359                         memcpy(&p->ctrl[w][height+2],&p->ctrl[w][height], sizeof(drawVert_t));
4360                 
4361                 // set two new row points
4362                 memcpy(&p->ctrl[w][h+2],&p->ctrl[w][h], sizeof(drawVert_t));
4363                 memcpy(&p->ctrl[w][h+1],&p->ctrl[w][h-1], sizeof(drawVert_t));
4364                 
4365                 for (i=0; i<3; i++) // xyz
4366                 {
4367                         vTemp[i] = p->ctrl[w][h].xyz[i] - p->ctrl[w][h-1].xyz[i];
4368                         p->ctrl[w][h+1].xyz[i] = p->ctrl[w][h+1].xyz[i] + (vTemp[i] / 2);
4369                         
4370                         vTemp[i] = p->ctrl[w][h-2].xyz[i] - p->ctrl[w][h-1].xyz[i];
4371                         p->ctrl[w][h-1].xyz[i] = p->ctrl[w][h-1].xyz[i] + (vTemp[i] / 2);
4372                         
4373                         vTemp[i] = p->ctrl[w][h+1].xyz[i] - p->ctrl[w][h-1].xyz[i];
4374                         p->ctrl[w][h].xyz[i] = p->ctrl[w][h-1].xyz[i] + (vTemp[i] / 2);
4375                 }
4376                 for (i=0; i<2; i++) // st
4377                 {
4378                         stTemp[i] = p->ctrl[w][h].st[i] - p->ctrl[w][h-1].st[i];
4379                         p->ctrl[w][h+1].st[i] = p->ctrl[w][h+1].st[i] + (stTemp[i] / 2);
4380                         
4381                         stTemp[i] = p->ctrl[w][h-2].st[i] - p->ctrl[w][h-1].st[i];
4382                         p->ctrl[w][h-1].st[i] = p->ctrl[w][h-1].st[i] + (stTemp[i] / 2);
4383                         
4384                         stTemp[i] = p->ctrl[w][h+1].st[i] - p->ctrl[w][h-1].st[i];
4385                         p->ctrl[w][h].st[i] = p->ctrl[w][h-1].st[i] + (stTemp[i] / 2);
4386                 }
4387         }
4388
4389         p->height += 2;
4390         // deselect all points to keep things neat
4391         if (g_qeglobals.d_select_mode == sel_curvepoint)
4392                 Patch_EditPatch();
4393
4394         UpdatePatchInspector();
4395 }
4396
4397 /*
4398 ==================
4399 Patch_RemoveRow
4400 ==================
4401 */
4402 void Patch_RemoveRow(patchMesh_t *p, bool bFirst)
4403 {
4404         int w, h, i, height;
4405         vec3_t vTemp;
4406         float stTemp[2];
4407         bool bExtrapolate = true;
4408   
4409         if (p->height <= MIN_PATCH_HEIGHT)
4410                 return;
4411
4412         for (w = 0; w < p->width; w++)
4413         {
4414                 for (h = 0; h < p->height; h+=2)
4415                         if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4416                                 break;
4417                 if (h < p->height)
4418                         break;
4419                 for (h = 1; h < p->height; h+=2)
4420                         if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4421                                 break;
4422                 if (h < p->height)
4423                         break;
4424         }
4425
4426         if (h >= p->height)
4427         {
4428                 bExtrapolate = false;
4429                 if (bFirst) h=p->height-3;
4430                 else h=2;
4431         }
4432         else if (h <= 0) h=2;
4433         else if (h > p->height-3) h = p->height-3;
4434         else if (h%2) h++;
4435         
4436         p->height -= 2;
4437
4438         for (w = 0; w < p->width; w++)
4439         {
4440                 if (bExtrapolate)
4441                 {
4442                         for (i = 0; i < 3; i++) // xyz
4443                         {
4444                                 vTemp[i] = p->ctrl[w][h+2].xyz[i] - p->ctrl[w][h-2].xyz[i];
4445                                 p->ctrl[w][h-1].xyz[i] = p->ctrl[w][h-2].xyz[i] + (vTemp[i] / 2);
4446
4447                                 vTemp[i] = p->ctrl[w][h].xyz[i] - p->ctrl[w][h-1].xyz[i];
4448                                 p->ctrl[w][h-1].xyz[i] = p->ctrl[w][h-1].xyz[i] + (vTemp[i] * 2);
4449                         }
4450
4451                         for (i = 0; i < 2; i++) // st
4452                         {
4453                                 stTemp[i] = p->ctrl[w][h+2].st[i] - p->ctrl[w][h-2].st[i];
4454                                 p->ctrl[w][h-1].st[i] = p->ctrl[w][h-2].st[i] + (stTemp[i] / 2);
4455
4456                                 stTemp[i] = p->ctrl[w][h].st[i] - p->ctrl[w][h-1].st[i];
4457                                 p->ctrl[w][h-1].st[i] = p->ctrl[w][h-1].st[i] + (stTemp[i] * 2);
4458                         }
4459                 }
4460                 else
4461                 {
4462                         if (!bFirst)
4463                         continue;
4464                         else h=0;
4465                 }
4466                 for (height = h; height < p->height; height++)
4467                         memcpy(&p->ctrl[w][height], &p->ctrl[w][height+2], sizeof(drawVert_t));                 
4468         }
4469         // deselect all points to keep things neat
4470         if (g_qeglobals.d_select_mode == sel_curvepoint)
4471                 Patch_EditPatch();
4472         
4473         UpdatePatchInspector();
4474 }
4475
4476 /*
4477 ==================
4478 Patch_RemoveColumn
4479 ==================
4480 */
4481 void Patch_RemoveColumn(patchMesh_t *p, bool bFirst)
4482 {
4483         int w, h, i, width;
4484         vec3_t vTemp;
4485         float stTemp[2];
4486         bool bExtrapolate = true;
4487   
4488         if (p->width <= MIN_PATCH_WIDTH)
4489                 return;
4490
4491         for (h = 0; h < p->height; h++)
4492         {
4493                 for (w = 0; w < p->width; w+=2)
4494                         if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4495                                 break;
4496                 if (w < p->width)
4497                         break;
4498                 for (w = 1; w < p->width; w+=2)
4499                         if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4500                                 break;
4501                 if (w < p->width)
4502                         break;
4503         }
4504
4505         if (w >= p->width)
4506         {
4507                 bExtrapolate = false;
4508                 if (bFirst) w=p->width-3;
4509                 else w=2;
4510         }
4511         else if (w<=0) w=2;
4512         else if (w > p->width-3) w = p->width-3;
4513         else if (w%2) w++;
4514         
4515         p->width -= 2;
4516
4517         for (h = 0; h < p->height; h++)
4518         {
4519                 if (bExtrapolate)
4520                 {
4521                         for (i = 0; i < 3; i++) // xyz
4522                         {
4523                                 vTemp[i] = p->ctrl[w+2][h].xyz[i] - p->ctrl[w-2][h].xyz[i];
4524                                 p->ctrl[w-1][h].xyz[i] = p->ctrl[w-2][h].xyz[i] + (vTemp[i] / 2);
4525
4526                                 vTemp[i] = p->ctrl[w][h].xyz[i] - p->ctrl[w-1][h].xyz[i];
4527                                 p->ctrl[w-1][h].xyz[i] = p->ctrl[w-1][h].xyz[i] + (vTemp[i] * 2);
4528                         }
4529
4530                         for (i = 0; i < 2; i++) // st
4531                         {
4532                                 stTemp[i] = p->ctrl[w+2][h].st[i] - p->ctrl[w-2][h].st[i];
4533                                 p->ctrl[w-1][h].st[i] = p->ctrl[w-2][h].st[i] + (stTemp[i] / 2);
4534
4535                                 stTemp[i] = p->ctrl[w][h].st[i] - p->ctrl[w-1][h].st[i];
4536                                 p->ctrl[w-1][h].st[i] = p->ctrl[w-1][h].st[i] + (stTemp[i] * 2);
4537                         }
4538                 }
4539                 else
4540                 {
4541                         if (!bFirst)
4542                         continue;
4543                         else w=0;
4544                 }
4545                 
4546                 for (width = w; width < p->width; width++)
4547                         memcpy(&p->ctrl[width][h], &p->ctrl[width+2][h], sizeof(drawVert_t));                   
4548         }
4549         // deselect all points to keep things neat
4550         if (g_qeglobals.d_select_mode == sel_curvepoint)
4551                 Patch_EditPatch();
4552
4553         UpdatePatchInspector();
4554 }
4555
4556 /*
4557 ==================
4558 Patch_AdjustColumns
4559 ==================
4560 */
4561 /*
4562 void Patch_AdjustColumns(patchMesh_t *p, int nCols)
4563 {
4564   vec3_t vTemp, vTemp2;
4565   int i, w, h;
4566
4567   if (nCols & 0x01 || p->width + nCols < 3 || p->width + nCols > MAX_PATCH_WIDTH)
4568     return;
4569
4570   // add in column adjustment
4571   p->width += nCols;
4572
4573   for (h = 0; h < p->height; h++)
4574   {
4575     // for each column, we need to evenly disperse p->width number 
4576     // of points across the old bounds
4577     
4578     // calc total distance to interpolate 
4579     VectorSubtract(p->ctrl[p->width - 1 - nCols][h].xyz, p->ctrl[0][h].xyz, vTemp);
4580
4581     // amount per cycle
4582     for (i = 0; i < 3; i ++)
4583     {
4584       vTemp2[i] = vTemp[i] / (p->width - 1);
4585     }
4586
4587     // move along
4588     for (w = 0; w < p->width-1; w++)
4589     {
4590       VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w+1][h].xyz);
4591     }
4592
4593   }
4594         for ( w = 0 ; w < p->width ; w++ ) 
4595   {
4596                 for ( h = 0 ; h < p->height ; h++ ) 
4597     {
4598                         p->ctrl[w][h].st[0] = 4 * (float)w / (p->width - 1);
4599                         p->ctrl[w][h].st[1] = 4 * (float)h / (p->height - 1);
4600                 }
4601         }
4602   UpdatePatchInspector();
4603 }
4604 */
4605
4606 /*
4607 ==================
4608 Patch_AdjustRows
4609 ==================
4610 */
4611 /*
4612 void Patch_AdjustRows(patchMesh_t *p, int nRows)
4613 {
4614   vec3_t vTemp, vTemp2;
4615   int i, w, h;
4616
4617   if (nRows & 0x01 || p->height + nRows < 3 || p->height + nRows > MAX_PATCH_HEIGHT)
4618     return;
4619
4620   // add in column adjustment
4621   p->height += nRows;
4622
4623   for (w = 0; w < p->width; w++)
4624   {
4625     // for each row, we need to evenly disperse p->height number 
4626     // of points across the old bounds
4627
4628     // calc total distance to interpolate 
4629     VectorSubtract(p->ctrl[w][p->height - 1 - nRows].xyz, p->ctrl[w][0].xyz, vTemp);
4630     
4631     //vTemp[0] = vTemp[1] = vTemp[2] = 0;
4632     //for (h = 0; h < p->height - nRows; h ++)
4633     //{
4634     //  VectorAdd(vTemp, p->ctrl[w][h], vTemp);
4635     //}
4636
4637     // amount per cycle
4638     for (i = 0; i < 3; i ++)
4639     {
4640       vTemp2[i] = vTemp[i] / (p->height - 1);
4641     }
4642
4643     // move along
4644     for (h = 0; h < p->height-1; h++)
4645     {
4646       VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w][h+1].xyz);
4647     }
4648
4649   }
4650         for ( w = 0 ; w < p->width ; w++ ) 
4651   {
4652                 for ( h = 0 ; h < p->height ; h++ ) 
4653     {
4654                         p->ctrl[w][h].st[0] = 4 * (float)w / (p->width - 1);
4655                         p->ctrl[w][h].st[1] = 4 * (float)h / (p->height - 1);
4656                 }
4657         }
4658   UpdatePatchInspector();
4659 }
4660 */
4661
4662 /*
4663 ==================
4664 Patch_DisperseRows
4665 ==================
4666 */
4667
4668 void Patch_DisperseRows()
4669 {
4670         vec3_t vTemp, vTemp2;
4671         int i, w, h;
4672         
4673         
4674         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
4675         {
4676      if (pb->patchBrush)
4677      {
4678        patchMesh_t *p = pb->pPatch;
4679        Patch_Rebuild(p);
4680        for (w = 0; w < p->width; w++)
4681        {
4682          // for each row, we need to evenly disperse p->height number 
4683          // of points across the old bounds
4684  
4685          // calc total distance to interpolate 
4686          VectorSubtract(p->ctrl[w][p->height - 1].xyz, p->ctrl[w][0].xyz, vTemp);
4687      
4688          //vTemp[0] = vTemp[1] = vTemp[2] = 0;
4689          //for (h = 0; h < p->height - nRows; h ++)
4690          //{
4691          //  VectorAdd(vTemp, p->ctrl[w][h], vTemp);
4692          //}
4693  
4694          // amount per cycle
4695          for (i = 0; i < 3; i ++)
4696          {
4697            vTemp2[i] = vTemp[i] / (p->height - 1);
4698          }
4699  
4700          // move along
4701          for (h = 0; h < p->height-1; h++)
4702          {
4703            VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w][h+1].xyz);
4704          }
4705          Patch_Naturalize(p);
4706  
4707        }
4708      }
4709    }
4710                 UpdatePatchInspector();
4711 }
4712
4713 /*
4714 ==================
4715 Patch_DisperseIntermediateRows
4716 ==================
4717 */
4718
4719 void Patch_DisperseIntermediateRows()
4720 {
4721         vec3_t vTemp, vTemp2;
4722         int i, w, h;
4723         
4724         
4725         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
4726         {
4727                 if (pb->patchBrush)
4728                 {
4729                         patchMesh_t *p = pb->pPatch;
4730                         Patch_Rebuild(p);
4731                         for (w = 0; w < p->width; w++)
4732                         {
4733                                 // move along
4734                                 for (h = 0; h < p->height; h+=2)
4735                                 {
4736                                         // calc distance to interpolate 
4737                                         VectorSubtract(p->ctrl[w][h+2].xyz, p->ctrl[w][h].xyz, vTemp);
4738                                         
4739                                         // halve distance
4740                                         for (i = 0; i < 3; i ++)
4741                                         {
4742                                                 vTemp2[i] = vTemp[i] / 2;
4743                                         }
4744                                         
4745                                         // move control points
4746                                         VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w][h+1].xyz);
4747                                 }
4748                         }
4749                 }
4750         }
4751         UpdatePatchInspector();
4752 }
4753
4754 /*
4755 ==================
4756 Patch_DisperseIntermediateColumns
4757 ==================
4758 */
4759 void Patch_DisperseIntermediateColumns()
4760 {
4761         vec3_t vTemp, vTemp2;
4762         int i, w, h;
4763         
4764         
4765         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
4766         {
4767                 if (pb->patchBrush)
4768                 {
4769                         patchMesh_t *p = pb->pPatch;
4770                         Patch_Rebuild(p);
4771                         for (h = 0; h < p->height; h++)
4772                         {
4773                                         // move along
4774                                 for (w = 0; w < p->width; w+=2)
4775                                 {
4776                                         // calc distance to interpolate 
4777                                         VectorSubtract(p->ctrl[w+2][h].xyz, p->ctrl[w][h].xyz, vTemp);
4778                                         
4779                                         // halve distance
4780                                         for (i = 0; i < 3; i ++)
4781                                         {
4782                                                 vTemp2[i] = vTemp[i] / 2;
4783                                         }
4784                                         
4785                                         // move control points
4786                                         VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w+1][h].xyz);
4787                                 }
4788                         }
4789                 }
4790         }
4791         UpdatePatchInspector();
4792 }
4793
4794
4795
4796 /*
4797 ==================
4798 Patch_AdjustSelected
4799 ==================
4800 */
4801 void Patch_AdjustSelected(bool bInsert, bool bColumn, bool bFlag)
4802 {
4803   bool bUpdate = false;
4804         for (brush_t* pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
4805         {
4806     if (pb->patchBrush)
4807     {
4808       if (bInsert)
4809       {
4810         if (bColumn)
4811         {
4812           Patch_InsertColumn(pb->pPatch, bFlag);
4813         }
4814         else
4815         {
4816           Patch_InsertRow(pb->pPatch, bFlag);
4817         }
4818       }
4819       else
4820       {
4821         if (bColumn)
4822         {
4823           Patch_RemoveColumn(pb->pPatch, bFlag);
4824         }
4825         else
4826         {
4827           Patch_RemoveRow(pb->pPatch, bFlag);
4828         }
4829       }
4830       bUpdate = true;
4831       vec3_t vMin, vMax;
4832       patchMesh_t *p = pb->pPatch;
4833       Patch_CalcBounds(p, vMin, vMax);
4834       Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
4835       pb->pPatch->bDirty = true; // rebuild LOD trees and their normals
4836     }
4837   }
4838   if (bUpdate)
4839   {
4840     Sys_UpdateWindows(W_ALL);
4841   }
4842 }
4843
4844
4845 /*
4846 ==================
4847 Patch_AdjustSelectedRowCols
4848 ==================
4849 */
4850 /*
4851 void Patch_AdjustSelectedRowCols(int nRows, int nCols)
4852 {
4853         for (brush_t* pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
4854         {
4855     if (pb->patchBrush)
4856     {
4857       Patch_InsertColumn(pb->pPatch, false);
4858       if (nRows != 0)
4859       {
4860         Patch_AdjustRows(pb->pPatch, nRows);
4861       }
4862       
4863       if (nCols != 0)
4864       {
4865         Patch_AdjustColumns(pb->pPatch, nCols);
4866       }
4867                 }
4868   }
4869   UpdatePatchInspector();
4870 }
4871 */
4872
4873 /*
4874 =================
4875 CheckName
4876 temporary stuff, detect potential problems when saving the texture name
4877 will correct the patch on the fly if problem detected
4878 =================
4879 */
4880 /*!
4881 \todo performance issue with CheckName calls
4882 don't call this too much, only when absolutely necessary
4883 strategies that call here too much are known to be slow
4884 patch 84 to bug 253 adds an additionnal check for textures/
4885 */
4886 void CheckName( patchMesh_t *p, char *pname )
4887 {
4888   if(strncmp(p->pShader->getName(), "textures/", 9) != 0)
4889     p->pShader = QERApp_Shader_ForName(SHADER_NOT_FOUND);
4890
4891         // some manage to get long filename textures (with spaces) in their maps
4892         if (strchr( p->pShader->getName(), ' ' ))
4893         {
4894                 char Msg1[1024];
4895                 sprintf( Msg1, "Can't save texture with spaces in name. Rename %s\nNOTE: This message may popup several times .. once for each buggy face detected.", p->pShader->getName() );
4896                 Sys_Printf("%s\n", Msg1 );
4897                 gtk_MessageBox(g_pParentWnd->m_pWidget, Msg1, "Error saving map", MB_OK );
4898                 strcpy( pname, SHADER_NOT_FOUND );
4899                 p->pShader = QERApp_Shader_ForName(SHADER_NOT_FOUND);
4900     p->d_texture = p->pShader->getTexture();
4901                 return;
4902         }
4903         strcpy( pname, p->pShader->getName()+9 ); // remove "textures/"
4904 }
4905
4906 /*
4907 ==================
4908 Patch_Write 
4909 ==================
4910 */
4911 void Patch_Write (patchMesh_t *p, MemStream *file)
4912 {
4913   char pname[1024];
4914
4915   MemFile_fprintf(file, " {\n  patchDef2\n  {\n");
4916
4917   CheckName( p, pname );
4918   MemFile_fprintf(file, "   %s\n", pname );
4919   MemFile_fprintf(file, "   ( %i %i %i %i %i ) \n", p->width, p->height, p->contents, p->flags, p->value);
4920
4921
4922         float           ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT][5];
4923
4924   int w, h;
4925   for (w = 0; w < p->width; w++)
4926   {
4927     for (h = 0; h < p->height; h++)
4928     {
4929       ctrl[w][h][0] = p->ctrl[w][h].xyz[0];
4930       ctrl[w][h][1] = p->ctrl[w][h].xyz[1];
4931       ctrl[w][h][2] = p->ctrl[w][h].xyz[2];
4932       ctrl[w][h][3] = p->ctrl[w][h].st[0];
4933       ctrl[w][h][4] = p->ctrl[w][h].st[1];
4934     }
4935   }
4936
4937   _Write3DMatrix(file, p->width, p->height, 5, reinterpret_cast<float*>(&ctrl));
4938
4939         if (g_qeglobals.m_bBrushPrimitMode)
4940   {
4941           if (p->epairs)
4942     {
4943                         for (epair_t *ep = p->epairs ; ep ; ep=ep->next)
4944       {
4945                                 MemFile_fprintf (file, "\"%s\" \"%s\"\n", ep->key, ep->value);
4946       }
4947     }
4948   }
4949
4950   MemFile_fprintf(file, "  }\n }\n");
4951 }
4952
4953 void Patch_Write (patchMesh_t *p, FILE *file)
4954 {
4955   char pname[1024];
4956
4957         fprintf(file, " {\n  patchDef2\n  {\n");
4958   {
4959           CheckName( p, pname );
4960           fprintf(file, "   %s\n", pname );
4961           fprintf(file, "   ( %i %i %i %i %i ) \n", p->width, p->height, p->contents, p->flags, p->value);
4962         }
4963
4964         float           ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT][5];
4965
4966   int w, h;
4967   for (w = 0; w < p->width; w++)
4968   {
4969     for (h = 0; h < p->height; h++)
4970     {
4971       ctrl[w][h][0] = p->ctrl[w][h].xyz[0];
4972       ctrl[w][h][1] = p->ctrl[w][h].xyz[1];
4973       ctrl[w][h][2] = p->ctrl[w][h].xyz[2];
4974       ctrl[w][h][3] = p->ctrl[w][h].st[0];
4975       ctrl[w][h][4] = p->ctrl[w][h].st[1];
4976     }
4977   }
4978
4979   _Write3DMatrix(file, p->width, p->height, 5, reinterpret_cast<float*>(&ctrl));
4980
4981         if (g_qeglobals.m_bBrushPrimitMode)
4982   {
4983           if (p->epairs)
4984     {
4985                         for (epair_t *ep = p->epairs ; ep ; ep=ep->next)
4986       {
4987                                 fprintf (file, "\"%s\" \"%s\"\n", ep->key, ep->value);
4988       }
4989     }
4990   }
4991
4992   fprintf(file, "  }\n }\n");
4993 }
4994
4995
4996 /*
4997 ==================
4998 Patch_RotateTexture
4999 ==================
5000 */
5001 void Patch_RotateTexture(patchMesh_t *p, float fAngle)
5002 {
5003   p->bDirty = true;
5004   float c = cos(fAngle * Q_PI / 180);
5005   float s = sin(fAngle * Q_PI / 180);
5006
5007   Patch_TransformLODTexture(p, c, s, ROTATE);
5008
5009   for (int w = 0; w < p->width; w++)
5010   {
5011     for (int h = 0; h < p->height; h++)
5012     {
5013       //if (g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(p->ctrl[w][h].xyz) == -1)
5014       //  continue;
5015
5016       float x = p->ctrl[w][h].st[0];
5017       float y = p->ctrl[w][h].st[1];
5018       p->ctrl[w][h].st[0] = x * c - y * s;
5019       p->ctrl[w][h].st[1] = y * c + x * s;
5020     }
5021   }
5022 }
5023
5024
5025 /*
5026 ==================
5027 Patch_ScaleTexture
5028 ==================
5029 */
5030 void Patch_ScaleTexture(patchMesh_t *p, float fx, float fy, bool bFixup)
5031 {
5032   // FIXME:
5033   // this hack turns scales into 1.1 or 0.9
5034   if (bFixup)
5035   {
5036     fx = (fx == 0) ? 1.0 : (fx > 0) ? 0.9 : 1.10;
5037     fy = (fy == 0) ? 1.0 : (fy > 0) ? 0.9 : 1.10;
5038   }
5039   else
5040   {
5041     if (fx == 0)
5042       fx = 1.0;
5043     if (fy == 0)
5044       fy = 1.0;
5045   }
5046   
5047   for (int w = 0; w < p->width; w++)
5048   {
5049     for (int h = 0; h < p->height; h++)
5050     {
5051       if (g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(p->ctrl[w][h].xyz) == -1)
5052         continue;
5053
5054       p->ctrl[w][h].st[0] *= fx;
5055       p->ctrl[w][h].st[1] *= fy;
5056     }
5057   }
5058   if (g_qeglobals.d_select_mode == sel_curvepoint)
5059   {
5060           p->bDirty = true;
5061           Patch_LODMatchAll();
5062   }
5063   else
5064   {
5065     Patch_TransformLODTexture(p, fx, fy, SCALE);
5066           p->LODUpdated = true;
5067   }
5068 }
5069
5070
5071 /*
5072 ==================
5073 Patch_ShiftTexture
5074 shift a texture given a pixel count
5075 ==================
5076 */
5077 void Patch_ShiftTexture(patchMesh_t *p, float fx, float fy)
5078 {
5079         qtexture_t *pTex;
5080         pTex = p->pShader->getTexture();
5081         fx = -1 * fx / pTex->width;
5082         fy = fy / pTex->height;
5083         Patch_ShiftTextureST(p, fx, fy);
5084 }
5085
5086 /*
5087 ====================
5088 Patch_ShiftTextureST
5089 shift a patch texture given an ST increment
5090 ====================
5091 */
5092 void Patch_ShiftTextureST(patchMesh_t *p, float fx, float fy)
5093 {
5094 #ifdef _DEBUG
5095         // NOTE: when called by Patch_ShiftTexture this warning may be bogus
5096         if ((ABS(fx) >= 1) || (ABS(fy) >= 1))
5097                 Sys_Printf("WARNING: increments exceed 1 in Patch_ShiftTextureST\n");
5098 #endif
5099   for (int w = 0; w < p->width; w++)
5100   {
5101     for (int h = 0; h < p->height; h++)
5102     {
5103       if (g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(p->ctrl[w][h].xyz) == -1)
5104         continue;
5105
5106       p->ctrl[w][h].st[0] += fx;
5107       p->ctrl[w][h].st[1] += fy;
5108     }
5109   }
5110   if (g_qeglobals.d_select_mode == sel_curvepoint)
5111   {
5112           p->bDirty = true;
5113           Patch_LODMatchAll();
5114   }
5115   else
5116   {
5117           Patch_TransformLODTexture(p, fx, fy, TRANSLATE);
5118           p->LODUpdated = true;
5119   }
5120 }
5121
5122 /*
5123 ==================
5124 Patch_ToggleInverted
5125 ==================
5126 */
5127 void Patch_ToggleInverted()
5128 {
5129   bool bUpdate = false;
5130
5131         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5132         {
5133     if (pb->patchBrush)
5134     {
5135       bUpdate = true;
5136       patchInvert(pb->pPatch);
5137     }
5138   }
5139
5140   if (bUpdate)
5141   {
5142     Sys_UpdateWindows(W_ALL);
5143   }
5144   UpdatePatchInspector();
5145 }
5146
5147 /*
5148 ==================
5149 Patch_ToggleInverted
5150 ==================
5151 */
5152 void Patch_InvertTexture(bool bY)
5153 {
5154   bool bUpdate = false;
5155
5156   float fTemp[2];
5157         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5158         {
5159     if (pb->patchBrush)
5160     {
5161       bUpdate = true;
5162       patchMesh_t *p = pb->pPatch;
5163       p->bDirty = true;
5164       if (bY)
5165       {
5166               for ( int i = 0 ; i < p->height ; i++ ) 
5167         {
5168           for (int j = 0; j < p->width / 2; j++)
5169           {
5170             memcpy(fTemp, &p->ctrl[p->width - 1- j][i].st[0], sizeof (float[2]));
5171             memcpy(&p->ctrl[p->width - 1- j][i].st[0], &p->ctrl[j][i].st[0], sizeof(float[2]));
5172             memcpy(&p->ctrl[j][i].st[0], fTemp, sizeof(float[2]));
5173                       }
5174               }
5175       }
5176       else
5177       {
5178               for ( int i = 0 ; i < p->width ; i++ ) 
5179         {
5180           for (int j = 0; j < p->height / 2; j++)
5181           {
5182             memcpy(fTemp, &p->ctrl[i][p->height - 1- j].st[0], sizeof (float[2]));
5183             memcpy(&p->ctrl[i][p->height - 1 - j].st[0], &p->ctrl[i][j].st[0], sizeof(float[2]));
5184             memcpy(&p->ctrl[i][j].st[0], fTemp, sizeof(float[2]));
5185                       }
5186               }
5187       }
5188     }
5189   }
5190
5191   if (bUpdate)
5192   {
5193     Sys_UpdateWindows(W_ALL);
5194   }
5195   UpdatePatchInspector();
5196 }
5197
5198
5199
5200
5201 /*
5202 ==================
5203 Patch_Save
5204 ==================
5205  Saves patch ctrl info (originally to deal with a 
5206  cancel in the surface dialog
5207 */
5208 void Patch_Save(patchMesh_t *p)
5209 {
5210   patchSave.width = p->width;
5211   patchSave.height = p->height;
5212   memcpy(patchSave.ctrl, p->ctrl, sizeof(p->ctrl));
5213 }
5214
5215
5216 /*
5217 ==================
5218 Patch_Restore
5219 ==================
5220 */
5221 void Patch_Restore(patchMesh_t *p)
5222 {
5223   p->width = patchSave.width;
5224   p->height = patchSave.height;
5225   memcpy(p->ctrl, patchSave.ctrl, sizeof(p->ctrl));
5226 }
5227
5228 void Patch_ResetTexturing(float fx, float fy)
5229 {
5230         for (brush_t* pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5231         {
5232     if (pb->patchBrush)
5233     {
5234       patchMesh_t *p = pb->pPatch;
5235       p->bDirty = true;
5236             for ( int i = 0 ; i < p->width ; i++ ) 
5237       {
5238                     for ( int j = 0 ; j < p->height ; j++ ) 
5239         {
5240                             p->ctrl[i][j].st[0] = fx * (float)i / (p->width - 1);
5241                             p->ctrl[i][j].st[1] = 1 - fy * (float)j / (p->height - 1);
5242                     }
5243             }
5244     }
5245   }
5246 }
5247
5248 // NOTE TTimo stub!
5249 void Patch_FitTexturing()
5250 {
5251   Patch_ResetTexturing(1.0f, 1.0f);
5252 }
5253
5254 void Patch_SetTextureInfo(texdef_t *pt)
5255 {
5256         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5257         {
5258     if (pb->patchBrush)
5259     {
5260       if (pt->rotate)
5261         Patch_RotateTexture(pb->pPatch, pt->rotate);
5262
5263       if (pt->shift[0] || pt->shift[1])
5264         Patch_ShiftTexture(pb->pPatch, pt->shift[0], pt->shift[1]);
5265
5266       if (pt->scale[0] || pt->scale[1])
5267         Patch_ScaleTexture(pb->pPatch, pt->scale[0], pt->scale[1], false);
5268
5269       patchMesh_t *p = pb->pPatch;
5270       p->contents = pt->contents;
5271       p->flags = pt->flags;
5272       p->value = pt->value;
5273     }
5274   }
5275 }
5276
5277 bool OnlyPatchesSelected()
5278 {
5279   if (g_ptrSelectedFaces.GetSize() > 0 || selected_brushes.next == &selected_brushes)
5280     return false;
5281         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5282         {
5283     if (!pb->patchBrush)
5284     {
5285       return false;
5286     }
5287   }
5288   return true;
5289 }
5290
5291 bool AnyPatchesSelected()
5292 {
5293   if (g_ptrSelectedFaces.GetSize() > 0  || selected_brushes.next == &selected_brushes)
5294     return false;
5295         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5296         {
5297     if (pb->patchBrush)
5298     {
5299       return true;
5300     }
5301   }
5302   return false;
5303 }
5304
5305 patchMesh_t* SinglePatchSelected()
5306 {
5307   if (selected_brushes.next->patchBrush)
5308   {
5309     return selected_brushes.next->pPatch;
5310   }
5311   return NULL;
5312 }
5313
5314 void Patch_BendToggle()
5315 {
5316   if (g_bPatchBendMode)
5317   {
5318     g_bPatchBendMode = false;
5319     HideInfoDialog();
5320     g_pParentWnd->UpdatePatchToolbarButtons() ;
5321     return;
5322   }
5323
5324         brush_t* b = selected_brushes.next;
5325
5326   if (!QE_SingleBrush(true) || !b->patchBrush)
5327   {
5328     Sys_Printf("Patch_BendToggle: you must have a single patch selected\n");
5329                 return;
5330   }
5331
5332   Patch_Save(b->pPatch);
5333         g_bPatchBendMode = true;
5334   g_nPatchBendState = BEND_SELECT_ROTATION;
5335   g_bPatchAxisOnRow = true;
5336   g_nPatchAxisIndex = 1;
5337   ShowInfoDialog(g_pBendStateMsg[BEND_SELECT_ROTATION]);
5338 }
5339
5340 void Patch_BendHandleTAB()
5341 {
5342   if (!g_bPatchBendMode)
5343   {
5344     return;
5345   }
5346
5347         brush_t* b = selected_brushes.next;
5348   if (!QE_SingleBrush() || !b->patchBrush)
5349   {
5350     Patch_BendToggle();
5351     Sys_Printf("No patch to bend!");
5352                 return;
5353   }
5354
5355   patchMesh_t *p = b->pPatch;
5356
5357   bool bShift = Sys_ShiftDown ();
5358
5359   if (g_nPatchBendState == BEND_SELECT_ROTATION)
5360   {
5361     // only able to deal with odd numbered rows/cols
5362     g_nPatchAxisIndex += (bShift) ? -2 : 2;
5363     if (g_bPatchAxisOnRow)
5364     {
5365       if ((bShift) ? g_nPatchAxisIndex <= 0 : g_nPatchAxisIndex >= p->height)
5366       {
5367         g_bPatchAxisOnRow = false;
5368         g_nPatchAxisIndex = (bShift) ? p->width-1 : 1;
5369       }
5370     }
5371     else
5372     {
5373       if ((bShift) ? g_nPatchAxisIndex <= 0 : g_nPatchAxisIndex >= p->width)
5374       {
5375         g_bPatchAxisOnRow = true;
5376         g_nPatchAxisIndex = (bShift) ? p->height-1 : 1;
5377       }
5378     }
5379   }
5380   else
5381   if (g_nPatchBendState == BEND_SELECT_ORIGIN)
5382   {
5383     g_nBendOriginIndex += (bShift) ? -1 : 1;
5384     if (g_bPatchAxisOnRow)
5385     {
5386       if (bShift)
5387       {
5388         if (g_nBendOriginIndex < 0)
5389           g_nBendOriginIndex = p->width-1;
5390       }
5391       else
5392       {
5393         if (g_nBendOriginIndex > p->width-1)
5394           g_nBendOriginIndex = 0;
5395       }
5396       VectorCopy(p->ctrl[g_nBendOriginIndex][g_nPatchAxisIndex].xyz, g_vBendOrigin);
5397     }
5398     else
5399     {
5400       if (bShift)
5401       {
5402         if (g_nBendOriginIndex < 0)
5403           g_nBendOriginIndex = p->height-1;
5404       }
5405       else
5406       {
5407         if (g_nBendOriginIndex > p->height-1)
5408           g_nBendOriginIndex = 0;
5409       }
5410       VectorCopy(p->ctrl[g_nPatchAxisIndex][g_nBendOriginIndex].xyz, g_vBendOrigin);
5411     }
5412   }
5413   else
5414   if (g_nPatchBendState == BEND_SELECT_EDGE)
5415   {
5416     g_bPatchLowerEdge ^= 1;
5417   }
5418   Sys_UpdateWindows(W_ALL);
5419 }
5420
5421 void Patch_BendHandleENTER()
5422 {
5423   if (!g_bPatchBendMode)
5424   {
5425     return;
5426   }
5427
5428   if (g_nPatchBendState  < BEND_BENDIT)
5429   {
5430     g_nPatchBendState++;
5431     ShowInfoDialog(g_pBendStateMsg[g_nPatchBendState]);
5432     if (g_nPatchBendState == BEND_SELECT_ORIGIN)
5433     {
5434       g_vBendOrigin[0] = g_vBendOrigin[1] = g_vBendOrigin[2] = 0;
5435       g_nBendOriginIndex = 0;
5436       Patch_BendHandleTAB();
5437     }
5438     else
5439     if (g_nPatchBendState == BEND_SELECT_EDGE)
5440     {
5441       g_bPatchLowerEdge = true;
5442     }
5443     else
5444     if (g_nPatchBendState == BEND_BENDIT)
5445     {
5446       // basically we go into rotation mode, set the axis to the center of the 
5447     }
5448   }
5449   else
5450   {
5451     // done
5452     Patch_BendToggle();
5453   }
5454   Sys_UpdateWindows(W_ALL);
5455
5456 }
5457
5458
5459 void Patch_BendHandleESC()
5460 {
5461   if (!g_bPatchBendMode)
5462   {
5463     return;
5464   }
5465   Patch_BendToggle();
5466         brush_t* b = selected_brushes.next;
5467   if (QE_SingleBrush() && b->patchBrush)
5468   {
5469     Patch_Restore(b->pPatch);
5470   }
5471   Sys_UpdateWindows(W_ALL);
5472 }
5473
5474 void Patch_SetBendRotateOrigin(patchMesh_t *p)
5475 {
5476 #if 1
5477   int nType = g_pParentWnd->ActiveXY()->GetViewType();
5478   int nDim3 = (nType == XY) ? 2 : (nType == YZ) ? 0 : 1;
5479
5480   g_vBendOrigin[nDim3] = 0;
5481   VectorCopy(g_vBendOrigin, g_pParentWnd->ActiveXY()->RotateOrigin());
5482   return;
5483 #else
5484   int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0;
5485   int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2;
5486
5487   float fxLo, fyLo, fxHi, fyHi;
5488   fxLo = fyLo = 9999;
5489   fxHi = fyHi = -9999;
5490
5491   if (g_bPatchAxisOnRow)
5492   {
5493     for (int i = 0; i < p->width; i++)
5494     {
5495       if (p->ctrl[i][g_nPatchAxisIndex].xyz[nDim1] < fxLo)
5496         fxLo = p->ctrl[i][g_nPatchAxisIndex].xyz[nDim1];
5497
5498       if (p->ctrl[i][g_nPatchAxisIndex].xyz[nDim1] > fxHi)
5499         fxHi = p->ctrl[i][g_nPatchAxisIndex].xyz[nDim1];
5500
5501       if (p->ctrl[i][g_nPatchAxisIndex].xyz[nDim2] < fyLo)
5502         fyLo = p->ctrl[i][g_nPatchAxisIndex].xyz[nDim2];
5503
5504       if (p->ctrl[i][g_nPatchAxisIndex].xyz[nDim2] > fyHi)
5505         fyHi = p->ctrl[i][g_nPatchAxisIndex].xyz[nDim2];
5506     }
5507   }
5508   else
5509   {
5510     for (int i = 0; i < p->height; i++)
5511     {
5512       if (p->ctrl[g_nPatchAxisIndex][i].xyz[nDim1] < fxLo)
5513         fxLo = p->ctrl[g_nPatchAxisIndex][i].xyz[nDim1];
5514
5515       if (p->ctrl[g_nPatchAxisIndex][i].xyz[nDim1] > fxHi)
5516         fxHi = p->ctrl[g_nPatchAxisIndex][i].xyz[nDim1];
5517
5518       if (p->ctrl[g_nPatchAxisIndex][i].xyz[nDim2] < fyLo)
5519         fyLo = p->ctrl[g_nPatchAxisIndex][i].xyz[nDim2];
5520
5521       if (p->ctrl[g_nPatchAxisIndex][i].xyz[nDim2] > fyHi)
5522         fyHi = p->ctrl[g_nPatchAxisIndex][i].xyz[nDim2];
5523     }
5524   }
5525
5526   g_pParentWnd->ActiveXY()->RotateOrigin()[0] = g_pParentWnd->ActiveXY()->RotateOrigin()[1] = g_pParentWnd->ActiveXY()->RotateOrigin()[2] = 0.0;
5527   g_pParentWnd->ActiveXY()->RotateOrigin()[nDim1] = (fxLo + fxHi) * 0.5;
5528   g_pParentWnd->ActiveXY()->RotateOrigin()[nDim2] = (fyLo + fyHi) * 0.5;
5529 #endif
5530 }
5531
5532 // also sets the rotational origin
5533 void Patch_SelectBendAxis()
5534 {
5535         brush_t* b = selected_brushes.next;
5536   if (!QE_SingleBrush() || !b->patchBrush)
5537   {
5538     // should not ever happen
5539     Patch_BendToggle();
5540                 return;
5541   }
5542
5543   patchMesh_t *p = b->pPatch;
5544   if (g_bPatchAxisOnRow)
5545   {
5546     SelectRow(p, g_nPatchAxisIndex, false);
5547   }
5548   else
5549   {
5550     SelectColumn(p, g_nPatchAxisIndex, false);
5551   }
5552
5553   //FIXME: this only needs to be set once... 
5554   Patch_SetBendRotateOrigin(p);
5555
5556 }
5557
5558 void Patch_SelectBendNormal()
5559 {
5560         brush_t* b = selected_brushes.next;
5561   if (!QE_SingleBrush() || !b->patchBrush)
5562   {
5563     // should not ever happen
5564     Patch_BendToggle();
5565                 return;
5566   }
5567
5568   patchMesh_t *p = b->pPatch;
5569
5570   g_qeglobals.d_num_move_points = 0;
5571   if (g_bPatchAxisOnRow)
5572   {
5573     if (g_bPatchLowerEdge)
5574     {
5575       for (int j = 0; j < g_nPatchAxisIndex; j++)
5576         SelectRow(p, j, true);
5577     }
5578     else
5579     {
5580       for (int j = p->height-1; j > g_nPatchAxisIndex; j--)
5581         SelectRow(p, j, true);
5582     }
5583   }
5584   else
5585   {
5586     if (g_bPatchLowerEdge)
5587     {
5588       for (int j = 0; j < g_nPatchAxisIndex; j++)
5589         SelectColumn(p, j, true);
5590     }
5591     else
5592     {
5593       for (int j = p->width-1; j > g_nPatchAxisIndex; j--)
5594         SelectColumn(p, j, true);
5595     }
5596   }
5597   Patch_SetBendRotateOrigin(p);
5598 }
5599
5600
5601
5602 /*
5603 void Patch_InsDelToggle()
5604 {
5605   if (g_bPatchInsertMode)
5606   {
5607     g_bPatchInsertMode = false;
5608     HideInfoDialog();
5609     g_pParentWnd->UpdatePatchToolbarButtons() ;
5610     return;
5611   }
5612
5613         brush_t* b = selected_brushes.next;
5614
5615   if (!QE_SingleBrush(true) || !b->patchBrush)
5616   {
5617     Sys_Printf("Patch_InsDelToggle: you must have a single patch selected\n");
5618                 return;
5619   }
5620
5621   Patch_Save(b->pPatch);
5622         g_bPatchInsertMode = true;
5623   g_nPatchInsertState = INSERT_SELECT_EDGE;
5624   g_bPatchAxisOnRow = true;
5625   g_nPatchAxisIndex = 0;
5626   ShowInfoDialog(g_pInsertStateMsg[INSERT_SELECT_EDGE]);
5627
5628 }
5629
5630 void Patch_InsDelESC()
5631 {
5632   if (!g_bPatchInsertMode)
5633   {
5634     return;
5635   }
5636   Patch_InsDelToggle();
5637   Sys_UpdateWindows(W_ALL);
5638 }
5639
5640
5641 void Patch_InsDelHandleENTER()
5642 {
5643 }
5644
5645 void Patch_InsDelHandleTAB()
5646 {
5647   if (!g_bPatchInsertMode)
5648   {
5649     Patch_InsDelToggle();
5650     return;
5651   }
5652
5653         brush_t* b = selected_brushes.next;
5654   if (!QE_SingleBrush() || !b->patchBrush)
5655   {
5656     Patch_BendToggle();
5657     Sys_Printf("No patch to bend!");
5658                 return;
5659   }
5660
5661   patchMesh_t *p = b->pPatch;
5662
5663   // only able to deal with odd numbered rows/cols
5664   g_nPatchAxisIndex += 2;
5665   if (g_bPatchAxisOnRow)
5666   {
5667     if (g_nPatchAxisIndex >= p->height-1)
5668     {
5669       g_bPatchAxisOnRow = false;
5670       g_nPatchAxisIndex = 0;
5671     }
5672   }
5673   else
5674   {
5675     if (g_nPatchAxisIndex >= p->width-1)
5676     {
5677       g_bPatchAxisOnRow = true;
5678       g_nPatchAxisIndex = 0;
5679     }
5680   }
5681   Sys_UpdateWindows(W_ALL);
5682 }
5683 */
5684
5685
5686 void _Write1DMatrix (FILE *f, int x, float *m) {
5687         int             i;
5688
5689         fprintf (f, "( ");
5690         for (i = 0 ; i < x ; i++) {
5691                 if (m[i] == (int)m[i] ) {
5692                         fprintf (f, "%i ", (int)m[i]);
5693                 } else {
5694                         fprintf (f, "%f ", m[i]);
5695                 }
5696         }
5697         fprintf (f, ")");
5698 }
5699
5700 void _Write2DMatrix (FILE *f, int y, int x, float *m) {
5701         int             i;
5702
5703         fprintf (f, "( ");
5704         for (i = 0 ; i < y ; i++) {
5705                 _Write1DMatrix (f, x, m + i*x);
5706                 fprintf (f, " ");
5707         }
5708         fprintf (f, ")\n");
5709 }
5710
5711
5712 void _Write3DMatrix (FILE *f, int z, int y, int x, float *m) {
5713         int             i;
5714
5715         fprintf (f, "(\n");
5716         for (i = 0 ; i < z ; i++) {
5717                 _Write2DMatrix (f, y, x, m + i*(x*MAX_PATCH_HEIGHT) );
5718         }
5719         fprintf (f, ")\n");
5720 }
5721
5722 void _Write1DMatrix (MemStream *f, int x, float *m) {
5723         int             i;
5724
5725         MemFile_fprintf (f, "( ");
5726         for (i = 0 ; i < x ; i++) {
5727                 if (m[i] == (int)m[i] ) {
5728                         MemFile_fprintf (f, "%i ", (int)m[i]);
5729                 } else {
5730                         MemFile_fprintf (f, "%f ", m[i]);
5731                 }
5732         }
5733         MemFile_fprintf (f, ")");
5734 }
5735
5736 void _Write2DMatrix (MemStream *f, int y, int x, float *m) {
5737         int             i;
5738
5739         MemFile_fprintf (f, "( ");
5740         for (i = 0 ; i < y ; i++) {
5741                 _Write1DMatrix (f, x, m + i*x);
5742                 MemFile_fprintf (f, " ");
5743         }
5744         MemFile_fprintf (f, ")\n");
5745 }
5746
5747
5748 void _Write3DMatrix (MemStream *f, int z, int y, int x, float *m) {
5749         int             i;
5750
5751         MemFile_fprintf (f, "(\n");
5752         for (i = 0 ; i < z ; i++) {
5753                 _Write2DMatrix (f, y, x, m + i*(x*MAX_PATCH_HEIGHT) );
5754         }
5755         MemFile_fprintf (f, ")\n");
5756 }
5757
5758 // NOTE: why the hell is this called Naturalize?
5759 // we dispatch either to Patch+Naturalize or Patch_CapTexture..
5760 void Patch_NaturalizeSelected(bool bCap)
5761 {
5762         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5763         {
5764     if (pb->patchBrush)
5765     {
5766       if (bCap)
5767         Patch_CapTexture(pb->pPatch);//, bCycleCap);
5768       else
5769         Patch_Naturalize(pb->pPatch);
5770     }
5771   }
5772 }
5773
5774 // go through the selected patches and call Patch_CapTexture
5775 // deal with cycling
5776 void Patch_CycleCapSelected()
5777 {
5778   // compute the g_vCycleCapNormal according to g_nCycleCapIndex
5779   VectorClear (g_vCycleCapNormal);
5780   g_vCycleCapNormal[g_nCycleCapIndex] = 1.0f; // cf VIEWTYPE defintion: enum VIEWTYPE {YZ, XZ, XY};
5781         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5782         {
5783     if (pb->patchBrush)
5784     {
5785       Patch_CapTexture(pb->pPatch, true);
5786     }
5787   }
5788   switch (g_nCycleCapIndex)
5789   {
5790   case YZ:
5791     g_nCycleCapIndex = XZ;
5792     break;
5793   case XZ:
5794     g_nCycleCapIndex = XY;
5795     break;
5796   case XY:
5797     g_nCycleCapIndex = YZ;
5798     break;
5799   }
5800 }
5801
5802 bool within(vec3_t vTest, vec3_t vTL, vec3_t vBR)
5803 {
5804   int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0;
5805   int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2;
5806   if ((vTest[nDim1] > vTL[nDim1] && vTest[nDim1] < vBR[nDim1]) ||
5807       (vTest[nDim1] < vTL[nDim1] && vTest[nDim1] > vBR[nDim1]))
5808   {
5809     if ((vTest[nDim2] > vTL[nDim2] && vTest[nDim2] < vBR[nDim2]) ||
5810         (vTest[nDim2] < vTL[nDim2] && vTest[nDim2] > vBR[nDim2]))
5811       return true;
5812   }
5813   return false;
5814 }
5815
5816
5817 void Patch_SelectAreaPoints(bool bMulti)
5818 {
5819   if (!bMulti)
5820     g_qeglobals.d_num_move_points = 0;
5821
5822   if( g_nPatchClickedView == W_CAMERA ) {
5823     // Clip against a pyramid
5824     // Create our 5 normals (that are pointing to the inside)
5825     camera_t *m_pCamera = g_pParentWnd->GetCamWnd()->Camera();
5826     vec3_t      norm[5];
5827     float               r[2], u[2], hh, hw;
5828     int                 idx;
5829     vec_t               corners[2][2];
5830     vec3_t      ray[4];
5831     vec3_t  check;
5832
5833     VectorCopy( m_pCamera->vpn, norm[0] ); // only points in front of the camera
5834
5835     // get our rectangle
5836     corners[0][0] = MIN( g_qeglobals.d_vAreaTL[0], g_qeglobals.d_vAreaBR[0] );
5837     corners[0][1] = MAX( g_qeglobals.d_vAreaTL[1], g_qeglobals.d_vAreaBR[1] );
5838     corners[1][0] = MAX( g_qeglobals.d_vAreaTL[0], g_qeglobals.d_vAreaBR[0] );
5839     corners[1][1] = MIN( g_qeglobals.d_vAreaTL[1], g_qeglobals.d_vAreaBR[1] );
5840
5841     // calculate our four ray vectors
5842     hh = m_pCamera->height/2;
5843     hw = m_pCamera->width/2;
5844     u[0] = (float)(corners[0][1] - hh) / (hw);
5845     r[0] = (float)(corners[0][0] - hw) / (hw);
5846     u[1] = (float)(corners[1][1] - hh) / (hw);
5847     r[1] = (float)(corners[1][0] - hw) / (hw);
5848
5849     for (idx=0 ; idx<3; idx++)
5850             ray[0][idx] = m_pCamera->vpn[idx] + m_pCamera->vright[idx] * r[0] + m_pCamera->vup[idx] * u[0];
5851     for (idx=0 ; idx<3; idx++)
5852             ray[1][idx] = m_pCamera->vpn[idx] + m_pCamera->vright[idx] * r[1] + m_pCamera->vup[idx] * u[0];
5853     for (idx=0 ; idx<3; idx++)
5854             ray[2][idx] = m_pCamera->vpn[idx] + m_pCamera->vright[idx] * r[1] + m_pCamera->vup[idx] * u[1];
5855     for (idx=0 ; idx<3; idx++)
5856             ray[3][idx] = m_pCamera->vpn[idx] + m_pCamera->vright[idx] * r[0] + m_pCamera->vup[idx] * u[1];
5857
5858     // Create our four other directions from these
5859     CrossProduct( ray[0], ray[1], norm[1] ); VectorNormalize( norm[1], norm[1] );
5860     CrossProduct( ray[1], ray[2], norm[2] ); VectorNormalize( norm[2], norm[2] );
5861     CrossProduct( ray[2], ray[3], norm[3] ); VectorNormalize( norm[3], norm[3] );
5862     CrossProduct( ray[3], ray[0], norm[4] ); VectorNormalize( norm[4], norm[4] );
5863                 
5864                 // 3D clipping
5865                 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5866                 {
5867                         if (pb->patchBrush)
5868                         {
5869                                 patchMesh_t *p = pb->pPatch;
5870                                 for (int i = 0; i < p->width; i++)
5871                                 {
5872                                         for (int j = 0; j < p->height; j++)
5873                                         {
5874                                                 VectorSubtract( m_pCamera->origin, p->ctrl[i][j].xyz, check );
5875                                                 VectorNormalize( check, check );
5876                                                 for (idx=0 ; idx<5; idx++)
5877                                                 {
5878                                                         if (DotProduct(check, norm[idx])>=0)
5879                                                                 break;
5880                                                 }
5881                                                 if (idx == 5) // all test were good
5882             {
5883                                                         if (bMulti && PointInMoveList(p->ctrl[i][j].xyz) != -1)
5884                                                                 RemovePointFromMoveList(p->ctrl[i][j].xyz);
5885               else
5886                                                           g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl[i][j].xyz;
5887             }
5888                                         }
5889                                 }
5890                         }
5891                 }
5892         } else
5893         {
5894                 // Simple 2D clipping
5895                 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5896                 {
5897                         if (pb->patchBrush)
5898                         {
5899                                 patchMesh_t *p = pb->pPatch;
5900                                 for (int i = 0; i < p->width; i++)
5901                                 {
5902                                         for (int j = 0; j < p->height; j++)
5903                                         {
5904                                                 if (within(p->ctrl[i][j].xyz, g_qeglobals.d_vAreaTL, g_qeglobals.d_vAreaBR))
5905                                                 {
5906                                                         if (bMulti && PointInMoveList(p->ctrl[i][j].xyz) != -1)
5907                                                                 RemovePointFromMoveList(p->ctrl[i][j].xyz);
5908                                                         else
5909                                                                 g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl[i][j].xyz;
5910                                                 }
5911                                         }
5912                                 }
5913                         }
5914                 }
5915         }
5916
5917         g_nPatchClickedView = -1;
5918 }
5919
5920 // TTimo: return the shader name for a patch
5921 const char* Patch_GetTextureName()
5922 {
5923         brush_t* b = selected_brushes.next;
5924   if (b->patchBrush)
5925   {
5926     patchMesh_t *p = b->pPatch;
5927     return p->pShader->getName();
5928   }
5929   return "";
5930 }
5931
5932 patchMesh_t* Patch_Duplicate(patchMesh_t *pFrom)
5933 {
5934   patchMesh_t* p = MakeNewPatch();
5935   memcpy(p, pFrom , sizeof(patchMesh_t));
5936   
5937         // spog - initialise patch LOD pointers (again)
5938   Patch_InitialiseLODPointers(p);
5939   p->drawLists = NULL;
5940
5941   p->bSelected = false;
5942   p->bDirty = true;
5943   p->bOverlay = false;
5944   p->nListID = -1;
5945   AddBrushForPatch(p);
5946  
5947   return p;
5948 }
5949
5950
5951 void Patch_Thicken(int nAmount, bool bSeam, qboolean bGroupResult)
5952 {
5953   int i, j, h, w;
5954   brush_t *b;
5955   patchMesh_t *pSeam;
5956   vec3_t vMin, vMax;
5957   CPtrArray brushes;
5958
5959   nAmount = -nAmount;
5960
5961
5962         if (!QE_SingleBrush())
5963   {
5964     Sys_Printf("Cannot thicken multiple patches. Please select a single patch.\n");
5965                 return;
5966   }
5967
5968         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5969         {
5970     if (pb->patchBrush)
5971     {
5972       patchMesh_t *p = pb->pPatch;
5973       Patch_MeshNormals(p);
5974       patchMesh_t *pNew = Patch_Duplicate(p);
5975       for (i = 0; i < p->width; i++)
5976       {
5977         for (j = 0; j < p->height; j++)
5978         {
5979                       VectorMA (p->ctrl[i][j].xyz, nAmount, p->ctrl[i][j].normal, pNew->ctrl[i][j].xyz);
5980         }
5981       }
5982
5983       Patch_Rebuild(pNew);
5984       pNew->type |= PATCH_THICK;
5985       brushes.Add(pNew->pSymbiot);
5986
5987       if (bSeam)
5988       {
5989
5990         // FIXME: this should detect if any edges of the patch are closed and act appropriately
5991         // 
5992         if (!(p->type & PATCH_CYLINDER))
5993         {
5994           b = Patch_GenericMesh(3, p->height, 2, false, true);
5995           pSeam = b->pPatch;
5996           pSeam->type |= PATCH_SEAM;
5997           for (i = 0; i < p->height; i++)
5998           {
5999             VectorCopy(p->ctrl[0][i].xyz, pSeam->ctrl[0][i].xyz);
6000             VectorCopy(pNew->ctrl[0][i].xyz, pSeam->ctrl[2][i].xyz);
6001             VectorAdd(pSeam->ctrl[0][i].xyz, pSeam->ctrl[2][i].xyz, pSeam->ctrl[1][i].xyz);
6002             VectorScale(pSeam->ctrl[1][i].xyz, 0.5, pSeam->ctrl[1][i].xyz);
6003           }
6004
6005
6006           Patch_CalcBounds(pSeam, vMin, vMax);
6007           Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax);
6008           //--Patch_CapTexture(pSeam);
6009           Patch_Naturalize(pSeam);
6010           patchInvert(pSeam);
6011           brushes.Add(b);
6012
6013           w = p->width - 1;
6014           b = Patch_GenericMesh(3, p->height, 2, false, true);
6015           pSeam = b->pPatch;
6016           pSeam->type |= PATCH_SEAM;
6017           for (i = 0; i < p->height; i++)
6018           {
6019             VectorCopy(p->ctrl[w][i].xyz, pSeam->ctrl[0][i].xyz);
6020             VectorCopy(pNew->ctrl[w][i].xyz, pSeam->ctrl[2][i].xyz);
6021             VectorAdd(pSeam->ctrl[0][i].xyz, pSeam->ctrl[2][i].xyz, pSeam->ctrl[1][i].xyz);
6022             VectorScale(pSeam->ctrl[1][i].xyz, 0.5, pSeam->ctrl[1][i].xyz);
6023           }
6024           Patch_CalcBounds(pSeam, vMin, vMax);
6025           Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax);
6026           //--Patch_CapTexture(pSeam);
6027           Patch_Naturalize(pSeam);
6028           brushes.Add(b);
6029         }
6030     
6031         //--{
6032           // otherwise we will add one per end
6033           b = Patch_GenericMesh(p->width, 3, 2, false, true);
6034           pSeam = b->pPatch;
6035           pSeam->type |= PATCH_SEAM;
6036           for (i = 0; i < p->width; i++)
6037           {
6038             VectorCopy(p->ctrl[i][0].xyz, pSeam->ctrl[i][0].xyz);
6039             VectorCopy(pNew->ctrl[i][0].xyz, pSeam->ctrl[i][2].xyz);
6040             VectorAdd(pSeam->ctrl[i][0].xyz, pSeam->ctrl[i][2].xyz, pSeam->ctrl[i][1].xyz);
6041             VectorScale(pSeam->ctrl[i][1].xyz, 0.5, pSeam->ctrl[i][1].xyz);
6042           }
6043
6044
6045           Patch_CalcBounds(pSeam, vMin, vMax);
6046           Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax);
6047           //--Patch_CapTexture(pSeam);
6048           Patch_Naturalize(pSeam);
6049           patchInvert(pSeam);
6050           brushes.Add(b);
6051
6052           h = p->height - 1;
6053           b = Patch_GenericMesh(p->width, 3, 2, false, true);
6054           pSeam = b->pPatch;
6055           pSeam->type |= PATCH_SEAM;
6056           for (i = 0; i < p->width; i++)
6057           {
6058             VectorCopy(p->ctrl[i][h].xyz, pSeam->ctrl[i][0].xyz);
6059             VectorCopy(pNew->ctrl[i][h].xyz, pSeam->ctrl[i][2].xyz);
6060             VectorAdd(pSeam->ctrl[i][0].xyz, pSeam->ctrl[i][2].xyz, pSeam->ctrl[i][1].xyz);
6061             VectorScale(pSeam->ctrl[i][1].xyz, 0.5, pSeam->ctrl[i][1].xyz);
6062           }
6063           Patch_CalcBounds(pSeam, vMin, vMax);
6064           Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax);
6065           //--Patch_CapTexture(pSeam);
6066           Patch_Naturalize(pSeam);
6067           brushes.Add(b);
6068
6069       }
6070       patchInvert(pNew);
6071     }
6072   }
6073
6074   for (i = 0; i < brushes.GetSize(); i++)
6075   {
6076     Select_Brush(reinterpret_cast<brush_t*>(brushes.GetAt(i)));
6077   }
6078
6079   if(bGroupResult) 
6080   {
6081     entity_t *e = Entity_Alloc();
6082     SetKeyValue(e, "classname", "func_group");
6083     SetKeyValue(e, "type", "patchThick");
6084     Select_GroupEntity(e);
6085     Entity_AddToList(e, &entities);
6086   }
6087   
6088   UpdatePatchInspector();
6089 }
6090
6091
6092 /*
6093 lets get another list together as far as necessities..
6094
6095 *snapping stuff to the grid (i will only snap movements by the mouse to the grid.. snapping the rotational bend stuff will fubar everything)
6096
6097 capping bevels/endcaps
6098
6099 hot keys
6100
6101 texture fix for caps
6102
6103 clear clipboard
6104
6105 *region fix
6106
6107 *surface dialog
6108
6109 */
6110
6111 void Patch_SetOverlays()
6112 {
6113         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
6114         {
6115     if (pb->patchBrush)
6116     {
6117       pb->pPatch->bOverlay = true;
6118     }
6119   }
6120 }
6121
6122
6123
6124 void Patch_ClearOverlays()
6125 {
6126   brush_t *pb;
6127         for (pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
6128         {
6129     if (pb->patchBrush)
6130     {
6131       pb->pPatch->bOverlay = false;
6132     }
6133   }
6134
6135         for (pb = active_brushes.next ; pb != &active_brushes ; pb = pb->next)
6136         {
6137     if (pb->patchBrush)
6138     {
6139       pb->pPatch->bOverlay = false;
6140     }
6141   }
6142
6143 }
6144
6145 // FIXME: spog - er, someone forgot to finish their patch point freezing feature?
6146 // freezes selected vertices
6147 void Patch_Freeze()
6148 {
6149   brush_t *pb;
6150         for (pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
6151         {
6152     if (pb->patchBrush)
6153     {
6154       pb->pPatch->bOverlay = false;
6155     }
6156   }
6157
6158         for (pb = active_brushes.next ; pb != &active_brushes ; pb = pb->next)
6159         {
6160     if (pb->patchBrush)
6161     {
6162       pb->pPatch->bOverlay = false;
6163     }
6164   }
6165
6166 }
6167
6168 void Patch_UnFreeze(bool bAll)
6169 {
6170 }
6171
6172 void Patch_Transpose()
6173 {
6174         int             i, j, w;
6175   drawVert_t dv;
6176         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
6177         {
6178     if (pb->patchBrush)
6179     {
6180       patchMesh_t *p = pb->pPatch;
6181
6182       if ( p->width > p->height ) 
6183       {
6184         for ( i = 0 ; i < p->height ; i++ ) 
6185         {
6186           for ( j = i + 1 ; j < p->width ; j++ ) 
6187           {
6188             if ( j < p->height ) 
6189             {
6190               // swap the value
6191               memcpy(&dv,&p->ctrl[j][i],sizeof(drawVert_t));
6192               memcpy(&p->ctrl[j][i],&p->ctrl[i][j], sizeof(drawVert_t));
6193               memcpy(&p->ctrl[i][j],&dv, sizeof(drawVert_t));
6194             } 
6195             else 
6196             {
6197                                         // just copy
6198                                         memcpy(&p->ctrl[i][j],&p->ctrl[j][i], sizeof(drawVert_t));
6199                                 }
6200                         }
6201                   }
6202             } 
6203       else 
6204       {
6205                 for ( i = 0 ; i < p->width ; i++ ) 
6206         {
6207                           for ( j = i + 1 ; j < p->height ; j++ ) 
6208           {
6209                                 if ( j < p->width ) 
6210             {
6211                                             // swap the value
6212                                             memcpy(&dv,&p->ctrl[i][j], sizeof(drawVert_t));
6213               memcpy(&p->ctrl[i][j],&p->ctrl[j][i], sizeof(drawVert_t));
6214                                         memcpy(&p->ctrl[j][i],&dv, sizeof(drawVert_t));
6215                                 } 
6216             else 
6217             {
6218                                         // just copy
6219                                         memcpy(&p->ctrl[j][i],&p->ctrl[i][j], sizeof(drawVert_t));
6220                                 }
6221                         }
6222                 }
6223         }
6224
6225       w = p->width;
6226       p->width = p->height;
6227       p->height = w;
6228       patchInvert(p);
6229       Patch_Rebuild(p);
6230                 }
6231         }
6232 }
6233
6234
6235
6236 void Patch_SnapToGrid(patchMesh_t *p)
6237 {
6238         int i,j,k;
6239         
6240         // if patch points selected, snap only selected points
6241         if (g_qeglobals.d_select_mode == sel_curvepoint && g_qeglobals.d_num_move_points != 0)
6242                 for (i=0; i<g_qeglobals.d_num_move_points; i++)
6243                         for (j = 0; j < 3; j++)
6244                                 g_qeglobals.d_move_points[i][j] = floor(g_qeglobals.d_move_points[i][j] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize;
6245                         
6246         // else snap all patch points
6247         else
6248                 for (i = 0; i < p->width; i++)
6249                         for (j = 0; j < p->height; j++)
6250                                 for (k = 0; k < 3; k++)
6251                                         p->ctrl[i][j].xyz[k] = floor(p->ctrl[i][j].xyz[k] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize;
6252                                 
6253         vec3_t vMin, vMax;
6254         Patch_CalcBounds(p, vMin, vMax);
6255         Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
6256 }
6257
6258
6259 void Patch_FindReplaceTexture(brush_t *pb, const char *pFind, const char *pReplace, bool bForce)
6260 {
6261   if (pb->patchBrush)
6262   {
6263     patchMesh_t *p = pb->pPatch;
6264     if (bForce || strcmpi(p->pShader->getName(), pFind) == 0)
6265     {
6266       p->pShader->DecRef();
6267       p->pShader = QERApp_Shader_ForName(pReplace);
6268       p->d_texture = p->pShader->getTexture();
6269     }
6270   }
6271 }
6272
6273 /* uncomment if necessary, currently not used
6274 void Patch_FromTriangle(vec5_t vx, vec5_t vy, vec5_t vz)
6275 {
6276   patchMesh_t* p = MakeNewPatch();
6277   p->pShader = g_qeglobals.d_texturewin.pShader;
6278   p->d_texture = g_qeglobals.d_texturewin.pShader->getTexture();
6279   p->width = 3;
6280   p->height = 3;
6281   p->type = PATCH_TRIANGLE;
6282
6283   // 0 0 goes to x
6284   // 0 1 goes to x
6285   // 0 2 goes to x
6286
6287   // 1 0 goes to mid of x and z
6288   // 1 1 goes to mid of x y and z
6289   // 1 2 goes to mid of x and y
6290
6291   // 2 0 goes to z
6292   // 2 1 goes to mid of y and z
6293   // 2 2 goes to y
6294
6295   vec5_t vMidXZ;
6296   vec5_t vMidXY;
6297   vec5_t vMidYZ;
6298   int j;
6299
6300   for (j = 0; j < 3; j++)
6301   {
6302     _Vector5Add(vx, vz, vMidXZ);
6303     _Vector5Scale(vMidXZ, 0.5, vMidXZ);
6304     //vMidXZ[j] = vx[j] + abs((vx[j] - vz[j]) * 0.5);
6305   }
6306
6307   for (j = 0; j < 3; j++)
6308   {
6309     _Vector5Add(vx, vy, vMidXY);
6310     _Vector5Scale(vMidXY, 0.5, vMidXY);
6311     //vMidXY[j] = vx[j] + abs((vx[j] - vy[j]) * 0.5);
6312   }
6313
6314   for (j = 0; j < 3; j++)
6315   {
6316     _Vector5Add(vy, vz, vMidYZ);
6317     _Vector5Scale(vMidYZ, 0.5, vMidYZ);
6318     //vMidYZ[j] = vy[j] + abs((vy[j] - vz[j]) * 0.5);
6319   }
6320
6321   _Vector53Copy(vx, p->ctrl[0][0].xyz);
6322   _Vector53Copy(vx, p->ctrl[0][1].xyz);
6323   _Vector53Copy(vx, p->ctrl[0][2].xyz);
6324   p->ctrl[0][0].st[0] = vx[3];
6325   p->ctrl[0][0].st[1] = vx[4];
6326   p->ctrl[0][1].st[0] = vx[3];
6327   p->ctrl[0][1].st[1] = vx[4];
6328   p->ctrl[0][2].st[0] = vx[3];
6329   p->ctrl[0][2].st[1] = vx[4];
6330
6331   _Vector53Copy(vMidXY, p->ctrl[1][0].xyz);
6332   _Vector53Copy(vx, p->ctrl[1][1].xyz);
6333   _Vector53Copy(vMidXZ, p->ctrl[1][2].xyz);
6334   p->ctrl[1][0].st[0] = vMidXY[3];
6335   p->ctrl[1][0].st[1] = vMidXY[4];
6336   p->ctrl[1][1].st[0] = vx[3];
6337   p->ctrl[1][1].st[1] = vx[4];
6338   p->ctrl[1][2].st[0] = vMidXZ[3];
6339   p->ctrl[1][2].st[1] = vMidXZ[4];
6340
6341   _Vector53Copy(vy, p->ctrl[2][0].xyz);
6342   _Vector53Copy(vMidYZ, p->ctrl[2][1].xyz);
6343   _Vector53Copy(vz, p->ctrl[2][2].xyz);
6344   p->ctrl[2][0].st[0] = vy[3];
6345   p->ctrl[2][0].st[1] = vy[4];
6346   p->ctrl[2][1].st[0] = vMidYZ[3];
6347   p->ctrl[2][1].st[1] = vMidYZ[4];
6348   p->ctrl[2][2].st[0] = vz[3];
6349   p->ctrl[2][2].st[1] = vz[4];
6350
6351
6352   //Patch_Naturalize(p);
6353
6354   //  brush_t *b = 
6355   AddBrushForPatch(p);
6356
6357 }
6358 */
6359
6360 #ifdef ENABLE_GROUPS
6361 /*
6362 ==============
6363 Patch_SetEpair
6364 sets an epair for the given patch
6365 ==============
6366 */
6367 void Patch_SetEpair(patchMesh_t *p, const char *pKey, const char *pValue)
6368 {
6369         if (g_qeglobals.m_bBrushPrimitMode)
6370         {
6371                 SetKeyValue(p->epairs, pKey, pValue);
6372         }
6373 }
6374
6375 /* 
6376 =================
6377 Patch_GetKeyValue
6378 =================
6379 */
6380 const char* Patch_GetKeyValue(patchMesh_t *p, const char *pKey)
6381 {
6382         if (g_qeglobals.m_bBrushPrimitMode)
6383         {
6384     return ValueForKey(p->epairs, pKey);
6385         }
6386   return "";
6387 }
6388 #endif
6389
6390
6391 //Real nitpicky, but could you make CTRL-S save the current map with the current name? (ie: File/Save)
6392 /*
6393 Feature addition.
6394 When reading in textures, please check for the presence of a file called "textures.link" or something, which contains one line such as;
6395
6396 g:\quake3\baseq3\textures\common
6397
6398  So that, when I'm reading in, lets say, my \eerie directory, it goes through and adds my textures to the palette, along with everything in common.
6399
6400   Don't forget to add "Finer texture alignment" to the list. I'd like to be able to move in 0.1 increments using the Shift-Arrow Keys.
6401
6402   No. Sometimes textures are drawn the wrong way on patches. We'd like the ability to flip a texture. Like the way X/Y scale -1 used to worked.
6403
6404   1) Easier way of deleting rows, columns
6405 2) Fine tuning of textures on patches (X/Y shifts other than with the surface dialog)
6406 2) Patch matrix transposition
6407
6408   1) Actually, bump texture flipping on patches to the top of the list of things to do.
6409 2) When you select a patch, and hit S, it should read in the selected patch texture. Should not work if you multiselect patches and hit S
6410 3) Brandon has a wierd anomoly. He fine-tunes a patch with caps. It looks fine when the patch is selected, but as soon as he escapes out, it reverts to it's pre-tuned state. When he selects the patch again, it looks tuned
6411
6412
6413 *1) Flipping textures on patches
6414 *2) When you select a patch, and hit S, it should read in the selected patch texture. Should not work if you multiselect patches and hit S
6415 3) Easier way of deleting rows columns
6416 *4) Thick Curves
6417 5) Patch matrix transposition
6418 6) Inverted cylinder capping
6419 *7) bugs
6420 *8) curve speed
6421
6422   Have a new feature request. "Compute Bounding Box" for mapobjects (md3 files). This would be used for misc_mapobject (essentially, drop in 3DS Max models into our maps)
6423
6424   Ok, Feature Request. Load and draw MD3's in the Camera view with proper bounding boxes. This should be off misc_model
6425
6426   Feature Addition: View/Hide Hint Brushes -- This should be a specific case.
6427 */