1 /*
2 * Portions of this file are copyright Rebirth contributors and licensed as
3 * described in COPYING.txt.
4 * Portions of this file are copyright Parallax Software and licensed
5 * according to the Parallax license below.
6 * See COPYING.txt for license details.
7
8 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
10 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
16 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
18 */
19
20 /*
21 *
22 * Med drawing functions.
23 *
24 */
25
26 #include <algorithm>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include "inferno.h"
32 #include "segment.h"
33 #include "segpoint.h"
34 #include "gameseg.h"
35 #include "gr.h"
36 #include "ui.h"
37 #include "editor/editor.h"
38 #include "editor/esegment.h"
39 #include "wall.h"
40 #include "switch.h"
41 #include "dxxerror.h"
42 #include "u_mem.h"
43 #include "render.h"
44 #include "func.h"
45 #include "textures.h"
46 #include "object.h"
47 #include "meddraw.h"
48 #include "d_enumerate.h"
49 #include "d_levelstate.h"
50 #include "d_range.h"
51 #include "compiler-range_for.h"
52 #include "segiter.h"
53 #include "d_zip.h"
54
55 #if DXX_USE_OGL
56 #include "ogl_init.h"
57 #endif
58
59 using std::min;
60
61 // Colors used in editor for indicating various kinds of segments.
62 #define SELECT_COLOR BM_XRGB( 63/2 , 41/2 , 0/2)
63 #define FOUND_COLOR BM_XRGB( 0/2 , 30/2 , 45/2)
64 #define WARNING_COLOR BM_XRGB( 63/2 , 0/2 , 0/2)
65 #define AXIS_COLOR BM_XRGB( 63/2 , 0/2 , 63/2)
66 #define PLAINSEG_COLOR BM_XRGB( 45/2 , 45/2 , 45/2)
67 #define MARKEDSEG_COLOR BM_XRGB( 0/2 , 63/2 , 0/2)
68 #define MARKEDSIDE_COLOR BM_XRGB( 0/2 , 63/2 , 63/2)
69 #define CURSEG_COLOR BM_XRGB( 63/2 , 63/2 , 63/2)
70 #define CURSIDE_COLOR BM_XRGB( 63/2 , 63/2 , 0/2)
71 #define CUREDGE_COLOR BM_XRGB( 0 , 63/2 , 0 )
72 #define GROUPSEG_COLOR BM_XRGB( 0/2 , 0/2 , 63/2)
73 #define GROUPSIDE_COLOR BM_XRGB( 63/2 , 0/2 , 45/2)
74 #define GROUP_COLOR BM_XRGB( 0/2 , 45/2 , 0/2)
75 #define ROBOT_COLOR BM_XRGB( 31 , 0 , 0 )
76 #define PLAYER_COLOR BM_XRGB( 0 , 0 , 31 )
77
78 constexpr std::integral_constant<unsigned, MAX_VERTICES * 4> MAX_EDGES{};
79
80 namespace {
81
82 enum class packed_edge : uint8_t
83 {
84 };
85
86 static int Search_mode=0; //if true, searching for segments at given x,y
87 static int Search_x,Search_y;
88 static int Automap_test=0; // Set to 1 to show wireframe in automap mode.
89
draw_seg_objects(grs_canvas & canvas,const unique_segment & seg)90 static void draw_seg_objects(grs_canvas &canvas, const unique_segment &seg)
91 {
92 auto &Objects = LevelUniqueObjectState.Objects;
93 auto &vcobjptridx = Objects.vcptridx;
94 range_for (const auto obj, objects_in(seg, vcobjptridx, vcsegptr))
95 {
96 auto sphere_point = g3_rotate_point(obj->pos);
97 const uint8_t color = (obj->type == OBJ_PLAYER && static_cast<icobjptridx_t::index_type>(obj) > 0)
98 ? BM_XRGB(0, 25, 0)
99 : (obj == ConsoleObject
100 ? PLAYER_COLOR
101 : ROBOT_COLOR
102 );
103 g3_draw_sphere(canvas, sphere_point, obj->size, color);
104 }
105 }
106
107 #if DXX_USE_OGL
108 #define draw_line(C,P0,P1,c) draw_line(P0,P1,c)
109 #define draw_segment(C,S,c) draw_segment(S,c)
110 #define draw_listed_segments(C,S,c) draw_listed_segments(S,c)
111 #endif
draw_line(grs_canvas & canvas,const vertnum_t pnum0,const vertnum_t pnum1,const color_palette_index color)112 static void draw_line(grs_canvas &canvas, const vertnum_t pnum0, const vertnum_t pnum1, const color_palette_index color)
113 {
114 g3_draw_line(canvas, Segment_points[pnum0], Segment_points[pnum1], color);
115 }
116
117 // ----------------------------------------------------------------------------
draw_segment(grs_canvas & canvas,const shared_segment & seg,const color_palette_index color)118 static void draw_segment(grs_canvas &canvas, const shared_segment &seg, const color_palette_index color)
119 {
120 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
121 auto &Vertices = LevelSharedVertexState.get_vertices();
122 if (seg.segnum == segment_none) //this segment doesn't exitst
123 return;
124
125 auto &svp = seg.verts;
126 auto &vcvertptr = Vertices.vcptr;
127 if (!rotate_list(vcvertptr, svp).uand)
128 { //all off screen?
129 range_for (const unsigned i, xrange(4u))
130 draw_line(canvas, svp[i], svp[i+4], color);
131
132 range_for (const unsigned i, xrange(3u))
133 {
134 draw_line(canvas, svp[i], svp[i+1], color);
135 draw_line(canvas, svp[i+4], svp[i+4+1], color);
136 }
137
138 draw_line(canvas, svp[0], svp[3], color);
139 draw_line(canvas, svp[4], svp[3+4], color);
140 }
141 }
142
143 //for looking for segment under a mouse click
check_segment(const vmsegptridx_t seg)144 static void check_segment(const vmsegptridx_t seg)
145 {
146 auto &svp = seg->verts;
147 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
148 auto &Vertices = LevelSharedVertexState.get_vertices();
149 auto &vcvertptr = Vertices.vcptr;
150 if (!rotate_list(vcvertptr, svp).uand)
151 { //all off screen?
152 #if DXX_USE_OGL
153 g3_end_frame();
154 #endif
155 {
156 uint8_t color = 0;
157 gr_pixel(grd_curcanv->cv_bitmap, Search_x, Search_y, color); //set our search pixel to color zero
158 }
159 #if DXX_USE_OGL
160 g3_start_frame(*grd_curcanv);
161 #endif
162 {
163 const uint8_t color = 1;
164 //and render in color one
165
166 range_for (auto &fn, Side_to_verts)
167 {
168 std::array<cg3s_point *, 3> vert_list;
169 vert_list[0] = &Segment_points[seg->verts[fn[0]]];
170 vert_list[1] = &Segment_points[seg->verts[fn[1]]];
171 vert_list[2] = &Segment_points[seg->verts[fn[2]]];
172 g3_check_and_draw_poly(*grd_curcanv, vert_list, color);
173
174 vert_list[1] = &Segment_points[seg->verts[fn[2]]];
175 vert_list[2] = &Segment_points[seg->verts[fn[3]]];
176 g3_check_and_draw_poly(*grd_curcanv, vert_list, color);
177 }
178 }
179
180 if (gr_ugpixel(grd_curcanv->cv_bitmap,Search_x,Search_y) == 1)
181 {
182 Found_segs.emplace_back(seg);
183 }
184 }
185 }
186
187 // ----------------------------------------------------------------------------
draw_seg_side(const shared_segment & seg,const unsigned side,const color_palette_index color)188 static void draw_seg_side(const shared_segment &seg, const unsigned side, const color_palette_index color)
189 {
190 auto &svp = seg.verts;
191 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
192 auto &Vertices = LevelSharedVertexState.get_vertices();
193 auto &vcvertptr = Vertices.vcptr;
194 if (!rotate_list(vcvertptr, svp).uand)
195 { //all off screen?
196 int i;
197
198 auto &stv = Side_to_verts[side];
199 for (i=0;i<3;i++)
200 draw_line(*grd_curcanv, svp[stv[i]], svp[stv[i+1]], color);
201
202 draw_line(*grd_curcanv, svp[stv[i]], svp[stv[0]], color);
203 }
204 }
205
draw_side_edge(const shared_segment & seg,const unsigned side,const unsigned edge,const color_palette_index color)206 static void draw_side_edge(const shared_segment &seg, const unsigned side, const unsigned edge, const color_palette_index color)
207 {
208 auto &svp = seg.verts;
209 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
210 auto &Vertices = LevelSharedVertexState.get_vertices();
211 auto &vcvertptr = Vertices.vcptr;
212 if (!rotate_list(vcvertptr, svp).uand) //on screen?
213 {
214 auto &stv = Side_to_verts[side];
215 draw_line(*grd_curcanv, svp[stv[edge]], svp[stv[(edge + 1) % 4]], color);
216 }
217 }
218
219 __attribute__((used))
220 int Show_triangulations=0;
221
222 //edge types - lower number types have precedence
223 #define ET_FACING 0 //this edge on a facing face
224 #define ET_NOTFACING 1 //this edge on a non-facing face
225 #define ET_NOTUSED 2 //no face uses this edge
226 #define ET_NOTEXTANT 3 //would exist if side were triangulated
227
228 #define ET_EMPTY 255 //this entry in array is empty
229
230 static
231 #if defined(DXX_BUILD_DESCENT_I)
232 constexpr
233 #endif
234 std::array<color_palette_index, 3> edge_colors{{54, 59, 64}};
235
236 struct seg_edge
237 {
238 vertnum_t v0, v1;
239 ushort type;
240 ubyte face_count, backface_count;
241 };
242
243 static std::array<seg_edge, MAX_EDGES> edge_list;
244 static std::array<int, MAX_EDGES> used_list; //which entries in edge_list have been used
245 static int n_used;
246
247 static unsigned edge_list_size; //set each frame
248
pack_edge(const unsigned a,const unsigned b)249 static constexpr packed_edge pack_edge(const unsigned a, const unsigned b)
250 {
251 return static_cast<packed_edge>((a << 3) | b);
252 }
253
254 //define edge numberings
255 constexpr std::array<packed_edge, 24> edges = {{
256 pack_edge(0, 1), // edge 0
257 pack_edge(0, 3), // edge 1
258 pack_edge(0, 4), // edge 2
259 pack_edge(1, 2), // edge 3
260 pack_edge(1, 5), // edge 4
261 pack_edge(2, 3), // edge 5
262 pack_edge(2, 6), // edge 6
263 pack_edge(3, 7), // edge 7
264 pack_edge(4, 5), // edge 8
265 pack_edge(4, 7), // edge 9
266 pack_edge(5, 6), // edge 10
267 pack_edge(6, 7), // edge 11
268
269 pack_edge(0, 5), // right cross
270 pack_edge(0, 7), // top cross
271 pack_edge(1, 3), // front cross
272 pack_edge(2, 5), // bottom cross
273 pack_edge(2, 7), // left cross
274 pack_edge(4, 6), // back cross
275
276 //crosses going the other way
277
278 pack_edge(1, 4), // other right cross
279 pack_edge(3, 4), // other top cross
280 pack_edge(0, 2), // other front cross
281 pack_edge(1, 6), // other bottom cross
282 pack_edge(3, 6), // other left cross
283 pack_edge(5, 7), // other back cross
284 }};
285
286 #define N_NORMAL_EDGES 12 //the normal edges of a box
287 #define N_EXTRA_EDGES 12 //ones created by triangulation
288 #define N_EDGES_PER_SEGMENT (N_NORMAL_EDGES+N_EXTRA_EDGES)
289
290 //given two vertex numbers on a segment (range 0..7), tell what edge number it is
find_edge_num(const int ev0,const int ev1)291 static std::size_t find_edge_num(const int ev0, const int ev1)
292 {
293 const auto &&[v0, v1] = std::minmax(ev0, ev1);
294 const auto vv = pack_edge(v0, v1);
295 const auto iter = std::find(edges.begin(), edges.end(), vv);
296 return std::distance(edges.begin(), iter);
297 }
298
299 //finds edge, filling in edge_ptr. if found old edge, returns index, else return -1
find_edge(const vertnum_t v0,const vertnum_t v1)300 static std::pair<seg_edge &, std::size_t> find_edge(const vertnum_t v0, const vertnum_t v1)
301 {
302 const auto &&hash_object = std::hash<vertnum_t>{};
303 const auto initial_hash_slot = (hash_object(v0) ^ (hash_object(v1) << 10)) % edge_list_size;
304
305 for (auto current_hash_slot = initial_hash_slot;;)
306 {
307 auto &e = edge_list[current_hash_slot];
308 if (e.type == ET_EMPTY)
309 return {e, UINT32_MAX};
310 else if (e.v0 == v0 && e.v1 == v1)
311 return {e, current_hash_slot};
312 else {
313 if (++ current_hash_slot == edge_list_size)
314 current_hash_slot = 0;
315 if (current_hash_slot == initial_hash_slot)
316 throw std::runtime_error("edge list full: search wrapped without finding a free slot");
317 }
318 }
319 }
320
321 //adds an edge to the edge list
add_edge(const vertnum_t ev0,const vertnum_t ev1,const uint8_t type)322 static void add_edge(const vertnum_t ev0, const vertnum_t ev1, const uint8_t type)
323 {
324 const auto &&[v0, v1] = std::minmax(ev0, ev1);
325 auto &&[e, current_hash_slot] = find_edge(v0, v1);
326
327 if (current_hash_slot == UINT32_MAX)
328 {
329 e.v0 = v0;
330 e.v1 = v1;
331 e.type = type;
332 used_list[n_used] = &e - edge_list.begin();
333 if (type == ET_FACING)
334 e.face_count++;
335 else if (type == ET_NOTFACING)
336 e.backface_count++;
337 n_used++;
338 } else {
339 if (e.type > type)
340 e.type = type;
341 if (type == ET_FACING)
342 e.face_count++;
343 else if (type == ET_NOTFACING)
344 e.backface_count++;
345 }
346 }
347
348 //adds a segment's edges to the edge list
add_edges(const shared_segment & seg)349 static void add_edges(const shared_segment &seg)
350 {
351 auto &svp = seg.verts;
352 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
353 auto &Vertices = LevelSharedVertexState.get_vertices();
354 auto &vcvertptr = Vertices.vcptr;
355 if (!rotate_list(vcvertptr, svp).uand)
356 { //all off screen?
357 int i,fn,vn;
358 int flag;
359 std::array<uint8_t, std::size(edges)> edge_flags;
360 for (i=0;i<N_NORMAL_EDGES;i++) edge_flags[i]=ET_NOTUSED;
361 for (;i<N_EDGES_PER_SEGMENT;i++) edge_flags[i]=ET_NOTEXTANT;
362
363 for (auto &&[idx, sidep] : enumerate(seg.sides))
364 {
365 int num_vertices;
366 const auto &&[num_faces, vertex_list] = create_all_vertex_lists(seg, sidep, idx);
367 if (num_faces == 1)
368 num_vertices = 4;
369 else
370 num_vertices = 3;
371
372 for (fn=0; fn<num_faces; fn++) {
373 //Note: normal check appears to be the wrong way since the normals points in, but we're looking from the outside
374 if (g3_check_normal_facing(vcvertptr(seg.verts[vertex_list[fn*3]]), sidep.normals[fn]))
375 flag = ET_NOTFACING;
376 else
377 flag = ET_FACING;
378
379 auto v0 = &vertex_list[fn*3];
380 for (vn=0; vn<num_vertices-1; vn++) {
381 if (const auto en = find_edge_num(*v0, *(v0 + 1)); en != edges.size())
382 if (flag < edge_flags[en]) edge_flags[en] = flag;
383
384 v0++;
385 }
386 if (const auto en = find_edge_num(*v0, vertex_list[fn * 3]); en != edges.size())
387 if (flag < edge_flags[en]) edge_flags[en] = flag;
388 }
389 }
390
391 for (i=0; i<N_EDGES_PER_SEGMENT; i++)
392 if (i<N_NORMAL_EDGES || (edge_flags[i]!=ET_NOTEXTANT && Show_triangulations))
393 add_edge(seg.verts[static_cast<uint8_t>(edges[i]) >> 3], seg.verts[static_cast<uint8_t>(edges[i]) & 7], edge_flags[i]);
394 }
395 }
396
397 // ----------------------------------------------------------------------------
draw_trigger_side(const shared_segment & seg,const unsigned side,const color_palette_index color)398 static void draw_trigger_side(const shared_segment &seg, const unsigned side, const color_palette_index color)
399 {
400 auto &svp = seg.verts;
401 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
402 auto &Vertices = LevelSharedVertexState.get_vertices();
403 auto &vcvertptr = Vertices.vcptr;
404 if (!rotate_list(vcvertptr, svp).uand)
405 { //all off screen?
406 // Draw diagonals
407 auto &stv = Side_to_verts[side];
408 draw_line(*grd_curcanv, svp[stv[0]], svp[stv[2]], color);
409 }
410 }
411
412 // ----------------------------------------------------------------------------
draw_wall_side(const shared_segment & seg,const unsigned side,const color_palette_index color)413 static void draw_wall_side(const shared_segment &seg, const unsigned side, const color_palette_index color)
414 {
415 auto &svp = seg.verts;
416 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
417 auto &Vertices = LevelSharedVertexState.get_vertices();
418 auto &vcvertptr = Vertices.vcptr;
419 if (!rotate_list(vcvertptr, svp).uand)
420 { //all off screen?
421 // Draw diagonals
422 auto &stv = Side_to_verts[side];
423 draw_line(*grd_curcanv, svp[stv[0]], svp[stv[2]], color);
424 draw_line(*grd_curcanv, svp[stv[1]], svp[stv[3]], color);
425 }
426 }
427
428 #define WALL_BLASTABLE_COLOR rgb_t{31, 0, 0} // RED
429 #define WALL_DOOR_COLOR rgb_t{0, 0, 31} // DARK BLUE
430 #define WALL_DOOR_LOCKED_COLOR rgb_t{0, 0, 63} // BLUE
431 #define WALL_AUTO_DOOR_COLOR rgb_t{0, 31, 0} // DARK GREEN
432 #define WALL_AUTO_DOOR_LOCKED_COLOR rgb_t{0, 63, 0} // GREEN
433 #define WALL_ILLUSION_COLOR rgb_t{63, 0, 63} // PURPLE
434
435 #define TRIGGER_COLOR BM_XRGB( 63/2 , 63/2 , 0/2) // YELLOW
436
437 // ----------------------------------------------------------------------------------------------------------------
438 // Draws special walls (for now these are just removable walls.)
draw_special_wall(const shared_segment & seg,const unsigned side)439 static void draw_special_wall(const shared_segment &seg, const unsigned side)
440 {
441 auto &Walls = LevelUniqueWallSubsystemState.Walls;
442 auto &vcwallptr = Walls.vcptr;
443 auto &w = *vcwallptr(seg.sides[side].wall_num);
444 const auto get_color = [=]() {
445 const auto type = w.type;
446 if (type != WALL_OPEN)
447 {
448 const auto flags = w.flags;
449 if (flags & wall_flag::door_locked)
450 return (flags & wall_flag::door_auto) ? WALL_AUTO_DOOR_LOCKED_COLOR : WALL_DOOR_LOCKED_COLOR;
451 if (flags & wall_flag::door_auto)
452 return WALL_AUTO_DOOR_COLOR;
453 if (type == WALL_BLASTABLE)
454 return WALL_BLASTABLE_COLOR;
455 if (type == WALL_DOOR)
456 return WALL_DOOR_COLOR;
457 if (type == WALL_ILLUSION)
458 return WALL_ILLUSION_COLOR;
459 }
460 return rgb_t{45, 45, 45};
461 };
462 const auto color = get_color();
463 draw_wall_side(seg,side, gr_find_closest_color(color.r, color.g, color.b));
464
465 if (w.trigger != trigger_none)
466 {
467 draw_trigger_side(seg,side, TRIGGER_COLOR);
468 }
469 }
470
471
472 // ----------------------------------------------------------------------------------------------------------------
473 // Recursively parse mine structure, drawing segments.
draw_mine_sub(const vmsegptridx_t segnum,int depth,visited_segment_bitarray_t & visited)474 static void draw_mine_sub(const vmsegptridx_t segnum,int depth, visited_segment_bitarray_t &visited)
475 {
476 if (visited[segnum]) return; // If segment already drawn, return.
477 visited[segnum] = true; // Say that this segment has been drawn.
478 auto mine_ptr = segnum;
479 // If this segment is active, process it, else skip it.
480
481 if (mine_ptr->segnum != segment_none) {
482
483 if (Search_mode) check_segment(mine_ptr);
484 else add_edges(mine_ptr); //add this segments edges to list
485
486 if (depth != 0) {
487 const shared_segment &sseg = *mine_ptr;
488 for (const auto &&[idx, child_segnum, sside] : enumerate(zip(sseg.children, sseg.sides)))
489 {
490 if (IS_CHILD(child_segnum))
491 {
492 if (sside.wall_num != wall_none)
493 draw_special_wall(mine_ptr, idx);
494 draw_mine_sub(segnum.absolute_sibling(child_segnum), depth-1, visited);
495 }
496 }
497 }
498 }
499 }
500
draw_mine_edges(int automap_flag)501 static void draw_mine_edges(int automap_flag)
502 {
503 int i,type;
504 seg_edge *e;
505
506 for (type=ET_NOTUSED;type>=ET_FACING;type--) {
507 const auto color = edge_colors[type];
508 for (i=0;i<n_used;i++) {
509 e = &edge_list[used_list[i]];
510 if (e->type == type)
511 if ((!automap_flag) || (e->face_count == 1))
512 draw_line(*grd_curcanv, e->v0, e->v1, color);
513 }
514 }
515 }
516
clear_edge_list()517 static void clear_edge_list()
518 {
519 range_for (auto &i, partial_range(edge_list, edge_list_size))
520 {
521 i.type = ET_EMPTY;
522 i.face_count = 0;
523 i.backface_count = 0;
524 }
525 }
526
527 //draws an entire mine
draw_mine(const vmsegptridx_t mine_ptr,int depth)528 static void draw_mine(const vmsegptridx_t mine_ptr,int depth)
529 {
530 visited_segment_bitarray_t visited;
531
532 edge_list_size = min(LevelSharedSegmentState.Num_segments * 12, MAX_EDGES.value); //make maybe smaller than max
533
534 // clear edge list
535 clear_edge_list();
536
537 n_used = 0;
538
539 draw_mine_sub(mine_ptr,depth, visited);
540
541 draw_mine_edges(0);
542
543 }
544
545 // -----------------------------------------------------------------------------
546 // Draw all segments, ignoring connectivity.
547 // A segment is drawn if its segnum != -1.
draw_mine_all(int automap_flag)548 static void draw_mine_all(int automap_flag)
549 {
550 edge_list_size = min(LevelSharedSegmentState.Num_segments * 12, MAX_EDGES.value); //make maybe smaller than max
551
552 // clear edge list
553 clear_edge_list();
554
555 n_used = 0;
556
557 range_for (const auto &&segp, vmsegptridx)
558 {
559 if (segp->segnum != segment_none)
560 {
561 for (auto &&[idx, value] : enumerate(segp->shared_segment::sides))
562 if (value.wall_num != wall_none)
563 draw_special_wall(segp, idx);
564 if (Search_mode)
565 check_segment(segp);
566 else {
567 add_edges(segp);
568 draw_seg_objects(*grd_curcanv, segp);
569 }
570 }
571 }
572
573 draw_mine_edges(automap_flag);
574
575 }
576
draw_listed_segments(grs_canvas & canvas,count_segment_array_t & s,const uint8_t color)577 static void draw_listed_segments(grs_canvas &canvas, count_segment_array_t &s, const uint8_t color)
578 {
579 range_for (const auto &ss, s)
580 {
581 const auto &&segp = vcsegptr(ss);
582 if (segp->segnum != segment_none)
583 draw_segment(canvas, segp, color);
584 }
585 }
586
draw_selected_segments(void)587 static void draw_selected_segments(void)
588 {
589 draw_listed_segments(*grd_curcanv, Selected_segs, SELECT_COLOR);
590 }
591
draw_found_segments(void)592 static void draw_found_segments(void)
593 {
594 draw_listed_segments(*grd_curcanv, Found_segs, FOUND_COLOR);
595 }
596
draw_warning_segments(void)597 static void draw_warning_segments(void)
598 {
599 draw_listed_segments(*grd_curcanv, Warning_segs, WARNING_COLOR);
600 }
601
draw_group_segments(void)602 static void draw_group_segments(void)
603 {
604 if (current_group > -1) {
605 draw_listed_segments(*grd_curcanv, GroupList[current_group].segments, GROUP_COLOR);
606 }
607 }
608
609
draw_special_segments(void)610 static void draw_special_segments(void)
611 {
612 // Highlight matcens, fuelcens, etc.
613 range_for (const auto &&segp, vcsegptr)
614 {
615 if (segp->segnum != segment_none)
616 {
617 unsigned r, g, b;
618 switch(segp->special)
619 {
620 case segment_special::fuelcen:
621 r = 29 * 2, g = 27 * 2, b = 13 * 2;
622 break;
623 case segment_special::controlcen:
624 r = 29 * 2, g = 0, b = 0;
625 break;
626 case segment_special::robotmaker:
627 r = 29 * 2, g = 0, b = 31 * 2;
628 break;
629 default:
630 continue;
631 }
632 const auto color = gr_find_closest_color(r, g, b);
633 draw_segment(*grd_curcanv, segp, color);
634 }
635 }
636 }
637
638
639 //find a free vertex. returns the vertex number
alloc_vert()640 static vertnum_t alloc_vert()
641 {
642 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
643
644 const auto Num_vertices = LevelSharedVertexState.Num_vertices;
645 assert(Num_vertices < MAX_SEGMENT_VERTICES);
646
647 auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
648 vertnum_t vn{};
649 for (; static_cast<unsigned>(vn) < Num_vertices && Vertex_active[vn]; ++vn)
650 {
651 }
652
653 Vertex_active[vn] = 1;
654
655 ++LevelSharedVertexState.Num_vertices;
656
657 return vn;
658 }
659
660 //frees a vertex
free_vert(vertnum_t vert_num)661 static void free_vert(vertnum_t vert_num)
662 {
663 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
664 auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
665 Vertex_active[vert_num] = 0;
666 --LevelSharedVertexState.Num_vertices;
667 }
668
669 // -----------------------------------------------------------------------------
draw_coordinate_axes(void)670 static void draw_coordinate_axes(void)
671 {
672 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
673 auto &Vertices = LevelSharedVertexState.get_vertices();
674 std::array<vertnum_t, 16> Axes_verts;
675 vms_vector tvec;
676
677 range_for (auto &i, Axes_verts)
678 i = alloc_vert();
679
680 create_coordinate_axes_from_segment(Cursegp,Axes_verts);
681
682 auto &vcvertptr = Vertices.vcptr;
683 auto &vmvertptr = Vertices.vmptr;
684 const auto &&av0 = vcvertptr(Axes_verts[0]);
685 const auto &&av1 = vcvertptr(Axes_verts[1]);
686 const auto &&av2 = vcvertptr(Axes_verts[2]);
687 const auto &&av3 = vcvertptr(Axes_verts[3]);
688 const auto &&av4 = vmvertptr(Axes_verts[4]);
689 const auto &&av6 = vmvertptr(Axes_verts[6]);
690 const auto &&av9 = vmvertptr(Axes_verts[9]);
691 const auto &&av10 = vmvertptr(Axes_verts[10]);
692 const auto &&av11 = vmvertptr(Axes_verts[11]);
693 const auto &&av12 = vmvertptr(Axes_verts[12]);
694 const auto &&av14 = vmvertptr(Axes_verts[14]);
695 const auto &&xvec = vm_vec_sub(av1, av0);
696 const auto &&yvec = vm_vec_sub(av2, av0);
697 const auto &&zvec = vm_vec_sub(av3, av0);
698
699 // Create the letter X
700 tvec = xvec;
701 vm_vec_add(av4, av1, vm_vec_scale(tvec, F1_0 / 16));
702 tvec = yvec;
703 vm_vec_add2(av4, vm_vec_scale(tvec, F1_0 / 8));
704 vm_vec_sub(av6, av4, vm_vec_scale(tvec, F2_0));
705 tvec = xvec;
706 vm_vec_scale(tvec, F1_0 / 8);
707 vm_vec_add(vmvertptr(Axes_verts[7]), av4, tvec);
708 vm_vec_add(vmvertptr(Axes_verts[5]), av6, tvec);
709
710 // Create the letter Y
711 tvec = yvec;
712 vm_vec_add(av11, av2, vm_vec_scale(tvec, F1_0 / 16));
713 vm_vec_add(vmvertptr(Axes_verts[8]), av11, tvec);
714 vm_vec_add(av9, av11, vm_vec_scale(tvec, F1_0 * 2));
715 vm_vec_add(av10, av11, tvec);
716 tvec = xvec;
717 vm_vec_scale(tvec, F1_0 / 16);
718 vm_vec_sub2(av9, tvec);
719 vm_vec_add2(av10, tvec);
720
721 // Create the letter Z
722 tvec = zvec;
723 vm_vec_add(av12, av3, vm_vec_scale(tvec, F1_0 / 16));
724 tvec = yvec;
725 vm_vec_add2(av12, vm_vec_scale(tvec, F1_0 / 8));
726 vm_vec_sub(av14, av12, vm_vec_scale(tvec, F2_0));
727 tvec = zvec;
728 vm_vec_scale(tvec, F1_0 / 8);
729 vm_vec_add(vmvertptr(Axes_verts[13]), av12, tvec);
730 vm_vec_add(vmvertptr(Axes_verts[15]), av14, tvec);
731
732 rotate_list(vcvertptr, Axes_verts);
733
734 const color_palette_index color = AXIS_COLOR;
735
736 draw_line(*grd_curcanv, Axes_verts[0], Axes_verts[1], color);
737 draw_line(*grd_curcanv, Axes_verts[0], Axes_verts[2], color);
738 draw_line(*grd_curcanv, Axes_verts[0], Axes_verts[3], color);
739
740 // draw the letter X
741 draw_line(*grd_curcanv, Axes_verts[4], Axes_verts[5], color);
742 draw_line(*grd_curcanv, Axes_verts[6], Axes_verts[7], color);
743
744 // draw the letter Y
745 draw_line(*grd_curcanv, Axes_verts[8], Axes_verts[9], color);
746 draw_line(*grd_curcanv, Axes_verts[8], Axes_verts[10], color);
747 draw_line(*grd_curcanv, Axes_verts[8], Axes_verts[11], color);
748
749 // draw the letter Z
750 draw_line(*grd_curcanv, Axes_verts[12], Axes_verts[13], color);
751 draw_line(*grd_curcanv, Axes_verts[13], Axes_verts[14], color);
752 draw_line(*grd_curcanv, Axes_verts[14], Axes_verts[15], color);
753
754 range_for (auto &i, Axes_verts)
755 free_vert(i);
756 }
757
758 }
759
draw_world(grs_canvas * screen_canvas,editor_view * v,const vmsegptridx_t mine_ptr,int depth)760 void draw_world(grs_canvas *screen_canvas,editor_view *v,const vmsegptridx_t mine_ptr,int depth)
761 {
762 vms_vector viewer_position;
763
764 gr_set_current_canvas(screen_canvas);
765
766 viewer_position = v->ev_matrix.fvec;
767 vm_vec_scale(viewer_position,-v->ev_dist);
768
769 vm_vec_add2(viewer_position,Ed_view_target);
770
771 gr_clear_canvas(*grd_curcanv, 0);
772 g3_start_frame(*grd_curcanv);
773 g3_set_view_matrix(viewer_position,v->ev_matrix,v->ev_zoom);
774
775 render_start_frame();
776
777 // Draw all segments or only connected segments.
778 // We might want to draw all segments if we have broken the mine into pieces.
779 if (Draw_all_segments)
780 draw_mine_all(Automap_test);
781 else
782 draw_mine(mine_ptr,depth);
783
784 // Draw the found segments
785 if (!Automap_test) {
786 draw_warning_segments();
787 draw_group_segments();
788 draw_found_segments();
789 draw_selected_segments();
790 draw_special_segments();
791
792 // Highlight group segment and side.
793 if (current_group > -1)
794 if (Groupsegp[current_group]) {
795 draw_segment(*grd_curcanv, vcsegptr(Groupsegp[current_group]), GROUPSEG_COLOR);
796 draw_seg_side(vcsegptr(Groupsegp[current_group]), Groupside[current_group], GROUPSIDE_COLOR);
797 }
798
799 // Highlight marked segment and side.
800 if (Markedsegp) {
801 draw_segment(*grd_curcanv, Markedsegp, MARKEDSEG_COLOR);
802 draw_seg_side(Markedsegp,Markedside, MARKEDSIDE_COLOR);
803 }
804
805 // Highlight current segment and current side.
806 draw_segment(*grd_curcanv, Cursegp, CURSEG_COLOR);
807
808 draw_seg_side(Cursegp,Curside, CURSIDE_COLOR);
809 draw_side_edge(Cursegp,Curside,Curedge, CUREDGE_COLOR);
810
811 // Draw coordinate axes if we are rendering the large view.
812 if (Show_axes_flag)
813 if (screen_canvas == LargeViewBox->canvas.get())
814 draw_coordinate_axes();
815
816 // Label the window
817 gr_set_fontcolor(*grd_curcanv, (v==current_view)?CRED:CWHITE, -1);
818 if ( screen_canvas == LargeViewBox->canvas.get() ) {
819 gr_ustring(*grd_curcanv, *grd_curcanv->cv_font, 5, 5, "USER VIEW");
820 switch (Large_view_index) {
821 case 0: gr_ustring(*grd_curcanv, *grd_curcanv->cv_font, 85, 5, "-- TOP"); break;
822 case 1: gr_ustring(*grd_curcanv, *grd_curcanv->cv_font, 85, 5, "-- FRONT"); break;
823 case 2: gr_ustring(*grd_curcanv, *grd_curcanv->cv_font, 85, 5, "-- RIGHT"); break;
824 }
825 } else
826 Error("Ortho views have been removed, what gives?\n");
827
828 }
829
830 g3_end_frame();
831
832 }
833
834 //find the segments that render at a given screen x,y
835 //parms other than x,y are like draw_world
836 //fills in globals N_found_segs & Found_segs
find_segments(short x,short y,grs_canvas * screen_canvas,editor_view * v,const vmsegptridx_t mine_ptr,int depth)837 void find_segments(short x,short y,grs_canvas *screen_canvas,editor_view *v,const vmsegptridx_t mine_ptr,int depth)
838 {
839 vms_vector viewer_position;
840
841 gr_set_current_canvas(screen_canvas);
842
843 viewer_position = v->ev_matrix.fvec;
844 vm_vec_scale(viewer_position,-v->ev_dist);
845
846 vm_vec_add2(viewer_position,Ed_view_target);
847
848 g3_start_frame(*grd_curcanv);
849 g3_set_view_matrix(viewer_position,v->ev_matrix,v->ev_zoom);
850
851 render_start_frame();
852
853 #if DXX_USE_OGL
854 g3_end_frame();
855 #endif
856 uint8_t color = 0;
857 gr_pixel(grd_curcanv->cv_bitmap, x, y, color); //set our search pixel to color zero
858 #if DXX_USE_OGL
859 g3_start_frame(*grd_curcanv);
860 #endif
861
862 Search_mode = -1;
863 Found_segs.clear();
864 Search_x = x; Search_y = y;
865
866 if (Draw_all_segments)
867 draw_mine_all(0);
868 else
869 draw_mine(mine_ptr,depth);
870
871 g3_end_frame();
872
873 Search_mode = 0;
874
875 }
876
877 namespace dsx {
meddraw_init_views(grs_canvas * canvas)878 void meddraw_init_views( grs_canvas * canvas)
879 {
880 #if defined(DXX_BUILD_DESCENT_II)
881 // sticking these here so the correct D2 colors are used
882 edge_colors[0] = BM_XRGB(45/2,45/2,45/2);
883 edge_colors[1] = BM_XRGB(45/3,45/3,45/3); //BM_RGB(0,0,45), //
884 edge_colors[2] = BM_XRGB(45/4,45/4,45/4); //BM_RGB(0,45,0)}; //
885 #endif
886
887 Views[0]->ev_canv = canvas;
888 }
889 }
890