og-odamex/common/doomtype.h
2026-07-31 18:30:10 -05:00

544 lines
13 KiB
C++

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id$
//
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 2006-2026 by The Odamex Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Simple basic typedefs, isolated here to make it easier
// separating modules.
//
//-----------------------------------------------------------------------------
#pragma once
#include <nonstd/span.hpp>
// Standard libc/STL includes we use in countless places
#include "version.h"
#include "errors.h"
#if defined(_MSC_VER)
#define forceinline __forceinline
#elif defined(__GNUC__)
#define forceinline inline __attribute__((always_inline))
#else
#define forceinline inline
#endif
// For __BIG_ENDIAN__ macro, requires forceinline
#include "m_swap.h"
using byte = uint8_t;
using OByteSpan = nonstd::span<byte>;
// Predefined with some OS.
#if !defined(UNIX) && !defined(_WIN32)
#include <limits.h>
#include <float.h>
#endif
#if defined _MSC_VER
#define DBL_EPSILON 2.2204460492503131e-016
#define FLT_EPSILON 1.192092896e-07F
#else
#include <float.h>
#endif
#ifdef UNIX
#define stricmp strcasecmp
#define strnicmp strncasecmp
#endif
namespace limits
{
inline constexpr char MAXCHAR = std::numeric_limits<char>::max();
inline constexpr short MAXSHORT = std::numeric_limits<short>::max();
inline constexpr int MAXINT = std::numeric_limits<int>::max();
inline constexpr unsigned int MAXUINT = std::numeric_limits<unsigned int>::max();
// todo: do we even want to be using long here instead of int32_t/int64_t
// this feels like a remnant from before 64 bit
inline constexpr long MAXLONG = std::numeric_limits<long>::max();
inline constexpr char MINCHAR = std::numeric_limits<char>::min();
inline constexpr short MINSHORT = std::numeric_limits<short>::min();
inline constexpr int MININT = std::numeric_limits<int>::min();
inline constexpr unsigned int MINUINT = std::numeric_limits<unsigned int>::min();
inline constexpr long MINLONG = std::numeric_limits<long>::min();
inline constexpr int32_t MAXFIXED = std::numeric_limits<int32_t>::max();
inline constexpr int32_t MINFIXED = std::numeric_limits<int32_t>::min();
inline constexpr int64_t MAXFIXED64 = std::numeric_limits<int64_t>::max();
inline constexpr int64_t MINFIXED64 = std::numeric_limits<int64_t>::min();
}
using dtime_t = uint64_t;
#ifdef _WIN32
#define PATHSEP "\\"
#define PATHSEPCHAR '\\'
#define PATHLISTSEP ";"
#define PATHLISTSEPCHAR ';'
#else
#define PATHSEP "/"
#define PATHSEPCHAR '/'
#define PATHLISTSEP ":"
#define PATHLISTSEPCHAR ':'
#endif
/**
* @brief Returns a bitfield with a specific bit set.
*/
#define BIT(a) (1U << (a))
/**
* @brief Returns a bitfield with a range of bits set from a to b, inclusive.
*
* @param a Low bit in the mask.
* @param b High bit in the mask.
*/
static constexpr uint32_t BIT_MASK(uint32_t a, uint32_t b)
{
return (static_cast<uint32_t>(-1) >> (31 - b)) & ~(BIT(a) - 1);
}
// game print flags
enum printlevel_t {
PRINT_PICKUP, // Pickup messages
PRINT_OBITUARY, // Death messages
PRINT_HIGH, // Regular messages
PRINT_CHAT, // Chat messages
PRINT_TEAMCHAT, // Chat messages from a teammate
PRINT_SERVERCHAT, // Chat messages from the server
PRINT_WARNING, // Warning messages
PRINT_ERROR, // Fatal error messages
PRINT_NORCON, // Do NOT send the message to any rcon client.
PRINT_FILTERCHAT, // Filter the message to not be displayed ingame, but only in the console (ugly hack)
PRINT_FILTERHIGH, // Filter the message to not be displayed ingame, but only in the console (ugly hack)
PRINT_MAXPRINT
};
//
// MIN
//
// Returns the minimum of a and b.
//
#ifdef MIN
#undef MIN
#endif
template<class T>
forceinline constexpr T MIN(const T a, const T b)
{
return a < b ? a : b;
}
//
// MAX
//
// Returns the maximum of a and b.
//
#ifdef MAX
#undef MAX
#endif
template<class T>
forceinline constexpr T MAX (const T a, const T b)
{
return a > b ? a : b;
}
//
// ARRAY_LENGTH
//
// Safely counts the number of items in an C array.
//
template <typename T, size_t N>
constexpr size_t ARRAY_LENGTH(T (&arr)[N])
{
return std::extent_v<T[N]>;
}
// ----------------------------------------------------------------------------
//
// Color Management Types
//
// ----------------------------------------------------------------------------
// 8-bit palette index
using palindex_t = uint8_t;
//
// argb_t class
//
// Allows ARGB8888 values to be accessed as a packed 32-bit integer or accessed
// by its individual 8-bit color and alpha channels.
//
class argb_t
{
public:
argb_t() { }
argb_t(uint32_t _value) : value(_value) { }
argb_t(uint8_t _r, uint8_t _g, uint8_t _b)
{ seta(255); setr(_r); setg(_g); setb(_b); }
argb_t(uint8_t _a, uint8_t _r, uint8_t _g, uint8_t _b)
{ seta(_a); setr(_r); setg(_g); setb(_b); }
inline operator uint32_t () const { return value; }
inline uint8_t geta() const
{ return channels[a_num]; }
inline uint8_t getr() const
{ return channels[r_num]; }
inline uint8_t getg() const
{ return channels[g_num]; }
inline uint8_t getb() const
{ return channels[b_num]; }
inline void seta(uint8_t n)
{ channels[a_num] = n; }
inline void setr(uint8_t n)
{ channels[r_num] = n; }
inline void setg(uint8_t n)
{ channels[g_num] = n; }
inline void setb(uint8_t n)
{ channels[b_num] = n; }
static void setChannels(uint8_t _a, uint8_t _r, uint8_t _g, uint8_t _b)
{ a_num = _a; r_num = _r; g_num = _g; b_num = _b; }
private:
static inline uint8_t a_num, r_num, g_num, b_num;
union
{
uint32_t value;
uint8_t channels[4];
};
};
//
// fargb_t class
//
// Stores ARGB color channels as four floats. This is ideal for color
// manipulation where precision is important.
//
class fargb_t
{
public:
fargb_t() { }
fargb_t(const argb_t color)
{
seta(color.geta() / 255.0f); setr(color.getr() / 255.0f);
setg(color.getg() / 255.0f); setb(color.getb() / 255.0f);
}
fargb_t(float _r, float _g, float _b)
{ seta(1.0f); setr(_r); setg(_g); setb(_b); }
fargb_t(float _a, float _r, float _g, float _b)
{ seta(_a); setr(_r); setg(_g); setb(_b); }
inline operator argb_t () const
{ return argb_t((uint8_t)(a * 255.0f), (uint8_t)(r * 255.0f), (uint8_t)(g * 255.0f), (uint8_t)(b * 255.0f)); }
inline float geta() const
{ return a; }
inline float getr() const
{ return r; }
inline float getg() const
{ return g; }
inline float getb() const
{ return b; }
inline void seta(float n)
{ a = n; }
inline void setr(float n)
{ r = n; }
inline void setg(float n)
{ g = n; }
inline void setb(float n)
{ b = n; }
private:
float a, r, g, b;
};
//
// fahsv_t class
//
// Stores AHSV color channels as four floats.
//
class fahsv_t
{
public:
fahsv_t() { }
fahsv_t(float _h, float _s, float _v)
{ seta(1.0f); seth(_h); sets(_s); setv(_v); }
fahsv_t(float _a, float _h, float _s, float _v)
{ seta(_a); seth(_h); sets(_s); setv(_v); }
inline float geta() const
{ return a; }
inline float geth() const
{ return h; }
inline float gets() const
{ return s; }
inline float getv() const
{ return v; }
inline void seta(float n)
{ a = n; }
inline void seth(float n)
{ h = n; }
inline void sets(float n)
{ s = n; }
inline void setv(float n)
{ v = n; }
private:
float a, h, s, v;
};
// ----------------------------------------------------------------------------
//
// Color Mapping classes
//
// ----------------------------------------------------------------------------
class translationref_t
{
const palindex_t *m_table;
int m_player_id;
public:
translationref_t();
translationref_t(const palindex_t *table);
translationref_t(const palindex_t *table, const int player_id);
palindex_t tlate(const byte c) const;
int getPlayerID() const;
const palindex_t *getTable() const;
operator bool() const;
};
forceinline palindex_t translationref_t::tlate(const byte c) const
{
#if ODAMEX_DEBUG
if (m_table == NULL)
throw CFatalError("translationref_t::tlate() called with NULL m_table");
#endif
return m_table[c];
}
forceinline int translationref_t::getPlayerID() const
{
return m_player_id;
}
forceinline const byte *translationref_t::getTable() const
{
return m_table;
}
forceinline translationref_t::operator bool() const
{
return m_table != NULL;
}
struct shademap_t {
palindex_t *colormap; // Colormap for 8-bit
argb_t *shademap; // ARGB8888 values for 32-bit
byte ramp[256]; // Light fall-off as a function of distance
// Light levels: 0 = black, 255 = full bright.
// Distance: 0 = near, 255 = far.
};
struct dyncolormap_t;
// This represents a clean reference to a map of both 8-bit colors and 32-bit shades.
struct shaderef_t {
private:
const shademap_t *m_colors; // The color/shade map to use
int m_mapnum; // Which index into the color/shade map to use
public:
mutable const palindex_t *m_colormap; // Computed colormap pointer
mutable const argb_t *m_shademap; // Computed shademap pointer
mutable const dyncolormap_t *m_dyncolormap; // Auto-discovered dynamic colormap
public:
shaderef_t();
shaderef_t(const shademap_t * const colors, const int mapnum);
// Determines if m_colors is NULL
bool isValid() const;
shaderef_t with(const int mapnum) const;
palindex_t index(const byte c) const;
argb_t shade(const byte c) const;
const shademap_t *map() const;
int mapnum() const;
byte ramp() const;
argb_t tlate(const translationref_t &translation, const byte c) const;
[[nodiscard]] bool operator==(const shaderef_t &other) const;
};
forceinline bool shaderef_t::isValid() const
{
return m_colors != NULL;
}
forceinline shaderef_t shaderef_t::with(const int mapnum) const
{
return shaderef_t(m_colors, m_mapnum + mapnum);
}
forceinline palindex_t shaderef_t::index(const palindex_t c) const
{
#if ODAMEX_DEBUG
if (m_colors == NULL)
throw CFatalError("shaderef_t::index(): Bad shaderef_t");
if (m_colors->colormap == NULL)
throw CFatalError("shaderef_t::index(): colormap == NULL!");
#endif
return m_colormap[c];
}
forceinline argb_t shaderef_t::shade(const palindex_t c) const
{
#if ODAMEX_DEBUG
if (m_colors == NULL)
throw CFatalError("shaderef_t::shade(): Bad shaderef_t");
if (m_colors->shademap == NULL)
throw CFatalError("shaderef_t::shade(): shademap == NULL!");
#endif
return m_shademap[c];
}
forceinline const shademap_t *shaderef_t::map() const
{
return m_colors;
}
forceinline int shaderef_t::mapnum() const
{
return m_mapnum;
}
forceinline bool shaderef_t::operator==(const shaderef_t &other) const
{
return m_colors == other.m_colors && m_mapnum == other.m_mapnum;
}
// NOTE(jsd): Rest of shaderef_t and translationref_t functions implemented in "r_main.h"
// NOTE(jsd): Constructors are implemented in "v_palette.cpp"
// rt_rawcolor does no color mapping and only uses the default palette.
template<typename pixel_t>
static forceinline pixel_t rt_rawcolor(const shaderef_t &pal, const byte c);
// rt_mapcolor does color mapping.
template<typename pixel_t>
static forceinline pixel_t rt_mapcolor(const shaderef_t &pal, const byte c);
// rt_tlatecolor does color mapping and translation.
template<typename pixel_t>
static forceinline pixel_t rt_tlatecolor(const shaderef_t &pal, const translationref_t &translation, const byte c);
// rt_blend2 does alpha blending between two colors.
template<typename pixel_t>
static forceinline pixel_t rt_blend2(const pixel_t bg, const int bga, const pixel_t fg, const int fga);
template<>
forceinline palindex_t rt_rawcolor<palindex_t>(const shaderef_t &pal, const byte c)
{
// NOTE(jsd): For rawcolor we do no index.
return (c);
}
template<>
forceinline argb_t rt_rawcolor<argb_t>(const shaderef_t &pal, const byte c)
{
return pal.shade(c);
}
template<>
forceinline palindex_t rt_mapcolor<palindex_t>(const shaderef_t &pal, const byte c)
{
return pal.index(c);
}
template<>
forceinline argb_t rt_mapcolor<argb_t>(const shaderef_t &pal, const byte c)
{
return pal.shade(c);
}
template<>
forceinline palindex_t rt_tlatecolor<palindex_t>(const shaderef_t &pal, const translationref_t &translation, const byte c)
{
return translation.tlate(pal.index(c));
}
template<>
forceinline argb_t rt_tlatecolor<argb_t>(const shaderef_t &pal, const translationref_t &translation, const byte c)
{
return pal.tlate(translation, c);
}