Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gamedev/fggl
  • onuralpsezer/fggl
2 results
Show changes
Showing
with 1892 additions and 2958 deletions
......@@ -41,7 +41,7 @@ namespace fggl::debug {
void draw();
inline void addWindow(const std::string &name, DebugUIDraw window) {
m_windows[name] = DebugWindow{true, std::move(window) };
m_windows[name] = DebugWindow{true, std::move(window)};
}
inline void visible(bool state) {
......
......@@ -230,6 +230,7 @@
//
#ifndef DEBUG_DRAW_OVERFLOWED
#include <cstdio>
#define DEBUG_DRAW_OVERFLOWED(message) std::fprintf(stderr, "%s\n", message)
#endif // DEBUG_DRAW_OVERFLOWED
......@@ -275,7 +276,7 @@ typedef float ddVec3[3];
// passing by const reference instead, however, some platforms have optimized
// hardware registers for vec3s/vec4s, so passing by value might also be efficient.
typedef const ddVec3 ddVec3_In;
typedef ddVec3 ddVec3_Out;
typedef ddVec3 ddVec3_Out;
#define DEBUG_DRAW_VEC3_TYPE_DEFINED 1
#endif // DEBUG_DRAW_VEC3_TYPE_DEFINED
......@@ -302,7 +303,7 @@ typedef float ddMat4x4[4 * 4];
// If you change it to some structured type, it might be more efficient
// passing by const reference instead.
typedef const ddMat4x4 ddMat4x4_In;
typedef ddMat4x4 ddMat4x4_Out;
typedef ddMat4x4 ddMat4x4_Out;
#define DEBUG_DRAW_MAT4X4_TYPE_DEFINED 1
#endif // DEBUG_DRAW_MAT4X4_TYPE_DEFINED
......@@ -315,23 +316,22 @@ typedef ddMat4x4 ddMat4x4_Out;
// null-terminated const char* string pointer. That's it.
// An array subscript operator [] is not required for ddStr.
#include <string>
typedef std::string ddStr;
typedef const ddStr & ddStr_In;
typedef ddStr & ddStr_Out;
typedef std::string ddStr;
typedef const ddStr &ddStr_In;
typedef ddStr &ddStr_Out;
#define DEBUG_DRAW_STRING_TYPE_DEFINED 1
#endif // DEBUG_DRAW_STRING_TYPE_DEFINED
namespace dd
{
namespace dd {
// ========================================================
// Optional built-in colors in RGB float format:
// ========================================================
#ifndef DEBUG_DRAW_NO_DEFAULT_COLORS
namespace colors
{
namespace colors {
extern const ddVec3 AliceBlue;
extern const ddVec3 AntiqueWhite;
extern const ddVec3 Aquamarine;
......@@ -480,8 +480,8 @@ namespace dd
#ifdef DEBUG_DRAW_EXPLICIT_CONTEXT
struct OpaqueContextType { };
typedef OpaqueContextType * ContextHandle;
#define DD_EXPLICIT_CONTEXT_ONLY(...) __VA_ARGS__
typedef OpaqueContextType * ContextHandle;
#define DD_EXPLICIT_CONTEXT_ONLY(...) __VA_ARGS__
#else // !DEBUG_DRAW_EXPLICIT_CONTEXT
#define DD_EXPLICIT_CONTEXT_ONLY(...) /* nothing */
#endif // DEBUG_DRAW_EXPLICIT_CONTEXT
......@@ -519,7 +519,7 @@ namespace dd
// The third element (Z) of the position vector is ignored.
// Note: Newlines and tabs are handled (1 tab = 4 spaces).
void screenText(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
const char * str,
const char *str,
ddVec3_In pos,
ddVec3_In color,
float scaling = 1.0f,
......@@ -530,7 +530,7 @@ namespace dd
// sx/sy, sw/sh are the viewport coordinates/size, in pixels.
// 'vpMatrix' is the view * projection transform to map the text from 3D to 2D.
void projectedText(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
const char * str,
const char *str,
ddVec3_In pos,
ddVec3_In color,
ddMat4x4_In vpMatrix,
......@@ -684,23 +684,19 @@ namespace dd
// The only drawing type the user has to interface with.
// ========================================================
union DrawVertex
{
struct
{
union DrawVertex {
struct {
float x, y, z;
float r, g, b;
float size;
} point;
struct
{
struct {
float x, y, z;
float r, g, b;
} line;
struct
{
struct {
float x, y;
float u, v;
float r, g, b;
......@@ -711,8 +707,8 @@ namespace dd
// Opaque handle to a texture object.
// Used by the debug text drawing functions.
//
struct OpaqueTextureType { };
typedef OpaqueTextureType * GlyphTextureHandle;
struct OpaqueTextureType {};
typedef OpaqueTextureType *GlyphTextureHandle;
// ========================================================
// Debug Draw rendering callbacks:
......@@ -720,8 +716,7 @@ namespace dd
// tie this code directly to a specific rendering API.
// ========================================================
class RenderInterface
{
class RenderInterface {
public:
//
......@@ -743,16 +738,16 @@ namespace dd
// The pixel values range from 255 for a pixel within a glyph to 0 for a transparent pixel.
// If createGlyphTexture() returns null, the renderer will disable all text drawing functions.
//
virtual GlyphTextureHandle createGlyphTexture(int width, int height, const void * pixels);
virtual GlyphTextureHandle createGlyphTexture(int width, int height, const void *pixels);
virtual void destroyGlyphTexture(GlyphTextureHandle glyphTex);
//
// Batch drawing methods for the primitives used by the debug renderer.
// If you don't wish to support a given primitive type, don't override the method.
//
virtual void drawPointList(const DrawVertex * points, int count, bool depthEnabled);
virtual void drawLineList(const DrawVertex * lines, int count, bool depthEnabled);
virtual void drawGlyphList(const DrawVertex * glyphs, int count, GlyphTextureHandle glyphTex);
virtual void drawPointList(const DrawVertex *points, int count, bool depthEnabled);
virtual void drawLineList(const DrawVertex *lines, int count, bool depthEnabled);
virtual void drawGlyphList(const DrawVertex *glyphs, int count, GlyphTextureHandle glyphTex);
// User defined cleanup. Nothing by default.
virtual ~RenderInterface() = 0;
......@@ -763,19 +758,18 @@ namespace dd
// ========================================================
// Flags for dd::flush()
enum FlushFlags
{
enum FlushFlags {
FlushPoints = 1 << 1,
FlushLines = 1 << 2,
FlushText = 1 << 3,
FlushAll = (FlushPoints | FlushLines | FlushText)
FlushLines = 1 << 2,
FlushText = 1 << 3,
FlushAll = (FlushPoints | FlushLines | FlushText)
};
// Initialize with the user-supplied renderer interface.
// Given object must remain valid until after dd::shutdown() is called!
// If 'renderer' is null, the Debug Draw functions become no-ops, but
// can still be safely called.
bool initialize(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle * outCtx,) RenderInterface * renderer);
bool initialize(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle * outCtx,) RenderInterface *renderer);
// After this is called, it is safe to dispose the dd::RenderInterface instance
// you passed to dd::initialize(). Shutdown will also attempt to free the glyph texture.
......@@ -814,29 +808,29 @@ namespace dd
#ifdef DEBUG_DRAW_IMPLEMENTATION
#ifndef DD_MALLOC
#include <cstdlib>
#define DD_MALLOC std::malloc
#define DD_MFREE std::free
#include <cstdlib>
#define DD_MALLOC std::malloc
#define DD_MFREE std::free
#endif // DD_MALLOC
#if DEBUG_DRAW_USE_STD_MATH
#include <math.h>
#include <float.h>
#include <math.h>
#include <float.h>
#endif // DEBUG_DRAW_USE_STD_MATH
namespace dd
{
#if defined(FLT_EPSILON) && DEBUG_DRAW_USE_STD_MATH
static const float FloatEpsilon = FLT_EPSILON;
static const float FloatEpsilon = FLT_EPSILON;
#else // !FLT_EPSILON || !DEBUG_DRAW_USE_STD_MATH
static const float FloatEpsilon = 1e-14;
static const float FloatEpsilon = 1e-14;
#endif // FLT_EPSILON && DEBUG_DRAW_USE_STD_MATH
#if defined(M_PI) && DEBUG_DRAW_USE_STD_MATH
static const float PI = static_cast<float>(M_PI);
static const float PI = static_cast<float>(M_PI);
#else // !M_PI || !DEBUG_DRAW_USE_STD_MATH
static const float PI = 3.1415926535897931f;
static const float PI = 3.1415926535897931f;
#endif // M_PI && DEBUG_DRAW_USE_STD_MATH
static const float HalfPI = PI * 0.5f;
......@@ -845,13 +839,13 @@ static const float TAU = PI * 2.0f;
template<typename T>
static inline float degreesToRadians(const T degrees)
{
return (static_cast<float>(degrees) * PI / 180.0f);
return (static_cast<float>(degrees) * PI / 180.0f);
}
template<typename T, int Size>
static inline int arrayLength(const T (&)[Size])
{
return Size;
return Size;
}
// ========================================================
......@@ -1009,35 +1003,35 @@ const ddVec3 YellowGreen = {0.603922f, 0.803922f, 0.196078f};
struct FontChar
{
std::uint16_t x;
std::uint16_t y;
std::uint16_t x;
std::uint16_t y;
};
struct FontCharSet
{
enum { MaxChars = 256 };
const std::uint8_t * bitmap;
int bitmapWidth;
int bitmapHeight;
int bitmapColorChannels;
int bitmapDecompressSize;
int charBaseHeight;
int charWidth;
int charHeight;
int charCount;
FontChar chars[MaxChars];
enum { MaxChars = 256 };
const std::uint8_t * bitmap;
int bitmapWidth;
int bitmapHeight;
int bitmapColorChannels;
int bitmapDecompressSize;
int charBaseHeight;
int charWidth;
int charHeight;
int charCount;
FontChar chars[MaxChars];
};
#if DEBUG_DRAW_CXX11_SUPPORTED
#define DD_ALIGNED_BUFFER(name) alignas(16) static const std::uint8_t name[]
#define DD_ALIGNED_BUFFER(name) alignas(16) static const std::uint8_t name[]
#else // !C++11
#if defined(__GNUC__) // Clang & GCC
#define DD_ALIGNED_BUFFER(name) static const std::uint8_t name[] __attribute__((aligned(16)))
#elif defined(_MSC_VER) // Visual Studio
#define DD_ALIGNED_BUFFER(name) __declspec(align(16)) static const std::uint8_t name[]
#else // Unknown compiler
#define DD_ALIGNED_BUFFER(name) static const std::uint8_t name[] /* hope for the best! */
#endif // Compiler id
#if defined(__GNUC__) // Clang & GCC
#define DD_ALIGNED_BUFFER(name) static const std::uint8_t name[] __attribute__((aligned(16)))
#elif defined(_MSC_VER) // Visual Studio
#define DD_ALIGNED_BUFFER(name) __declspec(align(16)) static const std::uint8_t name[]
#else // Unknown compiler
#define DD_ALIGNED_BUFFER(name) static const std::uint8_t name[] /* hope for the best! */
#endif // Compiler id
#endif // DEBUG_DRAW_CXX11_SUPPORTED
//
......@@ -1502,34 +1496,34 @@ static const int LzwMaxDictEntries = (1 << LzwMaxDictBits); // 4096
struct LzwDictionary
{
// Dictionary entries 0-255 are always reserved to the byte/ASCII range.
struct Entry
{
int code;
int value;
};
// Dictionary entries 0-255 are always reserved to the byte/ASCII range.
struct Entry
{
int code;
int value;
};
int size;
Entry entries[LzwMaxDictEntries];
int size;
Entry entries[LzwMaxDictEntries];
LzwDictionary();
int findIndex(int code, int value) const;
bool add(int code, int value);
bool flush(int & codeBitsWidth);
LzwDictionary();
int findIndex(int code, int value) const;
bool add(int code, int value);
bool flush(int & codeBitsWidth);
};
struct LzwBitStreamReader
{
const std::uint8_t * stream; // Pointer to the external bit stream. Not owned by the reader.
int sizeInBytes; // Size of the stream in bytes. Might include padding.
int sizeInBits; // Size of the stream in bits, padding not include.
int currBytePos; // Current byte being read in the stream.
int nextBitPos; // Bit position within the current byte to access next. 0 to 7.
int numBitsRead; // Total bits read from the stream so far. Never includes byte-rounding.
const std::uint8_t * stream; // Pointer to the external bit stream. Not owned by the reader.
int sizeInBytes; // Size of the stream in bytes. Might include padding.
int sizeInBits; // Size of the stream in bits, padding not include.
int currBytePos; // Current byte being read in the stream.
int nextBitPos; // Bit position within the current byte to access next. 0 to 7.
int numBitsRead; // Total bits read from the stream so far. Never includes byte-rounding.
LzwBitStreamReader(const std::uint8_t * bitStream, int byteCount, int bitCount);
bool readNextBit(int & outBit);
int readBits(int bitCount);
LzwBitStreamReader(const std::uint8_t * bitStream, int byteCount, int bitCount);
bool readNextBit(int & outBit);
int readBits(int bitCount);
};
// ========================================================
......@@ -1538,59 +1532,59 @@ struct LzwBitStreamReader
LzwDictionary::LzwDictionary()
{
// First 256 dictionary entries are reserved to the byte/ASCII
// range. Additional entries follow for the character sequences
// found in the input. Up to 4096 - 256 (LzwMaxDictEntries - LzwFirstCode).
size = LzwFirstCode;
for (int i = 0; i < size; ++i)
{
entries[i].code = LzwNil;
entries[i].value = i;
}
// First 256 dictionary entries are reserved to the byte/ASCII
// range. Additional entries follow for the character sequences
// found in the input. Up to 4096 - 256 (LzwMaxDictEntries - LzwFirstCode).
size = LzwFirstCode;
for (int i = 0; i < size; ++i)
{
entries[i].code = LzwNil;
entries[i].value = i;
}
}
int LzwDictionary::findIndex(const int code, const int value) const
{
if (code == LzwNil)
{
return value;
}
for (int i = 0; i < size; ++i)
{
if (entries[i].code == code && entries[i].value == value)
{
return i;
}
}
return LzwNil;
if (code == LzwNil)
{
return value;
}
for (int i = 0; i < size; ++i)
{
if (entries[i].code == code && entries[i].value == value)
{
return i;
}
}
return LzwNil;
}
bool LzwDictionary::add(const int code, const int value)
{
if (size == LzwMaxDictEntries)
{
return false;
}
entries[size].code = code;
entries[size].value = value;
++size;
return true;
if (size == LzwMaxDictEntries)
{
return false;
}
entries[size].code = code;
entries[size].value = value;
++size;
return true;
}
bool LzwDictionary::flush(int & codeBitsWidth)
{
if (size == (1 << codeBitsWidth))
{
++codeBitsWidth;
if (codeBitsWidth > LzwMaxDictBits)
{
// Clear the dictionary (except the first 256 byte entries).
codeBitsWidth = LzwStartBits;
size = LzwFirstCode;
return true;
}
}
return false;
if (size == (1 << codeBitsWidth))
{
++codeBitsWidth;
if (codeBitsWidth > LzwMaxDictBits)
{
// Clear the dictionary (except the first 256 byte entries).
codeBitsWidth = LzwStartBits;
size = LzwFirstCode;
return true;
}
}
return false;
}
// ========================================================
......@@ -1598,47 +1592,47 @@ bool LzwDictionary::flush(int & codeBitsWidth)
// ========================================================
LzwBitStreamReader::LzwBitStreamReader(const std::uint8_t * bitStream, const int byteCount, const int bitCount)
: stream(bitStream)
, sizeInBytes(byteCount)
, sizeInBits(bitCount)
, currBytePos(0)
, nextBitPos(0)
, numBitsRead(0)
: stream(bitStream)
, sizeInBytes(byteCount)
, sizeInBits(bitCount)
, currBytePos(0)
, nextBitPos(0)
, numBitsRead(0)
{ }
bool LzwBitStreamReader::readNextBit(int & outBit)
{
if (numBitsRead >= sizeInBits)
{
return false; // We are done.
}
const int mask = 1 << nextBitPos;
outBit = !!(stream[currBytePos] & mask);
++numBitsRead;
if (++nextBitPos == 8)
{
nextBitPos = 0;
++currBytePos;
}
return true;
if (numBitsRead >= sizeInBits)
{
return false; // We are done.
}
const int mask = 1 << nextBitPos;
outBit = !!(stream[currBytePos] & mask);
++numBitsRead;
if (++nextBitPos == 8)
{
nextBitPos = 0;
++currBytePos;
}
return true;
}
int LzwBitStreamReader::readBits(const int bitCount)
{
int num = 0;
for (int b = 0; b < bitCount; ++b)
{
int bit;
if (!readNextBit(bit))
{
break;
}
const int mask = 1 << b;
num = (num & ~mask) | (-bit & mask);
}
return num;
int num = 0;
for (int b = 0; b < bitCount; ++b)
{
int bit;
if (!readNextBit(bit))
{
break;
}
const int mask = 1 << b;
num = (num & ~mask) | (-bit & mask);
}
return num;
}
// ========================================================
......@@ -1647,129 +1641,129 @@ int LzwBitStreamReader::readBits(const int bitCount)
static bool lzwOutputByte(int code, std::uint8_t *& output, int outputSizeBytes, int & bytesDecodedSoFar)
{
if (code < 0 || code >= 256)
{
return false;
}
if (bytesDecodedSoFar >= outputSizeBytes)
{
return false;
}
*output++ = static_cast<std::uint8_t>(code);
++bytesDecodedSoFar;
return true;
if (code < 0 || code >= 256)
{
return false;
}
if (bytesDecodedSoFar >= outputSizeBytes)
{
return false;
}
*output++ = static_cast<std::uint8_t>(code);
++bytesDecodedSoFar;
return true;
}
static bool lzwOutputSequence(const LzwDictionary & dict, int code,
std::uint8_t *& output, int outputSizeBytes,
int & bytesDecodedSoFar, int & firstByte)
{
// A sequence is stored backwards, so we have to write
// it to a temp then output the buffer in reverse.
int i = 0;
std::uint8_t sequence[LzwMaxDictEntries];
do
{
sequence[i++] = dict.entries[code].value & 0xFF;
code = dict.entries[code].code;
} while (code >= 0);
firstByte = sequence[--i];
for (; i >= 0; --i)
{
if (!lzwOutputByte(sequence[i], output, outputSizeBytes, bytesDecodedSoFar))
{
return false;
}
}
return true;
std::uint8_t *& output, int outputSizeBytes,
int & bytesDecodedSoFar, int & firstByte)
{
// A sequence is stored backwards, so we have to write
// it to a temp then output the buffer in reverse.
int i = 0;
std::uint8_t sequence[LzwMaxDictEntries];
do
{
sequence[i++] = dict.entries[code].value & 0xFF;
code = dict.entries[code].code;
} while (code >= 0);
firstByte = sequence[--i];
for (; i >= 0; --i)
{
if (!lzwOutputByte(sequence[i], output, outputSizeBytes, bytesDecodedSoFar))
{
return false;
}
}
return true;
}
static int lzwDecompress(const void * compressedData, int compressedSizeBytes,
int compressedSizeBits, void * uncompressedData,
int uncompressedSizeBytes)
{
if (compressedData == nullptr || uncompressedData == nullptr)
{
return 0;
}
if (compressedSizeBytes <= 0 || compressedSizeBits <= 0 || uncompressedSizeBytes <= 0)
{
return 0;
}
int code = LzwNil;
int prevCode = LzwNil;
int codeBitsWidth = LzwStartBits;
int firstByte = 0;
int bytesDecoded = 0;
const std::uint8_t * compressedPtr = reinterpret_cast<const std::uint8_t *>(compressedData);
std::uint8_t * uncompressedPtr = reinterpret_cast<std::uint8_t *>(uncompressedData);
// We'll reconstruct the dictionary based on the bit stream codes.
LzwBitStreamReader bitStream(compressedPtr, compressedSizeBytes, compressedSizeBits);
LzwDictionary dictionary;
// We check to avoid an overflow of the user buffer.
// If the buffer is smaller than the decompressed size, we
// break the loop and return the current decompression count.
while (bitStream.numBitsRead < bitStream.sizeInBits)
{
if (codeBitsWidth > LzwMaxDictBits)
{
break;
}
code = bitStream.readBits(codeBitsWidth);
if (prevCode == LzwNil)
{
if (!lzwOutputByte(code, uncompressedPtr, uncompressedSizeBytes, bytesDecoded))
{
break;
}
firstByte = code;
prevCode = code;
continue;
}
if (code >= dictionary.size)
{
if (!lzwOutputSequence(dictionary, prevCode, uncompressedPtr,
uncompressedSizeBytes, bytesDecoded, firstByte))
{
break;
}
if (!lzwOutputByte(firstByte, uncompressedPtr, uncompressedSizeBytes, bytesDecoded))
{
break;
}
}
else
{
if (!lzwOutputSequence(dictionary, code, uncompressedPtr,
uncompressedSizeBytes, bytesDecoded, firstByte))
{
break;
}
}
if (!dictionary.add(prevCode, firstByte))
{
break;
}
if (dictionary.flush(codeBitsWidth))
{
prevCode = LzwNil;
}
else
{
prevCode = code;
}
}
return bytesDecoded;
int compressedSizeBits, void * uncompressedData,
int uncompressedSizeBytes)
{
if (compressedData == nullptr || uncompressedData == nullptr)
{
return 0;
}
if (compressedSizeBytes <= 0 || compressedSizeBits <= 0 || uncompressedSizeBytes <= 0)
{
return 0;
}
int code = LzwNil;
int prevCode = LzwNil;
int codeBitsWidth = LzwStartBits;
int firstByte = 0;
int bytesDecoded = 0;
const std::uint8_t * compressedPtr = reinterpret_cast<const std::uint8_t *>(compressedData);
std::uint8_t * uncompressedPtr = reinterpret_cast<std::uint8_t *>(uncompressedData);
// We'll reconstruct the dictionary based on the bit stream codes.
LzwBitStreamReader bitStream(compressedPtr, compressedSizeBytes, compressedSizeBits);
LzwDictionary dictionary;
// We check to avoid an overflow of the user buffer.
// If the buffer is smaller than the decompressed size, we
// break the loop and return the current decompression count.
while (bitStream.numBitsRead < bitStream.sizeInBits)
{
if (codeBitsWidth > LzwMaxDictBits)
{
break;
}
code = bitStream.readBits(codeBitsWidth);
if (prevCode == LzwNil)
{
if (!lzwOutputByte(code, uncompressedPtr, uncompressedSizeBytes, bytesDecoded))
{
break;
}
firstByte = code;
prevCode = code;
continue;
}
if (code >= dictionary.size)
{
if (!lzwOutputSequence(dictionary, prevCode, uncompressedPtr,
uncompressedSizeBytes, bytesDecoded, firstByte))
{
break;
}
if (!lzwOutputByte(firstByte, uncompressedPtr, uncompressedSizeBytes, bytesDecoded))
{
break;
}
}
else
{
if (!lzwOutputSequence(dictionary, code, uncompressedPtr,
uncompressedSizeBytes, bytesDecoded, firstByte))
{
break;
}
}
if (!dictionary.add(prevCode, firstByte))
{
break;
}
if (dictionary.flush(codeBitsWidth))
{
prevCode = LzwNil;
}
else
{
prevCode = code;
}
}
return bytesDecoded;
}
// ========================================================
......@@ -1784,39 +1778,39 @@ static inline const FontCharSet & getFontCharSet() { return s_fontMonoid1
static std::uint8_t * decompressFontBitmap()
{
const std::uint32_t * compressedData = reinterpret_cast<const std::uint32_t *>(getRawFontBitmapData());
// First two uint32s are the compressed size in
// bytes followed by the compressed size in bits.
const int compressedSizeBytes = *compressedData++;
const int compressedSizeBits = *compressedData++;
// Allocate the decompression buffer:
const int uncompressedSizeBytes = getFontCharSet().bitmapDecompressSize;
std::uint8_t * uncompressedData = static_cast<std::uint8_t *>(DD_MALLOC(uncompressedSizeBytes));
// Out of memory? Font rendering will be disable.
if (uncompressedData == nullptr)
{
return nullptr;
}
// Decode the bitmap pixels (stored with an LZW-flavor of compression):
const int bytesDecoded = lzwDecompress(compressedData,
compressedSizeBytes,
compressedSizeBits,
uncompressedData,
uncompressedSizeBytes);
// Unexpected decompression size? Probably a data mismatch in the font-tool.
if (bytesDecoded != uncompressedSizeBytes)
{
DD_MFREE(uncompressedData);
return nullptr;
}
// Must later free with DD_MFREE().
return uncompressedData;
const std::uint32_t * compressedData = reinterpret_cast<const std::uint32_t *>(getRawFontBitmapData());
// First two uint32s are the compressed size in
// bytes followed by the compressed size in bits.
const int compressedSizeBytes = *compressedData++;
const int compressedSizeBits = *compressedData++;
// Allocate the decompression buffer:
const int uncompressedSizeBytes = getFontCharSet().bitmapDecompressSize;
std::uint8_t * uncompressedData = static_cast<std::uint8_t *>(DD_MALLOC(uncompressedSizeBytes));
// Out of memory? Font rendering will be disable.
if (uncompressedData == nullptr)
{
return nullptr;
}
// Decode the bitmap pixels (stored with an LZW-flavor of compression):
const int bytesDecoded = lzwDecompress(compressedData,
compressedSizeBytes,
compressedSizeBits,
uncompressedData,
uncompressedSizeBytes);
// Unexpected decompression size? Probably a data mismatch in the font-tool.
if (bytesDecoded != uncompressedSizeBytes)
{
DD_MFREE(uncompressedData);
return nullptr;
}
// Must later free with DD_MFREE().
return uncompressedData;
}
// ========================================================
......@@ -1825,56 +1819,56 @@ static std::uint8_t * decompressFontBitmap()
struct DebugString
{
std::int64_t expiryDateMillis;
ddVec3 color;
float posX;
float posY;
float scaling;
ddStr text;
bool centered;
std::int64_t expiryDateMillis;
ddVec3 color;
float posX;
float posY;
float scaling;
ddStr text;
bool centered;
};
struct DebugPoint
{
std::int64_t expiryDateMillis;
ddVec3 position;
ddVec3 color;
float size;
bool depthEnabled;
std::int64_t expiryDateMillis;
ddVec3 position;
ddVec3 color;
float size;
bool depthEnabled;
};
struct DebugLine
{
std::int64_t expiryDateMillis;
ddVec3 posFrom;
ddVec3 posTo;
ddVec3 color;
bool depthEnabled;
std::int64_t expiryDateMillis;
ddVec3 posFrom;
ddVec3 posTo;
ddVec3 color;
bool depthEnabled;
};
struct InternalContext DD_EXPLICIT_CONTEXT_ONLY(: public OpaqueContextType)
{
int vertexBufferUsed;
int debugStringsCount;
int debugPointsCount;
int debugLinesCount;
std::int64_t currentTimeMillis; // Latest time value (in milliseconds) from dd::flush().
GlyphTextureHandle glyphTexHandle; // Our built-in glyph bitmap. If kept null, no text is rendered.
RenderInterface * renderInterface; // Ref to the external renderer. Can be null for a no-op debug draw.
DrawVertex vertexBuffer[DEBUG_DRAW_VERTEX_BUFFER_SIZE]; // Vertex buffer we use to expand the lines/points before calling on RenderInterface.
DebugString debugStrings[DEBUG_DRAW_MAX_STRINGS]; // Debug strings queue (2D screen-space strings + 3D projected labels).
DebugPoint debugPoints[DEBUG_DRAW_MAX_POINTS]; // 3D debug points queue.
DebugLine debugLines[DEBUG_DRAW_MAX_LINES]; // 3D debug lines queue.
InternalContext(RenderInterface * renderer)
: vertexBufferUsed(0)
, debugStringsCount(0)
, debugPointsCount(0)
, debugLinesCount(0)
, currentTimeMillis(0)
, glyphTexHandle(nullptr)
, renderInterface(renderer)
{ }
int vertexBufferUsed;
int debugStringsCount;
int debugPointsCount;
int debugLinesCount;
std::int64_t currentTimeMillis; // Latest time value (in milliseconds) from dd::flush().
GlyphTextureHandle glyphTexHandle; // Our built-in glyph bitmap. If kept null, no text is rendered.
RenderInterface * renderInterface; // Ref to the external renderer. Can be null for a no-op debug draw.
DrawVertex vertexBuffer[DEBUG_DRAW_VERTEX_BUFFER_SIZE]; // Vertex buffer we use to expand the lines/points before calling on RenderInterface.
DebugString debugStrings[DEBUG_DRAW_MAX_STRINGS]; // Debug strings queue (2D screen-space strings + 3D projected labels).
DebugPoint debugPoints[DEBUG_DRAW_MAX_POINTS]; // 3D debug points queue.
DebugLine debugLines[DEBUG_DRAW_MAX_LINES]; // 3D debug lines queue.
InternalContext(RenderInterface * renderer)
: vertexBufferUsed(0)
, debugStringsCount(0)
, debugPointsCount(0)
, debugLinesCount(0)
, currentTimeMillis(0)
, glyphTexHandle(nullptr)
, renderInterface(renderer)
{ }
};
// ========================================================
......@@ -1882,38 +1876,38 @@ struct InternalContext DD_EXPLICIT_CONTEXT_ONLY(: public OpaqueContextType)
// ========================================================
#if (defined(DEBUG_DRAW_PER_THREAD_CONTEXT) && defined(DEBUG_DRAW_EXPLICIT_CONTEXT))
#error "DEBUG_DRAW_PER_THREAD_CONTEXT and DEBUG_DRAW_EXPLICIT_CONTEXT are mutually exclusive!"
#error "DEBUG_DRAW_PER_THREAD_CONTEXT and DEBUG_DRAW_EXPLICIT_CONTEXT are mutually exclusive!"
#endif // DEBUG_DRAW_PER_THREAD_CONTEXT && DEBUG_DRAW_EXPLICIT_CONTEXT
#if defined(DEBUG_DRAW_EXPLICIT_CONTEXT)
//
// Explicit context passed as argument
//
#define DD_CONTEXT static_cast<InternalContext *>(ctx)
//
// Explicit context passed as argument
//
#define DD_CONTEXT static_cast<InternalContext *>(ctx)
#elif defined(DEBUG_DRAW_PER_THREAD_CONTEXT)
//
// Per-thread global context (MT safe)
//
#if defined(__GNUC__) || defined(__clang__) // GCC/Clang
#define DD_THREAD_LOCAL static __thread
#elif defined(_MSC_VER) // Visual Studio
#define DD_THREAD_LOCAL static __declspec(thread)
#else // Try C++11 thread_local
#if DEBUG_DRAW_CXX11_SUPPORTED
#define DD_THREAD_LOCAL static thread_local
#else // !DEBUG_DRAW_CXX11_SUPPORTED
#error "Unsupported compiler - unknown TLS model"
#endif // DEBUG_DRAW_CXX11_SUPPORTED
#endif // TLS model
DD_THREAD_LOCAL InternalContext * s_threadContext = nullptr;
#define DD_CONTEXT s_threadContext
#undef DD_THREAD_LOCAL
//
// Per-thread global context (MT safe)
//
#if defined(__GNUC__) || defined(__clang__) // GCC/Clang
#define DD_THREAD_LOCAL static __thread
#elif defined(_MSC_VER) // Visual Studio
#define DD_THREAD_LOCAL static __declspec(thread)
#else // Try C++11 thread_local
#if DEBUG_DRAW_CXX11_SUPPORTED
#define DD_THREAD_LOCAL static thread_local
#else // !DEBUG_DRAW_CXX11_SUPPORTED
#error "Unsupported compiler - unknown TLS model"
#endif // DEBUG_DRAW_CXX11_SUPPORTED
#endif // TLS model
DD_THREAD_LOCAL InternalContext * s_threadContext = nullptr;
#define DD_CONTEXT s_threadContext
#undef DD_THREAD_LOCAL
#else // Debug Draw context selection
//
// Global static context (single threaded operation)
//
static InternalContext * s_globalContext = nullptr;
#define DD_CONTEXT s_globalContext
//
// Global static context (single threaded operation)
//
static InternalContext * s_globalContext = nullptr;
#define DD_CONTEXT s_globalContext
#endif // Debug Draw context selection
// ========================================================
......@@ -1933,112 +1927,112 @@ static inline float floatInvSqrt(float x) { return (1.0f / sqrtf(x)); }
union Float2UInt
{
float asFloat;
std::uint32_t asUInt;
float asFloat;
std::uint32_t asUInt;
};
static inline float floatRound(float x)
{
// Probably slower than std::floor(), also depends of FPU settings,
// but we only need this for that special sin/cos() case anyways...
const int i = static_cast<int>(x);
return (x >= 0.0f) ? static_cast<float>(i) : static_cast<float>(i - 1);
// Probably slower than std::floor(), also depends of FPU settings,
// but we only need this for that special sin/cos() case anyways...
const int i = static_cast<int>(x);
return (x >= 0.0f) ? static_cast<float>(i) : static_cast<float>(i - 1);
}
static inline float floatAbs(float x)
{
// Mask-off the sign bit
Float2UInt i;
i.asFloat = x;
i.asUInt &= 0x7FFFFFFF;
return i.asFloat;
// Mask-off the sign bit
Float2UInt i;
i.asFloat = x;
i.asUInt &= 0x7FFFFFFF;
return i.asFloat;
}
static inline float floatInvSqrt(float x)
{
// Modified version of the emblematic Q_rsqrt() from Quake 3.
// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root
Float2UInt i;
float y, r;
y = x * 0.5f;
i.asFloat = x;
i.asUInt = 0x5F3759DF - (i.asUInt >> 1);
r = i.asFloat;
r = r * (1.5f - (r * r * y));
return r;
// Modified version of the emblematic Q_rsqrt() from Quake 3.
// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root
Float2UInt i;
float y, r;
y = x * 0.5f;
i.asFloat = x;
i.asUInt = 0x5F3759DF - (i.asUInt >> 1);
r = i.asFloat;
r = r * (1.5f - (r * r * y));
return r;
}
static inline float floatSin(float radians)
{
static const float A = -2.39e-08;
static const float B = 2.7526e-06;
static const float C = 1.98409e-04;
static const float D = 8.3333315e-03;
static const float E = 1.666666664e-01;
if (radians < 0.0f || radians >= TAU)
{
radians -= floatRound(radians / TAU) * TAU;
}
if (radians < PI)
{
if (radians > HalfPI)
{
radians = PI - radians;
}
}
else
{
radians = (radians > (PI + HalfPI)) ? (radians - TAU) : (PI - radians);
}
const float s = radians * radians;
return radians * (((((A * s + B) * s - C) * s + D) * s - E) * s + 1.0f);
static const float A = -2.39e-08;
static const float B = 2.7526e-06;
static const float C = 1.98409e-04;
static const float D = 8.3333315e-03;
static const float E = 1.666666664e-01;
if (radians < 0.0f || radians >= TAU)
{
radians -= floatRound(radians / TAU) * TAU;
}
if (radians < PI)
{
if (radians > HalfPI)
{
radians = PI - radians;
}
}
else
{
radians = (radians > (PI + HalfPI)) ? (radians - TAU) : (PI - radians);
}
const float s = radians * radians;
return radians * (((((A * s + B) * s - C) * s + D) * s - E) * s + 1.0f);
}
static inline float floatCos(float radians)
{
static const float A = -2.605e-07;
static const float B = 2.47609e-05;
static const float C = 1.3888397e-03;
static const float D = 4.16666418e-02;
static const float E = 4.999999963e-01;
if (radians < 0.0f || radians >= TAU)
{
radians -= floatRound(radians / TAU) * TAU;
}
float d;
if (radians < PI)
{
if (radians > HalfPI)
{
radians = PI - radians;
d = -1.0f;
}
else
{
d = 1.0f;
}
}
else
{
if (radians > (PI + HalfPI))
{
radians = radians - TAU;
d = 1.0f;
}
else
{
radians = PI - radians;
d = -1.0f;
}
}
const float s = radians * radians;
return d * (((((A * s + B) * s - C) * s + D) * s - E) * s + 1.0f);
static const float A = -2.605e-07;
static const float B = 2.47609e-05;
static const float C = 1.3888397e-03;
static const float D = 4.16666418e-02;
static const float E = 4.999999963e-01;
if (radians < 0.0f || radians >= TAU)
{
radians -= floatRound(radians / TAU) * TAU;
}
float d;
if (radians < PI)
{
if (radians > HalfPI)
{
radians = PI - radians;
d = -1.0f;
}
else
{
d = 1.0f;
}
}
else
{
if (radians > (PI + HalfPI))
{
radians = radians - TAU;
d = 1.0f;
}
else
{
radians = PI - radians;
d = -1.0f;
}
}
const float s = radians * radians;
return d * (((((A * s + B) * s - C) * s + D) * s - E) * s + 1.0f);
}
#endif // DEBUG_DRAW_USE_STD_MATH
......@@ -2051,74 +2045,74 @@ enum VecElements { X, Y, Z, W };
static inline void vecSet(ddVec3_Out dest, const float x, const float y, const float z)
{
dest[X] = x;
dest[Y] = y;
dest[Z] = z;
dest[X] = x;
dest[Y] = y;
dest[Z] = z;
}
static inline void vecCopy(ddVec3_Out dest, ddVec3_In src)
{
dest[X] = src[X];
dest[Y] = src[Y];
dest[Z] = src[Z];
dest[X] = src[X];
dest[Y] = src[Y];
dest[Z] = src[Z];
}
static inline void vecAdd(ddVec3_Out result, ddVec3_In a, ddVec3_In b)
{
result[X] = a[X] + b[X];
result[Y] = a[Y] + b[Y];
result[Z] = a[Z] + b[Z];
result[X] = a[X] + b[X];
result[Y] = a[Y] + b[Y];
result[Z] = a[Z] + b[Z];
}
static inline void vecSub(ddVec3_Out result, ddVec3_In a, ddVec3_In b)
{
result[X] = a[X] - b[X];
result[Y] = a[Y] - b[Y];
result[Z] = a[Z] - b[Z];
result[X] = a[X] - b[X];
result[Y] = a[Y] - b[Y];
result[Z] = a[Z] - b[Z];
}
static inline void vecScale(ddVec3_Out result, ddVec3_In v, const float s)
{
result[X] = v[X] * s;
result[Y] = v[Y] * s;
result[Z] = v[Z] * s;
result[X] = v[X] * s;
result[Y] = v[Y] * s;
result[Z] = v[Z] * s;
}
static inline void vecNormalize(ddVec3_Out result, ddVec3_In v)
{
const float lenSqr = v[X] * v[X] + v[Y] * v[Y] + v[Z] * v[Z];
const float invLen = floatInvSqrt(lenSqr);
result[X] = v[X] * invLen;
result[Y] = v[Y] * invLen;
result[Z] = v[Z] * invLen;
const float lenSqr = v[X] * v[X] + v[Y] * v[Y] + v[Z] * v[Z];
const float invLen = floatInvSqrt(lenSqr);
result[X] = v[X] * invLen;
result[Y] = v[Y] * invLen;
result[Z] = v[Z] * invLen;
}
static inline void vecOrthogonalBasis(ddVec3_Out left, ddVec3_Out up, ddVec3_In v)
{
// Produces two orthogonal vectors for normalized vector v.
float lenSqr, invLen;
if (floatAbs(v[Z]) > 0.7f)
{
lenSqr = v[Y] * v[Y] + v[Z] * v[Z];
invLen = floatInvSqrt(lenSqr);
up[X] = 0.0f;
up[Y] = v[Z] * invLen;
up[Z] = -v[Y] * invLen;
left[X] = lenSqr * invLen;
left[Y] = -v[X] * up[Z];
left[Z] = v[X] * up[Y];
}
else
{
lenSqr = v[X] * v[X] + v[Y] * v[Y];
invLen = floatInvSqrt(lenSqr);
left[X] = -v[Y] * invLen;
left[Y] = v[X] * invLen;
left[Z] = 0.0f;
up[X] = -v[Z] * left[Y];
up[Y] = v[Z] * left[X];
up[Z] = lenSqr * invLen;
}
// Produces two orthogonal vectors for normalized vector v.
float lenSqr, invLen;
if (floatAbs(v[Z]) > 0.7f)
{
lenSqr = v[Y] * v[Y] + v[Z] * v[Z];
invLen = floatInvSqrt(lenSqr);
up[X] = 0.0f;
up[Y] = v[Z] * invLen;
up[Z] = -v[Y] * invLen;
left[X] = lenSqr * invLen;
left[Y] = -v[X] * up[Z];
left[Z] = v[X] * up[Y];
}
else
{
lenSqr = v[X] * v[X] + v[Y] * v[Y];
invLen = floatInvSqrt(lenSqr);
left[X] = -v[Y] * invLen;
left[Y] = v[X] * invLen;
left[Z] = 0.0f;
up[X] = -v[Z] * left[Y];
up[Y] = v[Z] * left[X];
up[Z] = lenSqr * invLen;
}
}
// ========================================================
......@@ -2127,26 +2121,26 @@ static inline void vecOrthogonalBasis(ddVec3_Out left, ddVec3_Out up, ddVec3_In
static inline void matTransformPointXYZ(ddVec3_Out result, ddVec3_In p, ddMat4x4_In m)
{
result[X] = (m[0] * p[X]) + (m[4] * p[Y]) + (m[8] * p[Z]) + m[12]; // p[W] assumed to be 1
result[Y] = (m[1] * p[X]) + (m[5] * p[Y]) + (m[9] * p[Z]) + m[13];
result[Z] = (m[2] * p[X]) + (m[6] * p[Y]) + (m[10] * p[Z]) + m[14];
result[X] = (m[0] * p[X]) + (m[4] * p[Y]) + (m[8] * p[Z]) + m[12]; // p[W] assumed to be 1
result[Y] = (m[1] * p[X]) + (m[5] * p[Y]) + (m[9] * p[Z]) + m[13];
result[Z] = (m[2] * p[X]) + (m[6] * p[Y]) + (m[10] * p[Z]) + m[14];
}
static inline void matTransformPointXYZW(float result[4], ddVec3_In p, ddMat4x4_In m)
{
result[X] = (m[0] * p[X]) + (m[4] * p[Y]) + (m[8] * p[Z]) + m[12]; // p[W] assumed to be 1
result[Y] = (m[1] * p[X]) + (m[5] * p[Y]) + (m[9] * p[Z]) + m[13];
result[Z] = (m[2] * p[X]) + (m[6] * p[Y]) + (m[10] * p[Z]) + m[14];
result[W] = (m[3] * p[X]) + (m[7] * p[Y]) + (m[11] * p[Z]) + m[15];
result[X] = (m[0] * p[X]) + (m[4] * p[Y]) + (m[8] * p[Z]) + m[12]; // p[W] assumed to be 1
result[Y] = (m[1] * p[X]) + (m[5] * p[Y]) + (m[9] * p[Z]) + m[13];
result[Z] = (m[2] * p[X]) + (m[6] * p[Y]) + (m[10] * p[Z]) + m[14];
result[W] = (m[3] * p[X]) + (m[7] * p[Y]) + (m[11] * p[Z]) + m[15];
}
static inline float matTransformPointXYZW2(ddVec3_Out result, const float p[3], ddMat4x4_In m)
{
result[X] = (m[0] * p[X]) + (m[4] * p[Y]) + (m[8] * p[Z]) + m[12]; // p[W] assumed to be 1
result[Y] = (m[1] * p[X]) + (m[5] * p[Y]) + (m[9] * p[Z]) + m[13];
result[Z] = (m[2] * p[X]) + (m[6] * p[Y]) + (m[10] * p[Z]) + m[14];
float rw = (m[3] * p[X]) + (m[7] * p[Y]) + (m[11] * p[Z]) + m[15];
return rw;
result[X] = (m[0] * p[X]) + (m[4] * p[Y]) + (m[8] * p[Z]) + m[12]; // p[W] assumed to be 1
result[Y] = (m[1] * p[X]) + (m[5] * p[Y]) + (m[9] * p[Z]) + m[13];
result[Z] = (m[2] * p[X]) + (m[6] * p[Y]) + (m[10] * p[Z]) + m[14];
float rw = (m[3] * p[X]) + (m[7] * p[Y]) + (m[11] * p[Z]) + m[15];
return rw;
}
// ========================================================
......@@ -2155,369 +2149,369 @@ static inline float matTransformPointXYZW2(ddVec3_Out result, const float p[3],
enum DrawMode
{
DrawModePoints,
DrawModeLines,
DrawModeText
DrawModePoints,
DrawModeLines,
DrawModeText
};
static void flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) const DrawMode mode, const bool depthEnabled)
{
if (DD_CONTEXT->vertexBufferUsed == 0)
{
return;
}
switch (mode)
{
case DrawModePoints :
DD_CONTEXT->renderInterface->drawPointList(DD_CONTEXT->vertexBuffer,
DD_CONTEXT->vertexBufferUsed,
depthEnabled);
break;
case DrawModeLines :
DD_CONTEXT->renderInterface->drawLineList(DD_CONTEXT->vertexBuffer,
DD_CONTEXT->vertexBufferUsed,
depthEnabled);
break;
case DrawModeText :
DD_CONTEXT->renderInterface->drawGlyphList(DD_CONTEXT->vertexBuffer,
DD_CONTEXT->vertexBufferUsed,
DD_CONTEXT->glyphTexHandle);
break;
} // switch (mode)
DD_CONTEXT->vertexBufferUsed = 0;
if (DD_CONTEXT->vertexBufferUsed == 0)
{
return;
}
switch (mode)
{
case DrawModePoints :
DD_CONTEXT->renderInterface->drawPointList(DD_CONTEXT->vertexBuffer,
DD_CONTEXT->vertexBufferUsed,
depthEnabled);
break;
case DrawModeLines :
DD_CONTEXT->renderInterface->drawLineList(DD_CONTEXT->vertexBuffer,
DD_CONTEXT->vertexBufferUsed,
depthEnabled);
break;
case DrawModeText :
DD_CONTEXT->renderInterface->drawGlyphList(DD_CONTEXT->vertexBuffer,
DD_CONTEXT->vertexBufferUsed,
DD_CONTEXT->glyphTexHandle);
break;
} // switch (mode)
DD_CONTEXT->vertexBufferUsed = 0;
}
static void pushPointVert(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) const DebugPoint & point)
{
// Make room for one more vert:
if ((DD_CONTEXT->vertexBufferUsed + 1) >= DEBUG_DRAW_VERTEX_BUFFER_SIZE)
{
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModePoints, point.depthEnabled);
}
DrawVertex & v = DD_CONTEXT->vertexBuffer[DD_CONTEXT->vertexBufferUsed++];
v.point.x = point.position[X];
v.point.y = point.position[Y];
v.point.z = point.position[Z];
v.point.r = point.color[X];
v.point.g = point.color[Y];
v.point.b = point.color[Z];
v.point.size = point.size;
// Make room for one more vert:
if ((DD_CONTEXT->vertexBufferUsed + 1) >= DEBUG_DRAW_VERTEX_BUFFER_SIZE)
{
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModePoints, point.depthEnabled);
}
DrawVertex & v = DD_CONTEXT->vertexBuffer[DD_CONTEXT->vertexBufferUsed++];
v.point.x = point.position[X];
v.point.y = point.position[Y];
v.point.z = point.position[Z];
v.point.r = point.color[X];
v.point.g = point.color[Y];
v.point.b = point.color[Z];
v.point.size = point.size;
}
static void pushLineVert(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) const DebugLine & line)
{
// Make room for two more verts:
if ((DD_CONTEXT->vertexBufferUsed + 2) >= DEBUG_DRAW_VERTEX_BUFFER_SIZE)
{
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModeLines, line.depthEnabled);
}
DrawVertex & v0 = DD_CONTEXT->vertexBuffer[DD_CONTEXT->vertexBufferUsed++];
DrawVertex & v1 = DD_CONTEXT->vertexBuffer[DD_CONTEXT->vertexBufferUsed++];
v0.line.x = line.posFrom[X];
v0.line.y = line.posFrom[Y];
v0.line.z = line.posFrom[Z];
v0.line.r = line.color[X];
v0.line.g = line.color[Y];
v0.line.b = line.color[Z];
v1.line.x = line.posTo[X];
v1.line.y = line.posTo[Y];
v1.line.z = line.posTo[Z];
v1.line.r = line.color[X];
v1.line.g = line.color[Y];
v1.line.b = line.color[Z];
// Make room for two more verts:
if ((DD_CONTEXT->vertexBufferUsed + 2) >= DEBUG_DRAW_VERTEX_BUFFER_SIZE)
{
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModeLines, line.depthEnabled);
}
DrawVertex & v0 = DD_CONTEXT->vertexBuffer[DD_CONTEXT->vertexBufferUsed++];
DrawVertex & v1 = DD_CONTEXT->vertexBuffer[DD_CONTEXT->vertexBufferUsed++];
v0.line.x = line.posFrom[X];
v0.line.y = line.posFrom[Y];
v0.line.z = line.posFrom[Z];
v0.line.r = line.color[X];
v0.line.g = line.color[Y];
v0.line.b = line.color[Z];
v1.line.x = line.posTo[X];
v1.line.y = line.posTo[Y];
v1.line.z = line.posTo[Z];
v1.line.r = line.color[X];
v1.line.g = line.color[Y];
v1.line.b = line.color[Z];
}
static void pushGlyphVerts(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) const DrawVertex verts[4])
{
static const int indexes[6] = { 0, 1, 2, 2, 1, 3 };
static const int indexes[6] = { 0, 1, 2, 2, 1, 3 };
// Make room for one more glyph (2 tris):
if ((DD_CONTEXT->vertexBufferUsed + 6) >= DEBUG_DRAW_VERTEX_BUFFER_SIZE)
{
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModeText, false);
}
// Make room for one more glyph (2 tris):
if ((DD_CONTEXT->vertexBufferUsed + 6) >= DEBUG_DRAW_VERTEX_BUFFER_SIZE)
{
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModeText, false);
}
for (int i = 0; i < 6; ++i)
{
DD_CONTEXT->vertexBuffer[DD_CONTEXT->vertexBufferUsed++].glyph = verts[indexes[i]].glyph;
}
for (int i = 0; i < 6; ++i)
{
DD_CONTEXT->vertexBuffer[DD_CONTEXT->vertexBufferUsed++].glyph = verts[indexes[i]].glyph;
}
}
static void pushStringGlyphs(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) float x, float y,
const char * text, ddVec3_In color, const float scaling)
{
// Invariants for all characters:
const float initialX = x;
const float scaleU = static_cast<float>(getFontCharSet().bitmapWidth);
const float scaleV = static_cast<float>(getFontCharSet().bitmapHeight);
const float fixedWidth = static_cast<float>(getFontCharSet().charWidth);
const float fixedHeight = static_cast<float>(getFontCharSet().charHeight);
const float tabW = fixedWidth * 4.0f * scaling; // TAB = 4 spaces.
const float chrW = fixedWidth * scaling;
const float chrH = fixedHeight * scaling;
for (; *text != '\0'; ++text)
{
const int charVal = *text;
if (charVal >= FontCharSet::MaxChars)
{
continue;
}
if (charVal == ' ')
{
x += chrW;
continue;
}
if (charVal == '\t')
{
x += tabW;
continue;
}
if (charVal == '\n')
{
y += chrH;
x = initialX;
continue;
}
const FontChar fontChar = getFontCharSet().chars[charVal];
const float u0 = (fontChar.x + 0.5f) / scaleU;
const float v0 = (fontChar.y + 0.5f) / scaleV;
const float u1 = u0 + (fixedWidth / scaleU);
const float v1 = v0 + (fixedHeight / scaleV);
DrawVertex verts[4];
verts[0].glyph.x = x;
verts[0].glyph.y = y;
verts[0].glyph.u = u0;
verts[0].glyph.v = v0;
verts[0].glyph.r = color[X];
verts[0].glyph.g = color[Y];
verts[0].glyph.b = color[Z];
verts[1].glyph.x = x;
verts[1].glyph.y = y + chrH;
verts[1].glyph.u = u0;
verts[1].glyph.v = v1;
verts[1].glyph.r = color[X];
verts[1].glyph.g = color[Y];
verts[1].glyph.b = color[Z];
verts[2].glyph.x = x + chrW;
verts[2].glyph.y = y;
verts[2].glyph.u = u1;
verts[2].glyph.v = v0;
verts[2].glyph.r = color[X];
verts[2].glyph.g = color[Y];
verts[2].glyph.b = color[Z];
verts[3].glyph.x = x + chrW;
verts[3].glyph.y = y + chrH;
verts[3].glyph.u = u1;
verts[3].glyph.v = v1;
verts[3].glyph.r = color[X];
verts[3].glyph.g = color[Y];
verts[3].glyph.b = color[Z];
pushGlyphVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) verts);
x += chrW;
}
const char * text, ddVec3_In color, const float scaling)
{
// Invariants for all characters:
const float initialX = x;
const float scaleU = static_cast<float>(getFontCharSet().bitmapWidth);
const float scaleV = static_cast<float>(getFontCharSet().bitmapHeight);
const float fixedWidth = static_cast<float>(getFontCharSet().charWidth);
const float fixedHeight = static_cast<float>(getFontCharSet().charHeight);
const float tabW = fixedWidth * 4.0f * scaling; // TAB = 4 spaces.
const float chrW = fixedWidth * scaling;
const float chrH = fixedHeight * scaling;
for (; *text != '\0'; ++text)
{
const int charVal = *text;
if (charVal >= FontCharSet::MaxChars)
{
continue;
}
if (charVal == ' ')
{
x += chrW;
continue;
}
if (charVal == '\t')
{
x += tabW;
continue;
}
if (charVal == '\n')
{
y += chrH;
x = initialX;
continue;
}
const FontChar fontChar = getFontCharSet().chars[charVal];
const float u0 = (fontChar.x + 0.5f) / scaleU;
const float v0 = (fontChar.y + 0.5f) / scaleV;
const float u1 = u0 + (fixedWidth / scaleU);
const float v1 = v0 + (fixedHeight / scaleV);
DrawVertex verts[4];
verts[0].glyph.x = x;
verts[0].glyph.y = y;
verts[0].glyph.u = u0;
verts[0].glyph.v = v0;
verts[0].glyph.r = color[X];
verts[0].glyph.g = color[Y];
verts[0].glyph.b = color[Z];
verts[1].glyph.x = x;
verts[1].glyph.y = y + chrH;
verts[1].glyph.u = u0;
verts[1].glyph.v = v1;
verts[1].glyph.r = color[X];
verts[1].glyph.g = color[Y];
verts[1].glyph.b = color[Z];
verts[2].glyph.x = x + chrW;
verts[2].glyph.y = y;
verts[2].glyph.u = u1;
verts[2].glyph.v = v0;
verts[2].glyph.r = color[X];
verts[2].glyph.g = color[Y];
verts[2].glyph.b = color[Z];
verts[3].glyph.x = x + chrW;
verts[3].glyph.y = y + chrH;
verts[3].glyph.u = u1;
verts[3].glyph.v = v1;
verts[3].glyph.r = color[X];
verts[3].glyph.g = color[Y];
verts[3].glyph.b = color[Z];
pushGlyphVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) verts);
x += chrW;
}
}
static float calcTextWidth(const char * text, const float scaling)
{
const float fixedWidth = static_cast<float>(getFontCharSet().charWidth);
const float tabW = fixedWidth * 4.0f * scaling; // TAB = 4 spaces.
const float chrW = fixedWidth * scaling;
float x = 0.0f;
for (; *text != '\0'; ++text)
{
// Tabs are handled differently (4 spaces)
if (*text == '\t')
{
x += tabW;
}
else // Non-tab char (including whitespace)
{
x += chrW;
}
}
return x;
const float fixedWidth = static_cast<float>(getFontCharSet().charWidth);
const float tabW = fixedWidth * 4.0f * scaling; // TAB = 4 spaces.
const float chrW = fixedWidth * scaling;
float x = 0.0f;
for (; *text != '\0'; ++text)
{
// Tabs are handled differently (4 spaces)
if (*text == '\t')
{
x += tabW;
}
else // Non-tab char (including whitespace)
{
x += chrW;
}
}
return x;
}
static void drawDebugStrings(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx))
{
const int count = DD_CONTEXT->debugStringsCount;
if (count == 0)
{
return;
}
const DebugString * const debugStrings = DD_CONTEXT->debugStrings;
for (int i = 0; i < count; ++i)
{
const DebugString & dstr = debugStrings[i];
if (dstr.centered)
{
// 3D Labels are centered at the point of origin, e.g. center-aligned.
const float offset = calcTextWidth(dstr.text.c_str(), dstr.scaling) * 0.5f;
pushStringGlyphs(DD_EXPLICIT_CONTEXT_ONLY(ctx,) dstr.posX - offset, dstr.posY, dstr.text.c_str(), dstr.color, dstr.scaling);
}
else
{
// Left-aligned
pushStringGlyphs(DD_EXPLICIT_CONTEXT_ONLY(ctx,) dstr.posX, dstr.posY, dstr.text.c_str(), dstr.color, dstr.scaling);
}
}
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModeText, false);
const int count = DD_CONTEXT->debugStringsCount;
if (count == 0)
{
return;
}
const DebugString * const debugStrings = DD_CONTEXT->debugStrings;
for (int i = 0; i < count; ++i)
{
const DebugString & dstr = debugStrings[i];
if (dstr.centered)
{
// 3D Labels are centered at the point of origin, e.g. center-aligned.
const float offset = calcTextWidth(dstr.text.c_str(), dstr.scaling) * 0.5f;
pushStringGlyphs(DD_EXPLICIT_CONTEXT_ONLY(ctx,) dstr.posX - offset, dstr.posY, dstr.text.c_str(), dstr.color, dstr.scaling);
}
else
{
// Left-aligned
pushStringGlyphs(DD_EXPLICIT_CONTEXT_ONLY(ctx,) dstr.posX, dstr.posY, dstr.text.c_str(), dstr.color, dstr.scaling);
}
}
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModeText, false);
}
static void drawDebugPoints(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx))
{
const int count = DD_CONTEXT->debugPointsCount;
if (count == 0)
{
return;
}
const DebugPoint * const debugPoints = DD_CONTEXT->debugPoints;
//
// First pass, points with depth test ENABLED:
//
int numDepthlessPoints = 0;
for (int i = 0; i < count; ++i)
{
const DebugPoint & point = debugPoints[i];
if (point.depthEnabled)
{
pushPointVert(DD_EXPLICIT_CONTEXT_ONLY(ctx,) point);
}
numDepthlessPoints += !point.depthEnabled;
}
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModePoints, true);
//
// Second pass draws points with depth DISABLED:
//
if (numDepthlessPoints > 0)
{
for (int i = 0; i < count; ++i)
{
const DebugPoint & point = debugPoints[i];
if (!point.depthEnabled)
{
pushPointVert(DD_EXPLICIT_CONTEXT_ONLY(ctx,) point);
}
}
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModePoints, false);
}
const int count = DD_CONTEXT->debugPointsCount;
if (count == 0)
{
return;
}
const DebugPoint * const debugPoints = DD_CONTEXT->debugPoints;
//
// First pass, points with depth test ENABLED:
//
int numDepthlessPoints = 0;
for (int i = 0; i < count; ++i)
{
const DebugPoint & point = debugPoints[i];
if (point.depthEnabled)
{
pushPointVert(DD_EXPLICIT_CONTEXT_ONLY(ctx,) point);
}
numDepthlessPoints += !point.depthEnabled;
}
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModePoints, true);
//
// Second pass draws points with depth DISABLED:
//
if (numDepthlessPoints > 0)
{
for (int i = 0; i < count; ++i)
{
const DebugPoint & point = debugPoints[i];
if (!point.depthEnabled)
{
pushPointVert(DD_EXPLICIT_CONTEXT_ONLY(ctx,) point);
}
}
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModePoints, false);
}
}
static void drawDebugLines(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx))
{
const int count = DD_CONTEXT->debugLinesCount;
if (count == 0)
{
return;
}
const DebugLine * const debugLines = DD_CONTEXT->debugLines;
//
// First pass, lines with depth test ENABLED:
//
int numDepthlessLines = 0;
for (int i = 0; i < count; ++i)
{
const DebugLine & line = debugLines[i];
if (line.depthEnabled)
{
pushLineVert(DD_EXPLICIT_CONTEXT_ONLY(ctx,) line);
}
numDepthlessLines += !line.depthEnabled;
}
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModeLines, true);
//
// Second pass draws lines with depth DISABLED:
//
if (numDepthlessLines > 0)
{
for (int i = 0; i < count; ++i)
{
const DebugLine & line = debugLines[i];
if (!line.depthEnabled)
{
pushLineVert(DD_EXPLICIT_CONTEXT_ONLY(ctx,) line);
}
}
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModeLines, false);
}
const int count = DD_CONTEXT->debugLinesCount;
if (count == 0)
{
return;
}
const DebugLine * const debugLines = DD_CONTEXT->debugLines;
//
// First pass, lines with depth test ENABLED:
//
int numDepthlessLines = 0;
for (int i = 0; i < count; ++i)
{
const DebugLine & line = debugLines[i];
if (line.depthEnabled)
{
pushLineVert(DD_EXPLICIT_CONTEXT_ONLY(ctx,) line);
}
numDepthlessLines += !line.depthEnabled;
}
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModeLines, true);
//
// Second pass draws lines with depth DISABLED:
//
if (numDepthlessLines > 0)
{
for (int i = 0; i < count; ++i)
{
const DebugLine & line = debugLines[i];
if (!line.depthEnabled)
{
pushLineVert(DD_EXPLICIT_CONTEXT_ONLY(ctx,) line);
}
}
flushDebugVerts(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DrawModeLines, false);
}
}
template<typename T>
static void clearDebugQueue(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) T * queue, int & queueCount)
{
const std::int64_t time = DD_CONTEXT->currentTimeMillis;
if (time == 0)
{
queueCount = 0;
return;
}
int index = 0;
T * pElem = queue;
// Concatenate elements that still need to be draw on future frames:
for (int i = 0; i < queueCount; ++i, ++pElem)
{
if (pElem->expiryDateMillis > time)
{
if (index != i)
{
queue[index] = *pElem;
}
++index;
}
}
queueCount = index;
const std::int64_t time = DD_CONTEXT->currentTimeMillis;
if (time == 0)
{
queueCount = 0;
return;
}
int index = 0;
T * pElem = queue;
// Concatenate elements that still need to be draw on future frames:
for (int i = 0; i < queueCount; ++i, ++pElem)
{
if (pElem->expiryDateMillis > time)
{
if (index != i)
{
queue[index] = *pElem;
}
++index;
}
}
queueCount = index;
}
static void setupGlyphTexture(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx))
{
if (DD_CONTEXT->renderInterface == nullptr)
{
return;
}
if (DD_CONTEXT->glyphTexHandle != nullptr)
{
DD_CONTEXT->renderInterface->destroyGlyphTexture(DD_CONTEXT->glyphTexHandle);
DD_CONTEXT->glyphTexHandle = nullptr;
}
std::uint8_t * decompressedBitmap = decompressFontBitmap();
if (decompressedBitmap == nullptr)
{
return; // Failed to decompressed. No font rendering available.
}
DD_CONTEXT->glyphTexHandle = DD_CONTEXT->renderInterface->createGlyphTexture(
getFontCharSet().bitmapWidth,
getFontCharSet().bitmapHeight,
decompressedBitmap);
// No longer needed.
DD_MFREE(decompressedBitmap);
if (DD_CONTEXT->renderInterface == nullptr)
{
return;
}
if (DD_CONTEXT->glyphTexHandle != nullptr)
{
DD_CONTEXT->renderInterface->destroyGlyphTexture(DD_CONTEXT->glyphTexHandle);
DD_CONTEXT->glyphTexHandle = nullptr;
}
std::uint8_t * decompressedBitmap = decompressFontBitmap();
if (decompressedBitmap == nullptr)
{
return; // Failed to decompressed. No font rendering available.
}
DD_CONTEXT->glyphTexHandle = DD_CONTEXT->renderInterface->createGlyphTexture(
getFontCharSet().bitmapWidth,
getFontCharSet().bitmapHeight,
decompressedBitmap);
// No longer needed.
DD_MFREE(decompressedBitmap);
}
// ========================================================
......@@ -2526,766 +2520,766 @@ static void setupGlyphTexture(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx))
bool initialize(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle * outCtx,) RenderInterface * renderer)
{
if (renderer == nullptr)
{
return false;
}
void * buffer = DD_MALLOC(sizeof(InternalContext));
if (buffer == nullptr)
{
return false;
}
InternalContext * newCtx = ::new(buffer) InternalContext(renderer);
#ifdef DEBUG_DRAW_EXPLICIT_CONTEXT
if ((*outCtx) != nullptr) { shutdown(*outCtx); }
(*outCtx) = newCtx;
#else // !DEBUG_DRAW_EXPLICIT_CONTEXT
if (DD_CONTEXT != nullptr) { shutdown(); }
DD_CONTEXT = newCtx;
#endif // DEBUG_DRAW_EXPLICIT_CONTEXT
setupGlyphTexture(DD_EXPLICIT_CONTEXT_ONLY(*outCtx));
return true;
if (renderer == nullptr)
{
return false;
}
void * buffer = DD_MALLOC(sizeof(InternalContext));
if (buffer == nullptr)
{
return false;
}
InternalContext * newCtx = ::new(buffer) InternalContext(renderer);
#ifdef DEBUG_DRAW_EXPLICIT_CONTEXT
if ((*outCtx) != nullptr) { shutdown(*outCtx); }
(*outCtx) = newCtx;
#else // !DEBUG_DRAW_EXPLICIT_CONTEXT
if (DD_CONTEXT != nullptr) { shutdown(); }
DD_CONTEXT = newCtx;
#endif // DEBUG_DRAW_EXPLICIT_CONTEXT
setupGlyphTexture(DD_EXPLICIT_CONTEXT_ONLY(*outCtx));
return true;
}
void shutdown(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx))
{
if (DD_CONTEXT != nullptr)
{
// If this macro is defined, the user-provided ddStr type
// needs some extra cleanup before shutdown, so we run for
// all entries in the debugStrings[] array.
//
// We could call std::string::clear() here, but clear()
// doesn't deallocate memory in std string, so we might
// as well let the default destructor do the cleanup,
// when using the default (AKA std::string) ddStr.
#ifdef DEBUG_DRAW_STR_DEALLOC_FUNC
for (int i = 0; i < DEBUG_DRAW_MAX_STRINGS; ++i)
{
DEBUG_DRAW_STR_DEALLOC_FUNC(DD_CONTEXT->debugStrings[i].text);
}
#endif // DEBUG_DRAW_STR_DEALLOC_FUNC
if (DD_CONTEXT->renderInterface != nullptr && DD_CONTEXT->glyphTexHandle != nullptr)
{
DD_CONTEXT->renderInterface->destroyGlyphTexture(DD_CONTEXT->glyphTexHandle);
}
DD_CONTEXT->~InternalContext(); // Destroy first
DD_MFREE(DD_CONTEXT);
#ifndef DEBUG_DRAW_EXPLICIT_CONTEXT
DD_CONTEXT = nullptr;
#endif // DEBUG_DRAW_EXPLICIT_CONTEXT
}
if (DD_CONTEXT != nullptr)
{
// If this macro is defined, the user-provided ddStr type
// needs some extra cleanup before shutdown, so we run for
// all entries in the debugStrings[] array.
//
// We could call std::string::clear() here, but clear()
// doesn't deallocate memory in std string, so we might
// as well let the default destructor do the cleanup,
// when using the default (AKA std::string) ddStr.
#ifdef DEBUG_DRAW_STR_DEALLOC_FUNC
for (int i = 0; i < DEBUG_DRAW_MAX_STRINGS; ++i)
{
DEBUG_DRAW_STR_DEALLOC_FUNC(DD_CONTEXT->debugStrings[i].text);
}
#endif // DEBUG_DRAW_STR_DEALLOC_FUNC
if (DD_CONTEXT->renderInterface != nullptr && DD_CONTEXT->glyphTexHandle != nullptr)
{
DD_CONTEXT->renderInterface->destroyGlyphTexture(DD_CONTEXT->glyphTexHandle);
}
DD_CONTEXT->~InternalContext(); // Destroy first
DD_MFREE(DD_CONTEXT);
#ifndef DEBUG_DRAW_EXPLICIT_CONTEXT
DD_CONTEXT = nullptr;
#endif // DEBUG_DRAW_EXPLICIT_CONTEXT
}
}
bool isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx))
{
return (DD_CONTEXT != nullptr && DD_CONTEXT->renderInterface != nullptr);
return (DD_CONTEXT != nullptr && DD_CONTEXT->renderInterface != nullptr);
}
bool hasPendingDraws(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx))
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return false;
}
return (DD_CONTEXT->debugStringsCount + DD_CONTEXT->debugPointsCount + DD_CONTEXT->debugLinesCount) > 0;
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return false;
}
return (DD_CONTEXT->debugStringsCount + DD_CONTEXT->debugPointsCount + DD_CONTEXT->debugLinesCount) > 0;
}
void flush(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) const std::int64_t currTimeMillis, const std::uint32_t flags)
{
if (!hasPendingDraws(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (!hasPendingDraws(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
// Save the last know time value for next dd::line/dd::point calls.
DD_CONTEXT->currentTimeMillis = currTimeMillis;
// Save the last know time value for next dd::line/dd::point calls.
DD_CONTEXT->currentTimeMillis = currTimeMillis;
// Let the user set common render states.
DD_CONTEXT->renderInterface->beginDraw();
// Let the user set common render states.
DD_CONTEXT->renderInterface->beginDraw();
// Issue the render calls:
if (flags & FlushLines) { drawDebugLines(DD_EXPLICIT_CONTEXT_ONLY(ctx)); }
if (flags & FlushPoints) { drawDebugPoints(DD_EXPLICIT_CONTEXT_ONLY(ctx)); }
if (flags & FlushText) { drawDebugStrings(DD_EXPLICIT_CONTEXT_ONLY(ctx)); }
// Issue the render calls:
if (flags & FlushLines) { drawDebugLines(DD_EXPLICIT_CONTEXT_ONLY(ctx)); }
if (flags & FlushPoints) { drawDebugPoints(DD_EXPLICIT_CONTEXT_ONLY(ctx)); }
if (flags & FlushText) { drawDebugStrings(DD_EXPLICIT_CONTEXT_ONLY(ctx)); }
// And cleanup if needed.
DD_CONTEXT->renderInterface->endDraw();
// And cleanup if needed.
DD_CONTEXT->renderInterface->endDraw();
// Remove all expired objects, regardless of draw flags:
clearDebugQueue(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DD_CONTEXT->debugStrings, DD_CONTEXT->debugStringsCount);
clearDebugQueue(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DD_CONTEXT->debugPoints, DD_CONTEXT->debugPointsCount);
clearDebugQueue(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DD_CONTEXT->debugLines, DD_CONTEXT->debugLinesCount);
// Remove all expired objects, regardless of draw flags:
clearDebugQueue(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DD_CONTEXT->debugStrings, DD_CONTEXT->debugStringsCount);
clearDebugQueue(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DD_CONTEXT->debugPoints, DD_CONTEXT->debugPointsCount);
clearDebugQueue(DD_EXPLICIT_CONTEXT_ONLY(ctx,) DD_CONTEXT->debugLines, DD_CONTEXT->debugLinesCount);
}
void clear(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx))
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
// Let the user cleanup the debug strings:
#ifdef DEBUG_DRAW_STR_DEALLOC_FUNC
for (int i = 0; i < DEBUG_DRAW_MAX_STRINGS; ++i)
{
DEBUG_DRAW_STR_DEALLOC_FUNC(DD_CONTEXT->debugStrings[i].text);
}
#endif // DEBUG_DRAW_STR_DEALLOC_FUNC
DD_CONTEXT->vertexBufferUsed = 0;
DD_CONTEXT->debugStringsCount = 0;
DD_CONTEXT->debugPointsCount = 0;
DD_CONTEXT->debugLinesCount = 0;
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
// Let the user cleanup the debug strings:
#ifdef DEBUG_DRAW_STR_DEALLOC_FUNC
for (int i = 0; i < DEBUG_DRAW_MAX_STRINGS; ++i)
{
DEBUG_DRAW_STR_DEALLOC_FUNC(DD_CONTEXT->debugStrings[i].text);
}
#endif // DEBUG_DRAW_STR_DEALLOC_FUNC
DD_CONTEXT->vertexBufferUsed = 0;
DD_CONTEXT->debugStringsCount = 0;
DD_CONTEXT->debugPointsCount = 0;
DD_CONTEXT->debugLinesCount = 0;
}
void point(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In pos, ddVec3_In color,
const float size, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (DD_CONTEXT->debugPointsCount == DEBUG_DRAW_MAX_POINTS)
{
DEBUG_DRAW_OVERFLOWED("DEBUG_DRAW_MAX_POINTS limit reached! Dropping further debug point draws.");
return;
}
DebugPoint & point = DD_CONTEXT->debugPoints[DD_CONTEXT->debugPointsCount++];
point.expiryDateMillis = DD_CONTEXT->currentTimeMillis + durationMillis;
point.depthEnabled = depthEnabled;
point.size = size;
vecCopy(point.position, pos);
vecCopy(point.color, color);
const float size, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (DD_CONTEXT->debugPointsCount == DEBUG_DRAW_MAX_POINTS)
{
DEBUG_DRAW_OVERFLOWED("DEBUG_DRAW_MAX_POINTS limit reached! Dropping further debug point draws.");
return;
}
DebugPoint & point = DD_CONTEXT->debugPoints[DD_CONTEXT->debugPointsCount++];
point.expiryDateMillis = DD_CONTEXT->currentTimeMillis + durationMillis;
point.depthEnabled = depthEnabled;
point.size = size;
vecCopy(point.position, pos);
vecCopy(point.color, color);
}
void line(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In from, ddVec3_In to,
ddVec3_In color, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (DD_CONTEXT->debugLinesCount == DEBUG_DRAW_MAX_LINES)
{
DEBUG_DRAW_OVERFLOWED("DEBUG_DRAW_MAX_LINES limit reached! Dropping further debug line draws.");
return;
}
DebugLine & line = DD_CONTEXT->debugLines[DD_CONTEXT->debugLinesCount++];
line.expiryDateMillis = DD_CONTEXT->currentTimeMillis + durationMillis;
line.depthEnabled = depthEnabled;
vecCopy(line.posFrom, from);
vecCopy(line.posTo, to);
vecCopy(line.color, color);
ddVec3_In color, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (DD_CONTEXT->debugLinesCount == DEBUG_DRAW_MAX_LINES)
{
DEBUG_DRAW_OVERFLOWED("DEBUG_DRAW_MAX_LINES limit reached! Dropping further debug line draws.");
return;
}
DebugLine & line = DD_CONTEXT->debugLines[DD_CONTEXT->debugLinesCount++];
line.expiryDateMillis = DD_CONTEXT->currentTimeMillis + durationMillis;
line.depthEnabled = depthEnabled;
vecCopy(line.posFrom, from);
vecCopy(line.posTo, to);
vecCopy(line.color, color);
}
void screenText(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) const char * const str, ddVec3_In pos,
ddVec3_In color, const float scaling, const int durationMillis)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (DD_CONTEXT->glyphTexHandle == nullptr)
{
return;
}
if (DD_CONTEXT->debugStringsCount == DEBUG_DRAW_MAX_STRINGS)
{
DEBUG_DRAW_OVERFLOWED("DEBUG_DRAW_MAX_STRINGS limit reached! Dropping further debug string draws.");
return;
}
DebugString & dstr = DD_CONTEXT->debugStrings[DD_CONTEXT->debugStringsCount++];
dstr.expiryDateMillis = DD_CONTEXT->currentTimeMillis + durationMillis;
dstr.posX = pos[X];
dstr.posY = pos[Y];
dstr.scaling = scaling;
dstr.text = str;
dstr.centered = false;
vecCopy(dstr.color, color);
ddVec3_In color, const float scaling, const int durationMillis)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (DD_CONTEXT->glyphTexHandle == nullptr)
{
return;
}
if (DD_CONTEXT->debugStringsCount == DEBUG_DRAW_MAX_STRINGS)
{
DEBUG_DRAW_OVERFLOWED("DEBUG_DRAW_MAX_STRINGS limit reached! Dropping further debug string draws.");
return;
}
DebugString & dstr = DD_CONTEXT->debugStrings[DD_CONTEXT->debugStringsCount++];
dstr.expiryDateMillis = DD_CONTEXT->currentTimeMillis + durationMillis;
dstr.posX = pos[X];
dstr.posY = pos[Y];
dstr.scaling = scaling;
dstr.text = str;
dstr.centered = false;
vecCopy(dstr.color, color);
}
void projectedText(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) const char * const str, ddVec3_In pos, ddVec3_In color,
ddMat4x4_In vpMatrix, const int sx, const int sy, const int sw, const int sh, const float scaling,
const int durationMillis)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (DD_CONTEXT->glyphTexHandle == nullptr)
{
return;
}
if (DD_CONTEXT->debugStringsCount == DEBUG_DRAW_MAX_STRINGS)
{
DEBUG_DRAW_OVERFLOWED("DEBUG_DRAW_MAX_STRINGS limit reached! Dropping further debug string draws.");
return;
}
float tempPoint[4];
matTransformPointXYZW(tempPoint, pos, vpMatrix);
// Bail if W ended up as zero.
if (floatAbs(tempPoint[W]) < FloatEpsilon)
{
return;
}
// Bail if point is behind camera.
if (tempPoint[Z] < -tempPoint[W] || tempPoint[Z] > tempPoint[W])
{
return;
}
// Perspective divide (we only care about the 2D part now):
tempPoint[X] /= tempPoint[W];
tempPoint[Y] /= tempPoint[W];
// Map to window coordinates:
float scrX = ((tempPoint[X] * 0.5f) + 0.5f) * sw + sx;
float scrY = ((tempPoint[Y] * 0.5f) + 0.5f) * sh + sy;
// Need to invert the direction because on OGL the screen origin is the bottom-left corner.
// NOTE: This is not renderer agnostic, I think... Should add a #define or something!
scrY = static_cast<float>(sh) - scrY;
DebugString & dstr = DD_CONTEXT->debugStrings[DD_CONTEXT->debugStringsCount++];
dstr.expiryDateMillis = DD_CONTEXT->currentTimeMillis + durationMillis;
dstr.posX = scrX;
dstr.posY = scrY;
dstr.scaling = scaling;
dstr.text = str;
dstr.centered = true;
vecCopy(dstr.color, color);
ddMat4x4_In vpMatrix, const int sx, const int sy, const int sw, const int sh, const float scaling,
const int durationMillis)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (DD_CONTEXT->glyphTexHandle == nullptr)
{
return;
}
if (DD_CONTEXT->debugStringsCount == DEBUG_DRAW_MAX_STRINGS)
{
DEBUG_DRAW_OVERFLOWED("DEBUG_DRAW_MAX_STRINGS limit reached! Dropping further debug string draws.");
return;
}
float tempPoint[4];
matTransformPointXYZW(tempPoint, pos, vpMatrix);
// Bail if W ended up as zero.
if (floatAbs(tempPoint[W]) < FloatEpsilon)
{
return;
}
// Bail if point is behind camera.
if (tempPoint[Z] < -tempPoint[W] || tempPoint[Z] > tempPoint[W])
{
return;
}
// Perspective divide (we only care about the 2D part now):
tempPoint[X] /= tempPoint[W];
tempPoint[Y] /= tempPoint[W];
// Map to window coordinates:
float scrX = ((tempPoint[X] * 0.5f) + 0.5f) * sw + sx;
float scrY = ((tempPoint[Y] * 0.5f) + 0.5f) * sh + sy;
// Need to invert the direction because on OGL the screen origin is the bottom-left corner.
// NOTE: This is not renderer agnostic, I think... Should add a #define or something!
scrY = static_cast<float>(sh) - scrY;
DebugString & dstr = DD_CONTEXT->debugStrings[DD_CONTEXT->debugStringsCount++];
dstr.expiryDateMillis = DD_CONTEXT->currentTimeMillis + durationMillis;
dstr.posX = scrX;
dstr.posY = scrY;
dstr.scaling = scaling;
dstr.text = str;
dstr.centered = true;
vecCopy(dstr.color, color);
}
void axisTriad(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddMat4x4_In transform, const float size,
const float length, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 p0, p1, p2, p3;
ddVec3 xEnd, yEnd, zEnd;
ddVec3 origin, cR, cG, cB;
vecSet(cR, 1.0f, 0.0f, 0.0f);
vecSet(cG, 0.0f, 1.0f, 0.0f);
vecSet(cB, 0.0f, 0.0f, 1.0f);
vecSet(origin, 0.0f, 0.0f, 0.0f);
vecSet(xEnd, length, 0.0f, 0.0f);
vecSet(yEnd, 0.0f, length, 0.0f);
vecSet(zEnd, 0.0f, 0.0f, length);
matTransformPointXYZ(p0, origin, transform);
matTransformPointXYZ(p1, xEnd, transform);
matTransformPointXYZ(p2, yEnd, transform);
matTransformPointXYZ(p3, zEnd, transform);
arrow(DD_EXPLICIT_CONTEXT_ONLY(ctx,) p0, p1, cR, size, durationMillis, depthEnabled); // X: red axis
arrow(DD_EXPLICIT_CONTEXT_ONLY(ctx,) p0, p2, cG, size, durationMillis, depthEnabled); // Y: green axis
arrow(DD_EXPLICIT_CONTEXT_ONLY(ctx,) p0, p3, cB, size, durationMillis, depthEnabled); // Z: blue axis
const float length, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 p0, p1, p2, p3;
ddVec3 xEnd, yEnd, zEnd;
ddVec3 origin, cR, cG, cB;
vecSet(cR, 1.0f, 0.0f, 0.0f);
vecSet(cG, 0.0f, 1.0f, 0.0f);
vecSet(cB, 0.0f, 0.0f, 1.0f);
vecSet(origin, 0.0f, 0.0f, 0.0f);
vecSet(xEnd, length, 0.0f, 0.0f);
vecSet(yEnd, 0.0f, length, 0.0f);
vecSet(zEnd, 0.0f, 0.0f, length);
matTransformPointXYZ(p0, origin, transform);
matTransformPointXYZ(p1, xEnd, transform);
matTransformPointXYZ(p2, yEnd, transform);
matTransformPointXYZ(p3, zEnd, transform);
arrow(DD_EXPLICIT_CONTEXT_ONLY(ctx,) p0, p1, cR, size, durationMillis, depthEnabled); // X: red axis
arrow(DD_EXPLICIT_CONTEXT_ONLY(ctx,) p0, p2, cG, size, durationMillis, depthEnabled); // Y: green axis
arrow(DD_EXPLICIT_CONTEXT_ONLY(ctx,) p0, p3, cB, size, durationMillis, depthEnabled); // Z: blue axis
}
void arrow(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In from, ddVec3_In to, ddVec3_In color,
const float size, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
static const float arrowStep = 30.0f; // In degrees
static const float arrowSin[45] = {
0.0f, 0.5f, 0.866025f, 1.0f, 0.866025f, 0.5f, -0.0f, -0.5f, -0.866025f,
-1.0f, -0.866025f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
};
static const float arrowCos[45] = {
1.0f, 0.866025f, 0.5f, -0.0f, -0.5f, -0.866026f, -1.0f, -0.866025f, -0.5f, 0.0f,
0.5f, 0.866026f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
};
// Body line:
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) from, to, color, durationMillis, depthEnabled);
// Aux vectors to compute the arrowhead:
ddVec3 up, right, forward;
vecSub(forward, to, from);
vecNormalize(forward, forward);
vecOrthogonalBasis(right, up, forward);
vecScale(forward, forward, size);
// Arrowhead is a cone (sin/cos tables used here):
float degrees = 0.0f;
for (int i = 0; degrees < 360.0f; degrees += arrowStep, ++i)
{
float scale;
ddVec3 v1, v2, temp;
scale = 0.5f * size * arrowCos[i];
vecScale(temp, right, scale);
vecSub(v1, to, forward);
vecAdd(v1, v1, temp);
scale = 0.5f * size * arrowSin[i];
vecScale(temp, up, scale);
vecAdd(v1, v1, temp);
scale = 0.5f * size * arrowCos[i + 1];
vecScale(temp, right, scale);
vecSub(v2, to, forward);
vecAdd(v2, v2, temp);
scale = 0.5f * size * arrowSin[i + 1];
vecScale(temp, up, scale);
vecAdd(v2, v2, temp);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) v1, to, color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) v1, v2, color, durationMillis, depthEnabled);
}
const float size, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
static const float arrowStep = 30.0f; // In degrees
static const float arrowSin[45] = {
0.0f, 0.5f, 0.866025f, 1.0f, 0.866025f, 0.5f, -0.0f, -0.5f, -0.866025f,
-1.0f, -0.866025f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
};
static const float arrowCos[45] = {
1.0f, 0.866025f, 0.5f, -0.0f, -0.5f, -0.866026f, -1.0f, -0.866025f, -0.5f, 0.0f,
0.5f, 0.866026f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
};
// Body line:
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) from, to, color, durationMillis, depthEnabled);
// Aux vectors to compute the arrowhead:
ddVec3 up, right, forward;
vecSub(forward, to, from);
vecNormalize(forward, forward);
vecOrthogonalBasis(right, up, forward);
vecScale(forward, forward, size);
// Arrowhead is a cone (sin/cos tables used here):
float degrees = 0.0f;
for (int i = 0; degrees < 360.0f; degrees += arrowStep, ++i)
{
float scale;
ddVec3 v1, v2, temp;
scale = 0.5f * size * arrowCos[i];
vecScale(temp, right, scale);
vecSub(v1, to, forward);
vecAdd(v1, v1, temp);
scale = 0.5f * size * arrowSin[i];
vecScale(temp, up, scale);
vecAdd(v1, v1, temp);
scale = 0.5f * size * arrowCos[i + 1];
vecScale(temp, right, scale);
vecSub(v2, to, forward);
vecAdd(v2, v2, temp);
scale = 0.5f * size * arrowSin[i + 1];
vecScale(temp, up, scale);
vecAdd(v2, v2, temp);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) v1, to, color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) v1, v2, color, durationMillis, depthEnabled);
}
}
void cross(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In center, const float length,
const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 from, to;
ddVec3 cR, cG, cB;
vecSet(cR, 1.0f, 0.0f, 0.0f);
vecSet(cG, 0.0f, 1.0f, 0.0f);
vecSet(cB, 0.0f, 0.0f, 1.0f);
const float cx = center[X];
const float cy = center[Y];
const float cz = center[Z];
const float hl = length * 0.5f; // Half on each side.
// Red line: X - length/2 to X + length/2
vecSet(from, cx - hl, cy, cz);
vecSet(to, cx + hl, cy, cz);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) from, to, cR, durationMillis, depthEnabled);
// Green line: Y - length/2 to Y + length/2
vecSet(from, cx, cy - hl, cz);
vecSet(to, cx, cy + hl, cz);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) from, to, cG, durationMillis, depthEnabled);
// Blue line: Z - length/2 to Z + length/2
vecSet(from, cx, cy, cz - hl);
vecSet(to, cx, cy, cz + hl);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) from, to, cB, durationMillis, depthEnabled);
const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 from, to;
ddVec3 cR, cG, cB;
vecSet(cR, 1.0f, 0.0f, 0.0f);
vecSet(cG, 0.0f, 1.0f, 0.0f);
vecSet(cB, 0.0f, 0.0f, 1.0f);
const float cx = center[X];
const float cy = center[Y];
const float cz = center[Z];
const float hl = length * 0.5f; // Half on each side.
// Red line: X - length/2 to X + length/2
vecSet(from, cx - hl, cy, cz);
vecSet(to, cx + hl, cy, cz);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) from, to, cR, durationMillis, depthEnabled);
// Green line: Y - length/2 to Y + length/2
vecSet(from, cx, cy - hl, cz);
vecSet(to, cx, cy + hl, cz);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) from, to, cG, durationMillis, depthEnabled);
// Blue line: Z - length/2 to Z + length/2
vecSet(from, cx, cy, cz - hl);
vecSet(to, cx, cy, cz + hl);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) from, to, cB, durationMillis, depthEnabled);
}
void circle(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In center, ddVec3_In planeNormal, ddVec3_In color,
const float radius, const float numSteps, const int durationMillis, const bool depthEnabled)
const float radius, const float numSteps, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 left, up;
ddVec3 point, lastPoint;
ddVec3 left, up;
ddVec3 point, lastPoint;
vecOrthogonalBasis(left, up, planeNormal);
vecOrthogonalBasis(left, up, planeNormal);
vecScale(up, up, radius);
vecScale(left, left, radius);
vecAdd(lastPoint, center, up);
vecScale(up, up, radius);
vecScale(left, left, radius);
vecAdd(lastPoint, center, up);
for (int i = 1; i <= numSteps; ++i)
{
const float radians = TAU * i / numSteps;
for (int i = 1; i <= numSteps; ++i)
{
const float radians = TAU * i / numSteps;
ddVec3 vs, vc;
vecScale(vs, left, floatSin(radians));
vecScale(vc, up, floatCos(radians));
ddVec3 vs, vc;
vecScale(vs, left, floatSin(radians));
vecScale(vc, up, floatCos(radians));
vecAdd(point, center, vs);
vecAdd(point, point, vc);
vecAdd(point, center, vs);
vecAdd(point, point, vc);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) lastPoint, point, color, durationMillis, depthEnabled);
vecCopy(lastPoint, point);
}
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) lastPoint, point, color, durationMillis, depthEnabled);
vecCopy(lastPoint, point);
}
}
void plane(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In center, ddVec3_In planeNormal, ddVec3_In planeColor,
ddVec3_In normalVecColor, const float planeScale, const float normalVecScale, const int durationMillis,
const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 v1, v2, v3, v4;
ddVec3 tangent, bitangent;
vecOrthogonalBasis(tangent, bitangent, planeNormal);
// A little bit of preprocessor voodoo to make things more interesting :P
#define DD_PLANE_V(v, op1, op2) \
v[X] = (center[X] op1 (tangent[X] * planeScale) op2 (bitangent[X] * planeScale)); \
v[Y] = (center[Y] op1 (tangent[Y] * planeScale) op2 (bitangent[Y] * planeScale)); \
v[Z] = (center[Z] op1 (tangent[Z] * planeScale) op2 (bitangent[Z] * planeScale))
DD_PLANE_V(v1, -, -);
DD_PLANE_V(v2, +, -);
DD_PLANE_V(v3, +, +);
DD_PLANE_V(v4, -, +);
#undef DD_PLANE_V
// Draw the wireframe plane quadrilateral:
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) v1, v2, planeColor, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) v2, v3, planeColor, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) v3, v4, planeColor, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) v4, v1, planeColor, durationMillis, depthEnabled);
// Optionally add a line depicting the plane normal:
if (normalVecScale != 0.0f)
{
ddVec3 normalVec;
normalVec[X] = (planeNormal[X] * normalVecScale) + center[X];
normalVec[Y] = (planeNormal[Y] * normalVecScale) + center[Y];
normalVec[Z] = (planeNormal[Z] * normalVecScale) + center[Z];
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) center, normalVec, normalVecColor, durationMillis, depthEnabled);
}
ddVec3_In normalVecColor, const float planeScale, const float normalVecScale, const int durationMillis,
const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 v1, v2, v3, v4;
ddVec3 tangent, bitangent;
vecOrthogonalBasis(tangent, bitangent, planeNormal);
// A little bit of preprocessor voodoo to make things more interesting :P
#define DD_PLANE_V(v, op1, op2) \
v[X] = (center[X] op1 (tangent[X] * planeScale) op2 (bitangent[X] * planeScale)); \
v[Y] = (center[Y] op1 (tangent[Y] * planeScale) op2 (bitangent[Y] * planeScale)); \
v[Z] = (center[Z] op1 (tangent[Z] * planeScale) op2 (bitangent[Z] * planeScale))
DD_PLANE_V(v1, -, -);
DD_PLANE_V(v2, +, -);
DD_PLANE_V(v3, +, +);
DD_PLANE_V(v4, -, +);
#undef DD_PLANE_V
// Draw the wireframe plane quadrilateral:
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) v1, v2, planeColor, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) v2, v3, planeColor, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) v3, v4, planeColor, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) v4, v1, planeColor, durationMillis, depthEnabled);
// Optionally add a line depicting the plane normal:
if (normalVecScale != 0.0f)
{
ddVec3 normalVec;
normalVec[X] = (planeNormal[X] * normalVecScale) + center[X];
normalVec[Y] = (planeNormal[Y] * normalVecScale) + center[Y];
normalVec[Z] = (planeNormal[Z] * normalVecScale) + center[Z];
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) center, normalVec, normalVecColor, durationMillis, depthEnabled);
}
}
void sphere(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In center, ddVec3_In color,
const float radius, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
static const int stepSize = 15;
ddVec3 cache[360 / stepSize];
ddVec3 radiusVec;
vecSet(radiusVec, 0.0f, 0.0f, radius);
vecAdd(cache[0], center, radiusVec);
for (int n = 1; n < arrayLength(cache); ++n)
{
vecCopy(cache[n], cache[0]);
}
ddVec3 lastPoint, temp;
for (int i = stepSize; i <= 360; i += stepSize)
{
const float s = floatSin(degreesToRadians(i));
const float c = floatCos(degreesToRadians(i));
lastPoint[X] = center[X];
lastPoint[Y] = center[Y] + radius * s;
lastPoint[Z] = center[Z] + radius * c;
for (int n = 0, j = stepSize; j <= 360; j += stepSize, ++n)
{
temp[X] = center[X] + floatSin(degreesToRadians(j)) * radius * s;
temp[Y] = center[Y] + floatCos(degreesToRadians(j)) * radius * s;
temp[Z] = lastPoint[Z];
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) lastPoint, temp, color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) lastPoint, cache[n], color, durationMillis, depthEnabled);
vecCopy(cache[n], lastPoint);
vecCopy(lastPoint, temp);
}
}
const float radius, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
static const int stepSize = 15;
ddVec3 cache[360 / stepSize];
ddVec3 radiusVec;
vecSet(radiusVec, 0.0f, 0.0f, radius);
vecAdd(cache[0], center, radiusVec);
for (int n = 1; n < arrayLength(cache); ++n)
{
vecCopy(cache[n], cache[0]);
}
ddVec3 lastPoint, temp;
for (int i = stepSize; i <= 360; i += stepSize)
{
const float s = floatSin(degreesToRadians(i));
const float c = floatCos(degreesToRadians(i));
lastPoint[X] = center[X];
lastPoint[Y] = center[Y] + radius * s;
lastPoint[Z] = center[Z] + radius * c;
for (int n = 0, j = stepSize; j <= 360; j += stepSize, ++n)
{
temp[X] = center[X] + floatSin(degreesToRadians(j)) * radius * s;
temp[Y] = center[Y] + floatCos(degreesToRadians(j)) * radius * s;
temp[Z] = lastPoint[Z];
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) lastPoint, temp, color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) lastPoint, cache[n], color, durationMillis, depthEnabled);
vecCopy(cache[n], lastPoint);
vecCopy(lastPoint, temp);
}
}
}
void cone(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In apex, ddVec3_In dir, ddVec3_In color,
const float baseRadius, const float apexRadius, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
static const int stepSize = 20;
ddVec3 axis[3];
ddVec3 top, temp0, temp1, temp2;
ddVec3 p1, p2, lastP1, lastP2;
vecCopy(axis[2], dir);
vecNormalize(axis[2], axis[2]);
vecOrthogonalBasis(axis[0], axis[1], axis[2]);
axis[1][X] = -axis[1][X];
axis[1][Y] = -axis[1][Y];
axis[1][Z] = -axis[1][Z];
vecAdd(top, apex, dir);
vecScale(temp1, axis[1], baseRadius);
vecAdd(lastP2, top, temp1);
if (apexRadius == 0.0f)
{
for (int i = stepSize; i <= 360; i += stepSize)
{
vecScale(temp1, axis[0], floatSin(degreesToRadians(i)));
vecScale(temp2, axis[1], floatCos(degreesToRadians(i)));
vecAdd(temp0, temp1, temp2);
vecScale(temp0, temp0, baseRadius);
vecAdd(p2, top, temp0);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) lastP2, p2, color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) p2, apex, color, durationMillis, depthEnabled);
vecCopy(lastP2, p2);
}
}
else // A degenerate cone with open apex:
{
vecScale(temp1, axis[1], apexRadius);
vecAdd(lastP1, apex, temp1);
for (int i = stepSize; i <= 360; i += stepSize)
{
vecScale(temp1, axis[0], floatSin(degreesToRadians(i)));
vecScale(temp2, axis[1], floatCos(degreesToRadians(i)));
vecAdd(temp0, temp1, temp2);
vecScale(temp1, temp0, apexRadius);
vecScale(temp2, temp0, baseRadius);
vecAdd(p1, apex, temp1);
vecAdd(p2, top, temp2);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) lastP1, p1, color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) lastP2, p2, color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) p1, p2, color, durationMillis, depthEnabled);
vecCopy(lastP1, p1);
vecCopy(lastP2, p2);
}
}
const float baseRadius, const float apexRadius, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
static const int stepSize = 20;
ddVec3 axis[3];
ddVec3 top, temp0, temp1, temp2;
ddVec3 p1, p2, lastP1, lastP2;
vecCopy(axis[2], dir);
vecNormalize(axis[2], axis[2]);
vecOrthogonalBasis(axis[0], axis[1], axis[2]);
axis[1][X] = -axis[1][X];
axis[1][Y] = -axis[1][Y];
axis[1][Z] = -axis[1][Z];
vecAdd(top, apex, dir);
vecScale(temp1, axis[1], baseRadius);
vecAdd(lastP2, top, temp1);
if (apexRadius == 0.0f)
{
for (int i = stepSize; i <= 360; i += stepSize)
{
vecScale(temp1, axis[0], floatSin(degreesToRadians(i)));
vecScale(temp2, axis[1], floatCos(degreesToRadians(i)));
vecAdd(temp0, temp1, temp2);
vecScale(temp0, temp0, baseRadius);
vecAdd(p2, top, temp0);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) lastP2, p2, color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) p2, apex, color, durationMillis, depthEnabled);
vecCopy(lastP2, p2);
}
}
else // A degenerate cone with open apex:
{
vecScale(temp1, axis[1], apexRadius);
vecAdd(lastP1, apex, temp1);
for (int i = stepSize; i <= 360; i += stepSize)
{
vecScale(temp1, axis[0], floatSin(degreesToRadians(i)));
vecScale(temp2, axis[1], floatCos(degreesToRadians(i)));
vecAdd(temp0, temp1, temp2);
vecScale(temp1, temp0, apexRadius);
vecScale(temp2, temp0, baseRadius);
vecAdd(p1, apex, temp1);
vecAdd(p2, top, temp2);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) lastP1, p1, color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) lastP2, p2, color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) p1, p2, color, durationMillis, depthEnabled);
vecCopy(lastP1, p1);
vecCopy(lastP2, p2);
}
}
}
void box(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) const ddVec3 points[8], ddVec3_In color,
const int durationMillis, const bool depthEnabled)
{
// Build the lines from points using clever indexing tricks:
// (& 3 is a fancy way of doing % 4, but avoids the expensive modulo operation)
for (int i = 0; i < 4; ++i)
{
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) points[i], points[(i + 1) & 3], color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) points[4 + i], points[4 + ((i + 1) & 3)], color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) points[i], points[4 + i], color, durationMillis, depthEnabled);
}
const int durationMillis, const bool depthEnabled)
{
// Build the lines from points using clever indexing tricks:
// (& 3 is a fancy way of doing % 4, but avoids the expensive modulo operation)
for (int i = 0; i < 4; ++i)
{
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) points[i], points[(i + 1) & 3], color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) points[4 + i], points[4 + ((i + 1) & 3)], color, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) points[i], points[4 + i], color, durationMillis, depthEnabled);
}
}
void box(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In center, ddVec3_In color, const float width,
const float height, const float depth, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
const float cx = center[X];
const float cy = center[Y];
const float cz = center[Z];
const float w = width * 0.5f;
const float h = height * 0.5f;
const float d = depth * 0.5f;
// Create all the 8 points:
ddVec3 points[8];
#define DD_BOX_V(v, op1, op2, op3) \
v[X] = cx op1 w; \
v[Y] = cy op2 h; \
v[Z] = cz op3 d
DD_BOX_V(points[0], -, +, +);
DD_BOX_V(points[1], -, +, -);
DD_BOX_V(points[2], +, +, -);
DD_BOX_V(points[3], +, +, +);
DD_BOX_V(points[4], -, -, +);
DD_BOX_V(points[5], -, -, -);
DD_BOX_V(points[6], +, -, -);
DD_BOX_V(points[7], +, -, +);
#undef DD_BOX_V
box(DD_EXPLICIT_CONTEXT_ONLY(ctx,) points, color, durationMillis, depthEnabled);
const float height, const float depth, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
const float cx = center[X];
const float cy = center[Y];
const float cz = center[Z];
const float w = width * 0.5f;
const float h = height * 0.5f;
const float d = depth * 0.5f;
// Create all the 8 points:
ddVec3 points[8];
#define DD_BOX_V(v, op1, op2, op3) \
v[X] = cx op1 w; \
v[Y] = cy op2 h; \
v[Z] = cz op3 d
DD_BOX_V(points[0], -, +, +);
DD_BOX_V(points[1], -, +, -);
DD_BOX_V(points[2], +, +, -);
DD_BOX_V(points[3], +, +, +);
DD_BOX_V(points[4], -, -, +);
DD_BOX_V(points[5], -, -, -);
DD_BOX_V(points[6], +, -, -);
DD_BOX_V(points[7], +, -, +);
#undef DD_BOX_V
box(DD_EXPLICIT_CONTEXT_ONLY(ctx,) points, color, durationMillis, depthEnabled);
}
void aabb(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In mins, ddVec3_In maxs,
ddVec3_In color, const int durationMillis, const bool depthEnabled)
ddVec3_In color, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 bb[2];
ddVec3 points[8];
ddVec3 bb[2];
ddVec3 points[8];
vecCopy(bb[0], mins);
vecCopy(bb[1], maxs);
vecCopy(bb[0], mins);
vecCopy(bb[1], maxs);
// Expand min/max bounds:
for (int i = 0; i < arrayLength(points); ++i)
{
points[i][X] = bb[(i ^ (i >> 1)) & 1][X];
points[i][Y] = bb[(i >> 1) & 1][Y];
points[i][Z] = bb[(i >> 2) & 1][Z];
}
// Expand min/max bounds:
for (int i = 0; i < arrayLength(points); ++i)
{
points[i][X] = bb[(i ^ (i >> 1)) & 1][X];
points[i][Y] = bb[(i >> 1) & 1][Y];
points[i][Z] = bb[(i >> 2) & 1][Z];
}
// Build the lines:
box(DD_EXPLICIT_CONTEXT_ONLY(ctx,) points, color, durationMillis, depthEnabled);
// Build the lines:
box(DD_EXPLICIT_CONTEXT_ONLY(ctx,) points, color, durationMillis, depthEnabled);
}
void frustum(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddMat4x4_In invClipMatrix,
ddVec3_In color, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
// Start with the standard clip volume, then bring it back to world space.
static const float planes[8][3] = {
// near plane
{ -1.0f, -1.0f, -1.0f }, { 1.0f, -1.0f, -1.0f },
{ 1.0f, 1.0f, -1.0f }, { -1.0f, 1.0f, -1.0f },
// far plane
{ -1.0f, -1.0f, 1.0f }, { 1.0f, -1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f }, { -1.0f, 1.0f, 1.0f }
};
ddVec3 points[8];
float wCoords[8];
// Transform the planes by the inverse clip matrix:
for (int i = 0; i < arrayLength(planes); ++i)
{
wCoords[i] = matTransformPointXYZW2(points[i], planes[i], invClipMatrix);
}
// Divide by the W component of each:
for (int i = 0; i < arrayLength(planes); ++i)
{
// But bail if any W ended up as zero.
if (floatAbs(wCoords[W]) < FloatEpsilon)
{
return;
}
points[i][X] /= wCoords[i];
points[i][Y] /= wCoords[i];
points[i][Z] /= wCoords[i];
}
// Connect the dots:
box(DD_EXPLICIT_CONTEXT_ONLY(ctx,) points, color, durationMillis, depthEnabled);
ddVec3_In color, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
// Start with the standard clip volume, then bring it back to world space.
static const float planes[8][3] = {
// near plane
{ -1.0f, -1.0f, -1.0f }, { 1.0f, -1.0f, -1.0f },
{ 1.0f, 1.0f, -1.0f }, { -1.0f, 1.0f, -1.0f },
// far plane
{ -1.0f, -1.0f, 1.0f }, { 1.0f, -1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f }, { -1.0f, 1.0f, 1.0f }
};
ddVec3 points[8];
float wCoords[8];
// Transform the planes by the inverse clip matrix:
for (int i = 0; i < arrayLength(planes); ++i)
{
wCoords[i] = matTransformPointXYZW2(points[i], planes[i], invClipMatrix);
}
// Divide by the W component of each:
for (int i = 0; i < arrayLength(planes); ++i)
{
// But bail if any W ended up as zero.
if (floatAbs(wCoords[W]) < FloatEpsilon)
{
return;
}
points[i][X] /= wCoords[i];
points[i][Y] /= wCoords[i];
points[i][Z] /= wCoords[i];
}
// Connect the dots:
box(DD_EXPLICIT_CONTEXT_ONLY(ctx,) points, color, durationMillis, depthEnabled);
}
void vertexNormal(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In origin, ddVec3_In normal,
const float length, const int durationMillis, const bool depthEnabled)
const float length, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 normalVec;
ddVec3 normalColor;
ddVec3 normalVec;
ddVec3 normalColor;
vecSet(normalColor, 1.0f, 1.0f, 1.0f);
vecSet(normalColor, 1.0f, 1.0f, 1.0f);
normalVec[X] = (normal[X] * length) + origin[X];
normalVec[Y] = (normal[Y] * length) + origin[Y];
normalVec[Z] = (normal[Z] * length) + origin[Z];
normalVec[X] = (normal[X] * length) + origin[X];
normalVec[Y] = (normal[Y] * length) + origin[Y];
normalVec[Z] = (normal[Z] * length) + origin[Z];
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) origin, normalVec, normalColor, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) origin, normalVec, normalColor, durationMillis, depthEnabled);
}
void tangentBasis(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In origin, ddVec3_In normal, ddVec3_In tangent,
ddVec3_In bitangent, const float lengths, const int durationMillis, const bool depthEnabled)
ddVec3_In bitangent, const float lengths, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 cN, cT, cB;
ddVec3 vN, vT, vB;
ddVec3 cN, cT, cB;
ddVec3 vN, vT, vB;
vecSet(cN, 1.0f, 1.0f, 1.0f); // Vertex normals are WHITE
vecSet(cT, 1.0f, 1.0f, 0.0f); // Tangents are YELLOW
vecSet(cB, 1.0f, 0.0f, 1.0f); // Bi-tangents are MAGENTA
vecSet(cN, 1.0f, 1.0f, 1.0f); // Vertex normals are WHITE
vecSet(cT, 1.0f, 1.0f, 0.0f); // Tangents are YELLOW
vecSet(cB, 1.0f, 0.0f, 1.0f); // Bi-tangents are MAGENTA
vN[X] = (normal[X] * lengths) + origin[X];
vN[Y] = (normal[Y] * lengths) + origin[Y];
vN[Z] = (normal[Z] * lengths) + origin[Z];
vN[X] = (normal[X] * lengths) + origin[X];
vN[Y] = (normal[Y] * lengths) + origin[Y];
vN[Z] = (normal[Z] * lengths) + origin[Z];
vT[X] = (tangent[X] * lengths) + origin[X];
vT[Y] = (tangent[Y] * lengths) + origin[Y];
vT[Z] = (tangent[Z] * lengths) + origin[Z];
vT[X] = (tangent[X] * lengths) + origin[X];
vT[Y] = (tangent[Y] * lengths) + origin[Y];
vT[Z] = (tangent[Z] * lengths) + origin[Z];
vB[X] = (bitangent[X] * lengths) + origin[X];
vB[Y] = (bitangent[Y] * lengths) + origin[Y];
vB[Z] = (bitangent[Z] * lengths) + origin[Z];
vB[X] = (bitangent[X] * lengths) + origin[X];
vB[Y] = (bitangent[Y] * lengths) + origin[Y];
vB[Z] = (bitangent[Z] * lengths) + origin[Z];
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) origin, vN, cN, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) origin, vT, cT, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) origin, vB, cB, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) origin, vN, cN, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) origin, vT, cT, durationMillis, depthEnabled);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) origin, vB, cB, durationMillis, depthEnabled);
}
void xzSquareGrid(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) const float mins, const float maxs, const float y,
const float step, ddVec3_In color, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 from, to;
for (float i = mins; i <= maxs; i += step)
{
// Horizontal line (along the X)
vecSet(from, mins, y, i);
vecSet(to, maxs, y, i);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) from, to, color, durationMillis, depthEnabled);
// Vertical line (along the Z)
vecSet(from, i, y, mins);
vecSet(to, i, y, maxs);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) from, to, color, durationMillis, depthEnabled);
}
const float step, ddVec3_In color, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 from, to;
for (float i = mins; i <= maxs; i += step)
{
// Horizontal line (along the X)
vecSet(from, mins, y, i);
vecSet(to, maxs, y, i);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) from, to, color, durationMillis, depthEnabled);
// Vertical line (along the Z)
vecSet(from, i, y, mins);
vecSet(to, i, y, maxs);
line(DD_EXPLICIT_CONTEXT_ONLY(ctx,) from, to, color, durationMillis, depthEnabled);
}
}
// ========================================================
......
......@@ -16,10 +16,12 @@
// Created by webpigeon on 11/06/22.
//
#ifndef FGGL_DEBUG_IMPL_LOGGING_STD20_HPP
#define FGGL_DEBUG_IMPL_LOGGING_STD20_HPP
#ifndef FGGL_DEBUG_IMPL_LOGGING_FMT_HPP
#define FGGL_DEBUG_IMPL_LOGGING_FMT_HPP
#include <fmt/format.h>
#include <fmt/color.h>
#include <iostream>
#include <string>
#include <string_view>
......@@ -44,35 +46,28 @@ namespace fggl::debug {
constexpr std::string_view level_to_string(Level level) {
switch (level) {
case Level::critical:
return "CRITICAL";
case Level::error:
return "ERROR";
case Level::warning:
return "WARNING";
case Level::info:
return "INFO";
case Level::debug:
return "DEBUG";
case Level::trace:
return "TRACE";
default:
return "UNKNOWN";
case Level::critical: return "CRITICAL";
case Level::error: return "ERROR";
case Level::warning: return "WARNING";
case Level::info: return "INFO";
case Level::debug: return "DEBUG";
case Level::trace: return "TRACE";
default: return "UNKNOWN";
}
}
inline void vlog(const char* file, int line, fmt::string_view format, fmt::format_args args) {
inline void vlog(const char *file, int line, fmt::string_view format, fmt::format_args args) {
fmt::print("{}: {}", file, line);
fmt::vprint(format, args);
}
template <typename S, typename... Args>
void logf(const char* file, int line, const S& format, Args&&... args) {
vlog( file, line, format, fmt::make_args_checked<Args...>(format, args...));
template<typename S, typename... Args>
void logf(const char *file, int line, const S &format, Args &&... args) {
vlog(file, line, format, fmt::make_format_args(format, args...));
}
#define info_va(format, ...) \
logf(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__)
logf(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__)
template<typename ...T>
void log(Level level, FmtType fmt, T &&...args) {
......@@ -80,36 +75,38 @@ namespace fggl::debug {
fmt::print(CERR_FMT, level_to_string(level), fmtStr);
}
template<typename ...T>
void error(FmtType fmt, T &&...args) {
log( Level::error, fmt, args...);
}
// inlined, pre-set level versions of the log function above
template<typename ...T>
void warning(FmtType fmt, T &&...args) {
log( Level::warning, fmt, args...);
inline void error(FmtType fmt, T &&...args) {
auto fmtStr = fmt::format(fmt::runtime(fmt), args...);
fmt::print(fg(fmt::color::red), CERR_FMT, level_to_string(Level::error), fmtStr);
}
template<typename ...T>
void info(FmtType fmt, T &&...args) {
log( Level::info, fmt, args... );
inline void warning(FmtType fmt, T &&...args) {
auto fmtStr = fmt::format(fmt::runtime(fmt), args...);
fmt::print(fg(fmt::color::orange), CERR_FMT, level_to_string(Level::warning), fmtStr);
}
template<typename ...T>
void log(FmtType fmt, T &&...args) {
log( Level::info, fmt, args...);
inline void info(FmtType fmt, T &&...args) {
auto fmtStr = fmt::format(fmt::runtime(fmt), args...);
fmt::print(CERR_FMT, level_to_string(Level::info), fmtStr);
}
template<typename ...T>
void debug(FmtType fmt, T &&...args) {
log( Level::debug, fmt, args...);
inline void debug(FmtType fmt, T &&...args) {
auto fmtStr = fmt::format(fmt::runtime(fmt), args...);
fmt::print(CERR_FMT, level_to_string(Level::debug), fmtStr);
}
template<typename ...T>
void trace(FmtType fmt, T &&...args) {
log( Level::trace, fmt, args...);
inline void trace(FmtType fmt, T &&...args) {
auto fmtStr = fmt::format(fmt::runtime(fmt), args...);
fmt::print(CERR_FMT, level_to_string(Level::trace), fmtStr);
}
}
#endif //FGGL_DEBUG_IMPL_LOGGING_STD20_HPP
#endif //FGGL_DEBUG_IMPL_LOGGING_FMT_HPP
......@@ -34,60 +34,59 @@ namespace fggl::debug {
* Logging levels
*/
enum class Level {
critical = spdlog::level::critical,
error = spdlog::level::err,
warning = spdlog::level::warn,
info = spdlog::level::info,
debug = spdlog::level::debug,
trace = spdlog::level::trace
critical = spdlog::level::critical,
error = spdlog::level::err,
warning = spdlog::level::warn,
info = spdlog::level::info,
debug = spdlog::level::debug,
trace = spdlog::level::trace
};
template<typename ...T>
void error(const FmtType& fmt, T&& ...args) {
void error(const FmtType &fmt, T &&...args) {
spdlog::error(fmt, args...);
}
template<typename ...T>
void warning(const FmtType& fmt, T&& ...args ){
void warning(const FmtType &fmt, T &&...args) {
spdlog::warn(fmt, args...);
}
template<typename ...T>
void info(const FmtType& fmt, T&& ...args ) {
void info(const FmtType &fmt, T &&...args) {
spdlog::info(fmt, args...);
}
template<typename ...T>
void debug(const FmtType& fmt, T&& ...args ) {
void debug(const FmtType &fmt, T &&...args) {
spdlog::debug(fmt, args...);
}
template<typename ...T>
void trace(const FmtType& fmt, T&& ...args ) {
void trace(const FmtType &fmt, T &&...args) {
spdlog::trace(fmt, args...);
}
template<typename ...T>
void log(const FmtType& fmt, T&& ...args) {
void log(const FmtType &fmt, T &&...args) {
spdlog::log(Level::info, fmt, args...);
}
template<typename ...T>
void log(Level level, const FmtType& fmt, T&& ...args) {
void log(Level level, const FmtType &fmt, T &&...args) {
spdlog::log(level, fmt, args...);
}
class Logger {
public:
Logger();
public:
Logger();
template<typename ...Args>
void log(Level level, const FmtType& fmt, Args&& ...args) {
spdlog::log(level, fmt, args...);
}
template<typename ...Args>
void log(Level level, const FmtType &fmt, Args &&...args) {
spdlog::log(level, fmt, args...);
}
};
}
#endif //FGGL_DEBUG_IMPL_LOGGING_SPDLOG_HPP
......@@ -21,33 +21,37 @@
#include <string_view>
namespace fggl::debug {
std::string demangle(const char* name);
using FmtType = const std::string_view;
enum class Level;
template<typename ...T>
void error(FmtType fmt, T&& ...args);
void error(FmtType fmt, T &&...args);
template<typename ...T>
void warning(FmtType fmt, T&& ...args );
void warning(FmtType fmt, T &&...args);
template<typename ...T>
void info(FmtType fmt, T&& ...args );
void info(FmtType fmt, T &&...args);
template<typename ...T>
void debug(FmtType fmt, T&& ...args );
void debug(FmtType fmt, T &&...args);
template<typename ...T>
void trace(FmtType fmt, T&& ...args );
void trace(FmtType fmt, T &&...args);
template<typename ...T>
void log(FmtType fmt, T&& ...args);
void log(FmtType fmt, T &&...args);
template<typename ...T>
void log(Level level, FmtType fmt, T&& ...args);
void log(Level level, FmtType fmt, T &&...args);
}
#include "fggl/debug/impl/logging_std20.hpp"
#include "fggl/debug/impl/logging_fmt.hpp"
#endif //FGGL_DEBUG_LOGGING_HPP
......@@ -28,18 +28,19 @@
namespace fggl::display {
struct GLFW {
constexpr static const char* name = "fggl::display::glfw";
constexpr static const std::array<modules::ModuleService, 1> provides = {
constexpr static const char *name = "fggl::display::glfw";
constexpr static const std::array<modules::ServiceName, 1> provides = {
WindowService::service
};
constexpr static const std::array<modules::ModuleService, 2> depends = {
constexpr static const std::array<modules::ServiceName, 2> depends = {
fggl::input::Input::service,
fggl::gfx::WindowGraphics::service
};
static const modules::ServiceFactory factory;
static bool factory(modules::ServiceName name, modules::Services &serviceManager);
};
bool glfw_factory(modules::ModuleService service, modules::Services& services) {
bool GLFW::factory(modules::ServiceName service, modules::Services &services) {
if (service == WindowService::service) {
auto input = services.get<input::Input>();
auto graphics = services.get<gfx::WindowGraphics>();
......@@ -50,7 +51,6 @@ namespace fggl::display {
}
return false;
}
const modules::ServiceFactory GLFW::factory = glfw_factory;
} // namespace fggl::display
......
......@@ -29,10 +29,12 @@ namespace fggl::display::glfw {
class WindowService : public display::WindowService {
public:
explicit WindowService(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics* gfx) : m_context(std::move(context)), m_gfx(gfx), m_windows() {}
explicit WindowService(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics *gfx)
: m_context(std::move(context)), m_gfx(gfx), m_windows() {}
virtual ~WindowService() = default;
display::Window* create() override {
display::Window *create() override {
m_windows.push_back(std::make_unique<Window>(m_context, m_gfx));
return m_windows.back().get();
}
......@@ -43,7 +45,7 @@ namespace fggl::display::glfw {
private:
std::shared_ptr<GlfwContext> m_context;
gfx::WindowGraphics* m_gfx;
gfx::WindowGraphics *m_gfx;
std::vector<std::unique_ptr<Window>> m_windows;
};
......
......@@ -45,7 +45,7 @@ namespace fggl::display::glfw {
class GlfwContext {
public:
explicit GlfwContext(fggl::input::Input* input);
explicit GlfwContext(fggl::input::Input *input);
~GlfwContext();
void pollEvents();
......@@ -77,7 +77,7 @@ namespace fggl::display::glfw {
class Window : public display::Window {
public:
explicit Window(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics*);
explicit Window(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics *);
~Window() override;
Window(Window &) = delete;
......@@ -105,8 +105,8 @@ namespace fggl::display::glfw {
inline void framesize(int width, int height) {
m_framesize = math::vec2(width, height);
if ( m_graphics != nullptr ) {
m_graphics->resize( width, height );
if (m_graphics != nullptr) {
m_graphics->resize(width, height);
}
}
......@@ -117,7 +117,7 @@ namespace fggl::display::glfw {
return glfwWindowShouldClose(m_window);
}
inline void setTitle(const char* title) override {
inline void setTitle(const char *title) override {
assert(m_window != nullptr);
glfwSetWindowTitle(m_window, title);
}
......
......@@ -44,7 +44,7 @@ namespace fggl::display::glfw {
return *instance;
}
inline void setup(input::Input* input) {
inline void setup(input::Input *input) {
m_inputs = input;
}
......@@ -95,7 +95,7 @@ namespace fggl::display::glfw {
}
private:
input::Input* m_inputs;
input::Input *m_inputs;
};
......
......@@ -21,8 +21,10 @@
#include "fggl/modules/module.hpp"
#include "fggl/math/types.hpp"
#include "fggl/gfx/interfaces.hpp"
//! Classes responsible for interacting with display managers
namespace fggl::display {
class Window {
......@@ -30,12 +32,6 @@ namespace fggl::display {
virtual ~Window() = default;
virtual void activate() const = 0;
template<typename T, typename ...Args>
void make_graphics(Args... args) {
activate();
m_graphics = std::make_unique<T>(*this, args...);
}
// window-related getters
[[nodiscard]]
virtual math::vec2i frameSize() const = 0;
......@@ -51,7 +47,7 @@ namespace fggl::display {
return *m_graphics;
}
virtual void setTitle(const char* title) = 0;
virtual void setTitle(const char *title) = 0;
virtual void setFullscreen(bool state) = 0;
......@@ -59,14 +55,15 @@ namespace fggl::display {
virtual bool isFullscreen() const = 0;
protected:
std::unique_ptr<gfx::Graphics> m_graphics;
gfx::Graphics* m_graphics;
};
class WindowService {
public:
constexpr static const modules::ModuleService service = modules::make_service("fggl::display::WindowService");
constexpr static const auto
service = modules::make_service("fggl::display::WindowService");
virtual Window* create() = 0;
virtual Window *create() = 0;
virtual void pollEvents() = 0;
};
......
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 25/03/23.
//
#ifndef FGGL_DS_GRAPH_HPP
#define FGGL_DS_GRAPH_HPP
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
namespace fggl::ds {
template<typename T>
class DirectedGraph {
public:
/**
* Add a single edge to the graph.
*
* If the entry does not already exist, this will create the entry before adding the dependencies to it.
* If the entry already exists, this will append the provided dependencies to its existing list.
*
* @param start the entry which depends something else
* @param end the thing it depends on
*/
inline void addEdge(const T start, const T end) {
m_edges[start].push_back(end);
m_edges[end];
}
/**
* Add a single vertex to the graph.
*/
inline void addVertex(const T vertex) {
m_edges[vertex];
}
/**
* Add a series of dependencies for an entry.
*
* If the entry does not already exist, this will create the entry before adding the dependencies to it.
* If the entry already exists, this will append the provided dependencies to its existing list.
*
* @param name the entry having dependencies added
* @param dependencies the things it depends on
*/
void addEdges(const T name, const std::vector<T> &dependencies) {
auto existing = m_edges.find(name);
if (existing == m_edges.end()) {
m_edges[name] = dependencies;
} else {
existing->second.insert(existing->second.end(), dependencies.begin(), dependencies.end());
}
}
/**
* Clear all currently stored dependencies.
*
* This method will result in the dependency graph being empty, with no known modules.
*/
inline void clear() {
m_edges.clear();
}
inline auto begin() const {
return m_edges.begin();
}
inline auto end() const {
return m_edges.end();
}
bool getOrder(std::stack<T> &stack) {
std::set<T> visited{};
for (const auto &module : m_edges) {
if (!visited.contains(module.first)) {
sortUtil(module.first, visited, stack);
}
}
return true;
}
bool getOrderPartial(T first, std::stack<T> &stack) {
std::set<T> visited{};
sortUtil(first, visited, stack);
return true;
}
bool getOrderRev(std::queue<T> &stack) {
std::set<T> visited{};
for (const auto &module : m_edges) {
if (!visited.contains(module.first)) {
sortUtilRev(module.first, visited, stack);
}
}
return true;
}
bool getOrderPartialRev(T first, std::queue<T>& queue) {
std::set<T> visited{};
sortUtilRev(first, visited, queue);
return true;
}
private:
std::map<T, std::vector<T>> m_edges;
void sortUtil(T idx, std::set<T> &visited, std::stack<T> &stack) {
visited.emplace(idx);
for (auto dep : m_edges.at(idx)) {
if (!visited.contains(dep))
sortUtil(dep, visited, stack);
}
stack.push(idx);
}
void sortUtilRev(T idx, std::set<T> &visited, std::queue<T> &stack) {
visited.emplace(idx);
for (auto dep : m_edges.at(idx)) {
if (!visited.contains(dep))
sortUtilRev(dep, visited, stack);
}
stack.push(idx);
}
};
} // namespace fggl::ds
#endif //FGGL_DS_GRAPH_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Wrappers for types that probably should be implemented, but can be faked for now.
// These are placeholders for the more advanced/specialist data structures we should be using, but mocked out using
// more basic ones.
//
#ifndef FGGL_DS_PLACEHOLDER_HPP
#define FGGL_DS_PLACEHOLDER_HPP
#include <map>
#include <cassert>
namespace fggl::ds {
template<typename T, std::size_t N>
class FakeSlotMap {
public:
using WeakRef = std::size_t;
constexpr static WeakRef BAD_INDEX = 0;
inline bool valid(WeakRef idx) const {
return m_data.find(idx) != m_data.end();
}
WeakRef allocate() {
if (N <= m_data.size()) {
assert(0 && "Fake slot map emulated out of space");
return BAD_INDEX;
}
auto myIdx = m_nextIdx++;
m_data[myIdx] = T();
}
void free(WeakRef idx) {
m_data.erase(idx);
}
T &get(WeakRef idx) const {
assert(valid(idx));
return m_data[idx];
}
T *tryGet(WeakRef idx) const {
if (valid(idx)) {
return &m_data[idx];
}
return nullptr;
}
private:
std::map<WeakRef, T> m_data;
std::size_t m_nextIdx = 1;
};
} // namespace fggl::ds
#endif //FGGL_DS_PLACEHOLDER_HPP
......@@ -12,19 +12,20 @@
* If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef FGGL_DATA_PROCEDURE_HPP
#define FGGL_DATA_PROCEDURE_HPP
//
// Created by webpigeon on 23/07/22.
//
namespace fggl::data {
#ifndef FGGL_DS_SLOT_MAP_HPP
#define FGGL_DS_SLOT_MAP_HPP
class DataRegistry {
#include "fggl/ds/placeholder.hpp"
public:
DataRegistry();
~DataRegistry();
namespace fggl::ds {
};
template<typename T, std::size_t N>
using SlotMap = FakeSlotMap<T, N>;
}
} // namespace fggl::ds
#endif
#endif //FGGL_DS_SLOT_MAP_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef FGGL_ECS_COMPONENT_HPP
#define FGGL_ECS_COMPONENT_HPP
#include "utility.hpp"
#include "fggl/debug/logging.hpp"
#include <cassert>
#include <cstring>
#include <string>
#include <new>
#include <utility>
#include <iostream>
#include <typeinfo>
#include <vector>
#include <unordered_map>
#include "yaml-cpp/yaml.h"
namespace fggl::ecs {
template<typename T>
bool restore_config(T* comp, const YAML::Node& node);
class ComponentBase {
public:
using data_t = unsigned char;
virtual ~ComponentBase() {};
// in place
virtual void destroy(data_t *data) const = 0;
virtual void move(data_t *src, data_t *dest) const = 0;
virtual void bulkMove(data_t *src, data_t *dest, std::size_t count) = 0;
virtual void construct(data_t *data) const = 0;
// virtual
virtual void *construct() const = 0;
virtual void *restore(const YAML::Node& config) const = 0;
virtual void *copyConstruct(const void *src) = 0;
virtual const char *name() const = 0;
virtual const component_type_t id() const = 0;
virtual std::size_t size() const = 0;
};
template<class C>
class Component : public ComponentBase {
public:
virtual void destroy(data_t *data) const override {
C *location = std::launder(reinterpret_cast<C *>(data));
location->~C();
}
virtual const char *name() const override {
return C::name;
}
virtual const component_type_t id() const {
return Component<C>::typeID();
}
virtual void construct(unsigned char *data) const override {
new(data) C();
}
virtual void* restore(const YAML::Node& config) const override {
C* ptr = new C();
bool restored = restore_config<C>(ptr, config);
if ( !restored ) {
debug::error("error restoring {}", C::name);
assert( false && "failed to restore configuration when loading type!" );
}
return ptr;
}
void *copyConstruct(const void *src) override {
const C *srcPtr = (C *) src;
return new C(*srcPtr);
}
void *construct() const override {
return new C();
}
virtual void move(data_t *src, data_t *dest) const override {
assert(src != nullptr);
assert(dest != nullptr);
new(&dest[0]) C(std::move(*reinterpret_cast<C *>(src)));
}
virtual void bulkMove(data_t *src, data_t *dest, std::size_t count) {
if (std::is_trivially_copyable<C>::value) {
std::memcpy(dest, src, count * size());
} else {
unsigned char *srcPtr = src;
unsigned char *destPtr = dest;
for (std::size_t i = 0; i < count; ++i) {
new(destPtr) C(std::move(*reinterpret_cast<C *>(srcPtr)));
srcPtr += sizeof(C);
destPtr += sizeof(C);
}
}
}
virtual std::size_t size() const {
return sizeof(C);
}
static component_type_t typeID() {
return TypeIdGenerator<ComponentBase>::GetNewID<C>();
}
};
}
#endif
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 22/07/22.
//
#ifndef FGGL_ECS_COMPONENT_FWD_HPP
#define FGGL_ECS_COMPONENT_FWD_HPP
#include "fggl/ecs/component.hpp"
#include "fggl/math/types.hpp"
#include "fggl/gfx/phong.hpp"
#include "fggl/data/model.hpp"
#include "fggl/phys/types.hpp"
#ifdef __GNUC__
#include <cxxabi.h>
#endif
namespace fggl::ecs {
template<typename T>
bool restore_config(T* comp, const YAML::Node& node) {
char* realName;
#ifdef __GNUC__
int status;
realName = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status);
#endif
debug::log(debug::Level::warning, "restore_config is not implemented for {}", realName);
return false;
}
template<>
bool restore_config(math::Transform* comp, const YAML::Node& node);
template<>
bool restore_config(gfx::PhongMaterial* comp, const YAML::Node& node);
template<>
bool restore_config(data::StaticMesh* meshComp, const YAML::Node& node);
template<>
bool restore_config(phys::RigidBody* body, const YAML::Node& node);
template<>
bool restore_config(phys::CollisionCallbacks* callbacks, const YAML::Node& node);
template<>
bool restore_config(phys::CollisionCache* cache, const YAML::Node& node);
} // namespace fggl::ecs
#endif //FGGL_ECS_COMPONENT_FWD_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef FGGL_ECS_ECS_HPP
#define FGGL_ECS_ECS_HPP
#include "utility.hpp"
#include "component.hpp"
#include <iostream>
#include <algorithm>
#include <cassert>
#include <string>
#include <vector>
#include <unordered_map>
namespace fggl::ecs {
using archToken_t = std::vector<component_type_t>;
struct Archetype {
constexpr static unsigned int default_cap = 0;
const archToken_t type;
std::vector<ComponentBase::data_t *> data;
std::vector<std::size_t> dataSizes;
std::vector<entity_t> entities;
Archetype(const archToken_t &type_a);
inline archToken_t create(const component_type_t cid) const {
assert(!contains(cid));
// create the new type
auto newType = type;
newType.push_back(cid);
std::sort(newType.begin(), newType.end());
return newType;
}
inline bool contains(const component_type_t cid) const {
return (std::find(type.begin(), type.end(), cid) != type.end());
}
};
class ECS {
struct Record {
Archetype *archetype;
std::size_t index;
};
using componentmap_t = std::unordered_map<component_type_t, ComponentBase *>;
using entitymap_t = std::unordered_map<entity_t, Record>;
using archetype_t = std::vector<Archetype *>;
public:
ECS();
~ECS();
entity_t getNewID();
entity_t createEntity();
void removeEntity(const entity_t eid);
template<class C>
void registerComponent() {
component_type_t type = Component<C>::typeID();
if (m_componentMap.find(type) != m_componentMap.end())
return;
m_componentMap.emplace(type, new Component<C>);
}
template<class C>
bool isComponentRegistered() {
component_type_t type = Component<C>::typeID();
return (m_componentMap.find(type) != m_componentMap.end());
}
template<class C, typename... Args>
C *addComponent(const entity_t &id, Args &&... args) {
component_type_t type = Component<C>::typeID();
assert(isComponentRegistered<C>());
Record &record = m_entityArchtypes[id];
Archetype *oldArch = record.archetype;
C *newComp = nullptr;
Archetype *newArch = nullptr;
if (!oldArch) {
archToken_t newID(1, type);
const ComponentBase *const newCompType = m_componentMap[type];
// fetch type
newArch = getArchetype(newID);
assert(newArch->type.size() == 1);
// calculate if we have enouph space to allocate
std::size_t emplacementPos = ensureCapacity(newArch, 0, newCompType);
newComp = new(&newArch->data[0][emplacementPos])C(std::forward<Args>(args)...);
} else {
// check if the arch contains the component
if (oldArch->contains(type)) {
return nullptr;
}
// create a new archetype with the component
auto newID = oldArch->create(type);
newArch = getArchetype(newID);
// relocate the old data to the new archetype
for (std::size_t j = 0; j < newID.size(); ++j) {
const component_type_t compType = newID[j];
const ComponentBase *const comp = m_componentMap.at(compType);
// TODO this seems a little suspect - surely we could allocate all blocks at once?
// if per component the arrays could become out of sync...
int newOffset = ensureCapacity(newArch, j, comp);
int oldIdx = getComponentIdx(oldArch, compType);
if (oldIdx != -1) {
assert(oldArch->contains(compType));
const std::size_t compSize = comp->size();
const std::size_t oldOffset = record.index * compSize;
comp->move(&oldArch->data[oldIdx][oldOffset],
&newArch->data[j][newOffset]);
comp->destroy(&oldArch->data[oldIdx][oldOffset]);
} else {
assert(!oldArch->contains(compType));
newComp = new(&newArch->data[j][newOffset])
C(std::forward<Args>(args)...);
}
}
// ensure the old archetype is still contigious
const int lastEnt = oldArch->entities.size() - 1;
if (lastEnt != record.index) {
for (std::size_t i = 0; i < oldArch->type.size(); ++i) {
const component_type_t typeID = oldArch->type[i];
const ComponentBase *const comp = m_componentMap[typeID];
const std::size_t &compSize = comp->size();
// shift the empty record to the end of the list
std::size_t slotOffset = record.index * compSize;
std::size_t lastOffset = lastEnt * compSize;
// if we're not the last entity, swap
if (slotOffset != lastOffset) {
comp->move(&oldArch->data[i][lastOffset],
&oldArch->data[i][slotOffset]);
comp->destroy(&oldArch->data[i][lastOffset]);
}
}
// fix the position
oldArch->entities[record.index] = oldArch->entities[lastEnt];
}
oldArch->entities.pop_back();
}
// register the new data with the new archetype
newArch->entities.push_back(id);
record.index = newArch->entities.size() - 1;
record.archetype = newArch;
return newComp;
}
template<class C>
void removeComponent(const entity_t &entityId);
template<class C>
bool hasComponent(const entity_t &entityId) const {
const component_type_t componentID = Component<C>::typeID();
const auto *arch = m_entityArchtypes.at(entityId).archetype;
if (arch == nullptr) {
return false;
}
return (std::find(arch->type.begin(), arch->type.end(), componentID) !=
arch->type.end());
}
template<class C>
C *getComponent(const entity_t &entityId) {
assert(hasComponent<C>(entityId));
const auto type = Component<C>::typeID();
const ComponentBase *const newComp = m_componentMap[type];
const auto record = m_entityArchtypes.at(entityId);
const auto *arch = record.archetype;
// JWR: linear search... seems a little suspect, they're ordered after all
for (std::size_t i = 0; i < arch->type.size(); ++i) {
if (arch->type[i] == type) {
return reinterpret_cast<C *>(&(arch->data[i][record.index * newComp->size()]));
}
}
return nullptr;
}
template<class C>
const C *getComponent(const entity_t &entityId) const {
assert(hasComponent<C>(entityId));
const auto type = Component<C>::typeID();
const ComponentBase *const newComp = m_componentMap.at(type);
const auto record = m_entityArchtypes.at(entityId);
const auto *arch = record.archetype;
// JWR: linear search... seems a little suspect, they're ordered after all
for (std::size_t i = 0; i < arch->type.size(); ++i) {
if (arch->type[i] == type) {
return reinterpret_cast<C *>(&(arch->data[i][record.index * newComp->size()]));
}
}
return nullptr;
}
template<class... Cs>
std::vector<entity_t> getEntityWith() const {
// construct the key
archToken_t key;
(key.push_back(Component<Cs>::typeID()), ...);
// entities
std::vector<entity_t> entities;
for (Archetype *arch : m_archetypes) {
if (std::includes(arch->type.begin(), arch->type.end(), key.begin(), key.end())) {
if (!arch->entities.empty()) {
entities.insert(entities.begin(), arch->entities.begin(), arch->entities.end());
}
}
}
return entities;
}
private:
entitymap_t m_entityArchtypes;
archetype_t m_archetypes;
entity_t m_entityIDCounter;
componentmap_t m_componentMap;
Archetype *getArchetype(const archToken_t &id);
inline std::string arch2str(Archetype *arch) {
std::string str;
for (const auto &type : arch->type) {
str += std::to_string(type);
}
return str;
}
inline std::size_t ensureCapacity(Archetype *arch, const int idx, const ComponentBase *const comp) {
const std::size_t &compSize = comp->size();
std::size_t currSize = arch->entities.size() * compSize;
std::size_t newSize = currSize + compSize;
std::size_t cap = arch->dataSizes[idx];
if (newSize > arch->dataSizes[idx]) {
arch->dataSizes[idx] *= 2;
arch->dataSizes[idx] += compSize;
// copy data over
unsigned char *newData = new unsigned char[arch->dataSizes[idx]];
for (std::size_t e = 0; e < arch->entities.size(); ++e) {
const int offset = e * compSize;
comp->move(&arch->data[idx][offset], &newData[offset]);
comp->destroy(&arch->data[idx][offset]);
}
// free the old data and swap the pointers
delete[] arch->data[idx];
arch->data[idx] = newData;
}
return currSize;
}
inline int getComponentIdx(const Archetype *arch, const component_type_t goal) {
// JWR could do binary search for speedup
for (std::size_t i = 0; i < arch->type.size(); ++i) {
if (arch->type[i] == goal)
return i;
}
return -1;
}
};
}
#endif
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 23/10/2021.
//
#ifndef FGGL_ECS3_FAST_CONTAINER_HPP
#define FGGL_ECS3_FAST_CONTAINER_HPP
#include <cstdint>
#include <cstdarg>
#include <cassert>
#include <fggl/ecs3/types.hpp>
namespace fggl::ecs3 {
class Container {
public:
const RecordIdentifier m_identifier;
Container(const TypeRegistry &reg, RecordIdentifier id) :
m_identifier(id),
m_types(reg),
backingStore(nullptr),
m_size(0),
m_capacity(0) {};
~Container() {
delete[] backingStore;
}
std::size_t create();
void remove(std::size_t pos);
std::size_t expand(Container &other, std::size_t otherPos, component_type_t newComp);
void contract(Container &other, std::size_t otherPos);
void ensure(std::size_t size);
inline unsigned char *data_raw(component_type_t type) {
auto seek_id = m_identifier.idx(type);
// you asked for something I don't contain...
if (seek_id == m_identifier.count) {
std::cerr << "asked for " << type << " from " << m_identifier << std::endl;
assert(seek_id != m_identifier.count);
return nullptr;
}
// figure out the offset
return backingStore + offsets[seek_id];
}
template<typename T>
inline T *data() {
auto comp_id = Component<T>::typeID();
return (T *) data_raw(comp_id);
}
template<typename T>
T *set(std::size_t entity, T *compData) {
auto *comps = data<T>();
auto entityPos = idx(entity);
auto compMeta = m_types.template meta<T>();
unsigned char *usrPtr = (unsigned char *) &comps[entityPos];
compMeta->destroy(usrPtr);
compMeta->move((unsigned char *) compData, usrPtr);
return &comps[entityPos];
}
[[nodiscard]]
inline std::size_t size() const {
return m_size;
}
inline std::size_t idx(entity_t entity) {
auto *entityData = data<EntityMeta>();
for (std::size_t i = 0; i < m_size; i++) {
if (entityData[i].id == entity) {
return i;
}
}
return m_size;
}
private:
const TypeRegistry &m_types;
unsigned char *backingStore;
std::size_t offsets[RecordIdentifier::MAX_COMPS]{};
std::size_t m_size;
std::size_t m_capacity;
void move(std::size_t newPos, Container &oldContainer, std::size_t oldPos);
};
}
#endif //FGGL_ECS3_FAST_CONTAINER_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef FGGL_ECS3_FAST_ECS_HPP
#define FGGL_ECS3_FAST_ECS_HPP
#include <cstddef>
#include <cstdarg>
#include <cassert>
#include <cstring>
#include <map>
#include <algorithm>
#include <iostream>
#include <type_traits>
#include <fggl/ecs3/utils.hpp>
#include <fggl/ecs3/types.hpp>
#include <fggl/ecs3/fast/Container.hpp>
namespace fggl::ecs3::fast {
using entity_t = unsigned int;
constexpr entity_t NULL_ENTITY = 0;
class World {
public:
explicit World(TypeRegistry &reg) : m_registry(reg), m_last(NULL_ENTITY) {}
entity_t create() {
auto next = m_last++;
auto arch = make_id(1, Component<EntityMeta>::typeID());
auto &container = getContainer(arch);
m_entities[next] = container.m_identifier;
auto pos = container.create();
auto *entityMeta = container.data<EntityMeta>();
entityMeta[pos].id = next;
return next;
}
void remove(entity_t entity) {
auto arch = m_entities.at(entity);
auto container = m_records.at(arch);
auto entPos = container.idx(entity);
container.remove(entPos);
}
inline Container &getContainer(RecordIdentifier &arch) {
try {
return m_records.at(arch);
} catch (std::out_of_range &e) {
auto v = m_records.emplace(std::pair<RecordIdentifier, Container>(arch, {m_registry, arch}));
return v.first->second;
}
}
template<typename T>
T *add(const entity_t entity) {
auto currArch = m_entities.at(entity);
auto newArch = currArch.with<T>();
m_entities[entity] = newArch;
auto &oldContainer = m_records.at(currArch);
auto &newContainer = getContainer(newArch);
auto oldPos = oldContainer.idx(entity);
auto newPos = newContainer.expand(oldContainer, oldPos, Component<T>::typeID());
auto *data = newContainer.template data<T>();
return &data[newPos];
}
template<typename T>
T *set(const entity_t entity, T *record) {
auto currArch = m_entities.at(entity);
// check we already have that component type...
if (currArch.idx(Component<T>::typeID()) == currArch.count) {
add<T>(entity);
currArch = m_entities.at(entity);
}
auto &container = m_records.at(currArch);
auto pos = container.idx(entity);
return container.set<T>(pos, record);
}
template<typename T>
T *get(entity_t entity) {
auto currArch = m_entities.at(entity);
auto pos = currArch.idx(entity);
auto &container = m_records.at(currArch);
auto *data = container.template data<T>();
return &data[pos];
}
template<typename T>
const T *get(entity_t entity) const {
auto currArch = m_entities.at(entity);
auto pos = currArch.idx(entity);
auto &container = m_records.at(currArch);
auto *data = container.template data<T>();
return &data[pos];
}
template<typename T>
void remove(entity_t entity) {
auto currArch = m_entities.at(entity);
auto newArch = currArch.without<T>();
auto &oldContainer = m_records[currArch];
auto &newContainer = m_records[newArch];
auto oldPos = oldContainer.idx(entity);
auto newPos = newContainer.create();
m_records[newArch].contract(newPos, oldContainer, oldPos);
}
template<typename... T>
std::vector<entity_t> findMatching() const {
return {};
}
private:
TypeRegistry &m_registry;
std::map<RecordIdentifier, Container> m_records;
std::map<entity_t, RecordIdentifier> m_entities;
entity_t m_last{};
};
}
#endif
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 23/10/2021.
//
#ifndef FGGL_ECS3_PROTOTYPE_WORLD_HPP
#define FGGL_ECS3_PROTOTYPE_WORLD_HPP
#include <map>
#include <functional>
#include <unordered_set>
#include "fggl/ecs3/types.hpp"
#include "fggl/debug/logging.hpp"
#include <yaml-cpp/yaml.h>
/**
* A component based implementation of a game world.
*
* This is not a true ECS but exposes a similar API to it for testing (with a lot less headaches).
*/
namespace fggl::ecs3::prototype {
using EntityCallback = std::function<void(const entity_t)>;
class Entity {
public:
bool m_abstract;
explicit Entity(entity_t id) : m_abstract(false), m_id(id) {};
Entity(const Entity &entity) : m_id(entity.m_id), m_components(entity.m_components) {
//spdlog::info("entity created fro copy: {}", m_id);
}
~Entity() = default;
template<typename C>
C *add() {
C *ptr = new C();
m_components[Component<C>::typeID()] = ptr;
return ptr;
}
void *add(std::shared_ptr<ComponentBase> t) {
void *ptr = t->construct();
m_components[t->id()] = ptr;
return ptr;
}
void* add(const std::shared_ptr<ComponentBase>& compMeta, const YAML::Node& config) {
void* ptr = compMeta->restore(config);
m_components[ compMeta->id() ] = ptr;
return ptr;
}
template<typename C>
C *set(const C *ptr) {
C *newPtr = new C(*ptr);
m_components[Component<C>::typeID()] = newPtr;
return newPtr;
}
void *set(const std::shared_ptr<ComponentBase> &t, const void *ptr) {
void *newPtr = t->copyConstruct(ptr);
m_components[t->id()] = newPtr;
return newPtr;
}
template<typename C>
C *get() const {
void *ptr = m_components.at(Component<C>::typeID());
return (C *) ptr;
}
template<typename C>
void remove(){
m_components.erase(Component<C>::typeID());
}
inline void *get(component_type_t t) {
return m_components.at(t);
}
std::vector<component_type_t> getComponentIDs() {
std::vector<component_type_t> comps{};
for (auto &[k, _] : m_components) {
comps.push_back(k);
}
return comps;
}
bool hasComponents(std::vector<component_type_t> &Cs) const {
for (auto c : Cs) {
if (m_components.find(c) == m_components.end()) {
return false;
}
}
return true;
}
private:
entity_t m_id;
std::map<component_type_t, void *> m_components;
};
class World {
public:
explicit World(TypeRegistry &reg) : m_types(reg), m_next(1), m_entities() {};
~World() = default;
entity_t create(bool abstract) {
auto nextID = m_next++;
m_entities.emplace(nextID, nextID);
auto &entity = m_entities.at(nextID);
entity.m_abstract = abstract;
// meta data
auto *meta = entity.add<ecs3::EntityMeta>();
meta->id = nextID;
meta->abstract = abstract;
meta->typeName = "";
return nextID;
}
inline entity_t createFromPrototype(const std::string& name) {
auto prototype = findPrototype(name);
if ( prototype == NULL_ENTITY) {
debug::log(debug::Level::warning, "attempted to create from non-existant prototype: {}", name);
return NULL_ENTITY;
}
return copy( prototype );
}
entity_t copy(entity_t prototype) {
auto clone = create(false);
auto components = getComponents(prototype);
for (auto component : components) {
auto protoComp = get(prototype, component);
set(clone, component, protoComp);
}
return clone;
}
void addFromConfig(entity_t entity, component_type_t type, const YAML::Node& node) {
auto meta = m_types.meta(type);
auto& entityObj = m_entities.at(entity);
entityObj.add(meta, node);
m_types.fireAdd(this, entity, meta->id());
}
void createFromSpec(entity_t entity, const YAML::Node& compConfig) {
if ( compConfig ) {
for (const auto& itr : compConfig ) {
const auto name = itr.first.as<std::string>();
const auto& config = itr.second;
auto compType = m_types.find( name.c_str() );
addFromConfig(entity, compType, config);
}
}
}
inline auto alive(entity_t entity) const -> bool {
return entity != NULL_ENTITY
&& m_killList.find( entity ) == m_killList.end()
&& m_entities.find( entity ) != m_entities.end();
}
inline auto exists(entity_t entity) const -> bool {
return entity != NULL_ENTITY
&& m_entities.find( entity ) != m_entities.end();
}
void destroy(entity_t entity) {
assert( alive(entity) && "attempted to kill null entity" );
// TOOD resolve and clean components
//m_entities.erase(entity);
m_killList.insert(entity);
}
void reapEntities() {
for (const auto& entity : m_killList) {
//auto& entityObj = m_entities.at(entity);
//entityObj.clear();
for (auto& listener : m_deathListeners) {
listener( entity );
}
m_entities.erase(entity);
}
m_killList.clear();
}
inline TypeRegistry &types() {
return m_types;
}
std::vector<entity_t> all() {
std::vector<entity_t> entities{};
for (auto &[eid, entity] : m_entities) {
entities.push_back(eid);
}
return entities;
}
std::vector<component_type_t> getComponents(entity_t entityID) {
assert(alive(entityID) && "attempted to get components on dead entity");
std::vector<component_type_t> components{};
auto &entity = m_entities.at(entityID);
auto comps = entity.getComponentIDs();
for (auto id : comps) {
components.push_back(id);
}
return components;
}
template<typename... Cs>
std::vector<entity_t> findMatching() const {
// construct the key
std::vector<ecs::component_type_t> key;
(key.push_back(Component<Cs>::typeID()), ...);
// entities
std::vector<entity_t> entities{};
for (auto &[eid, entity] : m_entities) {
if (entity.hasComponents(key) && !entity.m_abstract) {
entities.push_back(eid);
}
}
return entities;
}
template<typename ...Cs>
bool has(entity_t entityIdx) const {
if ( !alive(entityIdx)) {
return false;
}
std::vector<ecs::component_type_t> key;
(key.push_back(Component<Cs>::typeID()), ...);
return m_entities.at(entityIdx).hasComponents(key);
}
template<typename C>
C *add(entity_t entity_id) {
assert( alive(entity_id) && "attempted to add component on null entity" );
//spdlog::info("component '{}' added to '{}'", C::name, entity_id);
auto &entity = m_entities.at(entity_id);
auto comp = entity.template add<C>();
m_types.fireAdd(this, entity_id, Component<C>::typeID());
return comp;
}
void *add(entity_t entity_id, component_type_t component_id) {
assert( alive(entity_id) && "attempted to add component on null entity" );
auto meta = m_types.meta(component_id);
auto &entity = m_entities.at(entity_id);
void *ptr = entity.add(meta);
m_types.fireAdd(this, entity_id, meta->id());
return ptr;
}
template<typename C>
C *set(entity_t entity_id, const C *ptr) {
assert( alive( entity_id ) && "attempted to set component on null entity" );
//spdlog::info("component '{}' set on '{}'", C::name, entity_id);
auto &entity = m_entities.at(entity_id);
auto comp = entity.set<C>(ptr);
m_types.fireAdd(this, entity_id, Component<C>::typeID());
return comp;
}
void *set(entity_t entity_id, component_type_t cid, const void *ptr) {
assert( alive( entity_id ) && "attempted to set component on null entity" );
auto &entity = m_entities.at(entity_id);
auto cMeta = m_types.meta(cid);
auto comp = entity.set(cMeta, ptr);
m_types.fireAdd(this, entity_id, cid);
return comp;
}
template<typename C>
C* tryGet(entity_t entity_id) const {
if ( entity_id == NULL_ENTITY) {
return nullptr;
}
try {
return get<C>(entity_id);
} catch ( std::out_of_range& e) {
fggl::debug::info("component {} on {} did not exist", C::name, entity_id);
return nullptr;
}
}
template<typename C>
C *get(entity_t entity_id) const {
assert( exists(entity_id) && "attempted to get component on null entity" );
const auto& entity = m_entities.at(entity_id);
return entity.get<C>();
}
template<typename C>
void remove(entity_t entity_id) {
assert( alive(entity_id) && "attempted to remove component on null entity" );
try {
auto &entity = m_entities.at(entity_id);
try {
return entity.remove<C>();
} catch ( std::out_of_range& e ) {
std::cerr << "entity " << entity_id << " does not have component "<< C::name << std::endl;
}
} catch ( std::out_of_range& e) {
std::cerr << "tried to delete component on entity that didn't exist, entity was: " << entity_id << std::endl;
}
}
void *get(entity_t entity_id, component_type_t componentType) {
assert( exists(entity_id) && "attempted to get component on null entity" );
auto &entity = m_entities.at(entity_id);
return entity.get(componentType);
}
void addDeathListener(const EntityCallback& callback) {
m_deathListeners.emplace_back(callback);
}
entity_t findPrototype(const std::string& name) const {
for ( const auto& [entity, obj] : m_entities ) {
if ( !obj.m_abstract ){
continue;
}
auto* metaData = obj.get<EntityMeta>();
if ( metaData->typeName == name){
return entity;
}
}
return NULL_ENTITY;
}
private:
std::vector<EntityCallback > m_deathListeners;
TypeRegistry &m_types;
entity_t m_next;
std::map<entity_t, Entity> m_entities;
std::unordered_set<entity_t> m_killList;
};
}
#endif //FGGL_ECS3_PROTOTYPE_WORLD_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef FGGL_ECS3_TYPES_HPP
#define FGGL_ECS3_TYPES_HPP
#include <cstdarg>
#include <utility>
#include <functional>
#include <fggl/ecs/component.hpp>
#include <fggl/ecs3/utils.hpp>
#include <iostream>
#include <memory>
#include <algorithm>
#include <map>
#include <unordered_map>
namespace fggl::ecs3 {
namespace {
using namespace fggl::ecs;
};
namespace prototype {
class World;
}
using fggl::ecs::component_type_t;
class ModuleManager;
using callback_t = std::function<void(prototype::World *, ecs3::entity_t)>;
struct TypeCallbacks {
std::vector<callback_t> add;
};
// core component types
struct EntityMeta {
constexpr static const char name[] = "meta";
entity_t id;
bool abstract;
std::string typeName;
};
struct RecordIdentifier {
constexpr static std::size_t MAX_COMPS = 32;
component_type_t types[MAX_COMPS];
std::size_t count;
[[nodiscard]]
inline std::size_t idx(component_type_t t) const {
return utils::search(types, count, t);
}
template<typename T>
[[nodiscard]]
RecordIdentifier with() const {
// check the caller wasn't a muppet
const auto typeID = ecs::Component<T>::typeID();
if (idx(typeID) != count) {
return *this;
}
RecordIdentifier re{};
re.count = count + 1;
re.types[count] = ecs::Component<T>::typeID();
// add old types
for (std::size_t i = 0; i < count; ++i) {
re.types[i] = types[i];
}
std::sort(re.types, re.types + re.count);
return re;
}
template<typename T>
[[nodiscard]]
RecordIdentifier without() const {
// check the caller wasn't a muppet
const auto typeID = ecs::Component<T>::typeID();
const auto typeIdx = idx(typeID);
if (typeIdx == count) {
return *this;
}
RecordIdentifier re{};
re.count = count - 1;
// add old types
for (std::size_t i = 0, j = 0; i < count; ++i) {
if (typeIdx != i) {
re.types[j] = types[i];
j++;
}
}
std::sort(re.types, re.types + re.count);
return re;
}
bool operator<(const RecordIdentifier &other) const {
if (count < other.count) {
return true;
} else if (count > other.count) {
return false;
} else {
for (std::size_t i = 0; i < count; i++) {
if (types[i] != other.types[i]) {
return types[i] < other.types[i];
}
}
return false;
}
}
bool operator==(const RecordIdentifier &arg) const {
if (arg.count != count) {
return false;
}
for (std::size_t i = 0; i < count; i++) {
if (types[i] != arg.types[i]) {
return false;
}
}
return true;
}
bool operator!=(const RecordIdentifier &arg) const {
return !(*this == arg);
}
};
std::ostream &operator<<(std::ostream &out, RecordIdentifier const &curr);
inline RecordIdentifier make_id(std::size_t count, ...) {
assert(count < RecordIdentifier::MAX_COMPS);
RecordIdentifier re{};
std::va_list args;
va_start(args, count);
for (std::size_t i = 0; i < count; ++i) {
re.types[i] = va_arg(args, component_type_t);
}
va_end(args);
re.count = count;
std::sort(re.types, re.types + count);
return re;
}
class TypeRegistry {
public:
TypeRegistry() : m_last_virtual(9000), m_callbacks() {
// core types always exist
make<EntityMeta>();
}
template<typename T>
void make() {
auto type_id = Component<T>::typeID();
if (m_types.find(type_id) != m_types.end())
return;
m_types[type_id] = std::make_shared<Component<T>>();
}
template<typename T>
bool exists() {
auto type_id = Component<T>::typeID();
return m_types.find(type_id) != m_types.end();
}
template<typename T>
std::shared_ptr<fggl::ecs::ComponentBase> meta() const {
auto type_id = Component<T>::typeID();
return m_types.at(type_id);
}
inline std::shared_ptr<fggl::ecs::ComponentBase> meta(component_type_t type_id) const {
try {
return m_types.at(type_id);
} catch (std::out_of_range& err) {
std::cerr << "asked for metadata on type " << type_id << " but no such type is in the type system" << std::endl;
return nullptr;
}
}
inline component_type_t find(const char *name) const {
for (const auto &[type, meta] : m_types) {
if (std::strcmp(name, meta->name()) == 0) {
return type;
}
}
debug::warning("asked for unknown/unregistered component type: {}", name);
assert(false && "unknown component type, are you sure it was registered?");
return 0;
}
inline void make_virtual(std::shared_ptr<ComponentBase> vtype) {
auto type_id = m_last_virtual++;
m_types[type_id] = std::move(vtype);
}
void callbackAdd(component_type_t component, const callback_t &callback) {
m_callbacks[component].add.push_back(callback);
}
void fireAdd(prototype::World *world, entity_t entity, component_type_t type) {
try {
auto &callbacks = m_callbacks.at(type).add;
for (auto &callback : callbacks) {
callback(world, entity);
}
} catch (std::out_of_range &e) {
//spdlog::debug("no callbacks for {}", m_types[type]->name());
}
}
private:
std::unordered_map<component_type_t, std::shared_ptr<fggl::ecs::ComponentBase>> m_types;
component_type_t m_last_virtual;
std::map<component_type_t, TypeCallbacks> m_callbacks;
};
};
#endif