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