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