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-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
18 */
19
20 /*
21 *
22 * Include file for functions which need to access segment data structure.
23 *
24 */
25
26 #pragma once
27
28 #include <physfs.h>
29 #include <type_traits>
30 #include "pstypes.h"
31 #include "maths.h"
32 #include "vecmat.h"
33 #include "dxxsconf.h"
34 #include "dsx-ns.h"
35
36 #ifdef __cplusplus
37 #include <cassert>
38 #include <cstdint>
39 #include <stdexcept>
40 #include "fwd-segment.h"
41 #include "countarray.h"
42 #include "valptridx.h"
43 #include "objnum.h"
44 #include "pack.h"
45
46
47 #ifdef dsx
48 namespace dsx {
49 // Returns true if segnum references a child, else returns false.
50 // Note that -1 means no connection, -2 means a connection to the outside world.
IS_CHILD(segnum_t s)51 static inline bool IS_CHILD(segnum_t s)
52 {
53 return s != segment_none && s != segment_exit;
54 }
55 }
56 #endif
57
58 namespace dcx {
59
60 //Structure for storing u,v,light values.
61 //NOTE: this structure should be the same as the one in 3d.h
62 struct uvl
63 {
64 fix u, v, l;
65 };
66
67 enum class side_type : uint8_t
68 {
69 quad = 1, // render side as quadrilateral
70 tri_02 = 2, // render side as two triangles, triangulated along edge from 0 to 2
71 tri_13 = 3, // render side as two triangles, triangulated along edge from 1 to 3
72 };
73
74 enum class segment_special : uint8_t
75 {
76 nothing,
77 fuelcen,
78 repaircen,
79 controlcen,
80 robotmaker,
81 goal_blue,
82 goal_red,
83 };
84
85 enum class materialization_center_number : uint8_t
86 {
87 None = 0xff,
88 };
89
90 enum class station_number : uint8_t
91 {
92 None = 0xff,
93 };
94
95 struct shared_side
96 {
97 struct illegal_type;
98 side_type m_type; // replaces num_faces and tri_edge, 1 = quad, 2 = 0:2 triangulation, 3 = 1:3 triangulation
get_typeshared_side99 const side_type &get_type() const { return m_type; }
set_typeshared_side100 void set_type(side_type t) { m_type = t; }
101 wallnum_t wall_num;
102 std::array<vms_vector, 2> normals; // 2 normals, if quadrilateral, both the same.
103 };
104
105 enum class texture1_value : uint16_t
106 {
107 None,
108 };
109
110 /* texture2_value uses the low 14 bits for the array index and the high
111 * 2 bits for texture2_rotation_high
112 */
113 enum class texture2_value : uint16_t
114 {
115 None,
116 };
117
118 enum class texture2_rotation_low : uint8_t
119 {
120 Normal = 0,
121 _1 = 1,
122 _2 = 2,
123 _3 = 3,
124 };
125
126 #define TEXTURE2_ROTATION_SHIFT 14u
127 #define TEXTURE2_ROTATION_INDEX_MASK ((1u << TEXTURE2_ROTATION_SHIFT) - 1u)
128
129 enum class texture2_rotation_high : uint16_t
130 {
131 Normal = 0,
132 _1 = static_cast<uint16_t>(texture2_rotation_low::_1) << TEXTURE2_ROTATION_SHIFT,
133 _2 = static_cast<uint16_t>(texture2_rotation_low::_2) << TEXTURE2_ROTATION_SHIFT,
134 _3 = static_cast<uint16_t>(texture2_rotation_low::_3) << TEXTURE2_ROTATION_SHIFT,
135 };
136
137 static constexpr texture2_rotation_high &operator++(texture2_rotation_high &t)
138 {
139 /* Cast to uint32, step to the next value, cast back. The cast back
140 * will truncate to the low 16 bits, causing
141 * texture2_rotation_high::_3 to roll over to
142 * texture2_rotation_high::Normal.
143 */
144 return (t = static_cast<texture2_rotation_high>(static_cast<uint32_t>(t) + (1u << TEXTURE2_ROTATION_SHIFT)));
145 }
146
get_texture_index(const texture1_value t)147 static constexpr texture_index get_texture_index(const texture1_value t)
148 {
149 return static_cast<texture_index>(t);
150 }
151
get_texture_index(const texture2_value t)152 static constexpr texture_index get_texture_index(const texture2_value t)
153 {
154 return static_cast<texture_index>(static_cast<uint16_t>(t) & TEXTURE2_ROTATION_INDEX_MASK);
155 }
156
get_texture_rotation_high(const texture2_value t)157 static constexpr texture2_rotation_high get_texture_rotation_high(const texture2_value t)
158 {
159 return static_cast<texture2_rotation_high>(static_cast<uint16_t>(t) & ~TEXTURE2_ROTATION_INDEX_MASK);
160 }
161
get_texture_rotation_low(const texture2_rotation_high t)162 static constexpr texture2_rotation_low get_texture_rotation_low(const texture2_rotation_high t)
163 {
164 return static_cast<texture2_rotation_low>(static_cast<uint16_t>(t) >> TEXTURE2_ROTATION_SHIFT);
165 }
166
get_texture_rotation_low(const texture2_value t)167 static constexpr texture2_rotation_low get_texture_rotation_low(const texture2_value t)
168 {
169 return get_texture_rotation_low(get_texture_rotation_high(t));
170 }
171
build_texture1_value(const texture_index t)172 static constexpr texture1_value build_texture1_value(const texture_index t)
173 {
174 return static_cast<texture1_value>(t);
175 }
176
build_texture2_value(const texture_index t,const texture2_rotation_high rotation)177 static constexpr texture2_value build_texture2_value(const texture_index t, const texture2_rotation_high rotation)
178 {
179 return static_cast<texture2_value>(static_cast<uint16_t>(t) | static_cast<uint16_t>(rotation));
180 }
181
182 #undef TEXTURE2_ROTATION_SHIFT
183 #undef TEXTURE2_ROTATION_INDEX_MASK
184
185 struct unique_side
186 {
187 texture1_value tmap_num;
188 texture2_value tmap_num2;
189 std::array<uvl, 4> uvls;
190 };
191
192 #ifdef dsx
193 struct shared_segment
194 {
195 #if DXX_USE_EDITOR
196 segnum_t segnum; // segment number, not sure what it means
197 short group; // group number to which the segment belongs.
198 #endif
199 std::array<segnum_t, MAX_SIDES_PER_SEGMENT> children; // indices of 6 children segments, front, left, top, right, bottom, back
200 std::array<vertnum_t, MAX_VERTICES_PER_SEGMENT> verts; // vertex ids of 4 front and 4 back vertices
201 segment_special special; // what type of center this is
202 materialization_center_number matcen_num; // which center segment is associated with.
203 station_number station_idx;
204 /* if DXX_BUILD_DESCENT_II */
205 uint8_t s2_flags;
206 /* endif */
207 std::array<shared_side, MAX_SIDES_PER_SEGMENT> sides;
208 };
209
210 struct unique_segment
211 {
212 objnum_t objects; // pointer to objects in this segment
213 // If bit n (1 << n) is set, then side #n in segment has had light subtracted from original (editor-computed) value.
214 uint8_t light_subtracted;
215 /* if DXX_BUILD_DESCENT_II */
216 uint8_t slide_textures;
217 /* endif */
218 fix static_light;
219 std::array<unique_side, MAX_SIDES_PER_SEGMENT> sides;
220 };
221
222 struct segment : unique_segment, shared_segment
223 {
224 };
225
226 template <typename S, typename U>
227 struct susegment
228 {
229 using qualified_segment = typename std::conditional<std::is_const<S>::value && std::is_const<U>::value, const segment, segment>::type;
230 S &s;
231 U &u;
susegmentsusegment232 constexpr susegment(qualified_segment &b) :
233 s(b), u(b)
234 {
235 }
236 constexpr susegment(const susegment &) = default;
237 constexpr susegment(susegment &&) = default;
238 template <typename S2, typename U2, typename std::enable_if<std::is_convertible<S2 &, S &>::value && std::is_convertible<U2 &, U &>::value, int>::type = 0>
susegmentsusegment239 constexpr susegment(const susegment<S2, U2> &r) :
240 s(r.s), u(r.u)
241 {
242 }
243 template <typename T, typename std::enable_if<std::is_convertible<T &&, qualified_segment &>::value, int>::type = 0>
susegmentsusegment244 constexpr susegment(T &&t) :
245 susegment(static_cast<qualified_segment &>(t))
246 {
247 }
248 operator S &() const
249 {
250 return s;
251 }
252 operator U &() const
253 {
254 return u;
255 }
256 };
257 #endif
258
259 struct count_segment_array_t : public count_array_t<segnum_t, MAX_SEGMENTS> {};
260
261 struct group
262 {
263 struct segment_array_type_t : public count_segment_array_t {};
264 struct vertex_array_type_t : public count_array_t<vertnum_t, MAX_VERTICES> {};
265 segment_array_type_t segments;
266 vertex_array_type_t vertices;
cleargroup267 void clear()
268 {
269 segments.clear();
270 vertices.clear();
271 }
272 };
273
274 #ifdef dsx
275 #define Highest_segment_index (Segments.get_count() - 1)
276 DXX_VALPTRIDX_DEFINE_GLOBAL_FACTORIES(segment, seg, Segments);
277 #endif
278
279 // Globals from mglobal.c
280 struct vertex : vms_vector
281 {
282 vertex() = default;
vertexvertex283 vertex(const fix &a, const fix &b, const fix &c) :
284 vms_vector{a, b, c}
285 {
286 }
vertexvertex287 explicit vertex(const vms_vector &v) :
288 vms_vector(v)
289 {
290 }
291 };
292
293 static constexpr vertnum_t operator++(vertnum_t &v)
294 {
295 return (v = static_cast<vertnum_t>(static_cast<unsigned>(v) + 1));
296 }
297 }
298
299 #ifdef dsx
300 struct shared_side::illegal_type : std::runtime_error
301 {
302 const shared_segment *const m_segment;
303 const shared_side *const m_side;
illegal_typeillegal_type304 illegal_type(const shared_segment &seg, const shared_side &s) :
305 runtime_error("illegal side type"),
306 m_segment(&seg), m_side(&s)
307 {
308 }
illegal_typeillegal_type309 illegal_type(const shared_side &s) :
310 runtime_error("illegal side type"),
311 m_segment(nullptr), m_side(&s)
312 {
313 }
314 };
315
316 namespace dsx {
317
318 #if defined(DXX_BUILD_DESCENT_II)
319 // New stuff, 10/14/95: For shooting out lights and monitors.
320 // Light cast upon vert_light vertices in segnum:sidenum by some light
321 struct delta_light : prohibit_void_ptr<delta_light>
322 {
323 segnum_t segnum;
324 uint8_t sidenum;
325 std::array<ubyte, 4> vert_light;
326 };
327
328 // Light at segnum:sidenum casts light on count sides beginning at index (in array Delta_lights)
329 struct dl_index {
330 segnum_t segnum;
331 uint8_t sidenum;
332 uint8_t count;
333 uint16_t index;
334 bool operator<(const dl_index &rhs) const
335 {
336 if (segnum < rhs.segnum)
337 return true;
338 if (segnum > rhs.segnum)
339 return false;
340 return sidenum < rhs.sidenum;
341 }
342 };
343
344 struct d_level_shared_destructible_light_state
345 {
346 valptridx<dl_index>::array_managed_type Dl_indices;
347 d_delta_light_array Delta_lights;
348 };
349 #endif
350
351 }
352 #endif
353
354 namespace dcx {
355
356 #ifdef dsx
357 struct d_level_shared_vertex_state
358 {
359 #if DXX_USE_EDITOR
360 /* Number of elements in Vertex_active which have a nonzero value.
361 * This can be less than the index of the highest defined vertex if
362 * there are unused vertices.
363 */
364 unsigned Num_vertices;
365 #endif
366 private:
367 vertex_array Vertices;
368 #if DXX_USE_EDITOR
369 enumerated_array<uint8_t, MAX_VERTICES, vertnum_t> Vertex_active; // !0 means vertex is in use, 0 means not in use.
370 #endif
371 public:
get_verticesd_level_shared_vertex_state372 auto &get_vertices()
373 {
374 return Vertices;
375 }
get_verticesd_level_shared_vertex_state376 const auto &get_vertices() const
377 {
378 return Vertices;
379 }
380 #if DXX_USE_EDITOR
get_vertex_actived_level_shared_vertex_state381 auto &get_vertex_active()
382 {
383 return Vertex_active;
384 }
get_vertex_actived_level_shared_vertex_state385 const auto &get_vertex_active() const
386 {
387 return Vertex_active;
388 }
389 #endif
390 };
391
392 struct d_level_shared_segment_state
393 {
394 unsigned Num_segments;
395 d_level_shared_vertex_state LevelSharedVertexState;
get_segmentsd_level_shared_segment_state396 auto &get_segments()
397 {
398 return Segments;
399 }
get_segmentsd_level_shared_segment_state400 const auto &get_segments() const
401 {
402 return Segments;
403 }
get_vertex_stated_level_shared_segment_state404 auto &get_vertex_state()
405 {
406 return LevelSharedVertexState;
407 }
get_vertex_stated_level_shared_segment_state408 const auto &get_vertex_state() const
409 {
410 return LevelSharedVertexState;
411 }
412 };
413
414 struct d_level_unique_automap_state
415 {
416 std::array<uint8_t, MAX_SEGMENTS> Automap_visited;
417 };
418
419 struct d_level_unique_segment_state
420 {
get_segmentsd_level_unique_segment_state421 auto &get_segments()
422 {
423 return Segments;
424 }
get_segmentsd_level_unique_segment_state425 const auto &get_segments() const
426 {
427 return Segments;
428 }
429 };
430
431 extern d_level_unique_automap_state LevelUniqueAutomapState;
432 extern d_level_unique_segment_state LevelUniqueSegmentState;
433 #endif
434
435 template <unsigned bits>
436 class visited_segment_mask_base
437 {
438 static_assert(bits == 1 || bits == 2 || bits == 4, "bits must align in bytes");
439 protected:
440 enum
441 {
442 divisor = 8 / bits,
443 };
444 using array_t = std::array<uint8_t, (MAX_SEGMENTS + (divisor - 1)) / divisor>;
445 typedef typename array_t::size_type size_type;
446 array_t a;
447 struct maskproxy_shift_count_type
448 {
449 using bitmask_low_aligned = std::integral_constant<unsigned, (1 << bits) - 1>;
450 const unsigned m_shift;
shiftmaskproxy_shift_count_type451 unsigned shift() const
452 {
453 return m_shift;
454 }
maskmaskproxy_shift_count_type455 typename array_t::value_type mask() const
456 {
457 return bitmask_low_aligned() << shift();
458 }
maskproxy_shift_count_typemaskproxy_shift_count_type459 maskproxy_shift_count_type(const unsigned s) :
460 m_shift(s * bits)
461 {
462 }
463 };
464 template <typename R>
465 struct maskproxy_byte_reference : public maskproxy_shift_count_type
466 {
467 R m_byte;
maskproxy_byte_referencemaskproxy_byte_reference468 maskproxy_byte_reference(R byte, const unsigned s) :
469 maskproxy_shift_count_type(s), m_byte(byte)
470 {
471 }
uint8_tmaskproxy_byte_reference472 operator uint8_t() const
473 {
474 return (this->m_byte >> this->shift()) & typename maskproxy_shift_count_type::bitmask_low_aligned();
475 }
476 };
477 template <typename R>
478 struct maskproxy_assignable_type : maskproxy_byte_reference<R>
479 {
480 DXX_INHERIT_CONSTRUCTORS(maskproxy_assignable_type, maskproxy_byte_reference<R>);
481 using typename maskproxy_shift_count_type::bitmask_low_aligned;
482 maskproxy_assignable_type &operator=(const uint8_t u)
483 {
484 assert(!(u & ~bitmask_low_aligned()));
485 auto &byte = this->m_byte;
486 byte = (byte & ~this->mask()) | (u << this->shift());
487 return *this;
488 }
489 maskproxy_assignable_type &operator|=(const uint8_t u)
490 {
491 assert(!(u & ~bitmask_low_aligned()));
492 this->m_byte |= (u << this->shift());
493 return *this;
494 }
495 };
496 template <typename R, typename A>
make_maskproxy(A & a,const size_type segnum)497 static R make_maskproxy(A &a, const size_type segnum)
498 {
499 const size_type idx = segnum / divisor;
500 if (idx >= a.size())
501 throw std::out_of_range("index exceeds segment range");
502 const size_type bit = segnum % divisor;
503 return R(a[idx], bit);
504 }
505 public:
506 /* Explicitly invoke the default constructor for `a` so that the
507 * storage is cleared.
508 */
visited_segment_mask_base()509 visited_segment_mask_base() :
510 a()
511 {
512 }
clear()513 void clear()
514 {
515 a = {};
516 }
517 };
518
519 template <>
520 template <typename R>
521 struct visited_segment_mask_base<1>::maskproxy_assignable_type : maskproxy_byte_reference<R>
522 {
523 DXX_INHERIT_CONSTRUCTORS(maskproxy_assignable_type, maskproxy_byte_reference<R>);
524 maskproxy_assignable_type &operator=(const bool b)
525 {
526 const auto m = this->mask();
527 auto &byte = this->m_byte;
528 if (b)
529 byte |= m;
530 else
531 byte &= ~m;
532 return *this;
533 }
534 };
535
536 /* This type must be separate so that its members are defined after the
537 * specialization of maskproxy_assignable_type. If these are defined
538 * inline in visited_segment_mask_base, gcc crashes.
539 * Known bad:
540 * gcc-4.9.4
541 * gcc-5.4.0
542 * gcc-6.4.0
543 * gcc-7.2.0
544 */
545 template <unsigned bits>
546 class visited_segment_mask_t : visited_segment_mask_base<bits>
547 {
548 using base_type = visited_segment_mask_base<bits>;
549 public:
550 auto operator[](const typename base_type::size_type segnum)
551 {
552 return this->template make_maskproxy<typename base_type::template maskproxy_assignable_type<typename base_type::array_t::reference>>(this->a, segnum);
553 }
554 auto operator[](const typename base_type::size_type segnum) const
555 {
556 return this->template make_maskproxy<typename base_type::template maskproxy_assignable_type<typename base_type::array_t::const_reference>>(this->a, segnum);
557 }
558 };
559
560 }
561
562 #ifdef dsx
563 namespace dsx {
564
565 #if defined(DXX_BUILD_DESCENT_II)
566 struct d_level_shared_segment_state : ::dcx::d_level_shared_segment_state
567 {
568 d_level_shared_destructible_light_state DestructibleLights;
569 segnum_t Secret_return_segment;
570 vms_matrix Secret_return_orient;
571 };
572 #endif
573
574 extern d_level_shared_segment_state LevelSharedSegmentState;
575
576 }
577 #endif
578 #endif
579