Wrap more GTK
[xonotic/netradiant.git] / contrib / gtkgensurf / view.cpp
1 /*
2    GenSurf plugin for GtkRadiant
3    Copyright (C) 2001 David Hyde, Loki software and qeradiant.com
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 #include <math.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include "gensurf.h"
24
25 #undef ISOMETRIC
26
27 extern double backface;
28 extern double dh, dv;
29 extern double xmin,xmax,ymin,ymax,zmin,zmax;
30
31 double SF, SFG;            // Graphics scale factors
32 double XLo, XHi, YLo, YHi, ZLo, ZHi;
33 double yaw,roll;
34 double elevation,azimuth;
35 int cxChar = 10, cyChar = 16;
36 int X0, Y0;
37 int X0G, Y0G;
38
39 static Rect rcCoord;   // where X= Y= is drawn
40 static Rect rcGrid;    // rectangle within rcLower that forms the border of the grid, plus
41                        //   a 3 pixel slop.
42 static Rect rcLower;   // lower half of window, where plan view is drawn
43 static Rect rcUpper;   // upper half or entire window, where isometric projection is drawn
44
45 void vertex_selected();
46 void texfont_init();
47 void texfont_write( const char *text, int l, int t );
48
49 #define PEN_GRID { \
50                 g_GLTable.m_pfn_qglLineWidth( 1 ); \
51                 g_GLTable.m_pfn_qglColor3f( 0, 1, 0 ); \
52                 g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE ); }
53
54 #define PEN_RED { \
55                 g_GLTable.m_pfn_qglLineWidth( 2 ); \
56                 g_GLTable.m_pfn_qglColor3f( 1, 0, 0 ); \
57                 g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE ); }
58
59 #define PEN_DASH { \
60                 g_GLTable.m_pfn_qglLineWidth( 1 ); \
61                 g_GLTable.m_pfn_qglColor3f( 0, 1, 0 ); \
62                 g_GLTable.m_pfn_qglLineStipple( 1, 0xF0F0 ); \
63                 g_GLTable.m_pfn_qglEnable( GL_LINE_STIPPLE ); }
64
65 #define DRAW_QUAD( rc,r,g,b ) { \
66                 g_GLTable.m_pfn_qglBegin( GL_QUADS ); \
67                 g_GLTable.m_pfn_qglColor3f( 0,1,0 ); \
68                 g_GLTable.m_pfn_qglVertex2i( rc.left - 1, rc.bottom ); \
69                 g_GLTable.m_pfn_qglVertex2i( rc.right, rc.bottom );     \
70                 g_GLTable.m_pfn_qglVertex2i( rc.right, rc.top + 1 ); \
71                 g_GLTable.m_pfn_qglVertex2i( rc.left - 1, rc.top + 1 ); \
72                 g_GLTable.m_pfn_qglColor3f( r,g,b ); \
73                 g_GLTable.m_pfn_qglVertex2i( rc.left, rc.bottom + 1 ); \
74                 g_GLTable.m_pfn_qglVertex2i( rc.right - 1, rc.bottom + 1 );     \
75                 g_GLTable.m_pfn_qglVertex2i( rc.right - 1, rc.top ); \
76                 g_GLTable.m_pfn_qglVertex2i( rc.left, rc.top ); \
77                 g_GLTable.m_pfn_qglEnd(); }
78
79
80 #ifndef ISOMETRIC
81 double D = 65536.;
82 double ct[3],st[3];
83 double Hhi, Hlo, Vhi, Vlo;
84 #endif
85
86 #define SUBDIVS 6
87
88
89 void ShowPreview(){
90         if ( Preview ) {
91                 if ( g_pWndPreview == NULL ) {
92                         CreateViewWindow();
93                 }
94                 g_pWndPreview.show();
95
96                 UpdatePreview( true );
97         }
98         else{
99                 gtk_widget_hide( g_pWndPreview );
100         }
101 }
102
103 static void draw_preview(){
104         int width = g_pPreviewWidget->allocation.width, height = g_pPreviewWidget->allocation.height;
105
106         g_GLTable.m_pfn_qglClearColor( 0, 0, 0, 1 );
107         g_GLTable.m_pfn_qglViewport( 0, 0, width, height );
108         g_GLTable.m_pfn_qglMatrixMode( GL_PROJECTION );
109         g_GLTable.m_pfn_qglLoadIdentity();
110         g_GLTable.m_pfn_qglOrtho( 0, width, 0, height, -1, 1 );
111         g_GLTable.m_pfn_qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
112
113         // ^Fishman - Antializing for the preview window.
114         if ( Antialiasing ) {
115                 g_GLTable.m_pfn_qglEnable( GL_BLEND );
116                 g_GLTable.m_pfn_qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
117                 g_GLTable.m_pfn_qglEnable( GL_LINE_SMOOTH );
118         }
119         else
120         {
121                 g_GLTable.m_pfn_qglDisable( GL_BLEND );
122                 g_GLTable.m_pfn_qglDisable( GL_LINE_SMOOTH );
123         }
124
125         texfont_init();
126
127         if ( !ValidSurface() ) {
128                 return;
129         }
130
131         rcUpper.left = 0;
132         rcUpper.right = width;
133         rcUpper.bottom = 0;
134         rcUpper.top = height;
135         rcLower.left = 0;
136         rcLower.right = width;
137         rcLower.bottom = 0;
138         rcLower.top = height;
139
140         if ( VertexMode ) {
141                 rcUpper.bottom = rcUpper.top / 2;
142                 DrawPreview( rcUpper );
143                 g_GLTable.m_pfn_qglBegin( GL_LINES );
144                 g_GLTable.m_pfn_qglVertex2i( rcUpper.left, rcUpper.bottom );
145                 g_GLTable.m_pfn_qglVertex2i( rcUpper.right, rcUpper.bottom );
146                 g_GLTable.m_pfn_qglEnd();
147                 rcLower.top = rcUpper.bottom - 1;
148                 DrawGrid( rcLower );
149                 rcCoord.left = rcLower.left;
150                 rcCoord.right = rcLower.right;
151                 rcCoord.bottom = rcLower.bottom;
152                 rcCoord.top = rcLower.top;
153                 rcCoord.top = rcCoord.bottom + cyChar;
154                 rcCoord.right = rcCoord.left + 15 * cxChar;
155                 rcGrid.left   = X0G - 3;
156                 rcGrid.bottom = Y0G - 3;
157                 rcGrid.right  = X0G + (int)( SFG * ( Hur - Hll ) ) + 3;
158                 rcGrid.top    = Y0G + (int)( SFG * ( Vur - Vll ) ) + 3;
159         }
160         else{
161                 DrawPreview( rcUpper );
162         }
163 }
164
165 static gint expose( GtkWidget *widget, GdkEventExpose *event, gpointer data ){
166         if ( event->count > 0 ) {
167                 return TRUE;
168         }
169
170         if ( !g_UIGtkTable.m_pfn_glwidget_make_current( g_pPreviewWidget ) ) {
171                 g_FuncTable.m_pfnSysPrintf( "GtkGenSurf: glMakeCurrent failed\n" );
172                 return TRUE;
173         }
174
175         draw_preview();
176
177         g_UIGtkTable.m_pfn_glwidget_swap_buffers( g_pPreviewWidget );
178         g_GLTable.m_pfn_QE_CheckOpenGLForErrors();
179
180         return TRUE;
181 }
182
183 static void button_press( GtkWidget *widget, GdkEventButton *event, gpointer data ){
184         Point pt = { (long)event->x, widget->allocation.height - (long)event->y };
185         bool Selected;
186         double x,y;
187         int i, j, k, ks;
188         int i0, i1, j0, j1;
189
190         if ( ( !VertexMode ) || ( event->button != 1 ) ) {
191                 return;
192         }
193
194         if ( !PtInRect( &rcGrid,pt ) ) {
195                 gdk_beep();
196                 return;
197         }
198
199         x = Hll + ( pt.x - X0G ) / SFG;
200         y = Vur - ( pt.y - Y0G ) / SFG;
201         i = (int)( floor( ( x - Hll ) / dh - 0.5 ) + 1 );
202         j = (int)( floor( ( y - Vll ) / dv - 0.5 ) + 1 );
203         if ( i < 0 || i > NH || j < 0 || j > NV ) {
204                 gdk_beep();
205                 return;
206         }
207
208         if ( !CanEdit( i,j ) ) {
209                 gdk_beep();
210                 return;
211         }
212
213         // Control key pressed - add this point, or remove it if already selected
214         if ( ( event->state & GDK_CONTROL_MASK ) != 0 ) {
215                 Selected = FALSE;
216                 if ( NumVerticesSelected ) {
217                         for ( k = 0; k < NumVerticesSelected && !Selected; k++ )
218                         {
219                                 if ( Vertex[k].i == i && Vertex[k].j == j ) {
220                                         Selected = TRUE;
221                                         ks = k;
222                                 }
223                         }
224                 }
225
226                 // Already selected - unselect it.
227                 if ( Selected ) {
228                         if ( ks < NumVerticesSelected ) {
229                                 for ( k = ks; k < NumVerticesSelected - 1; k++ )
230                                 {
231                                         Vertex[k].i = Vertex[k + 1].i;
232                                         Vertex[k].j = Vertex[k + 1].j;
233                                 }
234                                 NumVerticesSelected--;
235                         }
236                 }
237                 else
238                 {
239                         Vertex[NumVerticesSelected].i = i;
240                         Vertex[NumVerticesSelected].j = j;
241                         NumVerticesSelected++;
242                 }
243         }
244         else if ( ( event->state & GDK_SHIFT_MASK ) != 0 ) {
245                 if ( NumVerticesSelected ) {
246                         NumVerticesSelected = 1;
247                         i0 = min( Vertex[0].i, i );
248                         i1 = max( Vertex[0].i, i );
249                         j0 = min( Vertex[0].j, j );
250                         j1 = max( Vertex[0].j, j );
251                         for ( i = i0; i <= i1; i++ )
252                         {
253                                 for ( j = j0; j <= j1; j++ )
254                                 {
255                                         if ( i == 0  && j == 0 ) {
256                                                 continue;
257                                         }
258                                         if ( i == NH && j == 0 ) {
259                                                 continue;
260                                         }
261                                         if ( i == 0  && j == NV ) {
262                                                 continue;
263                                         }
264                                         if ( i == NH && j == NV ) {
265                                                 continue;
266                                         }
267                                         if ( i != Vertex[0].i || j != Vertex[0].j ) {
268                                                 Vertex[NumVerticesSelected].i = i;
269                                                 Vertex[NumVerticesSelected].j = j;
270                                                 NumVerticesSelected++;
271                                         }
272                                 }
273                         }
274                 }
275                 else
276                 {
277                         Vertex[0].i = i;
278                         Vertex[0].j = j;
279                         NumVerticesSelected = 1;
280                 }
281         }
282         else
283         {
284                 Vertex[0].i = i;
285                 Vertex[0].j = j;
286                 NumVerticesSelected = 1;
287         }
288
289         vertex_selected();
290 }
291
292 static void motion( GtkWidget *widget, GdkEventMotion *event, gpointer data ){
293         Point pt = { (long)event->x, widget->allocation.height - (long)event->y };
294
295         if ( !VertexMode ) {
296                 return;
297         }
298
299         if ( !g_UIGtkTable.m_pfn_glwidget_make_current( g_pPreviewWidget ) ) {
300                 g_FuncTable.m_pfnSysPrintf( "GtkGenSurf: glMakeCurrent failed\n" );
301                 return;
302         }
303
304         g_GLTable.m_pfn_qglEnable( GL_SCISSOR_TEST );
305         g_GLTable.m_pfn_qglScissor( rcCoord.left, rcCoord.bottom, rcCoord.right - rcCoord.left,
306                                                                 rcCoord.top - rcCoord.bottom );
307         g_GLTable.m_pfn_qglClear( GL_COLOR_BUFFER_BIT );
308
309         if ( PtInRect( &rcGrid,pt ) ) {
310                 GdkCursor *cursor = gdk_cursor_new( GDK_CROSS );
311                 gdk_window_set_cursor( g_pWndPreview->window, cursor );
312                 gdk_cursor_unref( cursor );
313
314                 char Text[32];
315                 int x, y;
316
317                 x = (int)( Hll + ( pt.x - X0G ) / SFG );
318                 y = (int)( Vur - ( pt.y - Y0G ) / SFG );
319                 switch ( Plane )
320                 {
321                 case PLANE_XZ0:
322                 case PLANE_XZ1:
323                         sprintf( Text," x=%d, z=%d   ",(int)( floor( x - 0.5 ) + 1. ),(int)( floor( y - 0.5 ) + 1. ) );
324                         break;
325                 case PLANE_YZ0:
326                 case PLANE_YZ1:
327                         sprintf( Text," y=%d, z=%d   ",(int)( floor( x - 0.5 ) + 1. ),(int)( floor( y - 0.5 ) + 1. ) );
328                         break;
329                 default:
330                         sprintf( Text," x=%d, y=%d   ",(int)( floor( x - 0.5 ) + 1. ),(int)( floor( y - 0.5 ) + 1. ) );
331                 }
332
333                 texfont_write( Text, rcCoord.left, rcCoord.top );
334         }
335         else
336         {
337                 gdk_window_set_cursor( g_pWndPreview->window, NULL );
338         }
339
340         g_UIGtkTable.m_pfn_glwidget_swap_buffers( g_pPreviewWidget );
341         g_GLTable.m_pfn_QE_CheckOpenGLForErrors();
342         g_GLTable.m_pfn_qglDisable( GL_SCISSOR_TEST );
343 }
344
345 static gint preview_close( GtkWidget *widget, gpointer data ){
346         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( g_object_get_data( G_OBJECT( g_pWnd ), "main_preview" ) ), FALSE );
347         return TRUE;
348 }
349
350 static void preview_focusout( GtkSpinButton *spin, GdkEventFocus *event, double *data ){
351         *data = DegreesToRadians( (double)( gtk_spin_button_get_value_as_int( spin ) % 360 ) );
352         UpdatePreview( false );
353 }
354
355 static gint doublevariable_spinfocusout( GtkWidget* widget, GdkEventFocus* event, gpointer data ){
356         preview_focusout( GTK_SPIN_BUTTON( widget ), event, reinterpret_cast<double*>( data ) );
357         return FALSE;
358 }
359
360 static void preview_spin( GtkAdjustment *adj, double *data ){
361         *data = DegreesToRadians( adj->value );
362         UpdatePreview( false );
363 }
364
365 void CreateViewWindow(){
366         GtkWidget *label, *spin;
367
368 #ifndef ISOMETRIC
369         elevation = PI / 6.;
370         azimuth   = PI / 6.;
371 #endif
372
373         auto dlg = g_pWndPreview = ui::Window( ui::window_type::TOP );
374         gtk_window_set_title( dlg, "GtkGenSurf Preview" );
375         dlg.connect( "delete_event", G_CALLBACK( preview_close ), NULL );
376         dlg.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
377         gtk_window_set_transient_for( dlg, g_pWnd );
378         gtk_window_set_default_size( dlg, 300, 400 );
379
380         auto vbox = ui::VBox( FALSE, 5 );
381         vbox.show();
382         dlg.add(vbox);
383
384 #ifndef ISOMETRIC
385         auto hbox = ui::HBox( TRUE, 5 );
386         hbox.show();
387         vbox.pack_start( hbox, FALSE, TRUE, 0 );
388         gtk_container_set_border_width( GTK_CONTAINER( hbox ), 3 );
389
390         label = ui::Label( "Elevation" );
391         label.show();
392         gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
393         hbox.pack_start( label, FALSE, TRUE, 0 );
394
395         auto adj = ui::Adjustment( 30, -90, 90, 1, 10, 0 );
396         adj.connect( "value_changed", G_CALLBACK( preview_spin ), &elevation );
397         spin = ui::SpinButton( adj, 1, 0 );
398         spin.show();
399         hbox.pack_start( spin, FALSE, TRUE, 0 );
400         spin.connect( "focus_out_event", G_CALLBACK( doublevariable_spinfocusout ), &elevation );
401
402         adj = ui::Adjustment( 30, 0, 359, 1, 10, 0 );
403         adj.connect( "value_changed", G_CALLBACK( preview_spin ), &azimuth );
404         spin = ui::SpinButton( adj, 1, 0 );
405         spin.show();
406         gtk_spin_button_set_wrap( GTK_SPIN_BUTTON( spin ), TRUE );
407         hbox.pack_end(spin, FALSE, TRUE, 0);
408
409         label = ui::Label( "Azimuth" );
410         label.show();
411         gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
412         hbox.pack_end(label, FALSE, TRUE, 0);
413         spin.connect( "focus_out_event", G_CALLBACK( doublevariable_spinfocusout ), &azimuth );
414 #endif
415
416         auto frame = ui::Frame(ui::null);
417         frame.show();
418         gtk_frame_set_shadow_type( GTK_FRAME( frame ), GTK_SHADOW_IN );
419         vbox.pack_start( frame, TRUE, TRUE, 0 );
420
421         g_pPreviewWidget = g_UIGtkTable.m_pfn_glwidget_new( FALSE, NULL );
422
423         gtk_widget_set_events( g_pPreviewWidget, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK );
424         g_pPreviewWidget.connect( "expose_event", G_CALLBACK( expose ), NULL );
425         g_pPreviewWidget.connect( "motion_notify_event", G_CALLBACK( motion ), NULL );
426         g_pPreviewWidget.connect( "button_press_event",
427                                                 G_CALLBACK( button_press ), NULL );
428
429         g_pPreviewWidget.show();
430         frame.add(ui::Widget(g_pPreviewWidget));
431
432         if ( Preview ) {
433                 g_pWndPreview.show();
434         }
435
436         UpdatePreview( true );
437 }
438
439 //=============================================================
440 /* DrawPreview */
441 void DrawPreview( Rect rc ){
442 #define COSXA 0.8660254037844
443 #define SINXA 0.5
444 #define COSYA 0.8660254037844
445 #define SINYA 0.5
446
447         double L;
448         double x,y;
449         int i, j;
450         Point pt[8];
451         XYZ v[8];
452         char axis[3][2] = {"X","Y","Z"};
453
454 #ifndef ISOMETRIC
455         evaluate();
456 #endif
457
458         XLo = xmin;
459         XHi = xmax;
460         YLo = ymin;
461         YHi = ymax;
462         ZLo = zmin;
463         ZHi = zmax;
464         switch ( Plane )
465         {
466         case PLANE_XY1:
467                 ZHi = backface;
468                 break;
469         case PLANE_XZ0:
470                 YLo = backface;
471                 break;
472         case PLANE_XZ1:
473                 YHi = backface;
474                 break;
475         case PLANE_YZ0:
476                 XLo = backface;
477                 break;
478         case PLANE_YZ1:
479                 XHi = backface;
480                 break;
481         default:
482                 ZLo = backface;
483         }
484
485
486
487         GetScaleFactor( rc );
488         //PEN_GRID
489         g_GLTable.m_pfn_qglLineWidth( 1 );
490         g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
491         g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
492
493         if ( Decimate > 0 && ( Game != QUAKE3 || UsePatches == 0 ) ) {
494                 XYZ  *vv;
495
496                 vv   = (XYZ *) malloc( gNumNodes * sizeof( XYZ ) );
497                 for ( i = 0; i < gNumNodes; i++ )
498                 {
499                         for ( j = 0; j < 3; j++ )
500                                 vv[i].p[j] = (double)( gNode[i].p[j] );
501                         project( &vv[i] );
502                 }
503
504                 for ( i = 0; i < gNumTris; i++ )
505                 {
506                         for ( j = 0; j < 3; j++ )
507                                 Scale( rc,vv[gTri[i].v[j]],&pt[j] );
508
509                         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
510                         g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
511                         g_GLTable.m_pfn_qglVertex2i( pt[1].x, pt[1].y );
512                         g_GLTable.m_pfn_qglVertex2i( pt[2].x, pt[2].y );
513                         g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
514                         g_GLTable.m_pfn_qglEnd();
515                 }
516                 free( vv );
517         }
518         else if ( Game == QUAKE3 && UsePatches != 0 ) {
519                 int axis, ii, jj, k;
520                 float u, v;
521                 XYZ uv[3][3];
522                 XYZ Ctrl[3],out;
523
524                 switch ( Plane )
525                 {
526                 case PLANE_XY0:
527                 case PLANE_XY1:
528                         k = 2;
529                         break;
530                 case PLANE_XZ0:
531                 case PLANE_XZ1:
532                         k = 1;
533                         break;
534                 default:
535                         k = 0;
536                 }
537                 for ( i = 0; i < NH; i += 2 )
538                 {
539                         for ( j = 0; j < NV; j += 2 )
540                         {
541                                 VectorCopy( xyz[i  ][j  ].p,uv[0][0].p );
542                                 VectorCopy( xyz[i + 1][j  ].p,uv[1][0].p );
543                                 VectorCopy( xyz[i + 2][j  ].p,uv[2][0].p );
544                                 VectorCopy( xyz[i  ][j + 1].p,uv[0][1].p );
545                                 VectorCopy( xyz[i + 1][j + 1].p,uv[1][1].p );
546                                 VectorCopy( xyz[i + 2][j + 1].p,uv[2][1].p );
547                                 VectorCopy( xyz[i  ][j + 2].p,uv[0][2].p );
548                                 VectorCopy( xyz[i + 1][j + 2].p,uv[1][2].p );
549                                 VectorCopy( xyz[i + 2][j + 2].p,uv[2][2].p );
550                                 uv[1][0].p[k] = ( 4 * xyz[i + 1][j  ].p[k] - xyz[i  ][j  ].p[k] - xyz[i + 2][j  ].p[k] ) / 2;
551                                 uv[0][1].p[k] = ( 4 * xyz[i  ][j + 1].p[k] - xyz[i  ][j  ].p[k] - xyz[i  ][j + 2].p[k] ) / 2;
552                                 uv[2][1].p[k] = ( 4 * xyz[i + 2][j + 1].p[k] - xyz[i + 2][j  ].p[k] - xyz[i + 2][j + 2].p[k] ) / 2;
553                                 uv[1][2].p[k] = ( 4 * xyz[i + 1][j + 2].p[k] - xyz[i  ][j + 2].p[k] - xyz[i + 2][j + 2].p[k] ) / 2;
554                                 uv[1][1].p[k] = ( 16 * xyz[i + 1][j + 1].p[k] -
555                                                                   xyz[i  ][j  ].p[k] - 2 * xyz[i + 1][j  ].p[k] -  xyz[i + 2][j  ].p[k] -
556                                                                   2 * xyz[i  ][j + 1].p[k]                        - 2 * xyz[i + 2][j + 1].p[k] -
557                                                                   xyz[i  ][j + 2].p[k] - 2 * xyz[i + 1][j + 2].p[k] -  xyz[i + 2][j + 2].p[k]   ) / 4;
558
559                                 for ( ii = 0; ii <= SUBDIVS; ii++ )
560                                 {
561                                         if ( ii == 0 || ii == SUBDIVS / 2 || ii == SUBDIVS ) {
562                                                 g_GLTable.m_pfn_qglLineWidth( 1 );
563                                                 g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
564                                                 g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
565                                                 // PEN_GRID
566                                         }
567                                         else
568                                         {
569                                                 g_GLTable.m_pfn_qglLineWidth( 1 );
570                                                 g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
571                                                 g_GLTable.m_pfn_qglLineStipple( 1, 0xF0F0 );
572                                                 g_GLTable.m_pfn_qglEnable( GL_LINE_STIPPLE );
573                                                 // PEN_DASH
574                                         }
575
576                                         u = (float)( ii ) / (float)( SUBDIVS );
577                                         for ( jj = 0; jj < 3; jj++ )
578                                         {
579                                                 for ( axis = 0; axis < 3; axis++ )
580                                                 {
581                                                         float a, b, c;
582                                                         float qA, qB, qC;
583                                                         a = (float)uv[0][jj].p[axis];
584                                                         b = (float)uv[1][jj].p[axis];
585                                                         c = (float)uv[2][jj].p[axis];
586                                                         qA = a - 2 * b + c;
587                                                         qB = 2 * b - 2 * a;
588                                                         qC = a;
589                                                         Ctrl[jj].p[axis] = qA * u * u + qB * u + qC;
590                                                 }
591                                         }
592                                         VectorCopy( Ctrl[0].p,out.p );
593                                         project( &out );
594                                         Scale( rc,out,&pt[0] );
595                                         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
596                                         g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
597                                         for ( jj = 1; jj <= SUBDIVS; jj++ )
598                                         {
599                                                 v = (float)( jj ) / (float)( SUBDIVS );
600                                                 for ( axis = 0 ; axis < 3 ; axis++ )
601                                                 {
602                                                         float a, b, c;
603                                                         float qA, qB, qC;
604                                                         a = (float)Ctrl[0].p[axis];
605                                                         b = (float)Ctrl[1].p[axis];
606                                                         c = (float)Ctrl[2].p[axis];
607                                                         qA = a - 2 * b + c;
608                                                         qB = 2 * b - 2 * a;
609                                                         qC = a;
610                                                         out.p[axis] = qA * v * v + qB * v + qC;
611                                                 }
612                                                 project( &out );
613                                                 Scale( rc,out,&pt[0] );
614                                                 g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
615                                         }
616                                         g_GLTable.m_pfn_qglEnd();
617                                 }
618                                 for ( jj = 0; jj <= SUBDIVS; jj++ )
619                                 {
620                                         if ( jj == 0 || jj == SUBDIVS / 2 || jj == SUBDIVS ) {
621                                                 g_GLTable.m_pfn_qglLineWidth( 1 );
622                                                 g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
623                                                 g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
624                                                 // PEN_GRID
625                                         }
626                                         else
627                                         {
628                                                 g_GLTable.m_pfn_qglLineWidth( 1 );
629                                                 g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
630                                                 g_GLTable.m_pfn_qglLineStipple( 1, 0xF0F0 );
631                                                 g_GLTable.m_pfn_qglEnable( GL_LINE_STIPPLE );
632                                                 // PEN_DASH
633                                         }
634
635                                         v = (float)( jj ) / (float)( SUBDIVS );
636                                         for ( ii = 0; ii < 3; ii++ )
637                                         {
638                                                 for ( axis = 0; axis < 3; axis++ )
639                                                 {
640                                                         float a, b, c;
641                                                         float qA, qB, qC;
642                                                         a = (float)uv[ii][0].p[axis];
643                                                         b = (float)uv[ii][1].p[axis];
644                                                         c = (float)uv[ii][2].p[axis];
645                                                         qA = a - 2 * b + c;
646                                                         qB = 2 * b - 2 * a;
647                                                         qC = a;
648                                                         Ctrl[ii].p[axis] = qA * v * v + qB * v + qC;
649                                                 }
650                                         }
651                                         VectorCopy( Ctrl[0].p,out.p );
652                                         project( &out );
653                                         Scale( rc,out,&pt[0] );
654                                         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
655                                         g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
656                                         for ( ii = 1; ii <= SUBDIVS; ii++ )
657                                         {
658                                                 u = (float)( ii ) / (float)( SUBDIVS );
659                                                 for ( axis = 0 ; axis < 3 ; axis++ )
660                                                 {
661                                                         float a, b, c;
662                                                         float qA, qB, qC;
663                                                         a = (float)Ctrl[0].p[axis];
664                                                         b = (float)Ctrl[1].p[axis];
665                                                         c = (float)Ctrl[2].p[axis];
666                                                         qA = a - 2 * b + c;
667                                                         qB = 2 * b - 2 * a;
668                                                         qC = a;
669                                                         out.p[axis] = qA * u * u + qB * u + qC;
670                                                 }
671                                                 project( &out );
672                                                 Scale( rc,out,&pt[0] );
673                                                 g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
674                                         }
675                                         g_GLTable.m_pfn_qglEnd();
676                                 }
677                         }
678                 }
679         }
680         else
681         {
682                 for ( i = 0; i <= NH; i++ )
683                 {
684                         Scale( rc,xyz[i][0],&pt[0] );
685                         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
686                         g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
687                         for ( j = 1; j <= NV; j++ )
688                         {
689                                 Scale( rc,xyz[i][j],&pt[0] );
690                                 g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
691                         }
692                         g_GLTable.m_pfn_qglEnd();
693                 }
694                 for ( j = 0; j <= NV; j++ )
695                 {
696                         Scale( rc,xyz[0][j],&pt[0] );
697                         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
698                         g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
699                         for ( i = 1; i <= NH; i++ )
700                         {
701                                 Scale( rc,xyz[i][j],&pt[0] );
702                                 g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
703                         }
704                         g_GLTable.m_pfn_qglEnd();
705                 }
706         }
707
708         if ( Game != QUAKE3 || UsePatches == 0 ) {
709                 // Draw lines from corners to base, and lines around base
710                 for ( i = 0; i <= NH; i += NH )
711                 {
712                         for ( j = 0; j <= NV; j += NV )
713                         {
714                                 VectorCopy( xyz[i][j].p, v[0].p );
715                                 switch ( Plane )
716                                 {
717                                 case PLANE_XZ0:
718                                 case PLANE_XZ1:
719                                         v[0].p[1] = backface;
720                                         break;
721                                 case PLANE_YZ0:
722                                 case PLANE_YZ1:
723                                         v[0].p[0] = backface;
724                                         break;
725                                 default:
726                                         v[0].p[2] = backface;
727                                 }
728                                 Scale( rc,xyz[i][j],&pt[0] );
729 #ifndef ISOMETRIC
730                                 project( &v[0] );
731 #endif
732                                 Scale( rc,v[0],&pt[1] );
733                                 g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
734                                 g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
735                                 g_GLTable.m_pfn_qglVertex2i( pt[1].x, pt[1].y );
736                                 g_GLTable.m_pfn_qglEnd();
737                         }
738                 }
739                 VectorCopy( xyz[ 0][ 0].p, v[0].p );
740                 VectorCopy( xyz[NH][ 0].p, v[1].p );
741                 VectorCopy( xyz[NH][NV].p, v[2].p );
742                 VectorCopy( xyz[ 0][NV].p, v[3].p );
743                 switch ( Plane )
744                 {
745                 case PLANE_XZ0:
746                 case PLANE_XZ1:
747                         v[0].p[1] = backface;;
748                         v[1].p[1] = v[0].p[1];
749                         v[2].p[1] = v[0].p[1];
750                         v[3].p[1] = v[0].p[1];
751                         break;
752                 case PLANE_YZ0:
753                 case PLANE_YZ1:
754                         v[0].p[0] = backface;
755                         v[1].p[0] = v[0].p[0];
756                         v[2].p[0] = v[0].p[0];
757                         v[3].p[0] = v[0].p[0];
758                         break;
759                 default:
760                         v[0].p[2] = backface;
761                         v[1].p[2] = v[0].p[2];
762                         v[2].p[2] = v[0].p[2];
763                         v[3].p[2] = v[0].p[2];
764                 }
765 #ifndef ISOMETRIC
766                 project( &v[3] );
767 #endif
768                 Scale( rc,v[3],&pt[0] );
769                 g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
770                 g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
771                 for ( i = 0; i < 3; i++ )
772                 {
773 #ifndef ISOMETRIC
774                         project( &v[i] );
775 #endif
776                         Scale( rc,v[i],&pt[1] );
777                         g_GLTable.m_pfn_qglVertex2i( pt[1].x, pt[1].y );
778                 }
779                 g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
780                 g_GLTable.m_pfn_qglEnd();
781         }
782
783         g_GLTable.m_pfn_qglLineWidth( 1 );
784         g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
785         g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
786
787 #ifdef ISOMETRIC
788         // Draw small depiction of coordinate axes
789         pt[0].x = rc.right  - cxChar   - cxChar / 2 -  cyChar;
790         pt[0].y = rc.bottom - cyChar / 2 - cxChar / 2;
791         pt[1].x = pt[0].x + (int)( cyChar * COSXA );
792         pt[1].y = pt[0].y - (int)( cyChar * SINXA );
793         MoveToEx( hdc,pt[0].x,pt[0].y,NULL );
794         LineTo( hdc,pt[1].x,pt[1].y );
795         SetTextAlign( hdc,TA_LEFT | TA_TOP );
796         TextOut( hdc,pt[1].x,pt[1].y - cyChar / 2,"X",1 );
797         pt[1].x = pt[0].x - (int)( cyChar * COSYA );
798         pt[1].y = pt[0].y - (int)( cyChar * SINYA );
799         MoveToEx( hdc,pt[0].x,pt[0].y,NULL );
800         LineTo( hdc,pt[1].x,pt[1].y );
801         SetTextAlign( hdc,TA_RIGHT | TA_TOP );
802         TextOut( hdc,pt[1].x,pt[1].y - cyChar / 2,"Y",1 );
803         pt[1].x = pt[0].x;
804         pt[1].y = pt[0].y - cyChar;
805         MoveToEx( hdc,pt[0].x,pt[0].y,NULL );
806         LineTo( hdc,pt[1].x,pt[1].y );
807         SetTextAlign( hdc,TA_CENTER | TA_BOTTOM );
808         TextOut( hdc,pt[1].x,pt[1].y,"Z",1 );
809 #else
810         L = 2 * (double)cyChar / SF;
811         v[0].p[0] = 0.;
812         v[0].p[1] = 0.;
813         v[0].p[2] = 0.;
814         v[1].p[0] = L;
815         v[1].p[1] = 0.;
816         v[1].p[2] = 0.;
817         v[2].p[0] = 0.;
818         v[2].p[1] = L;
819         v[2].p[2] = 0.;
820         v[3].p[0] = 0.;
821         v[3].p[1] = 0.;
822         v[3].p[2] = L;
823         for ( i = 0; i <= 3; i++ )
824         {
825                 project( &v[i] );
826                 Scale( rc,v[i],&pt[i] );
827         }
828         for ( i = 1; i <= 3; i++ )
829         {
830                 pt[i].x += -pt[0].x + rc.right  - 2 * cyChar;
831                 pt[i].y += -pt[0].y + rc.bottom + 2 * cyChar;
832         }
833         pt[0].x = rc.right  - 2 * cyChar;
834         pt[0].y = rc.bottom + 2 * cyChar;
835
836         for ( i = 1; i <= 3; i++ )
837         {
838                 g_GLTable.m_pfn_qglBegin( GL_LINES );
839                 g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
840                 g_GLTable.m_pfn_qglVertex2i( pt[i].x, pt[i].y );
841                 g_GLTable.m_pfn_qglEnd();
842                 texfont_write( axis[i - 1], pt[i].x - cxChar / 2,pt[i].y + cyChar / 2 );
843         }
844 #endif
845
846         // Draw player model's bounding box in red to give a sense of scale
847         // PEN_RED
848         g_GLTable.m_pfn_qglLineWidth( 2 );
849         g_GLTable.m_pfn_qglColor3f( 1, 0, 0 );
850         g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
851
852         switch ( Plane )
853         {
854         case PLANE_XY1:
855                 v[0].p[0] = xyz[NH / 2][NV / 2].p[0] + PlayerBox[Game].x[0];
856                 v[0].p[1] = xyz[NH / 2][NV / 2].p[1] + PlayerBox[Game].y[0];
857                 v[0].p[2] = zmin - PlayerBox[Game].z[0] - 32;
858                 break;
859         case PLANE_XZ0:
860                 v[0].p[0] = ( xmax + xmin ) / 2 + PlayerBox[Game].x[0];
861                 v[0].p[1] = ymax + 64;
862                 v[0].p[2] = zmin;
863                 break;
864         case PLANE_XZ1:
865                 v[0].p[0] = ( xmax + xmin ) / 2 + PlayerBox[Game].x[0];
866                 v[0].p[1] = ymin - 64;
867                 v[0].p[2] = zmin;
868                 break;
869         case PLANE_YZ0:
870                 v[0].p[0] = xmax + 64;
871                 v[0].p[1] = ( ymax + ymin ) / 2 + PlayerBox[Game].y[0];
872                 v[0].p[2] = zmin;
873                 break;
874         case PLANE_YZ1:
875                 v[0].p[0] = xmin - 64;
876                 v[0].p[1] = ( ymax + ymin ) / 2 + PlayerBox[Game].y[0];
877                 v[0].p[2] = zmin;
878                 break;
879         default:
880                 // Put player on a node. For patches, put on an even numbered node.
881                 if ( Game == QUAKE3 && UsePatches != 0 ) {
882                         if ( NH > 2 ) {
883                                 x = Hll + dh * (int)( NH / 2 + 1 );
884                         }
885                         else{
886                                 x = Hll + dh * (int)( NH / 2 );
887                         }
888                         if ( NV > 2 ) {
889                                 y = Vll + dv * (int)( NV / 2 + 1 );
890                         }
891                         else{
892                                 y = Vll + dv * (int)( NV / 2 );
893                         }
894                 }
895                 else
896                 {
897                         if ( NH > 1 ) {
898                                 x = Hll + dh * (int)( NH / 2 );
899                         }
900                         else{
901                                 x = Hll + dh / 2;
902                         }
903                         if ( NV > 1 ) {
904                                 y = Vll + dv * (int)( NV / 2 );
905                         }
906                         else{
907                                 y = Vll + dv / 2;
908                         }
909                 }
910 //              x = (Hll+Hur)/2.;
911 //              y = (Vll+Vur)/2.;
912                 v[0].p[0] = x + PlayerBox[Game].x[0];
913                 v[0].p[1] = y + PlayerBox[Game].y[0];
914                 v[0].p[2] = PlayerStartZ( x,y ) + PlayerBox[Game].z[0] + 8; // add 8 cuz I'm a pessimist
915         }
916         v[1].p[0] = v[0].p[0] + PlayerBox[Game].x[1] - PlayerBox[Game].x[0];
917         v[1].p[1] = v[0].p[1];
918         v[1].p[2] = v[0].p[2];
919         v[2].p[0] = v[1].p[0];
920         v[2].p[1] = v[1].p[1] + PlayerBox[Game].y[1] - PlayerBox[Game].y[0];
921         v[2].p[2] = v[0].p[2];
922         v[3].p[0] = v[0].p[0];
923         v[3].p[1] = v[2].p[1];
924         v[3].p[2] = v[0].p[2];
925         VectorCopy( v[0].p,v[4].p );
926         VectorCopy( v[1].p,v[5].p );
927         VectorCopy( v[2].p,v[6].p );
928         VectorCopy( v[3].p,v[7].p );
929         v[4].p[2] += PlayerBox[Game].z[1] - PlayerBox[Game].z[0];
930         v[5].p[2] += PlayerBox[Game].z[1] - PlayerBox[Game].z[0];
931         v[6].p[2] += PlayerBox[Game].z[1] - PlayerBox[Game].z[0];
932         v[7].p[2] += PlayerBox[Game].z[1] - PlayerBox[Game].z[0];
933         for ( i = 0; i <= 7; i++ )
934         {
935 #ifndef ISOMETRIC
936                 project( &v[i] );
937 #endif
938                 Scale( rc,v[i],&pt[i] );
939         }
940         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
941         g_GLTable.m_pfn_qglVertex2i( pt[3].x, pt[3].y );
942         for ( i = 0; i <= 3; i++ )
943                 g_GLTable.m_pfn_qglVertex2i( pt[i].x, pt[i].y );
944         g_GLTable.m_pfn_qglEnd();
945         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
946         g_GLTable.m_pfn_qglVertex2i( pt[7].x, pt[7].y );
947         for ( i = 4; i <= 7; i++ )
948                 g_GLTable.m_pfn_qglVertex2i( pt[i].x, pt[i].y );
949         g_GLTable.m_pfn_qglEnd();
950         g_GLTable.m_pfn_qglBegin( GL_LINES );
951         for ( i = 0; i <= 3; i++ )
952         {
953                 g_GLTable.m_pfn_qglVertex2i( pt[i].x,pt[i].y );
954                 g_GLTable.m_pfn_qglVertex2i( pt[i + 4].x,pt[i + 4].y );
955         }
956         g_GLTable.m_pfn_qglEnd();
957
958         g_GLTable.m_pfn_qglLineWidth( 1 );
959         g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
960         g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
961 }
962 //=============================================================
963 void DrawGrid( Rect rc ){
964         int i, j, k;
965         double h,w,x,y;
966         Point pt[2];
967         Rect rcBox;
968
969         w = (double)( rc.right - rc.left + 1 ) - cxChar;
970         h = (double)( rc.top - rc.bottom + 1 ) - cxChar - cyChar;
971
972         SFG = w / ( Hur - Hll );
973         SFG = min( SFG, h / ( Vur - Vll ) );
974
975         // Center drawing
976         X0G = (int)( rc.left + rc.right - (int)( SFG * ( Hur - Hll ) ) ) / 2;
977         Y0G = (int)( rc.top + rc.bottom + cyChar - (int)( SFG * ( Vur - Vll ) ) ) / 2;
978
979         g_GLTable.m_pfn_qglLineWidth( 2 );
980         g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
981         g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
982
983         pt[0].y = Y0G;
984         pt[1].y = Y0G + (int)( SFG * ( Vur - Vll ) );
985         g_GLTable.m_pfn_qglBegin( GL_LINES );
986         for ( i = 0; i <= NH; i++ )
987         {
988                 x = Hll + i * dh;
989                 pt[0].x = X0G + (int)( SFG * ( x - Hll ) );
990                 g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[0].y );
991                 g_GLTable.m_pfn_qglVertex2i( pt[0].x, pt[1].y );
992         }
993         g_GLTable.m_pfn_qglEnd();
994         pt[0].x = X0G;
995         pt[1].x = X0G + (int)( SFG * ( Hur - Hll ) );
996         g_GLTable.m_pfn_qglBegin( GL_LINES );
997         for ( i = 0; i <= NV; i++ )
998         {
999                 y = Vll + i * dv;
1000                 pt[0].y = Y0G + (int)( SFG * ( Vur - y ) );
1001                 g_GLTable.m_pfn_qglVertex2i( pt[0].x,pt[0].y );
1002                 g_GLTable.m_pfn_qglVertex2i( pt[1].x,pt[0].y );
1003         }
1004         g_GLTable.m_pfn_qglEnd();
1005
1006         g_GLTable.m_pfn_qglLineWidth( 1 );
1007
1008         // Draw axes
1009         pt[0].x = rc.right  - cyChar - cxChar - cyChar / 2;
1010         pt[0].y = rc.bottom + cyChar / 2;
1011         pt[1].x = pt[0].x + cyChar;
1012         pt[1].y = pt[0].y;
1013         g_GLTable.m_pfn_qglBegin( GL_LINES );
1014         g_GLTable.m_pfn_qglVertex2i( pt[0].x,pt[0].y );
1015         g_GLTable.m_pfn_qglVertex2i( pt[1].x,pt[1].y );
1016         g_GLTable.m_pfn_qglEnd();
1017         switch ( Plane )
1018         {
1019         case PLANE_YZ0:
1020         case PLANE_YZ1:
1021                 texfont_write( "Y", pt[1].x, pt[1].y + cyChar / 2 );
1022                 break;
1023         default:
1024                 texfont_write( "X", pt[1].x, pt[1].y + cyChar / 2 );
1025         }
1026         pt[1].x = pt[0].x;
1027         pt[1].y = pt[0].y + cyChar;
1028         g_GLTable.m_pfn_qglBegin( GL_LINES );
1029         g_GLTable.m_pfn_qglVertex2i( pt[0].x,pt[0].y );
1030         g_GLTable.m_pfn_qglVertex2i( pt[1].x,pt[1].y );
1031         g_GLTable.m_pfn_qglEnd();
1032         switch ( Plane )
1033         {
1034         case PLANE_XY0:
1035         case PLANE_XY1:
1036                 texfont_write( "Y", pt[1].x - cyChar / 2, pt[1].y + cyChar );
1037                 break;
1038         default:
1039                 texfont_write( "Z", pt[1].x - cyChar / 2, pt[1].y + cyChar );
1040         }
1041
1042         // Denote fixed points with a 5x5 red rectangle
1043         for ( i = 0; i <= NH; i++ )
1044         {
1045                 for ( j = 0; j <= NV; j++ )
1046                 {
1047                         if ( xyz[i][j].fixed ) {
1048                                 x = Hll + i * dh;
1049                                 y = Vll + j * dv;
1050                                 rcBox.left   = X0G + (int)( SFG * ( x - Hll ) ) - 2;
1051                                 rcBox.top    = Y0G + (int)( SFG * ( Vur - y ) ) + 2;
1052                                 rcBox.right  = rcBox.left + 5;
1053                                 rcBox.bottom = rcBox.top  - 5;
1054
1055                                 DRAW_QUAD( rcBox, 1,0,0 );
1056                         }
1057                 }
1058         }
1059
1060         // Denote currently selected point with a 5x5 green rectangle
1061         if ( NumVerticesSelected ) {
1062                 for ( k = 0; k < NumVerticesSelected; k++ )
1063                 {
1064                         x = Hll + Vertex[k].i * dh;
1065                         y = Vll + Vertex[k].j * dv;
1066                         rcBox.left   = X0G + (int)( SFG * ( x - Hll ) ) - 2;
1067                         rcBox.top    = Y0G + (int)( SFG * ( Vur - y ) ) + 2;
1068                         rcBox.right  = rcBox.left + 5;
1069                         rcBox.bottom = rcBox.top  - 5;
1070
1071                         DRAW_QUAD( rcBox, 0,1,0 );
1072                 }
1073         }
1074
1075         // Unmovable vertices
1076         for ( i = 0; i <= NH; i++ )
1077         {
1078                 for ( j = 0; j <= NV; j++ )
1079                 {
1080                         if ( !CanEdit( i,j ) ) {
1081                                 x = Hll + i * dh;
1082                                 y = Vll + j * dv;
1083                                 rcBox.left   = X0G + (int)( SFG * ( x - Hll ) ) - 2;
1084                                 rcBox.top    = Y0G + (int)( SFG * ( Vur - y ) ) + 2;
1085                                 rcBox.right  = rcBox.left + 5;
1086                                 rcBox.bottom = rcBox.top  - 5;
1087
1088                                 DRAW_QUAD( rcBox, 1,1,0 );
1089                         }
1090                 }
1091         }
1092
1093         // Legend
1094         rcBox.left   = rc.left + cxChar / 2 - 2;
1095         rcBox.top    = rc.top  - cyChar / 2 - 2;
1096         rcBox.right  = rcBox.left + 5;
1097         rcBox.bottom = rcBox.top  - 5;
1098         DRAW_QUAD( rcBox, 1,0,0 );
1099         texfont_write( "Fixed points", rcBox.right + cxChar,rcBox.top - 4 + cyChar / 2 );
1100
1101         rcBox.top    -= cyChar;
1102         rcBox.bottom -= cyChar;
1103         DRAW_QUAD( rcBox, 1,1,0 );
1104         texfont_write( "Not movable", rcBox.right + cxChar, rcBox.top - 4 + cyChar / 2 );
1105
1106         rcBox.top    -= cyChar;
1107         rcBox.bottom -= cyChar;
1108         DRAW_QUAD( rcBox, 0,1,0 );
1109         texfont_write( "Selected", rcBox.right + cxChar, rcBox.top - 4 + cyChar / 2 );
1110 }
1111
1112 //=============================================================
1113 void GetScaleFactor( Rect rc ){
1114 #ifdef ISOMETRIC
1115         double h, w;
1116
1117         w = (double)( rc.right - rc.left + 1 ) - cxChar;
1118         h = (double)( rc.top - rc.bottom + 1 ) - cxChar;
1119
1120         SF = w / ( ( XHi - XLo ) * COSXA + ( YHi - YLo ) * COSYA );
1121         SF = min( SF, h / ( ( XHi - XLo ) * SINXA + ( YHi - YLo ) * SINYA + ZHi - ZLo ) );
1122         // Center drawing
1123         X0 = (int)( rc.left + rc.right - (int)( SF * ( ( XHi - XLo ) * COSXA + ( YHi - YLo ) * COSYA ) ) ) / 2;
1124         Y0 = (int)( rc.top + rc.bottom - (int)( SF * ( ( XHi - XLo ) * SINXA + ( YHi - YLo ) * SINYA + ZHi - ZLo ) ) ) / 2;
1125
1126 #else
1127         double h, w;
1128
1129         w = (double)( rc.right - rc.left + 1 ) - cxChar;
1130         h = (double)( rc.top - rc.bottom + 1 ) - cxChar;
1131
1132         SF = w / ( Hhi - Hlo );
1133         SF = min( SF, h / ( Vhi - Vlo ) );
1134         X0 = (int)( rc.left + rc.right - (int)( SF * ( Hhi - Hlo ) ) ) / 2;
1135         Y0 = (int)( rc.top + rc.bottom + (int)( SF * ( Vhi - Vlo ) ) ) / 2;
1136 #endif
1137 }
1138
1139 //=============================================================
1140 void Scale( Rect rc,XYZ xyz,Point *pt ){
1141
1142 #ifdef ISOMETRIC
1143
1144         pt[0].x = X0 + (int)( SF * ( ( xyz.p[0] - XLo ) * COSXA +
1145                                                                  ( YHi - xyz.p[1] ) * COSYA   ) );
1146         pt[0].y = Y0 + (int)( SF * (  ZHi - xyz.p[2] +
1147                                                                   ( YHi - xyz.p[1] ) * SINYA +
1148                                                                   ( XHi - xyz.p[0] ) * SINXA   ) );
1149 #else
1150         pt[0].x = X0 + (int)( SF * ( xyz.pp[0] - Hlo ) );
1151         pt[0].y = Y0 - (int)( SF * ( Vhi - xyz.pp[1] ) );
1152 #endif
1153
1154 }
1155
1156 #ifndef ISOMETRIC
1157 /* ======================================================================= */
1158 void project( XYZ *v ){
1159         // project a 3D point (x,y,z) onto view plane
1160         double x, y, z, xa, ya, za;
1161
1162         x = v->p[0];
1163         y = v->p[1];
1164         z = v->p[2];
1165
1166         // yaw
1167         xa = ct[0] * x - st[0] * z;
1168         za = st[0] * x + ct[0] * z;
1169
1170         // roll
1171         x  = ct[1] * xa + st[1] * y;
1172         ya = ct[1] * y  - st[1] * xa;
1173
1174         // azimuth
1175         z  = ct[2] * za - st[2] * ya;
1176         y  = ct[2] * ya + st[2] * za;
1177
1178         // horizontal and vertical projections:
1179 //      v->pp[0] = D*x/z;
1180 //      v->pp[1] = D*y/z;
1181         v->pp[0] = -y;
1182         v->pp[1] =  x;
1183         v->pp[2] =  z;
1184
1185         // NOTE: if perspective transformation is desired,
1186         // set "persp" to the range from the surface,
1187         // then:
1188         // v->projected_h = -v->projected_h * persp/(v->projected_z-persp);
1189         // v->projected_v = -v->projected_v * persp/(v->projected_z-persp);
1190 }
1191 /*=======================================================================*/
1192 void evaluate(){
1193         int i, j;
1194         XYZ v[4];
1195
1196         if ( elevation > PI ) {
1197                 elevation -= 2. * PI;
1198         }
1199         roll = elevation * sin( azimuth );
1200         yaw  = 1.5 * PI + elevation*cos( azimuth );
1201
1202         //      Find angles from midpoint to viewpoint:
1203         st[0] = sin( yaw );
1204         st[1] = sin( roll );
1205         st[2] = sin( azimuth );
1206         ct[0] = cos( yaw );
1207         ct[1] = cos( roll );
1208         ct[2] = cos( azimuth );
1209
1210         for ( i = 0; i <= NH; i++ )
1211         {
1212                 for ( j = 0; j <= NV; j++ )
1213                 {
1214                         project( &xyz[i][j] );
1215                 }
1216         }
1217
1218         Hhi = xyz[0][0].pp[0];
1219         Hlo = Hhi;
1220         Vhi = xyz[0][0].pp[1];
1221         Vlo = Vhi;
1222         for ( i = 0; i <= NH; i++ )
1223         {
1224                 for ( j = 0; j <= NV; j++ )
1225                 {
1226                         Hlo = min( Hlo,xyz[i][j].pp[0] );
1227                         Hhi = max( Hhi,xyz[i][j].pp[0] );
1228                         Vlo = min( Vlo,xyz[i][j].pp[1] );
1229                         Vhi = max( Vhi,xyz[i][j].pp[1] );
1230                 }
1231         }
1232
1233         // Include backface in min-max
1234         VectorCopy( xyz[ 0][ 0].p,v[0].p );
1235         VectorCopy( xyz[NH][ 0].p,v[1].p );
1236         VectorCopy( xyz[NH][NV].p,v[2].p );
1237         VectorCopy( xyz[ 0][NV].p,v[3].p );
1238         switch ( Plane )
1239         {
1240         case PLANE_XZ0:
1241         case PLANE_XZ1:
1242                 v[0].p[1] = backface;
1243                 v[1].p[1] = v[0].p[1];
1244                 v[2].p[1] = v[0].p[1];
1245                 v[3].p[1] = v[0].p[1];
1246                 break;
1247         case PLANE_YZ0:
1248         case PLANE_YZ1:
1249                 v[0].p[0] = backface;
1250                 v[1].p[0] = v[0].p[0];
1251                 v[2].p[0] = v[0].p[0];
1252                 v[3].p[0] = v[0].p[0];
1253                 break;
1254         default:
1255                 v[0].p[2] = backface;
1256                 v[1].p[2] = v[0].p[2];
1257                 v[2].p[2] = v[0].p[2];
1258                 v[3].p[2] = v[0].p[2];
1259         }
1260         for ( i = 0; i <= 3; i++ )
1261         {
1262                 project( &v[i] );
1263                 Hlo = min( Hlo,v[i].pp[0] );
1264                 Hhi = max( Hhi,v[i].pp[0] );
1265                 Vlo = min( Vlo,v[i].pp[1] );
1266                 Vhi = max( Vhi,v[i].pp[1] );
1267         }
1268
1269 }
1270 #endif