dxx-rebirth/common/main/segment.h
Kp 6d3dce4e16 Use enum class for tmap_num2
Define separate enum values for rotation data in both the high bits,
where it is usually kept, and the low bits, where it is sometimes used
for math or comparisons.

Define an enum value to represent the composite of the index and the
rotation, since the composite is not suitable for use as an array
subscript.  Add helper functions to extract the component pieces.
2020-08-24 01:31:28 +00:00

599 lines
15 KiB
C++

/*
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING.txt.
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below.
* See COPYING.txt for license details.
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* Include file for functions which need to access segment data structure.
*
*/
#pragma once
#include <physfs.h>
#include <type_traits>
#include "pstypes.h"
#include "maths.h"
#include "vecmat.h"
#include "dxxsconf.h"
#include "dsx-ns.h"
#ifdef __cplusplus
#include <cassert>
#include <cstdint>
#include <stdexcept>
#include "fwd-segment.h"
#include "countarray.h"
#include "valptridx.h"
#include "objnum.h"
#include "pack.h"
#ifdef dsx
namespace dsx {
// Returns true if segnum references a child, else returns false.
// Note that -1 means no connection, -2 means a connection to the outside world.
static inline bool IS_CHILD(segnum_t s)
{
return s != segment_none && s != segment_exit;
}
}
#endif
namespace dcx {
//Structure for storing u,v,light values.
//NOTE: this structure should be the same as the one in 3d.h
struct uvl
{
fix u, v, l;
};
enum class side_type : uint8_t
{
quad = 1, // render side as quadrilateral
tri_02 = 2, // render side as two triangles, triangulated along edge from 0 to 2
tri_13 = 3, // render side as two triangles, triangulated along edge from 1 to 3
};
#if 0
struct wallnum_t : prohibit_void_ptr<wallnum_t>
{
typedef int16_t integral_type;
integral_type value;
wallnum_t() = default;
wallnum_t(const integral_type &v) : value(v)
{
#ifdef DXX_HAVE_BUILTIN_CONSTANT_P
if (__builtin_constant_p(v))
DXX_ALWAYS_ERROR_FUNCTION(dxx_trap_constant_wall, "constant wall number constructed");
#endif
}
wallnum_t &operator=(integral_type v)
{
#ifdef DXX_HAVE_BUILTIN_CONSTANT_P
if (__builtin_constant_p(v))
DXX_ALWAYS_ERROR_FUNCTION(dxx_trap_constant_wall, "constant wall number assigned");
#endif
value = v;
return *this;
}
template <integral_type I>
wallnum_t &operator=(const wall_magic_constant_t<I> &)
{
value = I;
return *this;
}
bool operator==(const wallnum_t &v) const { return value == v.value; }
bool operator==(const int &v) const
{
#ifdef DXX_HAVE_BUILTIN_CONSTANT_P
if (__builtin_constant_p(v))
DXX_ALWAYS_ERROR_FUNCTION(dxx_trap_constant_wall, "constant wall number compared");
#endif
return value == v;
}
template <integral_type I>
bool operator==(const wall_magic_constant_t<I> &) const { return value == I; }
template <typename T>
bool operator!=(const T &v) const { return !(*this == v); }
template <typename T>
bool operator==(const T &v) const = delete;
template <typename T>
bool operator<(const T &v) const
{
static_assert(std::is_integral<T>::value, "non-integral wall number compared");
#ifdef DXX_HAVE_BUILTIN_CONSTANT_P
if (__builtin_constant_p(v))
DXX_ALWAYS_ERROR_FUNCTION(dxx_trap_constant_wall, "constant wall number compared");
#endif
return value < v;
}
template <typename T>
bool operator>(const T &v) const
{
return v < *this;
}
template <typename T>
bool operator<=(const T &) const = delete;
template <typename T>
bool operator>=(const T &) const = delete;
constexpr operator integral_type() const { return value; }
operator integral_type &() { return value; }
};
#endif
struct shared_side
{
struct illegal_type;
side_type m_type; // replaces num_faces and tri_edge, 1 = quad, 2 = 0:2 triangulation, 3 = 1:3 triangulation
const side_type &get_type() const { return m_type; }
void set_type(side_type t) { m_type = t; }
wallnum_t wall_num;
std::array<vms_vector, 2> normals; // 2 normals, if quadrilateral, both the same.
};
/* texture2_value uses the low 14 bits for the array index and the high
* 2 bits for texture2_rotation_high
*/
enum class texture2_value : uint16_t
{
None,
};
enum class texture2_rotation_low : uint8_t
{
Normal = 0,
_1 = 1,
_2 = 2,
_3 = 3,
};
#define TEXTURE2_ROTATION_SHIFT 14u
#define TEXTURE2_ROTATION_INDEX_MASK ((1u << TEXTURE2_ROTATION_SHIFT) - 1u)
enum class texture2_rotation_high : uint16_t
{
Normal = 0,
_1 = static_cast<uint16_t>(texture2_rotation_low::_1) << TEXTURE2_ROTATION_SHIFT,
_2 = static_cast<uint16_t>(texture2_rotation_low::_2) << TEXTURE2_ROTATION_SHIFT,
_3 = static_cast<uint16_t>(texture2_rotation_low::_3) << TEXTURE2_ROTATION_SHIFT,
};
static constexpr texture2_rotation_high &operator++(texture2_rotation_high &t)
{
/* Cast to uint32, step to the next value, cast back. The cast back
* will truncate to the low 16 bits, causing
* texture2_rotation_high::_3 to roll over to
* texture2_rotation_high::Normal.
*/
return (t = static_cast<texture2_rotation_high>(static_cast<uint32_t>(t) + (1u << TEXTURE2_ROTATION_SHIFT)));
}
static constexpr uint16_t get_texture_index(const texture2_value t)
{
return static_cast<uint16_t>(t) & TEXTURE2_ROTATION_INDEX_MASK;
}
static constexpr texture2_rotation_high get_texture_rotation_high(const texture2_value t)
{
return static_cast<texture2_rotation_high>(static_cast<uint16_t>(t) & ~TEXTURE2_ROTATION_INDEX_MASK);
}
static constexpr texture2_rotation_low get_texture_rotation_low(const texture2_rotation_high t)
{
return static_cast<texture2_rotation_low>(static_cast<uint16_t>(t) >> TEXTURE2_ROTATION_SHIFT);
}
static constexpr texture2_rotation_low get_texture_rotation_low(const texture2_value t)
{
return get_texture_rotation_low(get_texture_rotation_high(t));
}
static constexpr texture2_value build_texture2_value(const unsigned t, const texture2_rotation_high rotation)
{
return static_cast<texture2_value>(t | static_cast<unsigned>(rotation));
}
#undef TEXTURE2_ROTATION_SHIFT
#undef TEXTURE2_ROTATION_INDEX_MASK
struct unique_side
{
int16_t tmap_num;
texture2_value tmap_num2;
std::array<uvl, 4> uvls;
};
#ifdef dsx
struct shared_segment
{
#if DXX_USE_EDITOR
segnum_t segnum; // segment number, not sure what it means
short group; // group number to which the segment belongs.
#endif
std::array<segnum_t, MAX_SIDES_PER_SEGMENT> children; // indices of 6 children segments, front, left, top, right, bottom, back
std::array<unsigned, MAX_VERTICES_PER_SEGMENT> verts; // vertex ids of 4 front and 4 back vertices
uint8_t special; // what type of center this is
int8_t matcen_num; // which center segment is associated with.
uint8_t station_idx;
/* if DXX_BUILD_DESCENT_II */
uint8_t s2_flags;
/* endif */
std::array<shared_side, MAX_SIDES_PER_SEGMENT> sides;
};
struct unique_segment
{
objnum_t objects; // pointer to objects in this segment
// If bit n (1 << n) is set, then side #n in segment has had light subtracted from original (editor-computed) value.
uint8_t light_subtracted;
/* if DXX_BUILD_DESCENT_II */
uint8_t slide_textures;
/* endif */
fix static_light;
std::array<unique_side, MAX_SIDES_PER_SEGMENT> sides;
};
struct segment : unique_segment, shared_segment
{
};
template <typename S, typename U>
struct susegment
{
using qualified_segment = typename std::conditional<std::is_const<S>::value && std::is_const<U>::value, const segment, segment>::type;
S &s;
U &u;
constexpr susegment(qualified_segment &b) :
s(b), u(b)
{
}
constexpr susegment(const susegment &) = default;
constexpr susegment(susegment &&) = default;
template <typename S2, typename U2, typename std::enable_if<std::is_convertible<S2 &, S &>::value && std::is_convertible<U2 &, U &>::value, int>::type = 0>
constexpr susegment(const susegment<S2, U2> &r) :
s(r.s), u(r.u)
{
}
template <typename T, typename std::enable_if<std::is_convertible<T &&, qualified_segment &>::value, int>::type = 0>
constexpr susegment(T &&t) :
susegment(static_cast<qualified_segment &>(t))
{
}
operator S &() const
{
return s;
}
operator U &() const
{
return u;
}
};
#endif
struct count_segment_array_t : public count_array_t<segnum_t, MAX_SEGMENTS> {};
struct group
{
struct segment_array_type_t : public count_segment_array_t {};
struct vertex_array_type_t : public count_array_t<unsigned, MAX_VERTICES> {};
segment_array_type_t segments;
vertex_array_type_t vertices;
void clear()
{
segments.clear();
vertices.clear();
}
};
#ifdef dsx
#define Highest_segment_index (Segments.get_count() - 1)
DXX_VALPTRIDX_DEFINE_GLOBAL_FACTORIES(segment, seg, Segments);
#endif
// Globals from mglobal.c
struct vertex : vms_vector
{
vertex() = default;
vertex(const fix &a, const fix &b, const fix &c) :
vms_vector{a, b, c}
{
}
explicit vertex(const vms_vector &v) :
vms_vector(v)
{
}
};
}
#ifdef dsx
struct shared_side::illegal_type : std::runtime_error
{
const shared_segment *const m_segment;
const shared_side *const m_side;
illegal_type(const shared_segment &seg, const shared_side &s) :
runtime_error("illegal side type"),
m_segment(&seg), m_side(&s)
{
}
illegal_type(const shared_side &s) :
runtime_error("illegal side type"),
m_segment(nullptr), m_side(&s)
{
}
};
namespace dsx {
#if defined(DXX_BUILD_DESCENT_II)
// New stuff, 10/14/95: For shooting out lights and monitors.
// Light cast upon vert_light vertices in segnum:sidenum by some light
struct delta_light : prohibit_void_ptr<delta_light>
{
segnum_t segnum;
uint8_t sidenum;
std::array<ubyte, 4> vert_light;
};
// Light at segnum:sidenum casts light on count sides beginning at index (in array Delta_lights)
struct dl_index {
segnum_t segnum;
uint8_t sidenum;
uint8_t count;
uint16_t index;
bool operator<(const dl_index &rhs) const
{
if (segnum < rhs.segnum)
return true;
if (segnum > rhs.segnum)
return false;
return sidenum < rhs.sidenum;
}
};
struct d_level_shared_destructible_light_state
{
valptridx<dl_index>::array_managed_type Dl_indices;
d_delta_light_array Delta_lights;
};
#endif
}
#endif
namespace dcx {
#ifdef dsx
struct d_level_shared_vertex_state
{
unsigned Num_vertices;
private:
vertex_array Vertices;
#if DXX_USE_EDITOR
std::array<uint8_t, MAX_VERTICES> Vertex_active; // !0 means vertex is in use, 0 means not in use.
#endif
public:
auto &get_vertices()
{
return Vertices;
}
const auto &get_vertices() const
{
return Vertices;
}
#if DXX_USE_EDITOR
auto &get_vertex_active()
{
return Vertex_active;
}
const auto &get_vertex_active() const
{
return Vertex_active;
}
#endif
};
struct d_level_shared_segment_state
{
unsigned Num_segments;
d_level_shared_vertex_state LevelSharedVertexState;
auto &get_segments()
{
return Segments;
}
const auto &get_segments() const
{
return Segments;
}
auto &get_vertex_state()
{
return LevelSharedVertexState;
}
const auto &get_vertex_state() const
{
return LevelSharedVertexState;
}
};
struct d_level_unique_automap_state
{
std::array<uint8_t, MAX_SEGMENTS> Automap_visited;
};
struct d_level_unique_segment_state
{
auto &get_segments()
{
return Segments;
}
const auto &get_segments() const
{
return Segments;
}
};
extern d_level_unique_automap_state LevelUniqueAutomapState;
extern d_level_unique_segment_state LevelUniqueSegmentState;
#endif
template <unsigned bits>
class visited_segment_mask_base
{
static_assert(bits == 1 || bits == 2 || bits == 4, "bits must align in bytes");
protected:
enum
{
divisor = 8 / bits,
};
using array_t = std::array<uint8_t, (MAX_SEGMENTS + (divisor - 1)) / divisor>;
typedef typename array_t::size_type size_type;
array_t a;
struct maskproxy_shift_count_type
{
using bitmask_low_aligned = std::integral_constant<unsigned, (1 << bits) - 1>;
const unsigned m_shift;
unsigned shift() const
{
return m_shift;
}
typename array_t::value_type mask() const
{
return bitmask_low_aligned() << shift();
}
maskproxy_shift_count_type(const unsigned s) :
m_shift(s * bits)
{
}
};
template <typename R>
struct maskproxy_byte_reference : public maskproxy_shift_count_type
{
R m_byte;
maskproxy_byte_reference(R byte, const unsigned s) :
maskproxy_shift_count_type(s), m_byte(byte)
{
}
operator uint8_t() const
{
return (this->m_byte >> this->shift()) & typename maskproxy_shift_count_type::bitmask_low_aligned();
}
};
template <typename R>
struct maskproxy_assignable_type : maskproxy_byte_reference<R>
{
DXX_INHERIT_CONSTRUCTORS(maskproxy_assignable_type, maskproxy_byte_reference<R>);
using typename maskproxy_shift_count_type::bitmask_low_aligned;
maskproxy_assignable_type &operator=(const uint8_t u)
{
assert(!(u & ~bitmask_low_aligned()));
auto &byte = this->m_byte;
byte = (byte & ~this->mask()) | (u << this->shift());
return *this;
}
maskproxy_assignable_type &operator|=(const uint8_t u)
{
assert(!(u & ~bitmask_low_aligned()));
this->m_byte |= (u << this->shift());
return *this;
}
};
template <typename R, typename A>
static R make_maskproxy(A &a, const size_type segnum)
{
const size_type idx = segnum / divisor;
if (idx >= a.size())
throw std::out_of_range("index exceeds segment range");
const size_type bit = segnum % divisor;
return R(a[idx], bit);
}
public:
/* Explicitly invoke the default constructor for `a` so that the
* storage is cleared.
*/
visited_segment_mask_base() :
a()
{
}
void clear()
{
a = {};
}
};
template <>
template <typename R>
struct visited_segment_mask_base<1>::maskproxy_assignable_type : maskproxy_byte_reference<R>
{
DXX_INHERIT_CONSTRUCTORS(maskproxy_assignable_type, maskproxy_byte_reference<R>);
maskproxy_assignable_type &operator=(const bool b)
{
const auto m = this->mask();
auto &byte = this->m_byte;
if (b)
byte |= m;
else
byte &= ~m;
return *this;
}
};
/* This type must be separate so that its members are defined after the
* specialization of maskproxy_assignable_type. If these are defined
* inline in visited_segment_mask_base, gcc crashes.
* Known bad:
* gcc-4.9.4
* gcc-5.4.0
* gcc-6.4.0
* gcc-7.2.0
*/
template <unsigned bits>
class visited_segment_mask_t : visited_segment_mask_base<bits>
{
using base_type = visited_segment_mask_base<bits>;
public:
auto operator[](const typename base_type::size_type segnum)
{
return this->template make_maskproxy<typename base_type::template maskproxy_assignable_type<typename base_type::array_t::reference>>(this->a, segnum);
}
auto operator[](const typename base_type::size_type segnum) const
{
return this->template make_maskproxy<typename base_type::template maskproxy_assignable_type<typename base_type::array_t::const_reference>>(this->a, segnum);
}
};
}
#ifdef dsx
namespace dsx {
#if defined(DXX_BUILD_DESCENT_II)
struct d_level_shared_segment_state : ::dcx::d_level_shared_segment_state
{
d_level_shared_destructible_light_state DestructibleLights;
segnum_t Secret_return_segment;
vms_matrix Secret_return_orient;
};
#endif
extern d_level_shared_segment_state LevelSharedSegmentState;
}
#endif
#endif