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