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