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