1 //------------------------------------------------------------------------
2 // EDITING CANVAS
3 //------------------------------------------------------------------------
4 //
5 // Eureka DOOM Editor
6 //
7 // Copyright (C) 2006-2019 Andrew Apted
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 //------------------------------------------------------------------------
20
21 #include "main.h"
22
23 #include <algorithm>
24
25 #ifndef NO_OPENGL
26 #include "FL/gl.h"
27 #endif
28
29 #include "ui_window.h"
30
31 #include "m_events.h"
32 #include "e_main.h"
33 #include "e_hover.h"
34 #include "e_linedef.h"
35 #include "e_sector.h"
36 #include "e_things.h"
37 #include "e_path.h" // SoundPropagation
38 #include "im_color.h"
39 #include "im_img.h"
40 #include "m_config.h"
41 #include "m_game.h"
42 #include "r_grid.h"
43 #include "r_subdiv.h"
44 #include "r_render.h"
45 #include "w_rawdef.h" // MLF_xxx
46 #include "w_texture.h"
47
48
49 #define CAMERA_COLOR fl_rgb_color(255, 192, 255)
50
51
52 typedef enum
53 {
54 LINFO_Nothing = 0,
55 LINFO_Length,
56 LINFO_Angle,
57 LINFO_Ratio,
58 LINFO_Length_Angle,
59 LINFO_Length_Ratio
60
61 } line_info_mode_e;
62
63
64 // config items
65 rgb_color_t dotty_axis_col = RGB_MAKE(0, 128, 255);
66 rgb_color_t dotty_major_col = RGB_MAKE(0, 0, 238);
67 rgb_color_t dotty_minor_col = RGB_MAKE(0, 0, 187);
68 rgb_color_t dotty_point_col = RGB_MAKE(0, 0, 255);
69
70 rgb_color_t normal_axis_col = RGB_MAKE(0, 128, 255);
71 rgb_color_t normal_main_col = RGB_MAKE(0, 0, 238);
72 rgb_color_t normal_flat_col = RGB_MAKE(60, 60, 120);
73 rgb_color_t normal_small_col = RGB_MAKE(60, 60, 120);
74
75 int highlight_line_info = (int)LINFO_Length;
76
77
78 int vertex_radius(double scale);
79
80
UI_Canvas(int X,int Y,int W,int H,const char * label)81 UI_Canvas::UI_Canvas(int X, int Y, int W, int H, const char *label) :
82 #ifdef NO_OPENGL
83 Fl_Widget(X, Y, W, H, label),
84 #else
85 Fl_Gl_Window(X, Y, W, H),
86 #endif
87 last_highlight(),
88 last_splitter(-1),
89 last_split_x(), last_split_y(),
90 snap_x(-1), snap_y(-1),
91 seen_sectors()
92 {
93 #ifdef NO_OPENGL
94 rgb_buf = NULL;
95 #endif
96 }
97
98
~UI_Canvas()99 UI_Canvas::~UI_Canvas()
100 { }
101
102
DeleteContext()103 void UI_Canvas::DeleteContext()
104 {
105 #ifndef NO_OPENGL
106 context(NULL, 0);
107
108 // ensure W_UnloadAllTextures() gets called on next draw()
109 invalidate();
110 #endif
111 }
112
113
resize(int X,int Y,int W,int H)114 void UI_Canvas::resize(int X, int Y, int W, int H)
115 {
116 #ifdef NO_OPENGL
117 Fl_Widget::resize(X, Y, W, H);
118 #else
119 Fl_Gl_Window::resize(X, Y, W, H);
120 #endif
121 }
122
123
draw()124 void UI_Canvas::draw()
125 {
126 #ifndef NO_OPENGL
127 if (! valid())
128 {
129 // reset the 'gl_tex' field of all loaded images, as the value
130 // belongs to a context which was (probably) just deleted and
131 // hence refer to textures which no longer exist.
132 W_UnloadAllTextures();
133 }
134 #endif
135
136 if (edit.render3d)
137 {
138 Render3D_Draw(x(), y(), w(), h());
139 return;
140 }
141
142 #ifdef NO_OPENGL
143 xx = x();
144 yy = y();
145
146 map_lx = floor(MAPX(xx));
147 map_ly = floor(MAPY(yy + h()));
148
149 map_hx = ceil(MAPX(xx + w()));
150 map_hy = ceil(MAPY(yy));
151
152 #else // OpenGL
153 xx = yy = 0;
154
155 map_lx = floor(MAPX(0));
156 map_ly = floor(MAPY(0));
157
158 map_hx = ceil(MAPX(w()));
159 map_hy = ceil(MAPY(h()));
160
161 // setup projection matrix for 2D drawing
162
163 // Note: this crud is a workaround for retina displays on MacOS
164 Fl::use_high_res_GL(true);
165 int pix = I_ROUND(main_win->canvas->pixels_per_unit());
166 Fl::use_high_res_GL(false);
167
168 glLoadIdentity();
169 glViewport(0, 0, w() * pix, h() * pix);
170 glOrtho(0, w(), 0, h(), -1, 1);
171 #endif
172
173 PrepareToDraw();
174
175 RenderColor(FL_WHITE);
176 RenderThickness(1);
177
178 // default font (for showing object numbers)
179 int font_size = (grid.Scale < 0.9) ? 14 : 19;
180 RenderFontSize(font_size);
181
182 DrawEverything();
183
184 Blit();
185 }
186
187
handle(int event)188 int UI_Canvas::handle(int event)
189 {
190 if (EV_HandleEvent(event))
191 return 1;
192
193 return Fl_Widget::handle(event);
194 }
195
196
NORMALX(int len,double dx,double dy)197 int UI_Canvas::NORMALX(int len, double dx, double dy)
198 {
199 #ifdef NO_OPENGL
200 double res = -dy;
201 #else
202 double res = dy;
203 #endif
204
205 double got_len = hypotf(dx, dy);
206 if (got_len < 0.01)
207 return 0;
208
209 return I_ROUND(res * len / got_len);
210 }
211
NORMALY(int len,double dx,double dy)212 int UI_Canvas::NORMALY(int len, double dx, double dy)
213 {
214 #ifdef NO_OPENGL
215 double res = dx;
216 #else
217 double res = -dx;
218 #endif
219
220 double got_len = hypotf(dx, dy);
221 if (got_len < 0.01)
222 return 0;
223
224 return I_ROUND(res * len / got_len);
225 }
226
227
PointerPos(bool in_event)228 void UI_Canvas::PointerPos(bool in_event)
229 {
230 if (edit.render3d)
231 return;
232
233 // we read current position outside of FLTK's event propagation.
234 int raw_x, raw_y;
235 Fl::get_mouse(raw_x, raw_y);
236
237 #ifdef NO_OPENGL
238 raw_x -= main_win->x_root();
239 raw_y -= main_win->y_root();
240
241 edit.map_x = MAPX(raw_x);
242 edit.map_y = MAPY(raw_y);
243
244 #else // OpenGL
245 raw_x -= x_root();
246 raw_y -= y_root();
247
248 edit.map_x = MAPX(raw_x);
249 edit.map_y = MAPY(h() - 1 - raw_y);
250 #endif
251
252 grid.NaturalSnapXY(edit.map_x, edit.map_y);
253
254 // no Z coord with the 2D map view
255 edit.map_z = -1;
256 }
257
258
ApproxBoxSize(int mx1,int my1,int mx2,int my2)259 int UI_Canvas::ApproxBoxSize(int mx1, int my1, int mx2, int my2)
260 {
261 if (mx2 < mx1) std::swap(mx1, mx2);
262 if (my2 < my1) std::swap(my1, my2);
263
264 int x1 = SCREENX(mx1);
265 int x2 = SCREENX(mx2);
266
267 int y1 = SCREENY(my2);
268 int y2 = SCREENY(my1);
269
270 if (x1 < 8 || x2 > w() - 8 ||
271 y1 < 8 || y2 > h() - 8)
272 return 1; // too big
273
274 float x_ratio = MAX(4, x2 - x1) / (float) MAX(4, w());
275 float y_ratio = MAX(4, y2 - y1) / (float) MAX(4, h());
276
277 if (MAX(x_ratio, y_ratio) < 0.25)
278 return -1; // too small
279
280 return 0;
281 }
282
283
284 //------------------------------------------------------------------------
285
286
DrawEverything()287 void UI_Canvas::DrawEverything()
288 {
289 // setup for drawing sector numbers
290 if (edit.show_object_numbers && edit.mode == OBJ_SECTORS)
291 {
292 seen_sectors.clear_all();
293 }
294
295 DrawMap();
296
297 DrawSelection(edit.Selected);
298
299 if (edit.action == ACT_DRAG && !edit.dragged.valid() && edit.drag_lines != NULL)
300 DrawSelection(edit.drag_lines);
301 else if (edit.action == ACT_TRANSFORM && edit.trans_lines != NULL)
302 DrawSelection(edit.trans_lines);
303
304 if (edit.action == ACT_DRAG && edit.dragged.valid())
305 {
306 double dx = 0;
307 double dy = 0;
308 DragDelta(&dx, &dy);
309
310 if (edit.mode == OBJ_VERTICES)
311 RenderColor(HI_AND_SEL_COL);
312 else
313 RenderColor(HI_COL);
314
315 if (edit.mode == OBJ_LINEDEFS || edit.mode == OBJ_SECTORS)
316 RenderThickness(2);
317
318 DrawHighlight(edit.mode, edit.dragged.num, false /* skip_lines */, dx, dy);
319
320 if (edit.mode == OBJ_VERTICES && edit.highlight.valid())
321 {
322 RenderColor(HI_COL);
323 DrawHighlight(edit.highlight.type, edit.highlight.num);
324 }
325
326 RenderThickness(1);
327
328 // when ratio lock is on, want to see the new line
329 if (edit.mode == OBJ_VERTICES && grid.ratio > 0 && edit.drag_other_vert >= 0)
330 {
331 const Vertex *v0 = Vertices[edit.drag_other_vert];
332 const Vertex *v1 = Vertices[edit.dragged.num];
333
334 RenderColor(RED);
335 DrawKnobbyLine(v0->x(), v0->y(), v1->x() + dx, v1->y() + dy);
336
337 DrawLineInfo(v0->x(), v0->y(), v1->x() + dx, v1->y() + dy, true);
338 }
339 }
340 else if (edit.highlight.valid())
341 {
342 if (edit.action != ACT_DRAW_LINE && edit.Selected->get(edit.highlight.num))
343 RenderColor(HI_AND_SEL_COL);
344 else
345 RenderColor(HI_COL);
346
347 if (edit.highlight.type == OBJ_LINEDEFS || edit.highlight.type == OBJ_SECTORS)
348 RenderThickness(2);
349
350 DrawHighlight(edit.highlight.type, edit.highlight.num);
351
352 if (! edit.error_mode)
353 {
354 RenderColor(LIGHTRED);
355 DrawTagged(edit.highlight.type, edit.highlight.num);
356 }
357
358 if (edit.mode == OBJ_LINEDEFS && !edit.show_object_numbers)
359 {
360 const LineDef *L = LineDefs[edit.highlight.num];
361 DrawLineInfo(L->Start()->x(), L->Start()->y(), L->End()->x(), L->End()->y(), false);
362 }
363
364 RenderThickness(1);
365 }
366
367 if (edit.action == ACT_SELBOX)
368 SelboxDraw();
369
370 if (edit.action == ACT_DRAW_LINE)
371 DrawCurrentLine();
372 }
373
374
375 //
376 // draw the whole map, except for hilight/selection/selbox
377 //
DrawMap()378 void UI_Canvas::DrawMap()
379 {
380 RenderColor(FL_BLACK);
381 RenderRect(xx, yy, w(), h());
382
383 if (edit.sector_render_mode && ! edit.error_mode)
384 {
385 for (int n = 0 ; n < NumSectors ; n++)
386 RenderSector(n);
387 }
388
389 // draw the grid first since it's in the background
390 if (grid.shown)
391 {
392 if (grid_style == 0)
393 DrawGrid_Normal();
394 else
395 DrawGrid_Dotty();
396 }
397
398 if (Debugging)
399 DrawMapBounds();
400
401 DrawCamera();
402
403 if (edit.mode != OBJ_THINGS)
404 DrawThings();
405
406 if (grid.snap && grid_snap_indicator)
407 DrawSnapPoint();
408
409 DrawLinedefs();
410
411 if (edit.mode == OBJ_VERTICES)
412 DrawVertices();
413
414 if (edit.mode == OBJ_THINGS)
415 {
416 if (edit.thing_render_mode > 0)
417 {
418 DrawThings();
419 DrawThingSprites();
420 }
421 else
422 {
423 DrawThingBodies();
424 DrawThings();
425 }
426 }
427 }
428
429
430 //
431 // draw the grid in the background of the edit window
432 //
DrawGrid_Normal()433 void UI_Canvas::DrawGrid_Normal()
434 {
435 float pixels_1 = grid.step * grid.Scale;
436
437 if (pixels_1 < 1.6)
438 {
439 RenderColor(DarkerColor(DarkerColor(normal_main_col)));
440 RenderRect(xx, yy, w(), h());
441
442 DrawAxes(normal_axis_col);
443 return;
444 }
445
446
447 int flat_step = 64;
448
449 float pixels_2 = flat_step * grid.Scale;
450
451 Fl_Color flat_col = (grid.step < 64) ? normal_main_col : normal_flat_col;
452
453 if (pixels_2 < 2.2)
454 flat_col = DarkerColor(flat_col);
455
456 RenderColor(flat_col);
457
458 if (pixels_2 < 1.6)
459 {
460 RenderRect(xx, yy, w(), h());
461 }
462 else
463 {
464 int gx = floor(map_lx / flat_step) * flat_step;
465
466 for (; gx <= map_hx; gx += flat_step)
467 DrawMapLine(gx, map_ly, gx, map_hy);
468
469 int gy = floor(map_ly / flat_step) * flat_step;
470
471 for (; gy <= map_hy; gy += flat_step)
472 DrawMapLine(map_lx, gy, map_hx, gy);
473 }
474
475
476 Fl_Color main_col = (grid.step < 64) ? normal_small_col : normal_main_col;
477
478 float pixels_3 = grid.step * grid.Scale;
479
480 if (pixels_3 < 4.2)
481 main_col = DarkerColor(main_col);
482
483 RenderColor(main_col);
484
485 {
486 int gx = floor(map_lx / grid.step) * grid.step;
487
488 for (; gx <= map_hx; gx += grid.step)
489 if ((grid.step >= 64 || (gx & 63) != 0) && (gx != 0))
490 DrawMapLine(gx, map_ly, gx, map_hy);
491
492 int gy = floor(map_ly / grid.step) * grid.step;
493
494 for (; gy <= map_hy; gy += grid.step)
495 if ((grid.step >= 64 || (gy & 63) != 0) && (gy != 0))
496 DrawMapLine(map_lx, gy, map_hx, gy);
497 }
498
499
500 DrawAxes(normal_axis_col);
501 }
502
503
DrawGrid_Dotty()504 void UI_Canvas::DrawGrid_Dotty()
505 {
506 int grid_step_1 = 1 * grid.step; // Map units between dots
507 int grid_step_2 = 8 * grid_step_1; // Map units between dim lines
508 int grid_step_3 = 8 * grid_step_2; // Map units between bright lines
509
510 float pixels_1 = grid.step * grid.Scale;
511
512
513 if (pixels_1 < 1.6)
514 {
515 RenderColor(DarkerColor(DarkerColor(dotty_point_col)));
516 RenderRect(xx, yy, w(), h());
517
518 DrawAxes(dotty_axis_col);
519 return;
520 }
521
522
523 RenderColor(dotty_major_col);
524 {
525 int gx = floor(map_lx / grid_step_3) * grid_step_3;
526
527 for (; gx <= map_hx; gx += grid_step_3)
528 DrawMapLine(gx, map_ly-2, gx, map_hy+2);
529
530 int gy = floor(map_ly / grid_step_3) * grid_step_3;
531
532 for (; gy <= map_hy; gy += grid_step_3)
533 DrawMapLine(map_lx, gy, map_hx, gy);
534 }
535
536
537 DrawAxes(dotty_axis_col);
538
539
540 RenderColor(dotty_minor_col);
541 {
542 int gx = floor(map_lx / grid_step_2) * grid_step_2;
543
544 for (; gx <= map_hx; gx += grid_step_2)
545 if (gx % grid_step_3 != 0)
546 DrawMapLine(gx, map_ly, gx, map_hy);
547
548 int gy = floor(map_ly / grid_step_2) * grid_step_2;
549
550 for (; gy <= map_hy; gy += grid_step_2)
551 if (gy % grid_step_3 != 0)
552 DrawMapLine(map_lx, gy, map_hx, gy);
553 }
554
555
556 if (pixels_1 < 4.02)
557 RenderColor(DarkerColor(dotty_point_col));
558 else
559 RenderColor(dotty_point_col);
560
561 {
562 int gx = floor(map_lx / grid_step_1) * grid_step_1;
563 int gy = floor(map_ly / grid_step_1) * grid_step_1;
564
565 for (int ny = gy; ny <= map_hy; ny += grid_step_1)
566 for (int nx = gx; nx <= map_hx; nx += grid_step_1)
567 {
568 int sx = SCREENX(nx);
569 int sy = SCREENY(ny);
570
571 if (pixels_1 < 24.1)
572 RenderRect(sx, sy, 1, 1);
573 else
574 RenderRect(sx, sy, 2, 2);
575 }
576 }
577 }
578
579
DrawAxes(Fl_Color col)580 void UI_Canvas::DrawAxes(Fl_Color col)
581 {
582 RenderColor(col);
583
584 DrawMapLine(0, map_ly, 0, map_hy);
585 DrawMapLine(map_lx, 0, map_hx, 0);
586 }
587
588
DrawMapBounds()589 void UI_Canvas::DrawMapBounds()
590 {
591 RenderColor(FL_RED);
592
593 DrawMapLine(Map_bound_x1, Map_bound_y1, Map_bound_x2, Map_bound_y1);
594 DrawMapLine(Map_bound_x1, Map_bound_y2, Map_bound_x2, Map_bound_y2);
595
596 DrawMapLine(Map_bound_x1, Map_bound_y1, Map_bound_x1, Map_bound_y2);
597 DrawMapLine(Map_bound_x2, Map_bound_y1, Map_bound_x2, Map_bound_y2);
598 }
599
600
601 //
602 // the apparent radius of a vertex, in pixels
603 //
vertex_radius(double scale)604 int vertex_radius(double scale)
605 {
606 int r = 6 * (0.26 + scale / 2);
607
608 if (r > 12) r = 12;
609
610 return r;
611 }
612
613
614
615 //
616 // draw the vertices, and possibly their numbers
617 //
DrawVertex(double map_x,double map_y,int r)618 void UI_Canvas::DrawVertex(double map_x, double map_y, int r)
619 {
620 int scrx = SCREENX(map_x);
621 int scry = SCREENY(map_y);
622
623 // BLOBBY TEST
624 #if 0
625 RenderLine(scrx - 1, scry - 2, scrx + 1, scry - 2);
626 RenderLine(scrx - 2, scry - 1, scrx + 2, scry - 1);
627 RenderLine(scrx - 2, scry + 0, scrx + 2, scry + 0);
628 RenderLine(scrx - 2, scry + 1, scrx + 2, scry + 1);
629 RenderLine(scrx - 1, scry + 2, scrx + 1, scry + 2);
630 #else
631 RenderLine(scrx - r, scry - r, scrx + r, scry + r);
632 RenderLine(scrx + r, scry - r, scrx - r, scry + r);
633
634 RenderLine(scrx - 1, scry, scrx + 1, scry);
635 RenderLine(scrx, scry - 1, scrx, scry + 1);
636 #endif
637 }
638
639
DrawVertices()640 void UI_Canvas::DrawVertices()
641 {
642 const int r = vertex_radius(grid.Scale);
643
644 RenderColor(FL_GREEN);
645
646 for (int n = 0 ; n < NumVertices ; n++)
647 {
648 double x = Vertices[n]->x();
649 double y = Vertices[n]->y();
650
651 if (Vis(x, y, r))
652 {
653 DrawVertex(x, y, r);
654 }
655 }
656
657 if (edit.show_object_numbers)
658 {
659 for (int n = 0 ; n < NumVertices ; n++)
660 {
661 double x = Vertices[n]->x();
662 double y = Vertices[n]->y();
663
664 if (! Vis(x, y, r))
665 continue;
666
667 int sx = SCREENX(x) + r * 3;
668 int sy = SCREENY(y) + r * 3;
669
670 DrawNumber(sx, sy, n);
671 }
672 }
673 }
674
675
676 //
677 // draw all the linedefs
678 //
DrawLinedefs()679 void UI_Canvas::DrawLinedefs()
680 {
681 for (int n = 0 ; n < NumLineDefs ; n++)
682 {
683 const LineDef *L = LineDefs[n];
684
685 double x1 = L->Start()->x();
686 double y1 = L->Start()->y();
687 double x2 = L->End ()->x();
688 double y2 = L->End ()->y();
689
690 if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
691 continue;
692
693 bool one_sided = (! L->Left());
694
695 Fl_Color col = LIGHTGREY;
696
697 // 'p' for plain, 'k' for knobbly, 's' for split
698 char line_kind = 'p';
699
700 switch (edit.mode)
701 {
702 case OBJ_VERTICES:
703 {
704 if (n == edit.split_line.num)
705 col = HI_AND_SEL_COL;
706 else if (edit.error_mode)
707 col = LIGHTGREY;
708 else if (L->right < 0)
709 col = RED;
710 else if (one_sided)
711 col = WHITE;
712
713 if (n == edit.split_line.num)
714 line_kind = 's';
715 else
716 line_kind = 'k';
717
718 // show info of last four added lines
719 if (n != edit.split_line.num && n >= (NumLineDefs - 4) &&
720 !edit.show_object_numbers)
721 {
722 DrawLineInfo(x1, y1, x2, y2, false);
723 }
724 }
725 break;
726
727 case OBJ_LINEDEFS:
728 {
729 if (edit.error_mode)
730 col = LIGHTGREY;
731 else if (! L->Right()) // no first sidedef?
732 col = RED;
733 else if (L->type != 0)
734 {
735 if (L->tag != 0)
736 col = LIGHTMAGENTA;
737 else
738 col = LIGHTGREEN;
739 }
740 else if (one_sided)
741 col = WHITE;
742 else if (L->flags & MLF_Blocking)
743 col = FL_CYAN;
744
745 line_kind = 'k';
746 }
747 break;
748
749 case OBJ_SECTORS:
750 {
751 int sd1 = L->right;
752 int sd2 = L->left;
753
754 int s1 = (sd1 < 0) ? NIL_OBJ : SideDefs[sd1]->sector;
755 int s2 = (sd2 < 0) ? NIL_OBJ : SideDefs[sd2]->sector;
756
757 if (edit.error_mode)
758 col = LIGHTGREY;
759 else if (sd1 < 0)
760 col = RED;
761 else if (edit.sector_render_mode == SREND_SoundProp)
762 {
763 if (L->flags & MLF_SoundBlock)
764 col = FL_MAGENTA;
765 else if (one_sided)
766 col = WHITE;
767 }
768 else
769 {
770 bool have_tag = false;
771 bool have_type = false;
772
773 if (Sectors[s1]->tag != 0)
774 have_tag = true;
775 if (Sectors[s1]->type != 0)
776 have_type = true;
777
778 if (s2 >= 0)
779 {
780 if (Sectors[s2]->tag != 0)
781 have_tag = true;
782
783 if (Sectors[s2]->type != 0)
784 have_type = true;
785 }
786
787 if (have_tag && have_type)
788 col = SECTOR_TAGTYPE;
789 else if (have_tag)
790 col = SECTOR_TAG;
791 else if (have_type)
792 col = SECTOR_TYPE;
793 else if (one_sided)
794 col = WHITE;
795 }
796
797 if (edit.show_object_numbers)
798 {
799 if (s1 != NIL_OBJ)
800 DrawSectorNum(x1, y1, x2, y2, SIDE_RIGHT, s1);
801
802 if (s2 != NIL_OBJ)
803 DrawSectorNum(x1, y1, x2, y2, SIDE_LEFT, s2);
804 }
805 }
806 break;
807
808 // OBJ_THINGS
809 default:
810 {
811 if (one_sided && ! edit.error_mode)
812 col = WHITE;
813 }
814 break;
815 }
816
817 RenderColor(col);
818
819 switch (line_kind)
820 {
821 case 'p':
822 DrawMapLine(x1, y1, x2, y2);
823 break;
824
825 case 'k':
826 DrawKnobbyLine(x1, y1, x2, y2);
827 break;
828
829 case 's':
830 DrawSplitLine(x1, y1, x2, y2);
831 break;
832 }
833 }
834
835 // draw the linedef numbers
836 if (edit.mode == OBJ_LINEDEFS && edit.show_object_numbers)
837 {
838 for (int n = 0 ; n < NumLineDefs ; n++)
839 {
840 double x1 = LineDefs[n]->Start()->x();
841 double y1 = LineDefs[n]->Start()->y();
842 double x2 = LineDefs[n]->End ()->x();
843 double y2 = LineDefs[n]->End ()->y();
844
845 if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
846 continue;
847
848 DrawLineNumber(x1, y1, x2, y2, 0, n);
849 }
850 }
851 }
852
853
DrawThing(double x,double y,int r,int angle,bool big_arrow)854 void UI_Canvas::DrawThing(double x, double y, int r, int angle, bool big_arrow)
855 {
856 DrawMapLine(x-r, y-r, x-r, y+r);
857 DrawMapLine(x-r, y+r, x+r, y+r);
858 DrawMapLine(x+r, y+r, x+r, y-r);
859 DrawMapLine(x+r, y-r, x-r, y-r);
860
861 if (big_arrow)
862 {
863 DrawMapArrow(x, y, r * 2, angle);
864 }
865 else
866 {
867 int dir = angle_to_direction(angle);
868
869 static const short xsign[] = { 1, 1, 0, -1, -1, -1, 0, 1, 0 };
870 static const short ysign[] = { 0, 1, 1, 1, 0, -1, -1, -1, 0 };
871
872 int corner_x = r * xsign[dir];
873 int corner_y = r * ysign[dir];
874
875 DrawMapLine(x, y, x + corner_x, y + corner_y);
876 }
877 }
878
879
880 //
881 // draw things as squares (outlines)
882 //
DrawThings()883 void UI_Canvas::DrawThings()
884 {
885 if (edit.mode != OBJ_THINGS)
886 RenderColor(DARKGREY);
887 else if (edit.error_mode)
888 RenderColor(LIGHTGREY);
889
890 for (int n = 0 ; n < NumThings ; n++)
891 {
892 double x = Things[n]->x();
893 double y = Things[n]->y();
894
895 if (! Vis(x, y, MAX_RADIUS))
896 continue;
897
898 const thingtype_t *info = M_GetThingType(Things[n]->type);
899
900 if (edit.mode == OBJ_THINGS && !edit.error_mode)
901 {
902 Fl_Color col = (Fl_Color)info->color;
903 RenderColor(col);
904 }
905
906 int r = info->radius;
907
908 DrawThing(x, y, r, Things[n]->angle, false);
909 }
910
911 // draw the thing numbers
912 if (edit.mode == OBJ_THINGS && edit.show_object_numbers)
913 {
914 for (int n = 0 ; n < NumThings ; n++)
915 {
916 double x = Things[n]->x();
917 double y = Things[n]->y();
918
919 if (! Vis(x, y, MAX_RADIUS))
920 continue;
921
922 const thingtype_t *info = M_GetThingType(Things[n]->type);
923
924 x += info->radius + 8;
925 y += info->radius + 8;
926
927 DrawNumber(SCREENX(x), SCREENY(y), n);
928 }
929 }
930 }
931
932
933 //
934 // draw bodies of things (solid boxes, darker than the outline)
935 //
DrawThingBodies()936 void UI_Canvas::DrawThingBodies()
937 {
938 if (edit.error_mode)
939 return;
940
941 for (int n = 0 ; n < NumThings ; n++)
942 {
943 double x = Things[n]->x();
944 double y = Things[n]->y();
945
946 if (! Vis(x, y, MAX_RADIUS))
947 continue;
948
949 const thingtype_t *info = M_GetThingType(Things[n]->type);
950
951 Fl_Color col = (Fl_Color)info->color;
952 RenderColor(DarkerColor(DarkerColor(col)));
953
954 int r = info->radius;
955
956 int sx1 = SCREENX(x - r);
957 int sy1 = SCREENY(y + r);
958 int sx2 = SCREENX(x + r);
959 int sy2 = SCREENY(y - r);
960
961 RenderRect(sx1, sy1, sx2 - sx1 + 1, sy2 - sy1 + 1);
962 }
963 }
964
965
DrawThingSprites()966 void UI_Canvas::DrawThingSprites()
967 {
968 #ifndef NO_OPENGL
969 glEnable(GL_TEXTURE_2D);
970 glEnable(GL_ALPHA_TEST);
971
972 glAlphaFunc(GL_GREATER, 0.5);
973 #endif
974
975 for (int n = 0 ; n < NumThings ; n++)
976 {
977 double x = Things[n]->x();
978 double y = Things[n]->y();
979
980 if (! Vis(x, y, MAX_RADIUS))
981 continue;
982
983 const thingtype_t *info = M_GetThingType(Things[n]->type);
984 float scale = info->scale;
985
986 Img_c *sprite = W_GetSprite(Things[n]->type);
987
988 if (! sprite)
989 {
990 sprite = IM_UnknownSprite();
991 scale = 0.66;
992 }
993
994 int sx = SCREENX(x);
995 int sy = SCREENY(y);
996
997 RenderSprite(sx, sy, scale * grid.Scale, sprite);
998 }
999
1000 #ifndef NO_OPENGL
1001 glDisable(GL_ALPHA_TEST);
1002 glDisable(GL_TEXTURE_2D);
1003 #endif
1004 }
1005
1006
RenderSprite(int sx,int sy,float scale,Img_c * img)1007 void UI_Canvas::RenderSprite(int sx, int sy, float scale, Img_c *img)
1008 {
1009 int W = img->width();
1010 int H = img->height();
1011
1012 scale = scale * 0.5;
1013
1014 #ifdef NO_OPENGL
1015 // software rendering
1016
1017 int bx1 = sx + (int)floor(-W * scale) - rgb_x;
1018 int bx2 = sx + (int)ceil ( W * scale) - rgb_x;
1019
1020 int by1 = sy + (int)floor(-H * scale) - rgb_y;
1021 int by2 = sy + (int)ceil ( H * scale) - rgb_y;
1022
1023 // prevent division by zero
1024 if (bx2 <= bx1) bx2 = bx1 + 1;
1025 if (by2 <= by1) by2 = by1 + 1;
1026
1027 // clip to screen
1028 int rx1 = MAX(bx1, 0);
1029 int ry1 = MAX(by1, 0);
1030
1031 int rx2 = MIN(bx2, rgb_w) - 1;
1032 int ry2 = MIN(by2, rgb_h) - 1;
1033
1034 if (rx1 >= rx2 || ry1 >= ry2)
1035 return;
1036
1037 for (int ry = ry1 ; ry <= ry2 ; ry++)
1038 {
1039 byte *dest = rgb_buf + 3 * (rx1 + ry * rgb_w);
1040
1041 for (int rx = rx1 ; rx <= rx2 ; rx++, dest += 3)
1042 {
1043 int ix = W * (rx - bx1) / (bx2 - bx1);
1044 int iy = H * (ry - by1) / (by2 - by1);
1045
1046 ix = CLAMP(0, ix, W - 1);
1047 iy = CLAMP(0, iy, H - 1);
1048
1049 img_pixel_t pix = img->buf()[iy * W + ix];
1050
1051 if (pix != TRANS_PIXEL)
1052 {
1053 IM_DecodePixel(pix, dest[0], dest[1], dest[2]);
1054 }
1055 }
1056 }
1057
1058 #else // OpenGL
1059 int bx1 = sx + (int)floor(-W * scale);
1060 int bx2 = sx + (int)ceil ( W * scale);
1061
1062 int by1 = sy + (int)floor(-H * scale);
1063 int by2 = sy + (int)ceil ( H * scale);
1064
1065 // don't make too small
1066 if (bx2 <= bx1) bx2 = bx1 + 1;
1067 if (by2 <= by1) by2 = by1 + 1;
1068
1069 // bind the sprite image (upload it to OpenGL if needed)
1070 img->bind_gl();
1071
1072 // choose texture coords based on image size
1073 float tx1 = 0.0;
1074 float ty1 = 0.0;
1075 float tx2 = (float)img->width() / (float)RoundPOW2(img->width());
1076 float ty2 = (float)img->height() / (float)RoundPOW2(img->height());
1077
1078 glColor3f(1, 1, 1);
1079
1080 glBegin(GL_QUADS);
1081
1082 glTexCoord2f(tx1, ty1); glVertex2i(bx1, by1);
1083 glTexCoord2f(tx1, ty2); glVertex2i(bx1, by2);
1084 glTexCoord2f(tx2, ty2); glVertex2i(bx2, by2);
1085 glTexCoord2f(tx2, ty1); glVertex2i(bx2, by1);
1086
1087 glEnd();
1088 #endif
1089 }
1090
1091
DrawSectorNum(int mx1,int my1,int mx2,int my2,int side,int n)1092 void UI_Canvas::DrawSectorNum(int mx1, int my1, int mx2, int my2, int side, int n)
1093 {
1094 // only draw a number for the first linedef actually visible
1095 if (seen_sectors.get(n))
1096 return;
1097
1098 seen_sectors.set(n);
1099
1100 DrawLineNumber(mx1, my1, mx2, my2, side, n);
1101 }
1102
1103
DrawLineNumber(int mx1,int my1,int mx2,int my2,int side,int n)1104 void UI_Canvas::DrawLineNumber(int mx1, int my1, int mx2, int my2, int side, int n)
1105 {
1106 int x1 = SCREENX(mx1);
1107 int y1 = SCREENY(my1);
1108 int x2 = SCREENX(mx2);
1109 int y2 = SCREENY(my2);
1110
1111 int sx = (x1 + x2) / 2;
1112 int sy = (y1 + y2) / 2;
1113
1114 // normally draw line numbers on back of line
1115 int want_len = -16 * CLAMP(0.25, grid.Scale, 1.0);
1116
1117 // for sectors, draw closer and on sector side
1118 if (side != 0)
1119 {
1120 want_len = 2 + 12 * CLAMP(0.25, grid.Scale, 1.0);
1121
1122 if (side == SIDE_LEFT)
1123 want_len = -want_len;
1124 }
1125
1126 sx += NORMALX(want_len*2, x2 - x1, y2 - y1);
1127 sy += NORMALY(want_len, x2 - x1, y2 - y1);
1128
1129 DrawNumber(sx, sy, n);
1130 }
1131
1132
DrawLineInfo(double map_x1,double map_y1,double map_x2,double map_y2,bool force_ratio)1133 void UI_Canvas::DrawLineInfo(double map_x1, double map_y1, double map_x2, double map_y2,
1134 bool force_ratio)
1135 {
1136 line_info_mode_e info = (line_info_mode_e)highlight_line_info;
1137
1138 if (info == LINFO_Nothing)
1139 return;
1140
1141 int x1 = SCREENX(map_x1);
1142 int y1 = SCREENY(map_y1);
1143 int x2 = SCREENX(map_x2);
1144 int y2 = SCREENY(map_y2);
1145
1146 int sx = (x1 + x2) / 2;
1147 int sy = (y1 + y2) / 2;
1148
1149 // if midpoint is off the screen, try to find a better one
1150 double mx = (map_x1 + map_x2) / 2.0;
1151 double my = (map_y1 + map_y2) / 2.0;
1152
1153 if (mx < map_lx + 4 || mx > map_hx - 4 ||
1154 my < map_ly + 4 || my > map_hy - 4)
1155 {
1156 double best_dist = 9e9;
1157
1158 for (double p = 0.1 ; p < 0.91 ; p += 0.1)
1159 {
1160 mx = map_x1 + (map_x2 - map_x1) * p;
1161 my = map_y1 + (map_y2 - map_y1) * p;
1162
1163 double dist_x = mx * 2.0 - (map_lx + map_hx);
1164 double dist_y = my * 2.0 - (map_ly + map_hy);
1165 double dist = hypot(dist_x, dist_y);
1166
1167 if (dist < best_dist)
1168 {
1169 sx = x1 + (x2 - x1) * p;
1170 sy = y1 + (y2 - y1) * p;
1171 best_dist = dist;
1172 }
1173 }
1174 }
1175
1176 // back of line is best place, no knob getting in the way
1177 int want_len = -16 * CLAMP(0.25, grid.Scale, 1.0);
1178
1179 sx += NORMALX(want_len*2, x2 - x1, y2 - y1);
1180 sy += NORMALY(want_len, x2 - x1, y2 - y1);
1181
1182 /* length */
1183
1184 fixcoord_t idx = MakeValidCoord(map_x2) - MakeValidCoord(map_x1);
1185 fixcoord_t idy = MakeValidCoord(map_y2) - MakeValidCoord(map_y1);
1186
1187 if (info == LINFO_Length || info >= LINFO_Length_Angle)
1188 {
1189 double length = hypot(FROM_COORD(idx), FROM_COORD(idy));
1190
1191 if (length > 0.1)
1192 {
1193 char buffer[64];
1194 snprintf(buffer, sizeof(buffer), "%1.1f", length);
1195
1196 RenderNumString(sx, sy, buffer);
1197
1198 sy = sy - cur_font;
1199 }
1200 }
1201
1202 /* angle */
1203
1204 if (info == LINFO_Angle || info == LINFO_Length_Angle)
1205 {
1206 double dx = FROM_COORD(idx);
1207 double dy = FROM_COORD(idy);
1208
1209 int degrees = (int)round(atan2(dy, dx) * 180.0 / M_PI);
1210 if (degrees < 0)
1211 degrees += 360;
1212
1213 char buffer[64];
1214 snprintf(buffer, sizeof(buffer), "%d^", degrees);
1215
1216 RenderNumString(sx, sy, buffer);
1217 }
1218
1219 /* ratio */
1220
1221 if (info == LINFO_Ratio || info == LINFO_Length_Ratio)
1222 {
1223 if (idx != 0 && idy != 0)
1224 {
1225 std::string ratio_name = LD_RatioName(idx, idy, true);
1226
1227 RenderNumString(sx, sy, ratio_name.c_str());
1228 }
1229 }
1230 }
1231
1232
1233 //
1234 // draw a number centered at screen coordinate (x, y)
1235 //
DrawNumber(int x,int y,int num)1236 void UI_Canvas::DrawNumber(int x, int y, int num)
1237 {
1238 char buffer[64];
1239 snprintf(buffer, sizeof(buffer), "%d", num);
1240
1241 #if 0 /* DEBUG */
1242 RenderColor(FL_RED);
1243 RenderRect(x - 1, y - 1, 3, 3);
1244 return;
1245 #endif
1246
1247 RenderNumString(x, y, buffer);
1248 }
1249
1250
CheckGridSnap()1251 void UI_Canvas::CheckGridSnap()
1252 {
1253 if (!grid.snap || !grid_snap_indicator)
1254 return;
1255
1256 double new_snap_x = grid.SnapX(edit.map_x);
1257 double new_snap_y = grid.SnapY(edit.map_y);
1258
1259 if (snap_x == new_snap_x && snap_y == new_snap_y)
1260 return;
1261
1262 snap_x = new_snap_x;
1263 snap_y = new_snap_y;
1264
1265 redraw();
1266 }
1267
1268
UpdateHighlight()1269 void UI_Canvas::UpdateHighlight()
1270 {
1271 bool changes = false;
1272
1273 if (! (last_highlight == edit.highlight))
1274 {
1275 last_highlight = edit.highlight;
1276 changes = true;
1277 }
1278
1279 int new_ld = edit.split_line.valid() ? edit.split_line.num : -1;
1280
1281 if (! (last_splitter == new_ld && last_split_x == edit.split_x && last_split_y == edit.split_y))
1282 {
1283 last_splitter = new_ld;
1284 last_split_x = edit.split_x;
1285 last_split_y = edit.split_y;
1286 changes = true;
1287 }
1288
1289 if (changes)
1290 redraw();
1291 }
1292
1293
1294 //
1295 // draw the given object in highlight color
1296 //
DrawHighlight(int objtype,int objnum,bool skip_lines,double dx,double dy)1297 void UI_Canvas::DrawHighlight(int objtype, int objnum, bool skip_lines,
1298 double dx, double dy)
1299 {
1300 // color and line thickness have been set by caller
1301
1302 // fprintf(stderr, "DrawHighlight: %d\n", objnum);
1303
1304 switch (objtype)
1305 {
1306 case OBJ_THINGS:
1307 {
1308 double x = dx + Things[objnum]->x();
1309 double y = dy + Things[objnum]->y();
1310
1311 if (! Vis(x, y, MAX_RADIUS))
1312 break;
1313
1314 const thingtype_t *info = M_GetThingType(Things[objnum]->type);
1315
1316 int r = info->radius;
1317
1318 if (edit.error_mode)
1319 DrawThing(x, y, r, Things[objnum]->angle, false /* big_arrow */);
1320
1321 r += r / 10 + 4;
1322
1323 DrawThing(x, y, r, Things[objnum]->angle, true);
1324 }
1325 break;
1326
1327 case OBJ_LINEDEFS:
1328 {
1329 double x1 = dx + LineDefs[objnum]->Start()->x();
1330 double y1 = dy + LineDefs[objnum]->Start()->y();
1331 double x2 = dx + LineDefs[objnum]->End ()->x();
1332 double y2 = dy + LineDefs[objnum]->End ()->y();
1333
1334 if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
1335 break;
1336
1337 DrawMapVector(x1, y1, x2, y2);
1338 }
1339 break;
1340
1341 case OBJ_VERTICES:
1342 {
1343 double x = dx + Vertices[objnum]->x();
1344 double y = dy + Vertices[objnum]->y();
1345
1346 int vert_r = vertex_radius(grid.Scale);
1347
1348 if (! Vis(x, y, vert_r))
1349 break;
1350
1351 DrawVertex(x, y, vert_r);
1352
1353 int r = vert_r * 3 / 2;
1354
1355 int sx1 = SCREENX(x) - r;
1356 int sy1 = SCREENY(y) - r;
1357 int sx2 = SCREENX(x) + r;
1358 int sy2 = SCREENY(y) + r;
1359
1360 RenderLine(sx1, sy1, sx2, sy1);
1361 RenderLine(sx2, sy1, sx2, sy2);
1362 RenderLine(sx2, sy2, sx1, sy2);
1363 RenderLine(sx1, sy2, sx1, sy1);
1364 }
1365 break;
1366
1367 case OBJ_SECTORS:
1368 {
1369 for (int n = 0 ; n < NumLineDefs ; n++)
1370 {
1371 const LineDef *L = LineDefs[n];
1372
1373 if (! L->TouchesSector(objnum))
1374 continue;
1375
1376 bool reverse = false;
1377
1378 // skip lines if both sides are in the selection
1379 if (skip_lines && L->TwoSided())
1380 {
1381 int sec1 = L->Right()->sector;
1382 int sec2 = L->Left ()->sector;
1383
1384 if ((sec1 == objnum || edit.Selected->get(sec1)) &&
1385 (sec2 == objnum || edit.Selected->get(sec2)))
1386 continue;
1387
1388 if (sec1 != objnum)
1389 reverse = true;
1390 }
1391
1392 double x1 = dx + L->Start()->x();
1393 double y1 = dy + L->Start()->y();
1394 double x2 = dx + L->End ()->x();
1395 double y2 = dy + L->End ()->y();
1396
1397 if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
1398 continue;
1399
1400 if (skip_lines)
1401 DrawKnobbyLine(x1, y1, x2, y2, reverse);
1402 else
1403 DrawMapLine(x1, y1, x2, y2);
1404 }
1405 }
1406 break;
1407 }
1408 }
1409
1410
DrawHighlightTransform(int objtype,int objnum)1411 void UI_Canvas::DrawHighlightTransform(int objtype, int objnum)
1412 {
1413 // color and line thickness have been set by caller
1414
1415 switch (objtype)
1416 {
1417 case OBJ_THINGS:
1418 {
1419 double x = Things[objnum]->x();
1420 double y = Things[objnum]->y();
1421
1422 edit.trans_param.Apply(&x, &y);
1423
1424 if (! Vis(x, y, MAX_RADIUS))
1425 break;
1426
1427 const thingtype_t *info = M_GetThingType(Things[objnum]->type);
1428
1429 int r = info->radius;
1430
1431 DrawThing(x, y, r * 3 / 2, Things[objnum]->angle, true);
1432 }
1433 break;
1434
1435 case OBJ_VERTICES:
1436 {
1437 double x = Vertices[objnum]->x();
1438 double y = Vertices[objnum]->y();
1439
1440 int vert_r = vertex_radius(grid.Scale);
1441
1442 edit.trans_param.Apply(&x, &y);
1443
1444 if (! Vis(x, y, vert_r))
1445 break;
1446
1447 DrawVertex(x, y, vert_r);
1448
1449 int r = vert_r * 3 / 2;
1450
1451 int sx1 = SCREENX(x) - r;
1452 int sy1 = SCREENY(y) - r;
1453 int sx2 = SCREENX(x) + r;
1454 int sy2 = SCREENY(y) + r;
1455
1456 RenderLine(sx1, sy1, sx2, sy1);
1457 RenderLine(sx2, sy1, sx2, sy2);
1458 RenderLine(sx2, sy2, sx1, sy2);
1459 RenderLine(sx1, sy2, sx1, sy1);
1460 }
1461 break;
1462
1463 case OBJ_LINEDEFS:
1464 {
1465 double x1 = LineDefs[objnum]->Start()->x();
1466 double y1 = LineDefs[objnum]->Start()->y();
1467 double x2 = LineDefs[objnum]->End ()->x();
1468 double y2 = LineDefs[objnum]->End ()->y();
1469
1470 edit.trans_param.Apply(&x1, &y1);
1471 edit.trans_param.Apply(&x2, &y2);
1472
1473 if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
1474 break;
1475
1476 DrawMapVector(x1, y1, x2, y2);
1477 }
1478 break;
1479
1480 case OBJ_SECTORS:
1481 {
1482 for (int n = 0 ; n < NumLineDefs ; n++)
1483 {
1484 if (! LineDefs[n]->TouchesSector(objnum))
1485 continue;
1486
1487 double x1 = LineDefs[n]->Start()->x();
1488 double y1 = LineDefs[n]->Start()->y();
1489 double x2 = LineDefs[n]->End ()->x();
1490 double y2 = LineDefs[n]->End ()->y();
1491
1492 edit.trans_param.Apply(&x1, &y1);
1493 edit.trans_param.Apply(&x2, &y2);
1494
1495 if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
1496 continue;
1497
1498 DrawMapLine(x1, y1, x2, y2);
1499 }
1500 }
1501 break;
1502 }
1503 }
1504
1505
DrawTagged(int objtype,int objnum)1506 void UI_Canvas::DrawTagged(int objtype, int objnum)
1507 {
1508 // color has been set by caller
1509
1510 // handle tagged linedefs : show matching sector(s)
1511 if (objtype == OBJ_LINEDEFS && LineDefs[objnum]->tag > 0)
1512 {
1513 for (int m = 0 ; m < NumSectors ; m++)
1514 if (Sectors[m]->tag == LineDefs[objnum]->tag)
1515 DrawHighlight(OBJ_SECTORS, m);
1516 }
1517
1518 // handle tagged sectors : show matching line(s)
1519 if (objtype == OBJ_SECTORS && Sectors[objnum]->tag > 0)
1520 {
1521 for (int m = 0 ; m < NumLineDefs ; m++)
1522 if (LineDefs[m]->tag == Sectors[objnum]->tag)
1523 DrawHighlight(OBJ_LINEDEFS, m);
1524 }
1525 }
1526
1527
DrawSectorSelection(selection_c * list,double dx,double dy)1528 void UI_Canvas::DrawSectorSelection(selection_c *list, double dx, double dy)
1529 {
1530 // color and line thickness have been set by caller
1531
1532 for (int n = 0 ; n < NumLineDefs ; n++)
1533 {
1534 const LineDef *L = LineDefs[n];
1535
1536 double x1 = dx + L->Start()->x();
1537 double y1 = dy + L->Start()->y();
1538 double x2 = dx + L->End ()->x();
1539 double y2 = dy + L->End ()->y();
1540
1541 if (! Vis(MIN(x1,x2), MIN(y1,y2), MAX(x1,x2), MAX(y1,y2)))
1542 continue;
1543
1544 if (L->right < 0 && L->left < 0)
1545 continue;
1546
1547 int sec1 = -1;
1548 int sec2 = -1;
1549
1550 if (L->right >= 0) sec1 = L->Right()->sector;
1551 if (L->left >= 0) sec2 = L->Left() ->sector;
1552
1553 bool touches1 = (sec1 >= 0) && list->get(sec1);
1554 bool touches2 = (sec2 >= 0) && list->get(sec2);
1555
1556 if (! (touches1 || touches2))
1557 continue;
1558
1559 // skip lines if both sides are in the selection
1560 if (touches1 && touches2)
1561 continue;
1562
1563 bool reverse = !touches1;
1564
1565 DrawKnobbyLine(x1, y1, x2, y2, reverse);
1566 }
1567 }
1568
1569 //
1570 // draw selected objects in light blue
1571 //
DrawSelection(selection_c * list)1572 void UI_Canvas::DrawSelection(selection_c * list)
1573 {
1574 if (! list || list->empty())
1575 return;
1576
1577 if (edit.action == ACT_TRANSFORM)
1578 {
1579 RenderColor(SEL_COL);
1580
1581 if (list->what_type() == OBJ_LINEDEFS || list->what_type() == OBJ_SECTORS)
1582 RenderThickness(2);
1583
1584 for (sel_iter_c it(list) ; !it.done() ; it.next())
1585 {
1586 DrawHighlightTransform(list->what_type(), *it);
1587 }
1588
1589 RenderThickness(1);
1590 return;
1591 }
1592
1593 double dx = 0;
1594 double dy = 0;
1595
1596 if (edit.action == ACT_DRAG && edit.dragged.is_nil())
1597 {
1598 DragDelta(&dx, &dy);
1599 }
1600
1601 RenderColor(edit.error_mode ? FL_RED : SEL_COL);
1602
1603 if (list->what_type() == OBJ_LINEDEFS || list->what_type() == OBJ_SECTORS)
1604 RenderThickness(2);
1605
1606 // special case when we have many sectors
1607 if (list->what_type() == OBJ_SECTORS && list->count_obj() > MAX_STORE_SEL)
1608 {
1609 DrawSectorSelection(list, dx, dy);
1610 }
1611 else
1612 {
1613 for (sel_iter_c it(list) ; !it.done() ; it.next())
1614 {
1615 DrawHighlight(list->what_type(), *it, true /* skip_lines */, dx, dy);
1616 }
1617 }
1618
1619 if (! edit.error_mode && dx == 0 && dy == 0)
1620 {
1621 RenderColor(LIGHTRED);
1622
1623 for (sel_iter_c it(list) ; !it.done() ; it.next())
1624 {
1625 DrawTagged(list->what_type(), *it);
1626 }
1627 }
1628
1629 RenderThickness(1);
1630 }
1631
1632
1633 //
1634 // draw a plain line at the given map coords
1635 //
DrawMapLine(double map_x1,double map_y1,double map_x2,double map_y2)1636 void UI_Canvas::DrawMapLine(double map_x1, double map_y1, double map_x2, double map_y2)
1637 {
1638 RenderLine(SCREENX(map_x1), SCREENY(map_y1),
1639 SCREENX(map_x2), SCREENY(map_y2));
1640 }
1641
1642
1643 //
1644 // draw a line with a "knob" showing the right (front) side
1645 //
DrawKnobbyLine(double map_x1,double map_y1,double map_x2,double map_y2,bool reverse)1646 void UI_Canvas::DrawKnobbyLine(double map_x1, double map_y1, double map_x2, double map_y2,
1647 bool reverse)
1648 {
1649 // color and thickness has been set by caller
1650
1651 int x1 = SCREENX(map_x1);
1652 int y1 = SCREENY(map_y1);
1653 int x2 = SCREENX(map_x2);
1654 int y2 = SCREENY(map_y2);
1655
1656 if (reverse)
1657 {
1658 std::swap(x1, x2);
1659 std::swap(y1, y2);
1660 }
1661
1662 RenderLine(x1, y1, x2, y2);
1663
1664 // indicate direction of line
1665 int mx = (x1 + x2) / 2;
1666 int my = (y1 + y2) / 2;
1667
1668 int len = MAX(abs(x2 - x1), abs(y2 - y1));
1669 int want_len = MIN(12, len / 5);
1670
1671 int dx = NORMALX(want_len, x2 - x1, y2 - y1);
1672 int dy = NORMALY(want_len, x2 - x1, y2 - y1);
1673
1674 if (! (dx == 0 && dy == 0))
1675 {
1676 RenderLine(mx, my, mx + dx, my + dy);
1677 }
1678 }
1679
1680
DrawSplitPoint(double map_x,double map_y)1681 void UI_Canvas::DrawSplitPoint(double map_x, double map_y)
1682 {
1683 int sx = SCREENX(map_x);
1684 int sy = SCREENY(map_y);
1685
1686 int size = (grid.Scale >= 5.0) ? 9 : (grid.Scale >= 1.0) ? 7 : 5;
1687
1688 // color set by caller
1689
1690 #ifdef NO_OPENGL
1691 RenderRect(sx - size/2, sy - size/2, size, size);
1692 #else
1693 glPointSize(size);
1694
1695 glBegin(GL_POINTS);
1696 glVertex2i(sx, sy);
1697 glEnd();
1698
1699 glPointSize(1.0);
1700 #endif
1701 }
1702
1703
DrawSplitLine(double map_x1,double map_y1,double map_x2,double map_y2)1704 void UI_Canvas::DrawSplitLine(double map_x1, double map_y1, double map_x2, double map_y2)
1705 {
1706 // show how and where the line will be split
1707
1708 // color has been set by caller
1709
1710 int scr_x1 = SCREENX(map_x1);
1711 int scr_y1 = SCREENY(map_y1);
1712 int scr_x2 = SCREENX(map_x2);
1713 int scr_y2 = SCREENY(map_y2);
1714
1715 int scr_mx = SCREENX(edit.split_x);
1716 int scr_my = SCREENY(edit.split_y);
1717
1718 RenderLine(scr_x1, scr_y1, scr_mx, scr_my);
1719 RenderLine(scr_x2, scr_y2, scr_mx, scr_my);
1720
1721 if (! edit.show_object_numbers)
1722 {
1723 double len1 = hypot(map_x1 - edit.split_x, map_y1 - edit.split_y);
1724 double len2 = hypot(map_x2 - edit.split_x, map_y2 - edit.split_y);
1725
1726 DrawLineNumber(map_x1, map_y1, edit.split_x, edit.split_y, 0, I_ROUND(len1));
1727 DrawLineNumber(map_x2, map_y2, edit.split_x, edit.split_y, 0, I_ROUND(len2));
1728 }
1729
1730 RenderColor(HI_AND_SEL_COL);
1731
1732 DrawSplitPoint(edit.split_x, edit.split_y);
1733 }
1734
1735
1736 //
1737 // draw a bolder linedef with an arrow on the end
1738 // (used for highlighted / selected lines)
1739 //
DrawMapVector(double map_x1,double map_y1,double map_x2,double map_y2)1740 void UI_Canvas::DrawMapVector(double map_x1, double map_y1, double map_x2, double map_y2)
1741 {
1742 int x1 = SCREENX(map_x1);
1743 int y1 = SCREENY(map_y1);
1744 int x2 = SCREENX(map_x2);
1745 int y2 = SCREENY(map_y2);
1746
1747 RenderLine(x1, y1, x2, y2);
1748
1749 // knob
1750 int mx = (x1 + x2) / 2;
1751 int my = (y1 + y2) / 2;
1752
1753 int klen = MAX(abs(x2 - x1), abs(y2 - y1));
1754 int want_len = CLAMP(12, klen / 4, 40);
1755
1756 int kx = NORMALX(want_len, x2 - x1, y2 - y1);
1757 int ky = NORMALY(want_len, x2 - x1, y2 - y1);
1758
1759 RenderLine(mx, my, mx + kx, my + ky);
1760
1761 // arrow
1762 double r2 = hypot((double) (x1 - x2), (double) (y1 - y2));
1763
1764 if (r2 < 1.0)
1765 r2 = 1.0;
1766
1767 double len = CLAMP(6.0, r2 / 10.0, 24.0);
1768
1769 int dx = (int) (len * (x1 - x2) / r2);
1770 int dy = (int) (len * (y1 - y2) / r2);
1771
1772 x1 = x2 + 2 * dx;
1773 y1 = y2 + 2 * dy;
1774
1775 RenderLine(x1 - dy, y1 + dx, x2, y2);
1776 RenderLine(x1 + dy, y1 - dx, x2, y2);
1777 }
1778
1779
1780 //
1781 // draw an arrow
1782 //
DrawMapArrow(double map_x1,double map_y1,int r,int angle)1783 void UI_Canvas::DrawMapArrow(double map_x1, double map_y1, int r, int angle)
1784 {
1785 float dx = r * cos(angle * M_PI / 180.0);
1786 float dy = r * sin(angle * M_PI / 180.0);
1787
1788 float map_x2 = map_x1 + dx;
1789 float map_y2 = map_y1 + dy;
1790
1791 DrawMapLine(map_x1, map_y1, map_x2, map_y2);
1792
1793 // arrow head
1794 float x3 = map_x2 - dx * 0.3 + dy * 0.3;
1795 float y3 = map_y2 - dy * 0.3 - dx * 0.3;
1796
1797 DrawMapLine(map_x2, map_y2, x3, y3);
1798
1799 x3 = map_x2 - dx * 0.3 - dy * 0.3;
1800 y3 = map_y2 - dy * 0.3 + dx * 0.3;
1801
1802 DrawMapLine(map_x2, map_y2, x3, y3);
1803 }
1804
1805
DrawCamera()1806 void UI_Canvas::DrawCamera()
1807 {
1808 double map_x, map_y;
1809 float angle;
1810
1811 Render3D_GetCameraPos(&map_x, &map_y, &angle);
1812
1813 float mx = map_x;
1814 float my = map_y;
1815
1816 float r = 40.0 / sqrt(grid.Scale);
1817
1818 float dx = r * cos(angle * M_PI / 180.0);
1819 float dy = r * sin(angle * M_PI / 180.0);
1820
1821 // arrow body
1822 float x1 = mx - dx;
1823 float y1 = my - dy;
1824
1825 float x2 = mx + dx;
1826 float y2 = my + dy;
1827
1828 RenderColor(CAMERA_COLOR);
1829 RenderThickness(1);
1830
1831 DrawMapLine(x1, y1, x2, y2);
1832
1833 // arrow head
1834 float x3 = x2 - dx * 0.6 + dy * 0.4;
1835 float y3 = y2 - dy * 0.6 - dx * 0.4;
1836
1837 DrawMapLine(x2, y2, x3, y3);
1838
1839 x3 = x2 - dx * 0.6 - dy * 0.4;
1840 y3 = y2 - dy * 0.6 + dx * 0.4;
1841
1842 DrawMapLine(x2, y2, x3, y3);
1843
1844 // notches on body
1845 DrawMapLine(mx - dy * 0.4, my + dx * 0.4,
1846 mx + dy * 0.4, my - dx * 0.4);
1847
1848 mx = mx - dx * 0.2;
1849 my = my - dy * 0.2;
1850
1851 DrawMapLine(mx - dy * 0.4, my + dx * 0.4,
1852 mx + dy * 0.4, my - dx * 0.4);
1853
1854 RenderThickness(1);
1855 }
1856
1857
DrawSnapPoint()1858 void UI_Canvas::DrawSnapPoint()
1859 {
1860 // don't draw if an action is occurring
1861 if (edit.action != ACT_NOTHING)
1862 return;
1863
1864 if (edit.split_line.valid())
1865 return;
1866
1867 if (! Vis(snap_x, snap_y, 10))
1868 return;
1869
1870 RenderColor(FL_CYAN);
1871
1872 int sx = SCREENX(snap_x);
1873 int sy = SCREENY(snap_y);
1874
1875 RenderRect(sx, sy-2, 2, 6);
1876 RenderRect(sx-2, sy, 6, 2);
1877 }
1878
1879
DrawCurrentLine()1880 void UI_Canvas::DrawCurrentLine()
1881 {
1882 if (edit.draw_from.is_nil())
1883 return;
1884
1885 const Vertex * V = Vertices[edit.draw_from.num];
1886
1887 double new_x = edit.draw_to_x;
1888 double new_y = edit.draw_to_y;
1889
1890 // should draw a vertex?
1891 if (! (edit.highlight.valid() || edit.split_line.valid()))
1892 {
1893 RenderColor(FL_GREEN);
1894 DrawVertex(new_x, new_y, vertex_radius(grid.Scale));
1895 }
1896
1897 RenderColor(RED);
1898 DrawKnobbyLine(V->x(), V->y(), new_x, new_y);
1899
1900 DrawLineInfo(V->x(), V->y(), new_x, new_y, grid.ratio > 0);
1901
1902 // draw all the crossing points
1903 crossing_state_c cross;
1904
1905 FindCrossingPoints(cross,
1906 V->x(), V->y(), edit.draw_from.num,
1907 new_x, new_y, edit.highlight.valid() ? edit.highlight.num : -1);
1908
1909 for (unsigned int k = 0 ; k < cross.points.size() ; k++)
1910 {
1911 cross_point_t& point = cross.points[k];
1912
1913 // ignore current split line (what new vertex is sitting on)
1914 if (point.ld >= 0 && point.ld == edit.split_line.num)
1915 continue;
1916
1917 if (point.vert >= 0)
1918 RenderColor(FL_GREEN);
1919 else
1920 RenderColor(HI_AND_SEL_COL);
1921
1922 DrawSplitPoint(point.x, point.y);
1923 }
1924 }
1925
1926
SelboxGet(double & x1,double & y1,double & x2,double & y2)1927 bool UI_Canvas::SelboxGet(double& x1, double& y1, double& x2, double& y2)
1928 {
1929 x1 = MIN(edit.selbox_x1, edit.selbox_x2);
1930 y1 = MIN(edit.selbox_y1, edit.selbox_y2);
1931 x2 = MAX(edit.selbox_x1, edit.selbox_x2);
1932 y2 = MAX(edit.selbox_y1, edit.selbox_y2);
1933
1934 int scr_dx = abs(SCREENX(x2) - SCREENX(x1));
1935 int scr_dy = abs(SCREENY(y2) - SCREENY(y1));
1936
1937 // small boxes should be ignored (treated as a click + release)
1938 if (scr_dx < 5 && scr_dy < 5)
1939 return false;
1940
1941 return true; // Ok
1942 }
1943
1944
SelboxDraw()1945 void UI_Canvas::SelboxDraw()
1946 {
1947 double x1 = MIN(edit.selbox_x1, edit.selbox_x2);
1948 double x2 = MAX(edit.selbox_x1, edit.selbox_x2);
1949 double y1 = MIN(edit.selbox_y1, edit.selbox_y2);
1950 double y2 = MAX(edit.selbox_y1, edit.selbox_y2);
1951
1952 RenderColor(FL_CYAN);
1953
1954 DrawMapLine(x1, y1, x2, y1);
1955 DrawMapLine(x2, y1, x2, y2);
1956 DrawMapLine(x2, y2, x1, y2);
1957 DrawMapLine(x1, y2, x1, y1);
1958 }
1959
1960
DragDelta(double * dx,double * dy)1961 void UI_Canvas::DragDelta(double *dx, double *dy)
1962 {
1963 *dx = edit.drag_cur_x - edit.drag_start_x;
1964 *dy = edit.drag_cur_y - edit.drag_start_y;
1965
1966 float pixel_dx = *dx * grid.Scale;
1967 float pixel_dy = *dy * grid.Scale;
1968
1969 // check that we have moved far enough from the start position,
1970 // giving the user the option to select the original place.
1971 if (MAX(abs(pixel_dx), abs(pixel_dy)) < minimum_drag_pixels*2)
1972 {
1973 *dx = *dy = 0;
1974 return;
1975 }
1976
1977 // handle ratio-lock of a single dragged vertex
1978 if (edit.mode == OBJ_VERTICES && grid.ratio > 0 &&
1979 edit.dragged.num >= 0 && edit.drag_other_vert >= 0)
1980 {
1981 const Vertex *v0 = Vertices[edit.drag_other_vert];
1982 const Vertex *v1 = Vertices[edit.dragged.num];
1983
1984 double new_x = edit.drag_cur_x;
1985 double new_y = edit.drag_cur_y;
1986
1987 grid.RatioSnapXY(new_x, new_y, v0->x(), v0->y());
1988
1989 *dx = new_x - v1->x();
1990 *dy = new_y - v1->y();
1991 return;
1992 }
1993
1994 if (grid.ratio > 0)
1995 {
1996 double new_x = edit.drag_cur_x;
1997 double new_y = edit.drag_cur_y;
1998
1999 grid.RatioSnapXY(new_x, new_y, edit.drag_start_x, edit.drag_start_y);
2000
2001 *dx = new_x - edit.drag_start_x;
2002 *dy = new_y - edit.drag_start_y;
2003 return;
2004 }
2005
2006 if (grid.snap)
2007 {
2008 double focus_x = edit.drag_focus_x + *dx;
2009 double focus_y = edit.drag_focus_y + *dy;
2010
2011 *dx = grid.SnapX(focus_x) - edit.drag_focus_x;
2012 *dy = grid.SnapY(focus_y) - edit.drag_focus_y;
2013 }
2014 }
2015
2016
2017 //------------------------------------------------------------------------
2018
RenderSector(int num)2019 void UI_Canvas::RenderSector(int num)
2020 {
2021 if (! Subdiv_SectorOnScreen(num, map_lx, map_ly, map_hx, map_hy))
2022 return;
2023
2024 sector_subdivision_c *subdiv = Subdiv_PolygonsForSector(num);
2025
2026 if (! subdiv)
2027 return;
2028
2029
2030 /// fprintf(stderr, "RenderSector %d\n", num);
2031
2032 rgb_color_t light_col = SectorLightColor(Sectors[num]->light);
2033 bool light_and_tex = false;
2034
2035 const char * tex_name = NULL;
2036
2037 Img_c * img = NULL;
2038
2039 if (edit.sector_render_mode == SREND_Lighting)
2040 {
2041 RenderColor(light_col);
2042 }
2043 else if (edit.sector_render_mode == SREND_SoundProp)
2044 {
2045 if (edit.mode != OBJ_SECTORS || !edit.highlight.valid())
2046 return;
2047
2048 const byte * prop = SoundPropagation(edit.highlight.num);
2049
2050 switch ((propagate_level_e) prop[num])
2051 {
2052 case PGL_Never: return;
2053 case PGL_Maybe: RenderColor(fl_rgb_color(64,64,192)); break;
2054 case PGL_Level_1: RenderColor(fl_rgb_color(192,32,32)); break;
2055 case PGL_Level_2: RenderColor(fl_rgb_color(192,96,32)); break;
2056 }
2057 }
2058 else
2059 {
2060 if (edit.sector_render_mode <= SREND_Ceiling)
2061 light_and_tex = true;
2062
2063 if (edit.sector_render_mode == SREND_Ceiling ||
2064 edit.sector_render_mode == SREND_CeilBright)
2065 tex_name = Sectors[num]->CeilTex();
2066 else
2067 tex_name = Sectors[num]->FloorTex();
2068
2069 if (is_sky(tex_name))
2070 {
2071 RenderColor(palette[Misc_info.sky_color]);
2072 }
2073 else
2074 {
2075 img = W_GetFlat(tex_name);
2076
2077 if (! img)
2078 {
2079 img = IM_UnknownTex();
2080 }
2081 }
2082 }
2083
2084 #ifdef NO_OPENGL
2085 int tw = img ? img->width() : 1;
2086 int th = img ? img->height() : 1;
2087
2088 const img_pixel_t *src_pix = img ? img->buf() : NULL;
2089
2090 for (unsigned int i = 0 ; i < subdiv->polygons.size() ; i++)
2091 {
2092 sector_polygon_t *poly = &subdiv->polygons[i];
2093
2094 float py1 = poly->my[1]; // north most
2095 float py2 = poly->my[0];
2096
2097 int sy1 = SCREENY(py1);
2098 int sy2 = SCREENY(py2);
2099
2100 // clip range to screen
2101 sy1 = MAX(sy1, y());
2102 sy2 = MIN(sy2, y() + h() - 1);
2103
2104 // reject polygons vertically off the screen
2105 if (sy1 > sy2)
2106 continue;
2107
2108 // get left and right edges, unpacking a triangle if necessary
2109 float lx1 = poly->mx[1];
2110 float lx2 = poly->mx[0];
2111
2112 float rx1 = poly->mx[2];
2113 float rx2 = poly->mx[3];
2114
2115 if (poly->count == 3)
2116 {
2117 if (poly->my[2] == poly->my[0])
2118 {
2119 rx1 = poly->mx[1];
2120 rx2 = poly->mx[2];
2121 }
2122 else // my[2] == my[1]
2123 {
2124 rx2 = poly->mx[0];
2125 }
2126 }
2127
2128 // visit each screen row
2129 for (short y = (short)sy1 ; y <= (short)sy2 ; y++)
2130 {
2131 // compute horizontal span
2132 float map_y = MAPY(y);
2133
2134 float lx = lx1 + (lx2 - lx1) * (map_y - py1) / (py2 - py1);
2135 float rx = rx1 + (rx2 - rx1) * (map_y - py1) / (py2 - py1);
2136
2137 int sx1 = SCREENX(lx);
2138 int sx2 = SCREENX(rx);
2139
2140 // clip span to screen
2141 sx1 = MAX(sx1, x());
2142 sx2 = MIN(sx2, x() + w() - 1);
2143
2144 // reject spans completely off the screen
2145 if (sx2 < sx1)
2146 continue;
2147
2148 /// fprintf(stderr, " span : y=%d x=%d..%d\n", y, x1, x2);
2149
2150 // solid color?
2151 if (! img)
2152 {
2153 RenderRect(sx1, y, sx2 - sx1 + 1, 1);
2154 continue;
2155 }
2156
2157 int x = sx1;
2158 int span_w = sx2 - sx1 + 1;
2159
2160 u8_t *dest = rgb_buf + ((x - rgb_x) + (y - rgb_y) * rgb_w) * 3;
2161 u8_t *dest_end = dest + span_w * 3;
2162
2163 // the logic here for non-64x64 textures matches the software
2164 // 3D renderer, but is different than ZDoom (which scales them).
2165 int ty = (0 - (int)MAPY(y)) & (th - 1);
2166
2167 if (light_and_tex)
2168 {
2169 int r = RGB_RED(light_col) * 0x101;
2170 int g = RGB_GREEN(light_col) * 0x101;
2171 int b = RGB_BLUE(light_col) * 0x101;
2172
2173 for (; dest < dest_end ; dest += 3, x++)
2174 {
2175 int tx = (int)MAPX(x) & (tw - 1);
2176
2177 img_pixel_t pix = src_pix[ty * tw + tx];
2178
2179 IM_DecodePixel(pix, dest[0], dest[1], dest[2]);
2180
2181 dest[0] = ((int)dest[0] * r) >> 16;
2182 dest[1] = ((int)dest[1] * g) >> 16;
2183 dest[2] = ((int)dest[2] * b) >> 16;
2184 }
2185 }
2186 else // fullbright version
2187 {
2188 for (; dest < dest_end ; dest += 3, x++)
2189 {
2190 int tx = (int)MAPX(x) & (tw - 1);
2191
2192 img_pixel_t pix = src_pix[ty * tw + tx];
2193
2194 IM_DecodePixel(pix, dest[0], dest[1], dest[2]);
2195 }
2196 }
2197 }
2198 }
2199
2200 #else // OpenGL
2201 if (img)
2202 {
2203 if (light_and_tex)
2204 RenderColor(light_col);
2205 else
2206 glColor3f(1, 1, 1);
2207
2208 glEnable(GL_TEXTURE_2D);
2209 glEnable(GL_ALPHA_TEST);
2210
2211 glAlphaFunc(GL_GREATER, 0.5);
2212
2213 img->bind_gl();
2214 }
2215 else
2216 {
2217 // color was set above, set texture to solid white
2218 glBindTexture(GL_TEXTURE_2D, 0);
2219 }
2220
2221 for (unsigned int i = 0 ; i < subdiv->polygons.size() ; i++)
2222 {
2223 sector_polygon_t *poly = &subdiv->polygons[i];
2224
2225 // draw polygon
2226 glBegin(GL_POLYGON);
2227
2228 for (int p = 0 ; p < poly->count ; p++)
2229 {
2230 int sx = SCREENX(poly->mx[p]);
2231 int sy = SCREENY(poly->my[p]);
2232
2233 if (img)
2234 {
2235 // this logic follows ZDoom, which scales large flats to
2236 // occupy a 64x64 unit area. I presume wall textures are
2237 // handled similarily....
2238 glTexCoord2f(poly->mx[p] / 64.0, poly->my[p] / 64.0);
2239 }
2240
2241 glVertex2i(sx, sy);
2242 }
2243
2244 glEnd();
2245 }
2246
2247 if (img)
2248 {
2249 glDisable(GL_TEXTURE_2D);
2250 glDisable(GL_ALPHA_TEST);
2251 }
2252 #endif
2253 }
2254
2255
2256 //------------------------------------------------------------------------
2257 // CUSTOM S/W DRAWING CODE
2258 //------------------------------------------------------------------------
2259
PrepareToDraw()2260 void UI_Canvas::PrepareToDraw()
2261 {
2262 #ifdef NO_OPENGL
2263 rgb_x = x();
2264 rgb_y = y();
2265
2266 if (rgb_w != w() || rgb_h != h())
2267 {
2268 if (rgb_buf)
2269 delete[] rgb_buf;
2270
2271 rgb_w = w();
2272 rgb_h = h();
2273
2274 rgb_buf = new byte[rgb_w * rgb_h * 3];
2275 }
2276 #endif
2277 }
2278
2279
Blit()2280 void UI_Canvas::Blit()
2281 {
2282 #ifdef NO_OPENGL
2283 fl_draw_image(rgb_buf, x(), y(), w(), h());
2284 #endif
2285 }
2286
2287
RenderColor(Fl_Color c)2288 void UI_Canvas::RenderColor(Fl_Color c)
2289 {
2290 #ifdef NO_OPENGL
2291 Fl::get_color(c, cur_col.r, cur_col.g, cur_col.b);
2292 #else
2293 gl_color(c);
2294 #endif
2295 }
2296
RenderFontSize(int size)2297 void UI_Canvas::RenderFontSize(int size)
2298 {
2299 cur_font = size;
2300 }
2301
2302
RenderThickness(int w)2303 void UI_Canvas::RenderThickness(int w)
2304 {
2305 #ifdef NO_OPENGL
2306 thickness = (w < 2) ? 1 : 2;
2307 #else
2308 glLineWidth(w);
2309 #endif
2310 }
2311
2312
RenderRect(int rx,int ry,int rw,int rh)2313 void UI_Canvas::RenderRect(int rx, int ry, int rw, int rh)
2314 {
2315 #ifndef NO_OPENGL
2316 gl_rectf(rx, ry, rw, rh);
2317
2318 #else
2319 // software version
2320 rx -= rgb_x;
2321 ry -= rgb_y;
2322
2323 // clip to screen
2324 if (rx + rw > rgb_w)
2325 {
2326 rw = rgb_w - rx;
2327 }
2328 if (rx < 0)
2329 {
2330 rw += rx;
2331 rx = 0;
2332 }
2333 if (rw <= 0)
2334 return;
2335
2336 if (ry + rh > rgb_h)
2337 {
2338 rh = rgb_h - ry;
2339 }
2340 if (ry < 0)
2341 {
2342 rh += ry;
2343 ry = 0;
2344 }
2345 if (rh <= 0)
2346 return;
2347
2348 // fast method for greyscale (especially BLACK)
2349 if (cur_col.r == cur_col.g && cur_col.g == cur_col.b)
2350 {
2351 byte *dest = rgb_buf + (ry * rgb_w * 3) + (rx * 3);
2352
2353 for ( ; rh > 0 ; rh--, dest += (rgb_w * 3))
2354 memset(dest, cur_col.r, rw * 3);
2355
2356 return;
2357 }
2358
2359 // slower method for all other colors
2360 byte *base = rgb_buf + (ry * rgb_w * 3) + (rx * 3);
2361
2362 for ( ; rh > 0 ; rh--, base += (rgb_w * 3))
2363 {
2364 byte *dest = base;
2365
2366 for (int w2 = rw ; w2 > 0 ; w2--)
2367 {
2368 *dest++ = cur_col.r;
2369 *dest++ = cur_col.g;
2370 *dest++ = cur_col.b;
2371 }
2372 }
2373 #endif
2374 }
2375
2376
2377 #ifdef NO_OPENGL
2378 enum outcode_flags_e
2379 {
2380 O_TOP = 1,
2381 O_BOTTOM = 2,
2382 O_LEFT = 4,
2383 O_RIGHT = 8,
2384 };
2385
Calc_Outcode(int x,int y)2386 int UI_Canvas::Calc_Outcode(int x, int y)
2387 {
2388 return
2389 ((y < 0) ? O_TOP : 0) |
2390 ((y >= rgb_h) ? O_BOTTOM : 0) |
2391 ((x < 0) ? O_LEFT : 0) |
2392 ((x >= rgb_w) ? O_RIGHT : 0);
2393 }
2394 #endif // NO_OPENGL
2395
2396
RenderLine(int x1,int y1,int x2,int y2)2397 void UI_Canvas::RenderLine(int x1, int y1, int x2, int y2)
2398 {
2399 #ifndef NO_OPENGL
2400 glBegin(GL_LINE_STRIP);
2401 glVertex2i(x1, y1);
2402 glVertex2i(x2, y2);
2403 glEnd();
2404 #else
2405 // software line drawing
2406 if (x1 == x2)
2407 {
2408 if (y1 > y2)
2409 std::swap(y1, y2);
2410
2411 RenderRect(x1, y1, thickness, y2 - y1 + thickness);
2412 return;
2413 }
2414 if (y1 == y2)
2415 {
2416 if (x1 > x2)
2417 std::swap(x1, x2);
2418
2419 RenderRect(x1, y1, x2 - x1 + thickness, thickness);
2420 return;
2421 }
2422
2423
2424 // completely off the screen?
2425 x1 -= rgb_x; y1 -= rgb_y;
2426 x2 -= rgb_x; y2 -= rgb_y;
2427
2428 int out1 = Calc_Outcode(x1, y1);
2429 int out2 = Calc_Outcode(x2, y2);
2430
2431 if (out1 & out2)
2432 return;
2433
2434
2435 // clip diagonal line to the map
2436 // (this is the Cohen-Sutherland clipping algorithm)
2437
2438 while (out1 | out2)
2439 {
2440 // may be partially inside box, find an outside point
2441 int outside = (out1 ? out1 : out2);
2442
2443 int dx = x2 - x1;
2444 int dy = y2 - y1;
2445
2446 // this almost certainly cannot happen, but for the sake of
2447 // robustness we check anyway (just in case)
2448 if (dx == 0 && dy == 0)
2449 return;
2450
2451 int new_x, new_y;
2452
2453 // clip to each side
2454 if (outside & O_TOP)
2455 {
2456 new_y = 0;
2457 new_x = x1 + dx * (new_y - y1) / dy;
2458 }
2459 else if (outside & O_BOTTOM)
2460 {
2461 new_y = rgb_h-1;
2462 new_x = x1 + dx * (new_y - y1) / dy;
2463 }
2464 else if (outside & O_LEFT)
2465 {
2466 new_x = 0;
2467 new_y = y1 + dy * (new_x - x1) / dx;
2468 }
2469 else
2470 {
2471 SYS_ASSERT(outside & O_RIGHT);
2472
2473 new_x = rgb_w-1;
2474 new_y = y1 + dy * (new_x - x1) / dx;
2475 }
2476
2477 if (out1)
2478 {
2479 x1 = new_x;
2480 y1 = new_y;
2481
2482 out1 = Calc_Outcode(x1, y1);
2483 }
2484 else
2485 {
2486 SYS_ASSERT(out2);
2487
2488 x2 = new_x;
2489 y2 = new_y;
2490
2491 out2 = Calc_Outcode(x2, y2);
2492 }
2493
2494 if (out1 & out2)
2495 return;
2496 }
2497
2498
2499 // this is the Bresenham line drawing algorithm
2500 // (based on code from am_map.c in the GPL DOOM source)
2501
2502 int dx = x2 - x1;
2503 int dy = y2 - y1;
2504
2505 int ax = 2 * (dx < 0 ? -dx : dx);
2506 int ay = 2 * (dy < 0 ? -dy : dy);
2507
2508 int sx = dx < 0 ? -1 : 1;
2509 int sy = dy < 0 ? -1 : 1;
2510
2511 int x = x1;
2512 int y = y1;
2513
2514 if (ax > ay) // horizontal stepping
2515 {
2516 int d = ay - ax/2;
2517
2518 raw_pixel(x, y);
2519 if (thickness == 2 && y+1 < rgb_h) raw_pixel(x, y+1);
2520
2521 while (x != x2)
2522 {
2523 if (d>=0)
2524 {
2525 y += sy;
2526 d -= ax;
2527 }
2528
2529 x += sx;
2530 d += ay;
2531
2532 raw_pixel(x, y);
2533 if (thickness == 2 && y+1 < rgb_h) raw_pixel(x, y+1);
2534 }
2535 }
2536 else // vertical stepping
2537 {
2538 int d = ax - ay/2;
2539
2540 raw_pixel(x, y);
2541 if (thickness == 2 && x+1 < rgb_w) raw_pixel(x+1, y);
2542
2543 while (y != y2)
2544 {
2545 if (d >= 0)
2546 {
2547 x += sx;
2548 d -= ay;
2549 }
2550
2551 y += sy;
2552 d += ax;
2553
2554 raw_pixel(x, y);
2555 if (thickness == 2 && x+1 < rgb_w) raw_pixel(x+1, y);
2556 }
2557 }
2558 #endif
2559 }
2560
2561
RenderNumString(int x,int y,const char * s)2562 void UI_Canvas::RenderNumString(int x, int y, const char *s)
2563 {
2564 // NOTE: string is limited to the digits '0' to '9', spaces,
2565 // and the characters '-', '.' and ':'.
2566
2567 Img_c *font_img;
2568 int font_cw;
2569 int font_ch;
2570 int font_step;
2571
2572 if (cur_font < 17)
2573 {
2574 font_img = IM_DigitFont_11x14();
2575 font_cw = 11;
2576 font_ch = 14;
2577 font_step = font_cw - 2;
2578 }
2579 else
2580 {
2581 font_img = IM_DigitFont_14x19();
2582 font_cw = 14;
2583 font_ch = 19;
2584 font_step = font_cw - 2;
2585 }
2586
2587 #ifndef NO_OPENGL
2588 glEnable(GL_TEXTURE_2D);
2589 glEnable(GL_ALPHA_TEST);
2590
2591 glAlphaFunc(GL_GREATER, 0.5);
2592
2593 // bind the sprite image (upload it to OpenGL if needed)
2594 font_img->bind_gl();
2595 #endif
2596
2597 // compute total size
2598 int total_w = static_cast<int>(strlen(s) * font_step + 2);
2599
2600 // center the string at the given coordinate
2601 x -= total_w / 2;
2602 y -= font_ch / 2;
2603
2604 for ( ; *s ; s++, x += font_step)
2605 {
2606 int ch = (*s & 0x7f);
2607 if (ch == ' ')
2608 continue;
2609
2610 if ('0' <= ch && ch <= '9')
2611 ch -= '0';
2612 else if (ch == ':')
2613 ch = 10;
2614 else if (ch == '.')
2615 ch = 11;
2616 else if (ch == '^')
2617 ch = 13;
2618 else // '-'
2619 ch = 12;
2620
2621 RenderFontChar(x, y, font_img, ch * font_cw, 0, font_cw, font_ch);
2622 }
2623
2624 #ifndef NO_OPENGL
2625 glDisable(GL_ALPHA_TEST);
2626 glDisable(GL_TEXTURE_2D);
2627 #endif
2628 }
2629
2630
RenderFontChar(int rx,int ry,Img_c * img,int ix,int iy,int iw,int ih)2631 void UI_Canvas::RenderFontChar(int rx, int ry, Img_c *img, int ix, int iy, int iw, int ih)
2632 {
2633 #ifdef NO_OPENGL
2634 // software rendering
2635
2636 rx -= rgb_x;
2637 ry -= rgb_y;
2638
2639 // clip to screen
2640 int sx1 = MAX(rx, 0);
2641 int sy1 = MAX(ry, 0);
2642
2643 int sx2 = MIN(rx + iw, rgb_w) - 1;
2644 int sy2 = MIN(ry + ih, rgb_h) - 1;
2645
2646 if (sx1 >= sx2 || sy1 >= sy2)
2647 return;
2648
2649 for (int sy = sy1 ; sy <= sy2 ; sy++, iy++)
2650 {
2651 const img_pixel_t *src = img->buf() + (ix + iy * img->width());
2652
2653 byte *dest = rgb_buf + 3 * (sx1 + sy * rgb_w);
2654
2655 for (int sx = sx1 ; sx <= sx2 ; sx++, dest += 3)
2656 {
2657 img_pixel_t pix = *src++;
2658
2659 if (pix != TRANS_PIXEL)
2660 {
2661 IM_DecodePixel(pix, dest[0], dest[1], dest[2]);
2662 }
2663 }
2664 }
2665
2666 #else // OpenGL
2667 int rx2 = rx + iw;
2668 int ry2 = ry + ih;
2669
2670 int pow2_width = RoundPOW2(img->width());
2671 int pow2_height = RoundPOW2(img->height());
2672
2673 float tx1 = (float)ix / (float)pow2_width;
2674 float ty1 = (float)iy / (float)pow2_height;
2675 float tx2 = (float)(ix + iw) / (float)pow2_width;
2676 float ty2 = (float)(iy + ih) / (float)pow2_height;
2677
2678 glColor3f(1, 1, 1);
2679
2680 glBegin(GL_QUADS);
2681
2682 glTexCoord2f(tx1, ty1); glVertex2i(rx, ry);
2683 glTexCoord2f(tx1, ty2); glVertex2i(rx, ry2);
2684 glTexCoord2f(tx2, ty2); glVertex2i(rx2, ry2);
2685 glTexCoord2f(tx2, ty1); glVertex2i(rx2, ry);
2686
2687 glEnd();
2688 #endif
2689 }
2690
2691 //--- editor settings ---
2692 // vi:ts=4:sw=4:noexpandtab
2693