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