mirror of
https://github.com/odamex/odamex
synced 2026-08-19 02:23:09 -04:00
544 lines
13 KiB
C++
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);
|
|
}
|