bobtools patch width/height fix from OSXNetRadiant. Now we seem to be mostly in sync...
[xonotic/netradiant.git] / contrib / bobtoolz / DPatch.cpp
1 /*
2 BobToolz plugin for GtkRadiant
3 Copyright (C) 2001 Gordon Biggans
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 // DPatch.cpp: implementation of the DPatch class.
21 //
22 //////////////////////////////////////////////////////////////////////
23
24 #include "DPatch.h"
25
26 #include <list>
27 #include "str.h"
28 #include "scenelib.h"
29
30 #include "ipatch.h"
31
32 #include "misc.h"
33 #include "./dialogs/dialogs-gtk.h"
34
35 //////////////////////////////////////////////////////////////////////
36 // Construction/Destruction
37 //////////////////////////////////////////////////////////////////////
38
39 //              Added patch merging, wahey!
40
41 //
42 //              problem is, you cant put patches into entities as yet :(
43 //
44
45 DPatch::DPatch()
46 {
47         width = MIN_PATCH_WIDTH;
48         height = MIN_PATCH_HEIGHT;
49         QER_entity = NULL;
50         QER_brush = NULL;
51 }
52
53 DPatch::~DPatch()
54 {
55
56 }
57
58 void DPatch::SetTexture(const char *textureName)
59 {
60         strcpy(texture, textureName);
61 }
62
63 void CopyDrawVert(const drawVert_t* in, drawVert_t* out)
64 {
65         out->lightmap[0] = in->lightmap[0];
66         out->lightmap[1] = in->lightmap[1];
67         out->st[0] = in->st[0];
68         out->st[1] = in->st[1];
69         VectorCopy(in->normal, out->normal);
70         VectorCopy(in->xyz, out->xyz);
71 }
72
73 void DPatch::BuildInRadiant(scene::Node* entity)
74 {
75         NodeSmartReference patch(GlobalPatchCreator().createPatch());
76
77   scene::Node& parent = entity != 0 ? *entity : GlobalRadiant().getMapWorldEntity();
78   Node_getTraversable(parent)->insert(patch);
79
80   GlobalPatchCreator().Patch_setShader(patch, texture);
81   GlobalPatchCreator().Patch_resize(patch, height, width);
82   PatchControlMatrix matrix = GlobalPatchCreator().Patch_getControlPoints(patch);
83         for(int x = 0; x < height; x++)
84   {
85                 for(int y = 0; y < width; y++)
86     {
87       PatchControl& p = matrix(y, x);
88       p.m_vertex[0] = points[x][y].xyz[0];
89       p.m_vertex[1] = points[x][y].xyz[1];
90       p.m_vertex[2] = points[x][y].xyz[2];
91       p.m_texcoord[0] = points[x][y].st[0];
92       p.m_texcoord[1] = points[x][y].st[1];
93     }
94   }
95   GlobalPatchCreator().Patch_controlPointsChanged(patch);
96   
97   QER_entity = entity;
98         QER_brush = patch.get_pointer();
99
100
101 #if 0
102         int nIndex = g_FuncTable.m_pfnCreatePatchHandle();
103     //$ FIXME: m_pfnGetPatchHandle
104         patchMesh_t* pm = g_FuncTable.m_pfnGetPatchData(nIndex);
105
106   b->patchBrush = true;
107   b->pPatch = Patch_Alloc();
108         b->pPatch->setDims(width,height);
109
110         for(int x = 0; x < width; x++)
111                 for(int y = 0; y < height; y++)
112                         CopyDrawVert(&points[x][y], &pm->ctrl[x][y]);
113
114 /*      if(entity)
115         {
116 //              strcpy(pm->d_texture->name, texture);
117
118                 brush_t* brush = (brush_t*)g_FuncTable.m_pfnCreateBrushHandle();
119                 brush->patchBrush = true;
120                 brush->pPatch = pm;             
121
122                 pm->pSymbiot = brush;
123                 pm->bSelected = false;
124                 pm->bOverlay = false;   // bleh, f*cks up, just have to wait for a proper function
125                 pm->bDirty = true;              // or get my own patch out....
126                 pm->nListID = -1;
127
128                 g_FuncTable.m_pfnCommitBrushHandleToEntity(brush, entity);
129         }
130         else*/  // patch to entity just plain dont work atm
131
132   if(entity)
133     g_FuncTable.m_pfnCommitPatchHandleToEntity(nIndex, pm, texture, entity);
134   else
135                 g_FuncTable.m_pfnCommitPatchHandleToMap(nIndex, pm, texture);
136
137         QER_brush = pm->pSymbiot;
138 #endif
139 }
140
141 void DPatch::LoadFromPatch(scene::Instance& patch)
142 {
143   QER_entity = patch.path().parent().get_pointer();
144         QER_brush = patch.path().top().get_pointer();
145
146   PatchControlMatrix matrix = GlobalPatchCreator().Patch_getControlPoints(patch.path().top());
147
148   width = static_cast<int>(matrix.x());
149         height = static_cast<int>(matrix.y());
150
151   for(int x = 0; x < height; x++)
152   {
153                 for(int y = 0; y < width; y++)
154     {
155       PatchControl& p = matrix(y, x);
156       points[x][y].xyz[0] = p.m_vertex[0];
157       points[x][y].xyz[1] = p.m_vertex[1];
158       points[x][y].xyz[2] = p.m_vertex[2];
159       points[x][y].st[0] = p.m_texcoord[0];
160       points[x][y].st[1] = p.m_texcoord[1];
161     }
162   }
163         SetTexture(GlobalPatchCreator().Patch_getShader(patch.path().top()));
164
165 #if 0
166         SetTexture(brush->pPatch->GetShader());
167
168         width = brush->pPatch->getWidth();
169         height = brush->pPatch->getHeight();
170
171   for(int x = 0; x < height; x++)
172   {
173                 for(int y = 0; y < width; y++)
174     {
175       float *p = brush->pPatch->ctrlAt(ROW,x,y);
176       p[0] = points[x][y].xyz[0];
177       p[1] = points[x][y].xyz[1];
178       p[2] = points[x][y].xyz[2];
179       p[3] = points[x][y].st[0];
180       p[4] = points[x][y].st[1];
181     }
182   }
183 #endif
184 }
185
186 bool DPatch::ResetTextures(const char *oldTextureName, const char *newTextureName)
187 {
188         if( !oldTextureName || !strcmp(texture, oldTextureName))
189         {
190                 strcpy(texture, newTextureName);
191                 return true;
192         }
193
194         return false;
195 }
196
197 void Build1dArray(vec3_t* array, drawVert_t points[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT], 
198                                   int startX, int startY, int number, bool horizontal, bool inverse)
199 {
200         int x = startX, y = startY, i, step;
201
202         if(inverse)
203                 step = -1;
204         else
205                 step = 1;
206
207         for(i = 0; i < number; i++)
208         {
209                 VectorCopy(points[x][y].xyz, array[i]);
210
211                 if(horizontal)
212                         x+=step;
213                 else
214                         y+=step;
215         }
216 }
217
218 void Print1dArray(vec3_t* array, int size)
219 {
220         for(int i = 0; i < size; i++)
221                 globalOutputStream() << "(" << array[i][0] << " " << array[i][1] << " " << array[i][2] << ")\t";
222         globalOutputStream() << "\n";
223 }
224
225 bool Compare1dArrays(vec3_t* a1, vec3_t* a2, int size)
226 {
227         int i;
228         bool equal = true;
229
230         for(i = 0; i < size; i++)
231         {
232                 if(!VectorCompare(a1[i], a2[size-i-1]))
233                 {
234                         equal = false;
235                         break;
236                 }
237         }
238         return equal;
239 }
240
241 patch_merge_t DPatch::IsMergable(DPatch *other)
242 {
243         int i, j;
244         vec3_t p1Array[4][MAX_PATCH_HEIGHT];
245         vec3_t p2Array[4][MAX_PATCH_HEIGHT];
246
247         int p1ArraySizes[4];
248         int p2ArraySizes[4];
249
250         patch_merge_t merge_info;
251
252         Build1dArray(p1Array[0], this->points, 0,                               0,                              this->width,    true,   false);
253         Build1dArray(p1Array[1], this->points, this->width-1,   0,                              this->height,   false,  false);
254         Build1dArray(p1Array[2], this->points, this->width-1,   this->height-1, this->width,    true,   true);
255         Build1dArray(p1Array[3], this->points, 0,                               this->height-1, this->height,   false,  true);
256
257         Build1dArray(p2Array[0], other->points, 0,                              0,                                      other->width,   true,   false);
258         Build1dArray(p2Array[1], other->points, other->width-1, 0,                                      other->height,  false,  false);
259         Build1dArray(p2Array[2], other->points, other->width-1, other->height-1,        other->width,   true,   true);
260         Build1dArray(p2Array[3], other->points, 0,                              other->height-1,        other->height,  false,  true);
261
262         p1ArraySizes[0] = this->width;
263         p1ArraySizes[1] = this->height;
264         p1ArraySizes[2] = this->width;
265         p1ArraySizes[3] = this->height;
266
267         p2ArraySizes[0] = other->width;
268         p2ArraySizes[1] = other->height;
269         p2ArraySizes[2] = other->width;
270         p2ArraySizes[3] = other->height;
271
272         for(i = 0; i < 4; i++)
273         {
274                 for(j = 0; j < 4; j++)
275                 {
276                         if(p1ArraySizes[i] == p2ArraySizes[j])
277                         {
278                                 if(Compare1dArrays(p1Array[i], p2Array[j], p1ArraySizes[i]))
279                                 {
280                                         merge_info.pos1 = i;
281                                         merge_info.pos2 = j;
282                                         merge_info.mergable = true;
283                                         return merge_info;
284                                 }
285                         }
286                 }
287         }
288         
289         merge_info.mergable = false;
290         return merge_info;
291 }
292
293 DPatch* DPatch::MergePatches(patch_merge_t merge_info, DPatch *p1, DPatch *p2)
294 {
295         while(merge_info.pos1 != 2)
296         {
297                 p1->Transpose();
298                 merge_info.pos1--;
299                 if(merge_info.pos1 < 0)
300                         merge_info.pos1 += 4;
301         }
302
303         while(merge_info.pos2 != 0)
304         {
305                 p2->Transpose();
306                 merge_info.pos2--;
307                 if(merge_info.pos2 < 0)
308                         merge_info.pos2 += 3;
309         }
310         //
311
312         int newHeight = p1->height + p2->height - 1; 
313         if(newHeight > MAX_PATCH_HEIGHT)
314                 return false;
315 /*      int newWidth = p1->width + p2->width - 1;
316         if(newWidth > MAX_PATCH_WIDTH)
317                 return NULL;
318 */      
319         DPatch* newPatch = new DPatch();
320         //switched..
321         newPatch->height        = newHeight;
322         newPatch->width         = p1->width;
323         newPatch->SetTexture(p1->texture);
324
325         for(int y = 0; y < p1->height; y++)
326                 for(int x = 0; x < p1->width; x++)
327                         newPatch->points[x][y] = p1->points[x][y];
328         
329         for(int y = 1; y < p2->height; y++)
330                 for(int x = 0; x < p2->width; x++)
331                         newPatch->points[x][(y + p1->height - 1)] = p2->points[x][y];
332         
333 //      newPatch->Invert();
334         return newPatch;
335 }
336
337 void DPatch::Invert()
338 {
339         int i, j;
340
341         for(i = 0 ; i < width ; i++ ) 
342         {
343                 for(j = 0; j < height / 2; j++)
344                 {
345       std::swap(points[i][height - 1- j], points[i][j]);
346                 }
347         }
348 }
349
350 void DPatch::Transpose()
351 {
352         int             i, j, w;
353
354         if ( width > height ) 
355         {
356                 for ( i = 0 ; i < height ; i++ ) 
357                 {
358                         for ( j = i + 1 ; j < width ; j++ ) 
359                         {
360                                 if ( j < height ) 
361                                 {
362                                         // swap the value
363           std::swap(points[j][i], points[i][j]);
364                                 } 
365                                 else 
366                                 {
367                                 // just copy
368                                         points[i][j] = points[j][i];
369                         }
370                 }
371                  }
372         } 
373         else 
374         {
375                 for ( i = 0 ; i < width ; i++ ) 
376         {
377                         for ( j = i + 1 ; j < height ; j++ ) 
378                         {
379                         if ( j < width ) 
380                                 {
381                                         // swap the value
382           std::swap(points[i][j], points[j][i]);
383                         } 
384                                 else 
385                                 {
386                                 // just copy
387                                         points[j][i] = points[i][j];
388                         }
389                 }
390         }
391     }
392
393         w = width;
394         width = height;
395         height = w;
396
397         Invert();
398 }
399
400 std::list<DPatch> DPatch::SplitRows()
401 {
402         std::list<DPatch> patchList;
403         int i;
404         int x, y;
405
406         if(height >= 5)
407         {
408                 for(i = 0; i < (height-1)/2; i++)
409                 {
410                         DPatch p;
411
412                         p.width = width;
413                         p.height = 3;
414                         p.SetTexture(texture);
415                         for(x = 0; x < 3; x++)
416                         {
417                                 for(y = 0; y < p.width; y++)
418                                 {
419                                         p.points[x][y] = points[(i*2)+x][y];
420                                 }
421                         }                       
422                         patchList.push_back(p);
423                 }
424         } else {
425                 //This returns exactly what comes in.. I don't know better :/
426                 //If nothing is returned, the Patch in Radiant is just deleted - I suppose for evil follow up erros.
427                 DPatch p;
428                 
429                 p.height = height;
430                 p.width = width;
431                 p.SetTexture(texture);
432                 
433                 for(x = 0; x < p.height; x++)
434                 {
435                         for(y = 0; y < p.width; y++)
436                         {
437                                 p.points[x][y] = points[x][y];
438                         }
439                 }
440                 patchList.push_back(p);
441         }
442         return patchList;
443 }
444
445 std::list<DPatch> DPatch::SplitCols()
446 {
447         std::list<DPatch> patchList;
448         int i;
449         int x, y;
450         
451         if(width >= 5)
452         {
453                 for(i = 0; i < (width-1)/2; i++)
454                 {
455                         DPatch p;
456                         
457                         p.width = 3;
458                         p.height = height;
459                         p.SetTexture(texture);
460                         
461                         for(x = 0; x < p.height; x++)
462                         {
463                                 for(y = 0; y < 3; y++)
464                                 {
465                                         p.points[x][y] = points[x][(i*2)+y];
466                                 }
467                         }
468                         patchList.push_back(p);
469                 }
470         } else 
471         {
472                 //This returns exactly what comes in.. I don't know better :/
473                 //If nothing is returned, the Patch in Radiant is just deleted - I suppose for evil follow up erros.
474                 DPatch p;
475                 
476                 p.height = height;
477                 p.width = width;
478                 p.SetTexture(texture);
479                 
480                 for(x = 0; x < p.height; x++)
481                 {
482                         for(y = 0; y < p.width; y++)
483                         {
484                                 p.points[x][y] = points[x][y];
485                         }
486                 }
487                 patchList.push_back(p);
488         }
489         return patchList;
490 }
491
492 std::list<DPatch> DPatch::Split()
493 {
494         std::list<DPatch> patchList;
495         int i;
496         int x, y;
497         
498         if(width >= 5)
499         {
500                 for(i = 0; i < (width-1)/2; i++)
501                 {
502                         DPatch p;
503                         
504                         p.width = 3;
505                         p.height = height;
506                         p.SetTexture(texture);
507                         
508                         for(x = 0; x < p.height; x++)
509                         {
510                                 for(y = 0; y < 3; y++)
511                                 {
512                                         p.points[x][y] = points[x][(i*2)+y];
513                                 }
514                         }
515                         patchList.push_back(p);
516                 }
517                 std::list<DPatch> patchList2;
518                 for(std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++)
519                 {
520                         std::list<DPatch> patchList3 = (*patches).SplitRows();
521                         for(std::list<DPatch>::iterator patches2 = patchList3.begin(); patches2 != patchList3.end(); patches2++)
522                                 patchList2.push_front(*patches2);
523                 }
524                 return patchList2;
525                 
526         } else  if(height >= 5) 
527         {
528                 for(i = 0; i < (height-1)/2; i++)
529                 {
530                         DPatch p;
531                         
532                         p.width = width;
533                         p.height = 3;
534                         p.SetTexture(texture);
535                         for(x = 0; x < 3; x++)
536                         {
537                                 for(y = 0; y < p.width; y++)
538                                 {
539                                         p.points[x][y] = points[(i*2)+x][y];
540                                 }
541                         }                       
542                         patchList.push_back(p);
543                 }               
544                 std::list<DPatch> patchList2;
545                 for(std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++)
546                 {
547                         std::list<DPatch> patchList3 = (*patches).SplitCols();
548                         for(std::list<DPatch>::iterator patches2 = patchList3.begin(); patches2 != patchList3.end(); patches2++)
549                                 patchList2.push_front(*patches2);
550                 }
551                 return patchList2;
552                 
553         } else 
554         {
555                 //This returns exactly what comes in.. I don't know better :/
556                 //If nothing is returned, the Patch in Radiant is just deleted - I suppose for evil follow up erros.
557                 DPatch p;
558                 
559                 p.height = height;
560                 p.width = width;
561                 p.SetTexture(texture);
562                 
563                 for(x = 0; x < p.height; x++)
564                 {
565                         for(y = 0; y < p.width; y++)
566                         {
567                                 p.points[x][y] = points[x][y];
568                         }
569                 }
570                 patchList.push_back(p);         
571         }
572         return patchList;
573 }