apply misc fixes from Markus Fischer and Rambetter
[xonotic/netradiant.git] / radiant / vertsel.cpp
1 /*
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #include "stdafx.h"
23 //#include "qe3.h"
24 #include "winding.h"
25
26 int     FindPoint (vec3_t point)
27 {
28         int             i, j;
29
30         for (i=0 ; i<g_qeglobals.d_numpoints ; i++)
31         {
32                 for (j=0 ; j<3 ; j++)
33                         if (fabs(point[j] - g_qeglobals.d_points[i][j]) > 0.1)
34                                 break;
35                 if (j == 3)
36                         return i;
37         }
38
39         VectorCopy (point, g_qeglobals.d_points[g_qeglobals.d_numpoints]);
40         //qeglobals.d_points[g_qeglobals.d_numpoints] = point;
41   if (g_qeglobals.d_numpoints < MAX_POINTS-1)
42   {
43           g_qeglobals.d_numpoints++;
44   }
45
46         return g_qeglobals.d_numpoints-1;
47 }
48
49 //#define DBG_WNDG
50 int FindEdge (int p1, int p2, face_t *f)
51 {
52         int             i;
53
54         for (i=0 ; i<g_qeglobals.d_numedges ; i++)
55                 if (g_qeglobals.d_edges[i].p1 == p2 && g_qeglobals.d_edges[i].p2 == p1)
56                 {
57                         g_qeglobals.d_edges[i].f2 = f;
58 #ifdef DBG_WNDG
59       Sys_Printf("g_qeglobals.d_edges[%d].f2 = %p\n", i, f);
60 #endif
61                         return i;
62                 }
63
64         g_qeglobals.d_edges[g_qeglobals.d_numedges].p1 = p1;
65         g_qeglobals.d_edges[g_qeglobals.d_numedges].p2 = p2;
66         g_qeglobals.d_edges[g_qeglobals.d_numedges].f1 = f;
67 #ifdef DBG_WNDG
68   Sys_Printf("g_qeglobals.d_edges[%d].f1 = %p\n", g_qeglobals.d_numedges, f);
69 #endif
70
71   if (g_qeglobals.d_numedges < MAX_EDGES-1)
72   {
73           g_qeglobals.d_numedges++;
74   }
75
76         return g_qeglobals.d_numedges-1;
77 }
78
79 void MakeFace (brush_t* b, face_t *f)
80 {
81         winding_t       *w;
82         int                     i;
83         int                     pnum[128];
84
85         w = Brush_MakeFaceWinding (b, f);
86         if (!w)
87                 return;
88         for (i=0 ; i<w->numpoints ; i++)
89                 pnum[i] = FindPoint (w->points[i]);
90         for (i=0 ; i<w->numpoints ; i++)
91                 FindEdge (pnum[i], pnum[(i+1)%w->numpoints], f);
92
93         free (w);
94 }
95
96 void SetupVertexSelection (void)
97 {
98         face_t  *f;
99         brush_t *b;
100
101         g_qeglobals.d_numpoints = 0;
102         g_qeglobals.d_numedges = 0;
103
104         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
105         {
106                 if (b->patchBrush || b->owner->eclass->fixedsize)
107                         continue; // don't make edge and vertex handles for patchbrushes
108                 for (f=b->brush_faces ; f ; f=f->next)
109                         MakeFace (b,f);
110         }
111 }
112
113 void SelectFaceEdge (brush_t* b, face_t *f, int p1, int p2)
114 {
115         winding_t       *w;
116         int                     i, j, k;
117         int                     pnum[128];
118
119 #ifdef DBG_WNDG
120   if (f==NULL)
121     Sys_Printf("SelectFaceEdge %p %p\n", b, f);
122 #endif
123
124         w = Winding_Clone(f->face_winding);//Brush_MakeFaceWinding (b, f);
125         if (!w)
126                 return;
127         for (i=0 ; i<w->numpoints ; i++)
128                 pnum[i] = FindPoint (w->points[i]);
129
130   for (i=0 ; i<w->numpoints ; i++)
131                 if (pnum[i] == p1 && pnum[(i+1)%w->numpoints] == p2)
132                 {
133                         VectorCopy (g_qeglobals.d_points[pnum[i]], f->planepts[0]);
134                         VectorCopy (g_qeglobals.d_points[pnum[(i+1)%w->numpoints]], f->planepts[1]);
135                         VectorCopy (g_qeglobals.d_points[pnum[(i+2)%w->numpoints]], f->planepts[2]);
136                         for (j=0 ; j<3 ; j++)
137                         {
138                                 for (k=0 ; k<3 ; k++)
139                                 {
140                                         f->planepts[j][k] = floor(f->planepts[j][k]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
141                                 }
142                         }
143
144                         AddPlanept (f->planepts[0]);
145                         AddPlanept (f->planepts[1]);
146                         break;
147                 }
148
149         if (i == w->numpoints)
150                 Sys_Printf ("SelectFaceEdge: failed\n");
151         Winding_Free (w);
152 }
153
154
155 void SelectVertex (int p1)
156 {
157         brush_t         *b;
158         winding_t       *w;
159         int             i;
160         face_t          *f;
161
162         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
163   {
164           for (f=b->brush_faces ; f ; f=f->next)
165           {
166                   w =  Brush_MakeFaceWinding (b, f);
167                   if (!w)
168                           continue;
169                   for (i=0 ; i<w->numpoints ; i++)
170                   {
171                           if (FindPoint (w->points[i]) == p1)
172                           {
173                                   VectorCopy (w->points[(i+w->numpoints-1)%w->numpoints], f->planepts[0]);
174                                   VectorCopy (w->points[i], f->planepts[1]);
175                                   VectorCopy (w->points[(i+1)%w->numpoints], f->planepts[2]);
176           // NOTE: used to be a planepts clamping to grid here
177
178                             AddPlanept (f->planepts[1]);
179
180                             break;
181         }
182                   }
183                   free (w);
184           }
185   }
186 }
187
188 #define SELECT_EPSILON 8
189
190 void SelectVertexByRay (vec3_t org, vec3_t dir)
191 {
192         int             i, besti;
193         float   d, bestd = VEC_MAX;
194   vec_t epsilon, divergence;
195   ray_t ray;
196   ray_construct_for_vec3(&ray, org, dir);
197
198         // find the point closest to the ray
199         besti = -1;
200         if ((fabs(org[0]) == g_MaxWorldCoord || fabs(org[1]) == g_MaxWorldCoord || fabs(org[2]) == g_MaxWorldCoord)
201                 && (fabs(dir[0]) == 1.0f || fabs(dir[1]) == 1.0f || fabs(dir[2]) == 1.0f)) // very unlikely unless 2d view
202   {
203     divergence = 0;
204                 epsilon = SELECT_EPSILON /  g_pParentWnd->GetXYWnd()->Scale(); // compensate for zoom level
205   }
206   else
207   {
208     divergence = SELECT_EPSILON / (g_pParentWnd->GetCamWnd()->Camera()->width*0.5); // radius / focal length
209     epsilon = 0;
210   }
211
212         for (i=0 ; i<g_qeglobals.d_numpoints ; i++)
213         {
214                 d = ray_intersect_point(&ray, g_qeglobals.d_points[i], epsilon, divergence);
215
216                 if (d < bestd)
217                 {
218                         bestd = d;
219                         besti = i;
220                 }
221         }
222
223         if (besti == -1)
224         {
225                 Sys_Printf ("Click didn't hit a vertex\n");
226                 return;
227         }
228         Sys_Printf ("hit vertex\n");
229         g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = g_qeglobals.d_points[besti];
230         if (!g_PrefsDlg.m_bVertexSplit)
231   {
232           SelectVertex (besti);
233   }
234 }
235
236 // TTimo: NOTE: we should not have to put extern funcs like that
237 //   those should be defined in qe3.h
238 extern void AddPatchMovePoint(vec3_t v, bool bMulti, bool bFull);
239 extern int PointInMoveList(float *pf);
240 void SelectCurvePointByRay (vec3_t org, vec3_t dir, int buttons)
241 {
242         int             i, j;
243         float   d, bestd = VEC_MAX;
244         vec3_t  *pPointBest;
245   vec_t epsilon, divergence;
246   ray_t ray;
247   ray_construct_for_vec3(&ray, org, dir);
248
249         // find the point closest to the ray
250         pPointBest = NULL;
251         if ((fabs(org[0]) == g_MaxWorldCoord || fabs(org[1]) == g_MaxWorldCoord || fabs(org[2]) == g_MaxWorldCoord)
252                 && (fabs(dir[0]) == 1.0f || fabs(dir[1]) == 1.0f || fabs(dir[2]) == 1.0f)) // very unlikely unless 2d view
253   {
254     divergence = 0;
255                 epsilon = SELECT_EPSILON /  g_pParentWnd->GetXYWnd()->Scale(); // compensate for zoom level
256   }
257   else
258   {
259     divergence = SELECT_EPSILON / (g_pParentWnd->GetCamWnd()->Camera()->width*0.5); // radius / focal length
260     epsilon = 0;
261   }
262
263
264         g_qeglobals.d_numpoints = 0;
265
266         for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
267         {
268                 if (pb->patchBrush)
269                 {
270                         patchMesh_t* p = pb->pPatch;
271                         
272                         for (i = 0 ; i < p->width ; i++ ) 
273                         {
274                                 for ( j = 0 ; j < p->height ; j++ )
275                                 {
276           d = ray_intersect_point(&ray, p->ctrl[i][j].xyz, epsilon, divergence);
277
278                                         if (d >= bestd)
279                                                 continue;
280                                         
281                                         bestd = d;
282
283                                         if (PointInMoveList(*pPointBest) != -1 && PointInMoveList(p->ctrl[i][j].xyz) == -1)
284                                                 continue; // choose selected points with preference over unselected
285
286                                         pPointBest = &p->ctrl[i][j].xyz;
287                                         
288                                 }
289                         }
290                 }
291         }
292
293         if (pPointBest == NULL)
294         {
295                 if (g_pParentWnd->ActiveXY()->AreaSelectOK())
296                 {
297                         g_qeglobals.d_select_mode = sel_area;
298                         VectorCopy(org, g_qeglobals.d_vAreaTL);
299                         VectorCopy(org, g_qeglobals.d_vAreaBR);
300                 }
301                 return;
302         }
303         else
304                 AddPatchMovePoint(pPointBest[0], buttons & MK_CONTROL, buttons & MK_SHIFT);
305 }
306
307 // optimization bug:
308 // had to use the #define DBG_WNDG to identify
309 // the first loop that checks the best edge is broken in release-optimized build
310 // unrolled the mid[] loop and forced floating consistency on seems to fix
311 #ifdef _WIN32
312 #pragma optimize( "p", on )
313 #endif
314 void SelectEdgeByRay (vec3_t org, vec3_t dir)
315 {
316         int             i, besti;
317         float   d, bestd = VEC_MAX;
318         vec3_t  mid;
319         pedge_t *e;
320   vec_t epsilon, divergence;
321   ray_t ray;
322   ray_construct_for_vec3(&ray, org, dir);
323
324         // find the edge closest to the ray
325         besti = -1;
326         if ((fabs(org[0]) == g_MaxWorldCoord || fabs(org[1]) == g_MaxWorldCoord || fabs(org[2]) == g_MaxWorldCoord)
327                 && (fabs(dir[0]) == 1.0f || fabs(dir[1]) == 1.0f || fabs(dir[2]) == 1.0f)) // very unlikely unless 2d view
328   {
329     divergence = 0;
330                 epsilon = SELECT_EPSILON /  g_pParentWnd->GetXYWnd()->Scale(); // compensate for zoom level
331   }
332   else
333   {
334     divergence = SELECT_EPSILON / (g_pParentWnd->GetCamWnd()->Camera()->width*0.5); // radius / focal length
335     epsilon = 0;
336   }
337
338         for (i=0 ; i<g_qeglobals.d_numedges ; i++)
339         {
340     mid[0] = 0.5f*(g_qeglobals.d_points[g_qeglobals.d_edges[i].p1][0] + g_qeglobals.d_points[g_qeglobals.d_edges[i].p2][0]);
341     mid[1] = 0.5f*(g_qeglobals.d_points[g_qeglobals.d_edges[i].p1][1] + g_qeglobals.d_points[g_qeglobals.d_edges[i].p2][1]);
342     mid[2] = 0.5f*(g_qeglobals.d_points[g_qeglobals.d_edges[i].p1][2] + g_qeglobals.d_points[g_qeglobals.d_edges[i].p2][2]);
343
344                 d = ray_intersect_point(&ray, mid, epsilon, divergence);
345
346 #ifdef DBG_WNDG
347     Sys_Printf("d: %f\n", d);
348 #endif
349                 if (d < bestd)
350                 {
351 #ifdef DBG_WNDG
352       Sys_Printf("bestd = d\n");
353 #endif
354                         bestd = d;
355                         besti = i;
356                 }
357         }
358
359         if (besti == -1)
360         {
361                 Sys_Printf ("Click didn't hit an edge\n");
362                 return;
363         }
364         Sys_Printf ("Hit edge\n");
365
366         // make the two faces that border the edge use the two edge points
367         // as primary drag points
368         g_qeglobals.d_num_move_points = 0;
369         e = &g_qeglobals.d_edges[besti];
370 #ifdef DBG_WNDG
371   Sys_Printf("besti: %d\n", besti);
372   if (e->f1 == NULL)
373   {
374     Sys_Printf ("e->f1 == NULL e->f2 %p\n", e->f2);
375   }
376   if (e->f2 == NULL)
377   {
378     Sys_Printf ("e->f1 %p e->f2 == NULL\n",e->f1);
379   }
380 #endif
381         for (brush_t* b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
382   {
383     SelectFaceEdge (b, e->f1, e->p1, e->p2);
384           SelectFaceEdge (b, e->f2, e->p2, e->p1);
385   }
386 }