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