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