]> git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/bobtoolz/funchandlers-GTK.cpp
57d06b1871c5d16a3f9919fafaa946b132dbce7a
[xonotic/netradiant.git] / contrib / bobtoolz / funchandlers-GTK.cpp
1 /*\r
2 BobToolz plugin for GtkRadiant\r
3 Copyright (C) 2001 Gordon Biggans\r
4 \r
5 This library is free software; you can redistribute it and/or\r
6 modify it under the terms of the GNU Lesser General Public\r
7 License as published by the Free Software Foundation; either\r
8 version 2.1 of the License, or (at your option) any later version.\r
9 \r
10 This library is distributed in the hope that it will be useful,\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
13 Lesser General Public License for more details.\r
14 \r
15 You should have received a copy of the GNU Lesser General Public\r
16 License along with this library; if not, write to the Free Software\r
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
18 */\r
19 \r
20 #include "StdAfx.h"\r
21 \r
22 #ifdef _WIN32\r
23 #pragma warning(disable : 4786)\r
24 #endif\r
25 \r
26 #include "dialogs/dialogs-gtk.h"\r
27 \r
28 #include "DEntity.h"\r
29 #include "DShape.h"\r
30 #include "DPatch.h"\r
31 \r
32 #include "misc.h"\r
33 #include "shapes.h"\r
34 #include "lists.h"\r
35 #include "funchandlers.h"\r
36 #include "visfind.h"\r
37 \r
38 // for autocaulk\r
39 list<Str> exclusionList;                // whole brush exclusion\r
40 list<Str> exclusionList_Face;   // single face exclusion\r
41 \r
42 bool el1Loaded =                FALSE;\r
43 bool el2Loaded =                FALSE;\r
44 bool clrLst1Loaded =    FALSE;\r
45 bool clrLst2Loaded =    FALSE;\r
46 \r
47 DBobView*               g_PathView =            NULL;\r
48 DVisDrawer*             g_VisView =                     NULL;\r
49 DTrainDrawer*   g_TrainView =           NULL;\r
50 DTreePlanter*   g_TreePlanter =         NULL;\r
51 // -------------\r
52 \r
53 //========================//\r
54 //    Helper Functions    //\r
55 //========================//\r
56 \r
57 void LoadLists()\r
58 {\r
59         char buffer[256];\r
60 \r
61         if(!el1Loaded)\r
62                 el1Loaded = LoadExclusionList(GetFilename(buffer, "bt/bt-el1.txt"), &exclusionList);\r
63         if(!el2Loaded)\r
64                 el2Loaded = LoadExclusionList(GetFilename(buffer, "bt/bt-el2.txt"), &exclusionList_Face);\r
65 }\r
66 \r
67 \r
68 //========================//\r
69 //     Main Functions     //\r
70 //========================//\r
71 \r
72 void DoIntersect()\r
73 {\r
74         IntersectRS rs;\r
75 \r
76         if(DoIntersectBox(&rs) == IDCANCEL)\r
77                 return;\r
78 \r
79         if(rs.nBrushOptions == BRUSH_OPT_SELECTED)\r
80         {\r
81                 if( g_FuncTable.m_pfnSelectedBrushCount() < 2 )\r
82                 {\r
83                         DoMessageBox("Invalid number of brushes selected, choose at least 2", "Error", MB_OK);\r
84                         return; \r
85                 }\r
86         }\r
87 \r
88         DEntity world;\r
89 \r
90         switch(rs.nBrushOptions)\r
91         {\r
92         case BRUSH_OPT_SELECTED:\r
93                 {\r
94                         world.LoadSelectedBrushes();\r
95                         break;\r
96                 }\r
97         case BRUSH_OPT_WHOLE_MAP:\r
98                 {\r
99                         world.LoadFromEntity(0, FALSE);\r
100                         break;\r
101                 }\r
102         }\r
103 \r
104         world.RemoveNonCheckBrushes(&exclusionList, rs.bUseDetail);\r
105 \r
106         bool* pbSelectList;\r
107         if(rs.bDuplicateOnly)\r
108                 pbSelectList = world.BuildDuplicateList();\r
109         else\r
110                 pbSelectList = world.BuildIntersectList();\r
111 \r
112         world.SelectBrushes(pbSelectList);\r
113 \r
114         delete[] pbSelectList;\r
115 }\r
116 \r
117 void DoPolygonsTB()\r
118 {\r
119   vec3_t vMin, vMax;\r
120 \r
121   // figure out vMin and vMax\r
122   g_FuncTable.m_pfnGetDispatchParams( vMin, vMax, NULL );\r
123 \r
124   DoPolygons( vMin, vMax );\r
125 }\r
126 \r
127 void DoPolygons(vec3_t vMin, vec3_t vMax)\r
128 {\r
129         // ensure we have something selected\r
130         if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )\r
131         {\r
132                 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", MB_OK);\r
133                 return; \r
134         }\r
135 \r
136         // tell Radiant we want to access the selected brushes\r
137         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
138                         \r
139         // get handle to size definition brush\r
140         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);\r
141         // cant release until we delete the brush, if we do...\r
142 \r
143         PolygonRS rs;\r
144 \r
145         // ask user for type, size, etc....\r
146         if(DoPolygonBox(&rs) == IDOK)\r
147         {\r
148                 g_FuncTable.m_pfnDeleteBrushHandle(brush);\r
149 \r
150                 DShape poly;\r
151 \r
152                 if(rs.bInverse)\r
153                         poly.BuildInversePrism(vMin, vMax, rs.nSides, rs.bAlignTop);\r
154                 else\r
155                 {\r
156                         if(rs.bUseBorder)\r
157                                 poly.BuildBorderedPrism(vMin, vMax, rs.nSides, rs.nBorderWidth, rs.bAlignTop);\r
158                         else\r
159                                 poly.BuildRegularPrism(vMin, vMax, rs.nSides, rs.bAlignTop);\r
160 \r
161                 }\r
162 \r
163                 poly.Commit();\r
164         }\r
165 \r
166 \r
167         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
168 }\r
169 \r
170 void DoFixBrushes()\r
171 {\r
172         DMap world;\r
173         world.LoadAll();\r
174 \r
175         int count = world.FixBrushes(TRUE);\r
176         \r
177         Sys_Printf("%i invalid/duplicate planes removed\n", count);\r
178 }\r
179 \r
180 void DoResetTextures()\r
181 {\r
182         static ResetTextureRS rs;\r
183 \r
184   const char* texName;\r
185         if(g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 1)\r
186   {\r
187     texName = NULL;\r
188   }\r
189   else\r
190   {\r
191     texName = GetCurrentTexture();\r
192           strcpy(rs.textureName, GetCurrentTexture());\r
193   }\r
194 \r
195   int ret;\r
196         if((ret = DoResetTextureBox(&rs)) == IDCANCEL)\r
197                 return;  \r
198 \r
199   if(rs.bResetTextureName)\r
200     texName = rs.textureName;\r
201 \r
202   if(ret == IDOK)\r
203   {\r
204           DEntity world;\r
205           world.LoadSelectedBrushes();\r
206           world.ResetTextures(texName,              rs.fScale,      rs.fShift,      rs.rotation, rs.newTextureName, \r
207                         rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation, TRUE);\r
208   }\r
209   else\r
210   {\r
211           DMap world;\r
212     world.LoadAll(TRUE);\r
213     world.ResetTextures(texName,              rs.fScale,      rs.fShift,      rs.rotation, rs.newTextureName, \r
214                         rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation);\r
215   }\r
216 }\r
217 \r
218 void DoBuildStairs(vec3_t vMin, vec3_t vMax)\r
219 {\r
220         BuildStairsRS rs;\r
221 \r
222         strcpy(rs.mainTexture, GetCurrentTexture());\r
223 \r
224         // ensure we have something selected\r
225         if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )\r
226         {\r
227                 DoMessageBox("Invalid number of brushes selected, chose 1 only", "Error", MB_OK);\r
228                 return; \r
229         }\r
230 \r
231         // tell Radiant we want to access the selected brushes\r
232         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
233                         \r
234         // get handle to size definition brush\r
235         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);\r
236         // cant release until we delete the brush, if we do...\r
237 \r
238 \r
239         // ask user for type, size, etc....\r
240         if(DoBuildStairsBox(&rs) == IDOK)\r
241         {\r
242                 // calc brush size\r
243                 vec3_t size;\r
244                 VectorSubtract(vMax, vMin, size);\r
245 \r
246                 if(((int)size[2] % rs.stairHeight) != 0)\r
247                 {\r
248                         // stairs must fit evenly into brush\r
249                         DoMessageBox("Invalid stair height\nHeight of block must be divisable by stair height", "Error", MB_OK);\r
250                 }\r
251                 else\r
252                 {\r
253                         \r
254                         // Remove Size Brush\r
255                         g_FuncTable.m_pfnDeleteBrushHandle(brush);\r
256 \r
257                                                 \r
258                         // Get Step Count\r
259                         int numSteps = (int)size[2] / rs.stairHeight;\r
260                         \r
261                         if(rs.style == STYLE_CORNER)\r
262                         {\r
263                                 BuildCornerStairs(vMin, vMax, numSteps, rs.mainTexture, rs.riserTexture);\r
264                         }\r
265                         else\r
266                         {\r
267 \r
268                                 // Get Step Dimensions\r
269                                 float stairHeight = (float)rs.stairHeight;\r
270                                 float stairWidth;\r
271                                 if((rs.direction == MOVE_EAST) || (rs.direction == MOVE_WEST))\r
272                                         stairWidth = (size[0])/numSteps;\r
273                                 else\r
274                                         stairWidth = (size[1])/numSteps;\r
275 \r
276 \r
277                                 // Build Base For Stair (bob's style)\r
278                                 if(rs.style == STYLE_BOB)\r
279                                         Build_Wedge(rs.direction, vMin, vMax, TRUE);\r
280 \r
281 \r
282                                 // Set First Step Starting Position\r
283                                 vMax[2] = vMin[2] + stairHeight;\r
284                                 SetInitialStairPos(rs.direction, vMin, vMax, stairWidth);\r
285 \r
286 \r
287                                 // Build The Steps\r
288                                 for(int i = 0; i < numSteps; i++)\r
289                                 {\r
290                                         if(rs.style == STYLE_BOB)\r
291                                                 Build_StairStep_Wedge(rs.direction, vMin, vMax, rs.mainTexture, rs.riserTexture, rs.bUseDetail);\r
292                                         else if(rs.style == STYLE_ORIGINAL)\r
293                                                 Build_StairStep(vMin, vMax, rs.mainTexture, rs.riserTexture, rs.direction);\r
294 \r
295                                         // get step into next position\r
296                                         MoveBlock(rs.direction, vMin, vMax, stairWidth);\r
297                                         vMax[2] += stairHeight;\r
298                                         if(rs.style == STYLE_BOB)\r
299                                                 vMin[2] += stairHeight; // wedge bottom must be raised\r
300                                 }\r
301                         }\r
302                 }\r
303         }\r
304 \r
305         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
306 }\r
307 \r
308 void DoBuildDoors(vec3_t vMin, vec3_t vMax)\r
309 {\r
310         // ensure we have something selected\r
311         if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )\r
312         {\r
313                 DoMessageBox("Invalid number of brushes selected, chose 1 only", "Error", MB_OK);\r
314                 return; \r
315         }\r
316 \r
317         // tell Radiant we want to access the selected brushes\r
318         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
319                         \r
320         // get handle to size definition brush\r
321         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);\r
322         // cant release until we delete the brush, if we do...\r
323 \r
324         DoorRS rs;\r
325         strcpy(rs.mainTexture, GetCurrentTexture());\r
326 \r
327         if(DoDoorsBox(&rs) == IDOK)\r
328         {\r
329                 g_FuncTable.m_pfnDeleteBrushHandle(brush);\r
330 \r
331                 BuildDoorsX2(vMin, vMax, \r
332                         rs.bScaleMainH, rs.bScaleMainV,\r
333                         rs.bScaleTrimH, rs.bScaleTrimV,\r
334                         rs.mainTexture, rs.trimTexture,\r
335                         rs.nOrientation);       // shapes.cpp\r
336         }\r
337 \r
338         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
339 }\r
340 \r
341 void DoPathPlotter()\r
342 {\r
343         PathPlotterRS rs;\r
344         int ret = DoPathPlotterBox(&rs);\r
345         if(ret == IDCANCEL)\r
346                 return;\r
347         if(ret == IDNO)\r
348         {\r
349                 if(g_PathView)\r
350                         delete g_PathView;\r
351                 return;\r
352         }\r
353 \r
354         if( g_FuncTable.m_pfnSelectedBrushCount() != 1)\r
355         {\r
356                 DoMessageBox("Invalid number of brushes selected, chose 1 only", "Error", MB_OK);\r
357                 return;\r
358         }\r
359 \r
360         // tell Radiant we want to access the selected brushes\r
361         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
362                         \r
363         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);\r
364         // should be our trigger brush\r
365 \r
366         DEntity world;\r
367         world.LoadEPairList(*g_EntityTable.m_pfnGetEntityKeyValList(brush->owner));\r
368 \r
369         DEPair* trigger_ep = world.FindEPairByKey("targetname");\r
370 \r
371         if(trigger_ep)\r
372         {\r
373                 if(!strcmp(world.m_Classname, "trigger_push"))\r
374                 {\r
375                         DEPair* target_ep = world.FindEPairByKey("target");\r
376                         if(target_ep)\r
377                         {\r
378                                 entity_s* entTarget = FindEntityFromTargetname(target_ep->value, NULL);\r
379                                 if(entTarget)\r
380                                 {\r
381                                         if(g_PathView)\r
382                                                 delete g_PathView;\r
383                                         g_PathView = new DBobView;\r
384 \r
385                                         g_PathView->Begin(trigger_ep->value, target_ep->value, rs.fMultiplier, rs.nPoints, rs.fGravity, rs.bNoUpdate, rs.bShowExtra);\r
386                                 }\r
387                                 else\r
388                                         DoMessageBox("trigger_push target could not be found.", "Error", MB_OK);\r
389                         }\r
390                         else\r
391                                 DoMessageBox("trigger_push has no target.", "Error", MB_OK);\r
392                 }\r
393                 else\r
394                         DoMessageBox("You must select a 'trigger_push' entity.", "Error", MB_OK);\r
395         }       \r
396         else\r
397                 DoMessageBox("Entity must have a targetname", "Error", MB_OK);\r
398 \r
399         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
400 }\r
401 \r
402 void DoPitBuilder(vec3_t vMin, vec3_t vMax)\r
403 {\r
404         // ensure we have something selected\r
405         if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )\r
406         {\r
407                 DoMessageBox("Invalid number of brushes selected, chose 1 only", "Error", MB_OK);\r
408                 return; \r
409         }\r
410 \r
411         // tell Radiant we want to access the selected brushes\r
412         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
413                         \r
414         // get handle to size definition brush\r
415         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);\r
416         // cant release until we delete the brush, if we do...\r
417 \r
418         DShape pit;\r
419 \r
420         if(pit.BuildPit(vMin, vMax))\r
421         {\r
422                 pit.Commit();\r
423 \r
424                 g_FuncTable.m_pfnDeleteBrushHandle(brush);\r
425         }\r
426         else\r
427                 DoMessageBox("Failed To Make Pit\nTry Making The Brush Bigger", "Error", MB_OK);\r
428 \r
429         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
430 }\r
431 \r
432 void DoMergePatches()\r
433 {\r
434   patch_merge_t merge_info;\r
435   DPatch mrgPatches[2];\r
436   int i;\r
437 \r
438   // ensure we have something selected\r
439   if ( g_FuncTable.m_pfnSelectedBrushCount() != 2 )\r
440   {\r
441     DoMessageBox("Invalid number of objects selected, chose 2 only", "Error", MB_OK);\r
442     return; \r
443   }\r
444 \r
445 \r
446   g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
447 \r
448   for (i = 0; i < 2; i++)\r
449   {\r
450     brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(i);\r
451 \r
452     if (!brush->pPatch)\r
453     {\r
454       g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
455       DoMessageBox("You must select ONLY patches", "Error", MB_OK);\r
456       return; \r
457     }\r
458 \r
459     mrgPatches[i].LoadFromBrush_t(brush);\r
460   }\r
461 \r
462   /*  mrgPatches[0].Transpose();\r
463       mrgPatches[0].RemoveFromRadiant();\r
464       mrgPatches[0].BuildInRadiant();*/\r
465 \r
466   merge_info = mrgPatches[0].IsMergable(&mrgPatches[1]);\r
467 \r
468   if (merge_info.mergable)\r
469   {\r
470     Sys_Printf("%i %i", merge_info.pos1, merge_info.pos2);\r
471 \r
472     Sys_Printf("Patches Mergable\n");\r
473     DPatch* newPatch = mrgPatches[0].MergePatches(merge_info, &mrgPatches[0], &mrgPatches[1]);\r
474 \r
475     /*                mrgPatches[0].RemoveFromRadiant();\r
476     mrgPatches[0].BuildInRadiant();\r
477     \r
478       mrgPatches[1].RemoveFromRadiant();\r
479       mrgPatches[1].BuildInRadiant();\r
480       \r
481         \r
482     delete newPatch;*/\r
483 \r
484     if (!newPatch)\r
485     {\r
486     } else\r
487     {\r
488       mrgPatches[0].RemoveFromRadiant();\r
489       mrgPatches[1].RemoveFromRadiant();\r
490 \r
491       newPatch->BuildInRadiant();\r
492       delete newPatch;\r
493     }\r
494   }\r
495 \r
496   g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
497 }\r
498 \r
499 void DoSplitPatch() {\r
500         DPatch patch;\r
501 \r
502         // ensure we have something selected\r
503         if( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {\r
504                 DoMessageBox("Invalid number of objects selected, select 1 patch only", "Error", MB_OK);\r
505                 return; \r
506         }\r
507 \r
508         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
509 \r
510         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);\r
511 \r
512         if( !brush->pPatch ) {\r
513                 g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
514                 DoMessageBox("You must select ONLY patches", "Error", MB_OK);\r
515                 return; \r
516         }\r
517 \r
518         patch.LoadFromBrush_t(brush);\r
519 \r
520         list<DPatch> patchList = patch.Split( true, true );\r
521         for(list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {\r
522                 (*patches).BuildInRadiant();\r
523         }\r
524 \r
525         patch.RemoveFromRadiant();\r
526 \r
527         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
528 }\r
529 \r
530 void DoVisAnalyse()\r
531 {\r
532         char filename[1024];\r
533 \r
534         if( g_FuncTable.m_pfnSelectedBrushCount() == 0 )\r
535         {\r
536                 if(g_VisView) \r
537                 {\r
538                         delete g_VisView;\r
539                         return;\r
540                 }\r
541         }\r
542 \r
543         if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )\r
544         {\r
545                 DoMessageBox("Invalid number of objects selected, select 1 only", "Error", MB_OK);\r
546                 return; \r
547         }\r
548 \r
549         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
550 \r
551         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);\r
552 \r
553         DBrush orgBrush;\r
554         orgBrush.LoadFromBrush_t(brush, false);\r
555 \r
556         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
557 \r
558         orgBrush.BuildBounds();\r
559         vec3_t origin;\r
560         origin[0] = (orgBrush.bbox_max[0] + orgBrush.bbox_min[0])/2.f;\r
561         origin[1] = (orgBrush.bbox_max[1] + orgBrush.bbox_min[1])/2.f;\r
562         origin[2] = (orgBrush.bbox_max[2] + orgBrush.bbox_min[2])/2.f;\r
563 \r
564 \r
565   char* rad_filename = g_FuncTable.m_pfnGetMapName();\r
566         if(!rad_filename)\r
567         {\r
568                 DoMessageBox("An Error Occurred While Trying\n To Get The Map Filename", "Error", MB_OK);\r
569                 return;\r
570         }\r
571 \r
572         strcpy(filename, rad_filename);\r
573                 \r
574         char* ext = strrchr(filename, '.')+1;\r
575         strcpy(ext, "bsp");// rename the extension\r
576 \r
577         list<DWinding*> *pointList = BuildTrace(filename, origin);\r
578 \r
579         if(!g_VisView)\r
580         {\r
581                 g_VisView = new DVisDrawer;\r
582                 g_VisView->Register();\r
583         }\r
584         \r
585         g_VisView->SetList(pointList);\r
586 }\r
587 \r
588 void DoTrainPathPlot() {\r
589         if(g_TrainView) {\r
590                 delete g_TrainView;\r
591                 g_TrainView = NULL;\r
592         }\r
593 \r
594         g_TrainView = new DTrainDrawer();\r
595 }\r
596 \r
597 void DoCaulkSelection( void ) {\r
598         DEntity world;\r
599         \r
600         float fScale[2] = { 0.5f, 0.5f };\r
601         float fShift[2] = { 0.0f, 0.0f };\r
602 \r
603         int bResetScale[2] = { false, false };\r
604         int bResetShift[2] = { false, false };\r
605 \r
606         world.LoadSelectedBrushes();\r
607         world.LoadSelectedPatches();\r
608         world.ResetTextures( NULL, fScale, fShift, 0, "textures/common/caulk", true, bResetScale, bResetShift, false, true );\r
609 }\r
610 \r
611 void DoTreePlanter( void ) {\r
612         if(g_TreePlanter) {\r
613                 delete g_TreePlanter;\r
614                 g_TreePlanter = NULL;\r
615                 return;\r
616         }\r
617 \r
618         g_TreePlanter = new DTreePlanter();\r
619 }\r
620 \r
621 void DoDropEnts( void ) {\r
622         if(g_TreePlanter) {\r
623                 g_TreePlanter->DropEntsToGround();\r
624         }\r
625 }\r
626 \r
627 void DoMakeChain( void ) {\r
628         DTreePlanter pl;\r
629         pl.MakeChain();\r
630 }\r
631 \r
632 typedef DPoint* pntTripple[3];\r
633 \r
634 bool bFacesNoTop[6] = {true, true, true, true, true, false};\r
635 \r
636 void DoFlipTerrain( void ) {\r
637         vec3_t vUp = { 0.f, 0.f, 1.f };\r
638   int i;\r
639 \r
640         // ensure we have something selected\r
641         if( g_FuncTable.m_pfnSelectedBrushCount() != 2 ) {\r
642                 DoMessageBox("Invalid number of objects selected, chose 2 only", "Error", MB_OK);\r
643                 return;\r
644         }\r
645 \r
646         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
647 \r
648         brush_t* brushes[2];\r
649         for( i = 0; i < 2; i++ ) {\r
650                 brushes[i] = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(i);\r
651         }\r
652 \r
653         DBrush Brushes[2];\r
654         DPlane* Planes[2];\r
655         pntTripple Points[2];\r
656         for( i = 0; i < 2; i++ ) {\r
657                 Brushes[i].LoadFromBrush_t( brushes[i], false );\r
658                 if(!(Planes[i] = Brushes[i].FindPlaneWithClosestNormal( vUp )) || Brushes[i].FindPointsForPlane( Planes[i], Points[i], 3 ) != 3) {\r
659                         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
660                         DoMessageBox("Error", "Error", MB_OK);\r
661                         return;\r
662                 }\r
663         }\r
664 \r
665         vec3_t mins1, mins2, maxs1, maxs2;\r
666         Brushes[0].GetBounds( mins1, maxs1 );\r
667         Brushes[1].GetBounds( mins2, maxs2 );\r
668 \r
669         entity_t* ents[2];\r
670         for( i = 0; i < 2; i++ ) {\r
671                 ents[i] = brushes[i]->owner;\r
672                 Brushes[i].RemoveFromRadiant();\r
673         }\r
674 \r
675         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
676 \r
677 \r
678 \r
679 \r
680         int dontmatch[2] = { -1, -1 };\r
681         bool found = false;\r
682         for( i = 0; i < 3; i++ ) {\r
683                 for( int j = 0; j < 3 && !found; j++ ) {\r
684                         if(VectorCompare( (Points[0])[i]->_pnt, (Points[1])[j]->_pnt )) {\r
685                                 found = true;\r
686                                 break;\r
687                         }\r
688                 }\r
689                 if(!found) {\r
690                         dontmatch[0] = i;\r
691                         break;\r
692                 }\r
693                 found = false;\r
694         }\r
695         if(dontmatch[0] == -1) {\r
696                 DoMessageBox("Error", "Error", MB_OK);\r
697                 return;\r
698         }\r
699 \r
700         for( i = 0; i < 3; i++ ) {\r
701                 for( int j = 0; j < 3 && !found; j++ ) {\r
702                         if(VectorCompare( (Points[1])[i]->_pnt, (Points[0])[j]->_pnt )) {\r
703                                 found = true;\r
704                                 break;\r
705                         }\r
706                 }\r
707                 if(!found) {\r
708                         dontmatch[1] = i;\r
709                         break;\r
710                 }\r
711                 found = false;\r
712         }\r
713         if(dontmatch[1] == -1) {\r
714                 DoMessageBox("Error", "Error", MB_OK);\r
715                 return;\r
716         }\r
717 \r
718         vec3_t plnpnts1[3];\r
719         vec3_t plnpnts2[3];\r
720         vec3_t plnpntsshr[3];\r
721 \r
722         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts1[0] );\r
723         for( i = 0; i < 3; i++ ) {\r
724                 if( dontmatch[0] != i ) {\r
725                         VectorCopy( (Points[0])[i]->_pnt, plnpnts1[1] );\r
726                         break;\r
727                 }\r
728         }\r
729         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts1[2] );\r
730 \r
731         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts2[0] );\r
732         for( i = 0; i < 3; i++ ) {\r
733                 if( dontmatch[1] != i && !VectorCompare( (Points[1])[i]->_pnt, plnpnts1[1] )) {\r
734                         VectorCopy( (Points[1])[i]->_pnt, plnpnts2[1] );\r
735                         break;\r
736                 }\r
737         }\r
738         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts2[2] );\r
739 \r
740         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[0] );\r
741         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[1] );\r
742         if( (Points[1])[dontmatch[1]]->_pnt[2] < (Points[0])[dontmatch[0]]->_pnt[2] ) {\r
743                 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[2] );\r
744         } else {\r
745                 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[2] );\r
746         }\r
747         plnpntsshr[2][2] -= 16;\r
748 \r
749         for( i = 0; i < 3; i++ ) {\r
750                 if( mins2[i] < mins1[i] ) {\r
751                         mins1[i] = mins2[i];\r
752                 }\r
753                 if( maxs2[i] > maxs1[i] ) {\r
754                         maxs1[i] = maxs2[i];\r
755                 }\r
756         }\r
757 \r
758         DBrush* newBrushes[2];\r
759         newBrushes[0] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);\r
760         newBrushes[1] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);\r
761 \r
762         vec3_t normal;\r
763         MakeNormal( plnpnts1[0], plnpnts1[1], plnpnts1[2], normal );\r
764         if( normal[2] >= 0 ) {\r
765                 newBrushes[0]->AddFace( plnpnts1[0], plnpnts1[1], plnpnts1[2], "textures/common/terrain", true );\r
766         } else {\r
767                 newBrushes[0]->AddFace( plnpnts1[2], plnpnts1[1], plnpnts1[0], "textures/common/terrain", true );\r
768         }\r
769 \r
770         MakeNormal( plnpnts2[0], plnpnts2[1], plnpnts2[2], normal );\r
771         if( normal[2] >= 0 ) {\r
772                 newBrushes[1]->AddFace( plnpnts2[0], plnpnts2[1], plnpnts2[2], "textures/common/terrain", true );\r
773         } else {\r
774                 newBrushes[1]->AddFace( plnpnts2[2], plnpnts2[1], plnpnts2[0], "textures/common/terrain", true );\r
775         }\r
776 \r
777         vec3_t vec;\r
778         MakeNormal( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], normal );      \r
779         \r
780         VectorSubtract( plnpnts1[2], plnpnts1[1], vec );\r
781         if( DotProduct( vec, normal ) >= 0 ) {\r
782                 newBrushes[0]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );\r
783         } else {\r
784                 newBrushes[0]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );\r
785         }\r
786 \r
787         VectorSubtract( plnpnts2[2], plnpnts2[1], vec );\r
788         if( DotProduct( vec, normal ) >= 0 ) {\r
789                 newBrushes[1]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );\r
790         } else {\r
791                 newBrushes[1]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );\r
792         }\r
793 \r
794         for( i = 0; i < 2; i++ ) {\r
795                 newBrushes[i]->RemoveRedundantPlanes();\r
796                 newBrushes[i]->BuildInRadiant( false, NULL, ents[i] );\r
797                 delete newBrushes[i];\r
798         }\r
799 \r
800 }\r