]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/gtkgensurf/view.cpp
uncrustify! now the code is only ugly on the *inside*
[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, float l, float 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_qglVertex2f( rc.left - 1, rc.bottom ); \
69                 g_GLTable.m_pfn_qglVertex2f( rc.right, rc.bottom );     \
70                 g_GLTable.m_pfn_qglVertex2f( rc.right, rc.top + 1 ); \
71                 g_GLTable.m_pfn_qglVertex2f( rc.left - 1, rc.top + 1 ); \
72                 g_GLTable.m_pfn_qglColor3f( r,g,b ); \
73                 g_GLTable.m_pfn_qglVertex2f( rc.left, rc.bottom + 1 ); \
74                 g_GLTable.m_pfn_qglVertex2f( rc.right - 1, rc.bottom + 1 );     \
75                 g_GLTable.m_pfn_qglVertex2f( rc.right - 1, rc.top ); \
76                 g_GLTable.m_pfn_qglVertex2f( 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                 gtk_widget_show( g_pWndPreview );
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_qglVertex2f( rcUpper.left, rcUpper.bottom );
145                 g_GLTable.m_pfn_qglVertex2f( 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 *dlg, *vbox, *hbox, *label, *spin, *frame;
367         GtkObject *adj;
368
369 #ifndef ISOMETRIC
370         elevation = PI / 6.;
371         azimuth   = PI / 6.;
372 #endif
373
374         g_pWndPreview = dlg = gtk_window_new( GTK_WINDOW_TOPLEVEL );
375         gtk_window_set_title( GTK_WINDOW( dlg ), "GtkGenSurf Preview" );
376         gtk_signal_connect( GTK_OBJECT( dlg ), "delete_event", GTK_SIGNAL_FUNC( preview_close ), NULL );
377         gtk_signal_connect( GTK_OBJECT( dlg ), "destroy", GTK_SIGNAL_FUNC( gtk_widget_destroy ), NULL );
378         gtk_window_set_transient_for( GTK_WINDOW( dlg ), GTK_WINDOW( g_pWnd ) );
379         gtk_window_set_default_size( GTK_WINDOW( dlg ), 300, 400 );
380
381         vbox = gtk_vbox_new( FALSE, 5 );
382         gtk_widget_show( vbox );
383         gtk_container_add( GTK_CONTAINER( dlg ), vbox );
384
385 #ifndef ISOMETRIC
386         hbox = gtk_hbox_new( TRUE, 5 );
387         gtk_widget_show( hbox );
388         gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, TRUE, 0 );
389         gtk_container_set_border_width( GTK_CONTAINER( hbox ), 3 );
390
391         label = gtk_label_new( "Elevation" );
392         gtk_widget_show( label );
393         gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
394         gtk_box_pack_start( GTK_BOX( hbox ), label, FALSE, TRUE, 0 );
395
396         adj = gtk_adjustment_new( 30, -90, 90, 1, 10, 10 );
397         gtk_signal_connect( adj, "value_changed", GTK_SIGNAL_FUNC( preview_spin ), &elevation );
398         spin = gtk_spin_button_new( GTK_ADJUSTMENT( adj ), 1, 0 );
399         gtk_widget_show( spin );
400         gtk_box_pack_start( GTK_BOX( hbox ), spin, FALSE, TRUE, 0 );
401         g_signal_connect( G_OBJECT( spin ), "focus_out_event", G_CALLBACK( doublevariable_spinfocusout ), &elevation );
402
403         adj = gtk_adjustment_new( 30, 0, 359, 1, 10, 10 );
404         gtk_signal_connect( adj, "value_changed", GTK_SIGNAL_FUNC( preview_spin ), &azimuth );
405         spin = gtk_spin_button_new( GTK_ADJUSTMENT( adj ), 1, 0 );
406         gtk_widget_show( spin );
407         gtk_spin_button_set_wrap( GTK_SPIN_BUTTON( spin ), TRUE );
408         gtk_box_pack_end( GTK_BOX( hbox ), spin, FALSE, TRUE, 0 );
409
410         label = gtk_label_new( "Azimuth" );
411         gtk_widget_show( label );
412         gtk_misc_set_alignment( GTK_MISC( label ), 1, 0.5 );
413         gtk_box_pack_end( GTK_BOX( hbox ), label, FALSE, TRUE, 0 );
414         g_signal_connect( G_OBJECT( spin ), "focus_out_event", G_CALLBACK( doublevariable_spinfocusout ), &azimuth );
415 #endif
416
417         frame = gtk_frame_new( NULL );
418         gtk_widget_show( frame );
419         gtk_frame_set_shadow_type( GTK_FRAME( frame ), GTK_SHADOW_IN );
420         gtk_box_pack_start( GTK_BOX( vbox ), frame, TRUE, TRUE, 0 );
421
422         g_pPreviewWidget = g_UIGtkTable.m_pfn_glwidget_new( FALSE, NULL );
423
424         gtk_widget_set_events( g_pPreviewWidget, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK );
425         gtk_signal_connect( GTK_OBJECT( g_pPreviewWidget ), "expose_event", GTK_SIGNAL_FUNC( expose ), NULL );
426         gtk_signal_connect( GTK_OBJECT( g_pPreviewWidget ), "motion_notify_event", GTK_SIGNAL_FUNC( motion ), NULL );
427         gtk_signal_connect( GTK_OBJECT( g_pPreviewWidget ), "button_press_event",
428                                                 GTK_SIGNAL_FUNC( button_press ), NULL );
429
430         gtk_widget_show( g_pPreviewWidget );
431         gtk_container_add( GTK_CONTAINER( frame ), g_pPreviewWidget );
432
433         if ( Preview ) {
434                 gtk_widget_show( g_pWndPreview );
435         }
436
437         UpdatePreview( true );
438 }
439
440 //=============================================================
441 /* DrawPreview */
442 void DrawPreview( RECT rc ){
443 #define COSXA 0.8660254037844
444 #define SINXA 0.5
445 #define COSYA 0.8660254037844
446 #define SINYA 0.5
447
448         double L;
449         double x,y;
450         int i, j;
451         POINT pt[8];
452         XYZ v[8];
453         char axis[3][2] = {"X","Y","Z"};
454
455 #ifndef ISOMETRIC
456         evaluate();
457 #endif
458
459         XLo = xmin;
460         XHi = xmax;
461         YLo = ymin;
462         YHi = ymax;
463         ZLo = zmin;
464         ZHi = zmax;
465         switch ( Plane )
466         {
467         case PLANE_XY1:
468                 ZHi = backface;
469                 break;
470         case PLANE_XZ0:
471                 YLo = backface;
472                 break;
473         case PLANE_XZ1:
474                 YHi = backface;
475                 break;
476         case PLANE_YZ0:
477                 XLo = backface;
478                 break;
479         case PLANE_YZ1:
480                 XHi = backface;
481                 break;
482         default:
483                 ZLo = backface;
484         }
485
486
487
488         GetScaleFactor( rc );
489         //PEN_GRID
490         g_GLTable.m_pfn_qglLineWidth( 1 );
491         g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
492         g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
493
494         if ( Decimate > 0 && ( Game != QUAKE3 || UsePatches == 0 ) ) {
495                 XYZ  *vv;
496
497                 vv   = (XYZ *) malloc( gNumNodes * sizeof( XYZ ) );
498                 for ( i = 0; i < gNumNodes; i++ )
499                 {
500                         for ( j = 0; j < 3; j++ )
501                                 vv[i].p[j] = (double)( gNode[i].p[j] );
502                         project( &vv[i] );
503                 }
504
505                 for ( i = 0; i < gNumTris; i++ )
506                 {
507                         for ( j = 0; j < 3; j++ )
508                                 Scale( rc,vv[gTri[i].v[j]],&pt[j] );
509
510                         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
511                         g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
512                         g_GLTable.m_pfn_qglVertex2f( pt[1].x, pt[1].y );
513                         g_GLTable.m_pfn_qglVertex2f( pt[2].x, pt[2].y );
514                         g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
515                         g_GLTable.m_pfn_qglEnd();
516                 }
517                 free( vv );
518         }
519         else if ( Game == QUAKE3 && UsePatches != 0 ) {
520                 int axis, ii, jj, k;
521                 float u, v;
522                 XYZ uv[3][3];
523                 XYZ Ctrl[3],out;
524
525                 switch ( Plane )
526                 {
527                 case PLANE_XY0:
528                 case PLANE_XY1:
529                         k = 2;
530                         break;
531                 case PLANE_XZ0:
532                 case PLANE_XZ1:
533                         k = 1;
534                         break;
535                 default:
536                         k = 0;
537                 }
538                 for ( i = 0; i < NH; i += 2 )
539                 {
540                         for ( j = 0; j < NV; j += 2 )
541                         {
542                                 VectorCopy( xyz[i  ][j  ].p,uv[0][0].p );
543                                 VectorCopy( xyz[i + 1][j  ].p,uv[1][0].p );
544                                 VectorCopy( xyz[i + 2][j  ].p,uv[2][0].p );
545                                 VectorCopy( xyz[i  ][j + 1].p,uv[0][1].p );
546                                 VectorCopy( xyz[i + 1][j + 1].p,uv[1][1].p );
547                                 VectorCopy( xyz[i + 2][j + 1].p,uv[2][1].p );
548                                 VectorCopy( xyz[i  ][j + 2].p,uv[0][2].p );
549                                 VectorCopy( xyz[i + 1][j + 2].p,uv[1][2].p );
550                                 VectorCopy( xyz[i + 2][j + 2].p,uv[2][2].p );
551                                 uv[1][0].p[k] = ( 4 * xyz[i + 1][j  ].p[k] - xyz[i  ][j  ].p[k] - xyz[i + 2][j  ].p[k] ) / 2;
552                                 uv[0][1].p[k] = ( 4 * xyz[i  ][j + 1].p[k] - xyz[i  ][j  ].p[k] - xyz[i  ][j + 2].p[k] ) / 2;
553                                 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;
554                                 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;
555                                 uv[1][1].p[k] = ( 16 * xyz[i + 1][j + 1].p[k] -
556                                                                   xyz[i  ][j  ].p[k] - 2 * xyz[i + 1][j  ].p[k] -  xyz[i + 2][j  ].p[k] -
557                                                                   2 * xyz[i  ][j + 1].p[k]                        - 2 * xyz[i + 2][j + 1].p[k] -
558                                                                   xyz[i  ][j + 2].p[k] - 2 * xyz[i + 1][j + 2].p[k] -  xyz[i + 2][j + 2].p[k]   ) / 4;
559
560                                 for ( ii = 0; ii <= SUBDIVS; ii++ )
561                                 {
562                                         if ( ii == 0 || ii == SUBDIVS / 2 || ii == SUBDIVS ) {
563                                                 g_GLTable.m_pfn_qglLineWidth( 1 );
564                                                 g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
565                                                 g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
566                                                 // PEN_GRID
567                                         }
568                                         else
569                                         {
570                                                 g_GLTable.m_pfn_qglLineWidth( 1 );
571                                                 g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
572                                                 g_GLTable.m_pfn_qglLineStipple( 1, 0xF0F0 );
573                                                 g_GLTable.m_pfn_qglEnable( GL_LINE_STIPPLE );
574                                                 // PEN_DASH
575                                         }
576
577                                         u = (float)( ii ) / (float)( SUBDIVS );
578                                         for ( jj = 0; jj < 3; jj++ )
579                                         {
580                                                 for ( axis = 0; axis < 3; axis++ )
581                                                 {
582                                                         float a, b, c;
583                                                         float qA, qB, qC;
584                                                         a = (float)uv[0][jj].p[axis];
585                                                         b = (float)uv[1][jj].p[axis];
586                                                         c = (float)uv[2][jj].p[axis];
587                                                         qA = a - 2 * b + c;
588                                                         qB = 2 * b - 2 * a;
589                                                         qC = a;
590                                                         Ctrl[jj].p[axis] = qA * u * u + qB * u + qC;
591                                                 }
592                                         }
593                                         VectorCopy( Ctrl[0].p,out.p );
594                                         project( &out );
595                                         Scale( rc,out,&pt[0] );
596                                         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
597                                         g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
598                                         for ( jj = 1; jj <= SUBDIVS; jj++ )
599                                         {
600                                                 v = (float)( jj ) / (float)( SUBDIVS );
601                                                 for ( axis = 0 ; axis < 3 ; axis++ )
602                                                 {
603                                                         float a, b, c;
604                                                         float qA, qB, qC;
605                                                         a = (float)Ctrl[0].p[axis];
606                                                         b = (float)Ctrl[1].p[axis];
607                                                         c = (float)Ctrl[2].p[axis];
608                                                         qA = a - 2 * b + c;
609                                                         qB = 2 * b - 2 * a;
610                                                         qC = a;
611                                                         out.p[axis] = qA * v * v + qB * v + qC;
612                                                 }
613                                                 project( &out );
614                                                 Scale( rc,out,&pt[0] );
615                                                 g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
616                                         }
617                                         g_GLTable.m_pfn_qglEnd();
618                                 }
619                                 for ( jj = 0; jj <= SUBDIVS; jj++ )
620                                 {
621                                         if ( jj == 0 || jj == SUBDIVS / 2 || jj == SUBDIVS ) {
622                                                 g_GLTable.m_pfn_qglLineWidth( 1 );
623                                                 g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
624                                                 g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
625                                                 // PEN_GRID
626                                         }
627                                         else
628                                         {
629                                                 g_GLTable.m_pfn_qglLineWidth( 1 );
630                                                 g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
631                                                 g_GLTable.m_pfn_qglLineStipple( 1, 0xF0F0 );
632                                                 g_GLTable.m_pfn_qglEnable( GL_LINE_STIPPLE );
633                                                 // PEN_DASH
634                                         }
635
636                                         v = (float)( jj ) / (float)( SUBDIVS );
637                                         for ( ii = 0; ii < 3; ii++ )
638                                         {
639                                                 for ( axis = 0; axis < 3; axis++ )
640                                                 {
641                                                         float a, b, c;
642                                                         float qA, qB, qC;
643                                                         a = (float)uv[ii][0].p[axis];
644                                                         b = (float)uv[ii][1].p[axis];
645                                                         c = (float)uv[ii][2].p[axis];
646                                                         qA = a - 2 * b + c;
647                                                         qB = 2 * b - 2 * a;
648                                                         qC = a;
649                                                         Ctrl[ii].p[axis] = qA * v * v + qB * v + qC;
650                                                 }
651                                         }
652                                         VectorCopy( Ctrl[0].p,out.p );
653                                         project( &out );
654                                         Scale( rc,out,&pt[0] );
655                                         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
656                                         g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
657                                         for ( ii = 1; ii <= SUBDIVS; ii++ )
658                                         {
659                                                 u = (float)( ii ) / (float)( SUBDIVS );
660                                                 for ( axis = 0 ; axis < 3 ; axis++ )
661                                                 {
662                                                         float a, b, c;
663                                                         float qA, qB, qC;
664                                                         a = (float)Ctrl[0].p[axis];
665                                                         b = (float)Ctrl[1].p[axis];
666                                                         c = (float)Ctrl[2].p[axis];
667                                                         qA = a - 2 * b + c;
668                                                         qB = 2 * b - 2 * a;
669                                                         qC = a;
670                                                         out.p[axis] = qA * u * u + qB * u + qC;
671                                                 }
672                                                 project( &out );
673                                                 Scale( rc,out,&pt[0] );
674                                                 g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
675                                         }
676                                         g_GLTable.m_pfn_qglEnd();
677                                 }
678                         }
679                 }
680         }
681         else
682         {
683                 for ( i = 0; i <= NH; i++ )
684                 {
685                         Scale( rc,xyz[i][0],&pt[0] );
686                         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
687                         g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
688                         for ( j = 1; j <= NV; j++ )
689                         {
690                                 Scale( rc,xyz[i][j],&pt[0] );
691                                 g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
692                         }
693                         g_GLTable.m_pfn_qglEnd();
694                 }
695                 for ( j = 0; j <= NV; j++ )
696                 {
697                         Scale( rc,xyz[0][j],&pt[0] );
698                         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
699                         g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
700                         for ( i = 1; i <= NH; i++ )
701                         {
702                                 Scale( rc,xyz[i][j],&pt[0] );
703                                 g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
704                         }
705                         g_GLTable.m_pfn_qglEnd();
706                 }
707         }
708
709         if ( Game != QUAKE3 || UsePatches == 0 ) {
710                 // Draw lines from corners to base, and lines around base
711                 for ( i = 0; i <= NH; i += NH )
712                 {
713                         for ( j = 0; j <= NV; j += NV )
714                         {
715                                 VectorCopy( xyz[i][j].p, v[0].p );
716                                 switch ( Plane )
717                                 {
718                                 case PLANE_XZ0:
719                                 case PLANE_XZ1:
720                                         v[0].p[1] = backface;
721                                         break;
722                                 case PLANE_YZ0:
723                                 case PLANE_YZ1:
724                                         v[0].p[0] = backface;
725                                         break;
726                                 default:
727                                         v[0].p[2] = backface;
728                                 }
729                                 Scale( rc,xyz[i][j],&pt[0] );
730 #ifndef ISOMETRIC
731                                 project( &v[0] );
732 #endif
733                                 Scale( rc,v[0],&pt[1] );
734                                 g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
735                                 g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
736                                 g_GLTable.m_pfn_qglVertex2f( pt[1].x, pt[1].y );
737                                 g_GLTable.m_pfn_qglEnd();
738                         }
739                 }
740                 VectorCopy( xyz[ 0][ 0].p, v[0].p );
741                 VectorCopy( xyz[NH][ 0].p, v[1].p );
742                 VectorCopy( xyz[NH][NV].p, v[2].p );
743                 VectorCopy( xyz[ 0][NV].p, v[3].p );
744                 switch ( Plane )
745                 {
746                 case PLANE_XZ0:
747                 case PLANE_XZ1:
748                         v[0].p[1] = backface;;
749                         v[1].p[1] = v[0].p[1];
750                         v[2].p[1] = v[0].p[1];
751                         v[3].p[1] = v[0].p[1];
752                         break;
753                 case PLANE_YZ0:
754                 case PLANE_YZ1:
755                         v[0].p[0] = backface;
756                         v[1].p[0] = v[0].p[0];
757                         v[2].p[0] = v[0].p[0];
758                         v[3].p[0] = v[0].p[0];
759                         break;
760                 default:
761                         v[0].p[2] = backface;
762                         v[1].p[2] = v[0].p[2];
763                         v[2].p[2] = v[0].p[2];
764                         v[3].p[2] = v[0].p[2];
765                 }
766 #ifndef ISOMETRIC
767                 project( &v[3] );
768 #endif
769                 Scale( rc,v[3],&pt[0] );
770                 g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
771                 g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
772                 for ( i = 0; i < 3; i++ )
773                 {
774 #ifndef ISOMETRIC
775                         project( &v[i] );
776 #endif
777                         Scale( rc,v[i],&pt[1] );
778                         g_GLTable.m_pfn_qglVertex2f( pt[1].x, pt[1].y );
779                 }
780                 g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
781                 g_GLTable.m_pfn_qglEnd();
782         }
783
784         g_GLTable.m_pfn_qglLineWidth( 1 );
785         g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
786         g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
787
788 #ifdef ISOMETRIC
789         // Draw small depiction of coordinate axes
790         pt[0].x = rc.right  - cxChar   - cxChar / 2 -  cyChar;
791         pt[0].y = rc.bottom - cyChar / 2 - cxChar / 2;
792         pt[1].x = pt[0].x + (int)( cyChar * COSXA );
793         pt[1].y = pt[0].y - (int)( cyChar * SINXA );
794         MoveToEx( hdc,pt[0].x,pt[0].y,NULL );
795         LineTo( hdc,pt[1].x,pt[1].y );
796         SetTextAlign( hdc,TA_LEFT | TA_TOP );
797         TextOut( hdc,pt[1].x,pt[1].y - cyChar / 2,"X",1 );
798         pt[1].x = pt[0].x - (int)( cyChar * COSYA );
799         pt[1].y = pt[0].y - (int)( cyChar * SINYA );
800         MoveToEx( hdc,pt[0].x,pt[0].y,NULL );
801         LineTo( hdc,pt[1].x,pt[1].y );
802         SetTextAlign( hdc,TA_RIGHT | TA_TOP );
803         TextOut( hdc,pt[1].x,pt[1].y - cyChar / 2,"Y",1 );
804         pt[1].x = pt[0].x;
805         pt[1].y = pt[0].y - cyChar;
806         MoveToEx( hdc,pt[0].x,pt[0].y,NULL );
807         LineTo( hdc,pt[1].x,pt[1].y );
808         SetTextAlign( hdc,TA_CENTER | TA_BOTTOM );
809         TextOut( hdc,pt[1].x,pt[1].y,"Z",1 );
810 #else
811         L = 2 * (double)cyChar / SF;
812         v[0].p[0] = 0.;
813         v[0].p[1] = 0.;
814         v[0].p[2] = 0.;
815         v[1].p[0] = L;
816         v[1].p[1] = 0.;
817         v[1].p[2] = 0.;
818         v[2].p[0] = 0.;
819         v[2].p[1] = L;
820         v[2].p[2] = 0.;
821         v[3].p[0] = 0.;
822         v[3].p[1] = 0.;
823         v[3].p[2] = L;
824         for ( i = 0; i <= 3; i++ )
825         {
826                 project( &v[i] );
827                 Scale( rc,v[i],&pt[i] );
828         }
829         for ( i = 1; i <= 3; i++ )
830         {
831                 pt[i].x += -pt[0].x + rc.right  - 2 * cyChar;
832                 pt[i].y += -pt[0].y + rc.bottom + 2 * cyChar;
833         }
834         pt[0].x = rc.right  - 2 * cyChar;
835         pt[0].y = rc.bottom + 2 * cyChar;
836
837         for ( i = 1; i <= 3; i++ )
838         {
839                 g_GLTable.m_pfn_qglBegin( GL_LINES );
840                 g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
841                 g_GLTable.m_pfn_qglVertex2f( pt[i].x, pt[i].y );
842                 g_GLTable.m_pfn_qglEnd();
843                 texfont_write( axis[i - 1], pt[i].x - cxChar / 2,pt[i].y + cyChar / 2 );
844         }
845 #endif
846
847         // Draw player model's bounding box in red to give a sense of scale
848         // PEN_RED
849         g_GLTable.m_pfn_qglLineWidth( 2 );
850         g_GLTable.m_pfn_qglColor3f( 1, 0, 0 );
851         g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
852
853         switch ( Plane )
854         {
855         case PLANE_XY1:
856                 v[0].p[0] = xyz[NH / 2][NV / 2].p[0] + PlayerBox[Game].x[0];
857                 v[0].p[1] = xyz[NH / 2][NV / 2].p[1] + PlayerBox[Game].y[0];
858                 v[0].p[2] = zmin - PlayerBox[Game].z[0] - 32;
859                 break;
860         case PLANE_XZ0:
861                 v[0].p[0] = ( xmax + xmin ) / 2 + PlayerBox[Game].x[0];
862                 v[0].p[1] = ymax + 64;
863                 v[0].p[2] = zmin;
864                 break;
865         case PLANE_XZ1:
866                 v[0].p[0] = ( xmax + xmin ) / 2 + PlayerBox[Game].x[0];
867                 v[0].p[1] = ymin - 64;
868                 v[0].p[2] = zmin;
869                 break;
870         case PLANE_YZ0:
871                 v[0].p[0] = xmax + 64;
872                 v[0].p[1] = ( ymax + ymin ) / 2 + PlayerBox[Game].y[0];
873                 v[0].p[2] = zmin;
874                 break;
875         case PLANE_YZ1:
876                 v[0].p[0] = xmin - 64;
877                 v[0].p[1] = ( ymax + ymin ) / 2 + PlayerBox[Game].y[0];
878                 v[0].p[2] = zmin;
879                 break;
880         default:
881                 // Put player on a node. For patches, put on an even numbered node.
882                 if ( Game == QUAKE3 && UsePatches != 0 ) {
883                         if ( NH > 2 ) {
884                                 x = Hll + dh * (int)( NH / 2 + 1 );
885                         }
886                         else{
887                                 x = Hll + dh * (int)( NH / 2 );
888                         }
889                         if ( NV > 2 ) {
890                                 y = Vll + dv * (int)( NV / 2 + 1 );
891                         }
892                         else{
893                                 y = Vll + dv * (int)( NV / 2 );
894                         }
895                 }
896                 else
897                 {
898                         if ( NH > 1 ) {
899                                 x = Hll + dh * (int)( NH / 2 );
900                         }
901                         else{
902                                 x = Hll + dh / 2;
903                         }
904                         if ( NV > 1 ) {
905                                 y = Vll + dv * (int)( NV / 2 );
906                         }
907                         else{
908                                 y = Vll + dv / 2;
909                         }
910                 }
911 //              x = (Hll+Hur)/2.;
912 //              y = (Vll+Vur)/2.;
913                 v[0].p[0] = x + PlayerBox[Game].x[0];
914                 v[0].p[1] = y + PlayerBox[Game].y[0];
915                 v[0].p[2] = PlayerStartZ( x,y ) + PlayerBox[Game].z[0] + 8; // add 8 cuz I'm a pessimist
916         }
917         v[1].p[0] = v[0].p[0] + PlayerBox[Game].x[1] - PlayerBox[Game].x[0];
918         v[1].p[1] = v[0].p[1];
919         v[1].p[2] = v[0].p[2];
920         v[2].p[0] = v[1].p[0];
921         v[2].p[1] = v[1].p[1] + PlayerBox[Game].y[1] - PlayerBox[Game].y[0];
922         v[2].p[2] = v[0].p[2];
923         v[3].p[0] = v[0].p[0];
924         v[3].p[1] = v[2].p[1];
925         v[3].p[2] = v[0].p[2];
926         VectorCopy( v[0].p,v[4].p );
927         VectorCopy( v[1].p,v[5].p );
928         VectorCopy( v[2].p,v[6].p );
929         VectorCopy( v[3].p,v[7].p );
930         v[4].p[2] += PlayerBox[Game].z[1] - PlayerBox[Game].z[0];
931         v[5].p[2] += PlayerBox[Game].z[1] - PlayerBox[Game].z[0];
932         v[6].p[2] += PlayerBox[Game].z[1] - PlayerBox[Game].z[0];
933         v[7].p[2] += PlayerBox[Game].z[1] - PlayerBox[Game].z[0];
934         for ( i = 0; i <= 7; i++ )
935         {
936 #ifndef ISOMETRIC
937                 project( &v[i] );
938 #endif
939                 Scale( rc,v[i],&pt[i] );
940         }
941         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
942         g_GLTable.m_pfn_qglVertex2f( pt[3].x, pt[3].y );
943         for ( i = 0; i <= 3; i++ )
944                 g_GLTable.m_pfn_qglVertex2f( pt[i].x, pt[i].y );
945         g_GLTable.m_pfn_qglEnd();
946         g_GLTable.m_pfn_qglBegin( GL_LINE_STRIP );
947         g_GLTable.m_pfn_qglVertex2f( pt[7].x, pt[7].y );
948         for ( i = 4; i <= 7; i++ )
949                 g_GLTable.m_pfn_qglVertex2f( pt[i].x, pt[i].y );
950         g_GLTable.m_pfn_qglEnd();
951         g_GLTable.m_pfn_qglBegin( GL_LINES );
952         for ( i = 0; i <= 3; i++ )
953         {
954                 g_GLTable.m_pfn_qglVertex2f( pt[i].x,pt[i].y );
955                 g_GLTable.m_pfn_qglVertex2f( pt[i + 4].x,pt[i + 4].y );
956         }
957         g_GLTable.m_pfn_qglEnd();
958
959         g_GLTable.m_pfn_qglLineWidth( 1 );
960         g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
961         g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
962 }
963 //=============================================================
964 void DrawGrid( RECT rc ){
965         int i, j, k;
966         double h,w,x,y;
967         POINT pt[2];
968         RECT rcBox;
969
970         w = (double)( rc.right - rc.left + 1 ) - cxChar;
971         h = (double)( rc.top - rc.bottom + 1 ) - cxChar - cyChar;
972
973         SFG = w / ( Hur - Hll );
974         SFG = min( SFG, h / ( Vur - Vll ) );
975
976         // Center drawing
977         X0G = (int)( rc.left + rc.right - (int)( SFG * ( Hur - Hll ) ) ) / 2;
978         Y0G = (int)( rc.top + rc.bottom + cyChar - (int)( SFG * ( Vur - Vll ) ) ) / 2;
979
980         g_GLTable.m_pfn_qglLineWidth( 2 );
981         g_GLTable.m_pfn_qglColor3f( 0, 1, 0 );
982         g_GLTable.m_pfn_qglDisable( GL_LINE_STIPPLE );
983
984         pt[0].y = Y0G;
985         pt[1].y = Y0G + (int)( SFG * ( Vur - Vll ) );
986         g_GLTable.m_pfn_qglBegin( GL_LINES );
987         for ( i = 0; i <= NH; i++ )
988         {
989                 x = Hll + i * dh;
990                 pt[0].x = X0G + (int)( SFG * ( x - Hll ) );
991                 g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[0].y );
992                 g_GLTable.m_pfn_qglVertex2f( pt[0].x, pt[1].y );
993         }
994         g_GLTable.m_pfn_qglEnd();
995         pt[0].x = X0G;
996         pt[1].x = X0G + (int)( SFG * ( Hur - Hll ) );
997         g_GLTable.m_pfn_qglBegin( GL_LINES );
998         for ( i = 0; i <= NV; i++ )
999         {
1000                 y = Vll + i * dv;
1001                 pt[0].y = Y0G + (int)( SFG * ( Vur - y ) );
1002                 g_GLTable.m_pfn_qglVertex2f( pt[0].x,pt[0].y );
1003                 g_GLTable.m_pfn_qglVertex2f( pt[1].x,pt[0].y );
1004         }
1005         g_GLTable.m_pfn_qglEnd();
1006
1007         g_GLTable.m_pfn_qglLineWidth( 1 );
1008
1009         // Draw axes
1010         pt[0].x = rc.right  - cyChar - cxChar - cyChar / 2;
1011         pt[0].y = rc.bottom + cyChar / 2;
1012         pt[1].x = pt[0].x + cyChar;
1013         pt[1].y = pt[0].y;
1014         g_GLTable.m_pfn_qglBegin( GL_LINES );
1015         g_GLTable.m_pfn_qglVertex2f( pt[0].x,pt[0].y );
1016         g_GLTable.m_pfn_qglVertex2f( pt[1].x,pt[1].y );
1017         g_GLTable.m_pfn_qglEnd();
1018         switch ( Plane )
1019         {
1020         case PLANE_YZ0:
1021         case PLANE_YZ1:
1022                 texfont_write( "Y", pt[1].x, pt[1].y + cyChar / 2 );
1023                 break;
1024         default:
1025                 texfont_write( "X", pt[1].x, pt[1].y + cyChar / 2 );
1026         }
1027         pt[1].x = pt[0].x;
1028         pt[1].y = pt[0].y + cyChar;
1029         g_GLTable.m_pfn_qglBegin( GL_LINES );
1030         g_GLTable.m_pfn_qglVertex2f( pt[0].x,pt[0].y );
1031         g_GLTable.m_pfn_qglVertex2f( pt[1].x,pt[1].y );
1032         g_GLTable.m_pfn_qglEnd();
1033         switch ( Plane )
1034         {
1035         case PLANE_XY0:
1036         case PLANE_XY1:
1037                 texfont_write( "Y", pt[1].x - cyChar / 2, pt[1].y + cyChar );
1038                 break;
1039         default:
1040                 texfont_write( "Z", pt[1].x - cyChar / 2, pt[1].y + cyChar );
1041         }
1042
1043         // Denote fixed points with a 5x5 red rectangle
1044         for ( i = 0; i <= NH; i++ )
1045         {
1046                 for ( j = 0; j <= NV; j++ )
1047                 {
1048                         if ( xyz[i][j].fixed ) {
1049                                 x = Hll + i * dh;
1050                                 y = Vll + j * dv;
1051                                 rcBox.left   = X0G + (int)( SFG * ( x - Hll ) ) - 2;
1052                                 rcBox.top    = Y0G + (int)( SFG * ( Vur - y ) ) + 2;
1053                                 rcBox.right  = rcBox.left + 5;
1054                                 rcBox.bottom = rcBox.top  - 5;
1055
1056                                 DRAW_QUAD( rcBox, 1,0,0 );
1057                         }
1058                 }
1059         }
1060
1061         // Denote currently selected point with a 5x5 green rectangle
1062         if ( NumVerticesSelected ) {
1063                 for ( k = 0; k < NumVerticesSelected; k++ )
1064                 {
1065                         x = Hll + Vertex[k].i * dh;
1066                         y = Vll + Vertex[k].j * dv;
1067                         rcBox.left   = X0G + (int)( SFG * ( x - Hll ) ) - 2;
1068                         rcBox.top    = Y0G + (int)( SFG * ( Vur - y ) ) + 2;
1069                         rcBox.right  = rcBox.left + 5;
1070                         rcBox.bottom = rcBox.top  - 5;
1071
1072                         DRAW_QUAD( rcBox, 0,1,0 );
1073                 }
1074         }
1075
1076         // Unmovable vertices
1077         for ( i = 0; i <= NH; i++ )
1078         {
1079                 for ( j = 0; j <= NV; j++ )
1080                 {
1081                         if ( !CanEdit( i,j ) ) {
1082                                 x = Hll + i * dh;
1083                                 y = Vll + j * dv;
1084                                 rcBox.left   = X0G + (int)( SFG * ( x - Hll ) ) - 2;
1085                                 rcBox.top    = Y0G + (int)( SFG * ( Vur - y ) ) + 2;
1086                                 rcBox.right  = rcBox.left + 5;
1087                                 rcBox.bottom = rcBox.top  - 5;
1088
1089                                 DRAW_QUAD( rcBox, 1,1,0 );
1090                         }
1091                 }
1092         }
1093
1094         // Legend
1095         rcBox.left   = rc.left + cxChar / 2 - 2;
1096         rcBox.top    = rc.top  - cyChar / 2 - 2;
1097         rcBox.right  = rcBox.left + 5;
1098         rcBox.bottom = rcBox.top  - 5;
1099         DRAW_QUAD( rcBox, 1,0,0 );
1100         texfont_write( "Fixed points", rcBox.right + cxChar,rcBox.top - 4 + cyChar / 2 );
1101
1102         rcBox.top    -= cyChar;
1103         rcBox.bottom -= cyChar;
1104         DRAW_QUAD( rcBox, 1,1,0 );
1105         texfont_write( "Not movable", rcBox.right + cxChar, rcBox.top - 4 + cyChar / 2 );
1106
1107         rcBox.top    -= cyChar;
1108         rcBox.bottom -= cyChar;
1109         DRAW_QUAD( rcBox, 0,1,0 );
1110         texfont_write( "Selected", rcBox.right + cxChar, rcBox.top - 4 + cyChar / 2 );
1111 }
1112
1113 //=============================================================
1114 void GetScaleFactor( RECT rc ){
1115 #ifdef ISOMETRIC
1116         double h, w;
1117
1118         w = (double)( rc.right - rc.left + 1 ) - cxChar;
1119         h = (double)( rc.top - rc.bottom + 1 ) - cxChar;
1120
1121         SF = w / ( ( XHi - XLo ) * COSXA + ( YHi - YLo ) * COSYA );
1122         SF = min( SF, h / ( ( XHi - XLo ) * SINXA + ( YHi - YLo ) * SINYA + ZHi - ZLo ) );
1123         // Center drawing
1124         X0 = (int)( rc.left + rc.right - (int)( SF * ( ( XHi - XLo ) * COSXA + ( YHi - YLo ) * COSYA ) ) ) / 2;
1125         Y0 = (int)( rc.top + rc.bottom - (int)( SF * ( ( XHi - XLo ) * SINXA + ( YHi - YLo ) * SINYA + ZHi - ZLo ) ) ) / 2;
1126
1127 #else
1128         double h, w;
1129
1130         w = (double)( rc.right - rc.left + 1 ) - cxChar;
1131         h = (double)( rc.top - rc.bottom + 1 ) - cxChar;
1132
1133         SF = w / ( Hhi - Hlo );
1134         SF = min( SF, h / ( Vhi - Vlo ) );
1135         X0 = (int)( rc.left + rc.right - (int)( SF * ( Hhi - Hlo ) ) ) / 2;
1136         Y0 = (int)( rc.top + rc.bottom + (int)( SF * ( Vhi - Vlo ) ) ) / 2;
1137 #endif
1138 }
1139
1140 //=============================================================
1141 void Scale( RECT rc,XYZ xyz,POINT *pt ){
1142
1143 #ifdef ISOMETRIC
1144
1145         pt[0].x = X0 + (int)( SF * ( ( xyz.p[0] - XLo ) * COSXA +
1146                                                                  ( YHi - xyz.p[1] ) * COSYA   ) );
1147         pt[0].y = Y0 + (int)( SF * (  ZHi - xyz.p[2] +
1148                                                                   ( YHi - xyz.p[1] ) * SINYA +
1149                                                                   ( XHi - xyz.p[0] ) * SINXA   ) );
1150 #else
1151         pt[0].x = X0 + (int)( SF * ( xyz.pp[0] - Hlo ) );
1152         pt[0].y = Y0 - (int)( SF * ( Vhi - xyz.pp[1] ) );
1153 #endif
1154
1155 }
1156
1157 #ifndef ISOMETRIC
1158 /* ======================================================================= */
1159 void project( XYZ *v ){
1160         // project a 3D point (x,y,z) onto view plane
1161         double x, y, z, xa, ya, za;
1162
1163         x = v->p[0];
1164         y = v->p[1];
1165         z = v->p[2];
1166
1167         // yaw
1168         xa = ct[0] * x - st[0] * z;
1169         za = st[0] * x + ct[0] * z;
1170
1171         // roll
1172         x  = ct[1] * xa + st[1] * y;
1173         ya = ct[1] * y  - st[1] * xa;
1174
1175         // azimuth
1176         z  = ct[2] * za - st[2] * ya;
1177         y  = ct[2] * ya + st[2] * za;
1178
1179         // horizontal and vertical projections:
1180 //      v->pp[0] = D*x/z;
1181 //      v->pp[1] = D*y/z;
1182         v->pp[0] = -y;
1183         v->pp[1] =  x;
1184         v->pp[2] =  z;
1185
1186         // NOTE: if perspective transformation is desired,
1187         // set "persp" to the range from the surface,
1188         // then:
1189         // v->projected_h = -v->projected_h * persp/(v->projected_z-persp);
1190         // v->projected_v = -v->projected_v * persp/(v->projected_z-persp);
1191 }
1192 /*=======================================================================*/
1193 void evaluate(){
1194         int i, j;
1195         XYZ v[4];
1196
1197         if ( elevation > PI ) {
1198                 elevation -= 2. * PI;
1199         }
1200         roll = elevation * sin( azimuth );
1201         yaw  = 1.5 * PI + elevation*cos( azimuth );
1202
1203         //      Find angles from midpoint to viewpoint:
1204         st[0] = sin( yaw );
1205         st[1] = sin( roll );
1206         st[2] = sin( azimuth );
1207         ct[0] = cos( yaw );
1208         ct[1] = cos( roll );
1209         ct[2] = cos( azimuth );
1210
1211         for ( i = 0; i <= NH; i++ )
1212         {
1213                 for ( j = 0; j <= NV; j++ )
1214                 {
1215                         project( &xyz[i][j] );
1216                 }
1217         }
1218
1219         Hhi = xyz[0][0].pp[0];
1220         Hlo = Hhi;
1221         Vhi = xyz[0][0].pp[1];
1222         Vlo = Vhi;
1223         for ( i = 0; i <= NH; i++ )
1224         {
1225                 for ( j = 0; j <= NV; j++ )
1226                 {
1227                         Hlo = min( Hlo,xyz[i][j].pp[0] );
1228                         Hhi = max( Hhi,xyz[i][j].pp[0] );
1229                         Vlo = min( Vlo,xyz[i][j].pp[1] );
1230                         Vhi = max( Vhi,xyz[i][j].pp[1] );
1231                 }
1232         }
1233
1234         // Include backface in min-max
1235         VectorCopy( xyz[ 0][ 0].p,v[0].p );
1236         VectorCopy( xyz[NH][ 0].p,v[1].p );
1237         VectorCopy( xyz[NH][NV].p,v[2].p );
1238         VectorCopy( xyz[ 0][NV].p,v[3].p );
1239         switch ( Plane )
1240         {
1241         case PLANE_XZ0:
1242         case PLANE_XZ1:
1243                 v[0].p[1] = backface;
1244                 v[1].p[1] = v[0].p[1];
1245                 v[2].p[1] = v[0].p[1];
1246                 v[3].p[1] = v[0].p[1];
1247                 break;
1248         case PLANE_YZ0:
1249         case PLANE_YZ1:
1250                 v[0].p[0] = backface;
1251                 v[1].p[0] = v[0].p[0];
1252                 v[2].p[0] = v[0].p[0];
1253                 v[3].p[0] = v[0].p[0];
1254                 break;
1255         default:
1256                 v[0].p[2] = backface;
1257                 v[1].p[2] = v[0].p[2];
1258                 v[2].p[2] = v[0].p[2];
1259                 v[3].p[2] = v[0].p[2];
1260         }
1261         for ( i = 0; i <= 3; i++ )
1262         {
1263                 project( &v[i] );
1264                 Hlo = min( Hlo,v[i].pp[0] );
1265                 Hhi = max( Hhi,v[i].pp[0] );
1266                 Vlo = min( Vlo,v[i].pp[1] );
1267                 Vhi = max( Vhi,v[i].pp[1] );
1268         }
1269
1270 }
1271 #endif