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 4888 additions and 0 deletions
/*
* 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 19/11/22.
//
#ifndef FGGL_ASSETS_PACKED_PACKED_HPP
#define FGGL_ASSETS_PACKED_PACKED_HPP
#include <cstdint>
#include <cstdio>
#include <memory>
#include "fggl/assets/types.hpp"
#include "fggl/assets/packed/direct.hpp"
/**
* Packed file reader.
*
* Read assets stored as sequential [header,data] blocks. This reader does not care about dependencies, it assumes this
* was handled before storage (ie, asset dependencies are assumed to be stored before the asset that relies on them).
* The caller is also responsible for ensuring that assets in other archives are already loaded by the time the system
* assembles the composite assets into something usable.
*
* If either of these constraints are violated, the results are undefined.
*/
namespace fggl::assets {
struct Header {
uint64_t name;
uint64_t type;
std::size_t size;
};
bool read_header(std::FILE* stream, Header* header) {
constexpr auto headerSize = sizeof(Header);
auto readBytes = std::fread(header, headerSize, 1, stream);
return readBytes == headerSize;
}
bool read_data(std::FILE* stream, void* block, std::size_t size) {
auto readBytes = std::fread(block, size, 1, stream);
return readBytes == size;
}
void read_archive(RawCheckin* checkin, std::FILE* stream) {
while ( !std::feof(stream) ) {
Header header;
bool headRead = read_header(stream, &header);
if ( headRead && header.size != 0 ) {
// header has data
void* memBlock = std::malloc( header.size );
bool valid = read_data( stream, memBlock, header.size );
// read the data, check it in
if (valid) {
MemoryBlock block{
.data = (std::byte*)memBlock,
.size = header.size
};
checkin->check(header.name, header.type, block);
}
}
}
}
}
#endif //FGGL_ASSETS_PACKED_PACKED_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/>.
*/
//
// Created by webpigeon on 03/07/22.
//
#ifndef FGGL_ASSETS_TYPES_HPP
#define FGGL_ASSETS_TYPES_HPP
#include <filesystem>
#include <variant>
#include <functional>
#include "fggl/util/safety.hpp"
#include "fggl/util/guid.hpp"
namespace fggl::assets {
using AssetType = util::OpaqueName<std::string_view, struct AssetTag>;
using AssetGUID = std::string;
using AssetPath = std::filesystem::path;
using AssetID = util::OpaqueName<uint64_t, struct AssetIDTag>;
template<unsigned L1, unsigned L2>
constexpr AssetID make_asset_id(const char (&pack)[L1], const char (&path)[L2]) {
auto hash = util::hash_fnv1a_64( util::cat( pack, ":", path ).c );
return AssetID::make( hash );
}
template<unsigned L1, unsigned L2, unsigned L3>
constexpr AssetID make_asset_id(const char (&pack)[L1], const char (&path)[L2], const char (&view)[L3]) {
auto hash = util::hash_fnv1a_64( util::cat( pack, ":", path, "[", view, "]").c );
return AssetID::make( hash );
}
AssetID make_asset_id_rt(const std::string &pack, const std::string &path, const std::string &view = "");
AssetID asset_from_user(const std::string &input, const std::string &pack = "core");
using AssetTypeID = util::OpaqueName<uint64_t, struct AssetTypeTag>;
constexpr auto INVALID_ASSET_TYPE = AssetTypeID::make(0);
constexpr AssetTypeID make_asset_type(const char* type) {
return AssetTypeID::make( util::hash_fnv1a_64(type) );
}
struct MemoryBlock {
std::byte *data;
std::size_t size;
template<typename T>
T* viewAs(std::size_t offset = 0) {
static_assert( std::is_standard_layout<T>::value );
return (T*)( data[offset] );
}
};
using AssetRefRaw = std::shared_ptr<void>;
struct LoaderContext {
std::string pack;
std::filesystem::path packRoot;
std::filesystem::path assetPath;
inline std::filesystem::path relParent() const {
return std::filesystem::relative( assetPath, packRoot ).parent_path();
}
inline assets::AssetID makeRef(const char* name) const {
return assets::make_asset_id_rt(pack, relParent() / name );
}
};
class Loader;
using Checkin = std::function<AssetRefRaw(Loader* loader, const AssetID &, const LoaderContext &, void* userPtr)>;
}
// formatter
template<> struct fmt::formatter<fggl::assets::AssetID> {
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const fggl::assets::AssetID & guid, FormatContext& ctx) const -> decltype(ctx.out()) {
#ifndef NDEBUG
return fmt::format_to(ctx.out(), "ASSET[{}]", guid_to_string( fggl::util::GUID::make( guid.get() ) ));
#else
return fmt::format_to(ctx.out(), "ASSET[{}]", guid.get());
#endif
}
};
#endif //FGGL_ASSETS_TYPES_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_AUDIO_AUDIO_HPP
#define FGGL_AUDIO_AUDIO_HPP
#include <string>
#include "fggl/data/storage.hpp"
#include "fggl/modules/module.hpp"
#include "fggl/assets/module.hpp"
#include "fggl/assets/packed/module.hpp"
//! backend independent audio interface
namespace fggl::audio {
/**
* AudioClip is bit of audio loaded into memory.
*
* If the sampleCount is -1, the clip is invalid.
*/
template<typename T>
struct AudioClip {
int channels = 0;
int sampleRate = 0;
int sampleCount = -1;
T *data = nullptr;
AudioClip() = default;
AudioClip(const AudioClip&) = delete;
inline ~AudioClip() {
std::free(data);
data = nullptr;
}
[[nodiscard]]
inline int size() const {
return sampleCount * sizeof(T);
}
};
using AudioClipShort = AudioClip<short>;
using AudioClipByte = AudioClip<char>;
constexpr auto ASSET_CLIP_SHORT = assets::make_asset_type("Audio:Clip:Short");
constexpr auto ASSET_CLIP_BYTE = assets::make_asset_type("Audio:Clip:Byte");
constexpr auto SERVICE_AUDIO_PLAYBACK = modules::make_service("fggl::audio::AudioService");
/**
*
* \ingroup services
*/
class AudioService {
public:
constexpr static const modules::ServiceName service = SERVICE_AUDIO_PLAYBACK;
virtual void play(const assets::AssetGUID &asset, bool looping = false) = 0;
virtual void play(const AudioClipShort &clip, bool looping = false) = 0;
virtual ~AudioService() = default;
};
} // namespace fggl::audio
#endif //FGGL_AUDIO_AUDIO_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/>.
*/
//
// Created by webpigeon on 27/06/22.
//
#ifndef FGGL_AUDIO_NULL_AUDIO_HPP
#define FGGL_AUDIO_NULL_AUDIO_HPP
#include "fggl/audio/audio.hpp"
namespace fggl::audio {
class NullAudioService : public AudioService {
public:
NullAudioService() = default;
~NullAudioService() override = default;
NullAudioService(NullAudioService&) = delete;
NullAudioService(NullAudioService&&) = delete;
NullAudioService& operator=(const NullAudioService&) = delete;
NullAudioService& operator=(NullAudioService&&) = delete;
void play(const std::string & /*filename*/, bool /*looping = false*/) override;
void play(const AudioClipShort & /*clip*/, bool /*looping = false*/) override;
};
//! A dummy audio module that does nothing
struct NullAudio {
constexpr static const char *name = "fggl::audio::NULL";
constexpr static const std::array<modules::ServiceName, 1> provides = {
SERVICE_AUDIO_PLAYBACK
};
constexpr static const std::array<modules::ServiceName, 0> depends = {};
bool factory(modules::ServiceName, modules::Services&);
};
} // namespace fggl::audio
#endif //FGGL_AUDIO_NULL_AUDIO_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/>.
*/
//
// Created by webpigeon on 01/05/22.
//
#ifndef FGGL_AUDIO_OPENAL_AUDIO_HPP
#define FGGL_AUDIO_OPENAL_AUDIO_HPP
#include <AL/al.h>
#include <AL/alc.h>
#include "fggl/audio/audio.hpp"
#include "fggl/assets/manager.hpp"
#include "fggl/data/storage.hpp"
#include "fggl/math/types.hpp"
#include <string>
#include <iostream>
#include <memory>
//! Audio backed by openal-soft
namespace fggl::audio::openal {
constexpr uint32_t NULL_BUFFER_ID = 0;
assets::AssetRefRaw load_vorbis(assets::Loader* loader, const assets::AssetID &, const assets::LoaderContext &, void* userPtr);
bool load_vorbis_short(std::filesystem::path path, assets::MemoryBlock& block);
assets::AssetTypeID check_vorbis( const std::filesystem::path& path );
enum class AudioType {
MONO_8 = AL_FORMAT_MONO8,
MONO_16 = AL_FORMAT_MONO16,
STEREO_8 = AL_FORMAT_STEREO8,
STEREO_16 = AL_FORMAT_STEREO16
};
static void check_error(const std::string& context) {
auto code = alGetError();
if (code == AL_NO_ERROR) {
return;
}
// now we check the error message
std::string error = "unknown";
switch (code) {
case ALC_INVALID_DEVICE: error = "Invalid Device";
break;
case ALC_INVALID_CONTEXT: error = "Invalid Context";
break;
case ALC_INVALID_ENUM: error = "Invalid enum";
break;
case ALC_INVALID_VALUE: error = "Invalid value";
break;
case ALC_OUT_OF_MEMORY: error = "Out of memory";
break;
default: error = "unknown error";
}
debug::error("OpenAL error: context={}, error={}", context, error);
}
class AudioBuffer {
public:
AudioBuffer() : m_buffer(NULL_BUFFER_ID) {
alGenBuffers(1, &m_buffer);
}
~AudioBuffer() {
alDeleteBuffers(1, &m_buffer);
}
AudioBuffer(const AudioBuffer&) = delete;
AudioBuffer(const AudioBuffer&&) = delete;
AudioBuffer& operator=(const AudioBuffer&) = delete;
AudioBuffer& operator=(const AudioBuffer&&) = delete;
inline void setData(AudioType type, void *data, ALsizei size, ALsizei frequency) {
assert( m_buffer != 0 );
assert( data != nullptr );
alBufferData(m_buffer, (ALenum) type, data, size, frequency);
}
inline ALuint value() const {
return m_buffer;
}
private:
ALuint m_buffer = 0;
};
class AudioSource {
public:
AudioSource() : m_source(0), m_splat() {
alGenSources(1, &m_source);
}
~AudioSource() {
alDeleteSources(1, &m_source);
}
AudioSource(const AudioSource& source) = delete;
AudioSource(const AudioSource&& source) = delete;
AudioSource& operator=(const AudioSource&) = delete;
AudioSource& operator=(AudioSource&&) = delete;
inline void play() {
alSourcePlay(m_source);
}
inline void stop() {
alSourceStop(m_source);
}
inline void pause() {
alSourcePause(m_source);
}
inline void rewind() {
alSourceRewind(m_source);
}
inline void play(AudioBuffer &buffer, bool looping) {
alSourcei(m_source, AL_BUFFER, buffer.value());
alSourcei(m_source, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
alSourcePlay(m_source);
}
inline void play(const AudioClipShort &clip, bool looping = false);
inline void velocity(math::vec3 &value) {
alSource3f(m_source, AL_VELOCITY, value.x, value.y, value.z);
}
inline void position(math::vec3 &value) {
alSource3f(m_source, AL_POSITION, value.x, value.y, value.z);
}
void direction(math::vec3 &value) {
alSource3f(m_source, AL_DIRECTION, value.x, value.y, value.z);
}
private:
ALuint m_source;
AudioBuffer m_splat;
};
class AudioServiceOAL : public AudioService {
public:
explicit AudioServiceOAL(assets::AssetManager *assets) : m_device(alcOpenDevice(nullptr)), m_assets(assets) {
if (m_device != nullptr) {
m_context = alcCreateContext(m_device, nullptr);
alcMakeContextCurrent(m_context);
check_error("context setup");
m_defaultSource = std::make_unique<AudioSource>();
check_error("default source setup");
}
}
~AudioServiceOAL() override {
if (m_device != nullptr) {
release();
}
}
void play(const assets::AssetGUID &filename, bool looping = false) override;
void play(const AudioClipShort &clip, bool looping = false) override;
private:
ALCdevice *m_device;
ALCcontext *m_context{nullptr};
std::unique_ptr<AudioSource> m_defaultSource{nullptr};
assets::AssetManager* m_assets;
void release();
};
} // namespace fggl::audio::openal
namespace fggl::audio {
using openAL = openal::AudioServiceOAL;
} // namepace fggl::audio
#endif //FGGL_AUDIO_OPENAL_AUDIO_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/>.
*/
//
// Created by webpigeon on 27/06/22.
//
#ifndef FGGL_AUDIO_OPENAL_MODULE_HPP
#define FGGL_AUDIO_OPENAL_MODULE_HPP
#include <array>
#include <string>
#include "fggl/assets/module.hpp"
#include "fggl/assets/packed/module.hpp"
#include "fggl/audio/audio.hpp"
#include "fggl/audio/openal/audio.hpp"
namespace fggl::audio {
constexpr auto OGG_VORBIS = assets::AssetType::make("audio/vorbis");
constexpr auto RES_OGG_VORBIS = assets::from_mime("audio/vorbis");
//! an audio module which uses openal(-soft) as a backend.
struct OpenAL {
constexpr static const char *name = "fggl::audio::OpenAL";
constexpr static const std::array<modules::ServiceName, 1> provides = {
SERVICE_AUDIO_PLAYBACK
};
constexpr static const std::array<modules::ServiceName, 2> depends = {
assets::AssetManager::service,
assets::CheckinAdapted::service
};
static bool factory(modules::ServiceName name, modules::Services &serviceManager);
};
} // namespace fggl::audio
#endif //FGGL_AUDIO_OPENAL_MODULE_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/>.
*/
//
// Created by webpigeon on 18/10/22.
//
#ifndef FGGL_DATA_ASSIMP_MODULE_HPP
#define FGGL_DATA_ASSIMP_MODULE_HPP
#include "fggl/modules/module.hpp"
#include "fggl/assets/loader.hpp"
#include "fggl/assets/packed/module.hpp"
#include "fggl/data/texture.hpp"
namespace fggl::data::models {
constexpr auto MODEL_PROVIDER = modules::make_service("fggl::data::Model");
constexpr auto ASSIMP_MODEL = assets::AssetType::make("model::assimp");
constexpr auto MIME_JPG = assets::from_mime("image/jpeg");
constexpr auto MIME_PNG = assets::from_mime("image/png");
constexpr auto MIME_OBJ = assets::from_mime("model/obj");
constexpr auto MIME_FBX = assets::from_mime("model/fbx");
constexpr auto MODEL_MULTI3D = assets::make_asset_type("model/multi3D");
constexpr auto TEXTURE_RGBA = assets::make_asset_type("texture/rgba");
// old-style loaders
assets::AssetRefRaw load_assimp_model(assets::Loader* loader, const assets::AssetID& guid, const assets::LoaderContext& data, void* userPtr);
assets::AssetRefRaw load_assimp_texture(assets::Loader* loader, const assets::AssetID& guid, const assets::LoaderContext& data, void* userPtr);
// new style loaders (textures)
bool load_tex_stb(const std::filesystem::path& filePath, assets::MemoryBlock& block);
assets::AssetTypeID is_tex_stb(const std::filesystem::path& filePath);
// new style loaders (models)
assets::AssetTypeID is_model_assimp(const std::filesystem::path& filePath);
bool extract_requirements(const std::string& packName, const std::filesystem::path& packRoot, assets::ResourceRecord& rr);
struct AssimpModule {
constexpr static const char *name = "fggl::data::Assimp";
constexpr static const std::array<modules::ServiceName, 1> provides = {
MODEL_PROVIDER
};
constexpr static const std::array<modules::ServiceName, 2> depends = {
assets::Loader::service,
assets::CheckinAdapted::service
};
static bool factory(modules::ServiceName service, modules::Services &serviceManager);
};
}
namespace fggl::data {
using AssimpLoader = models::AssimpModule;
}
#endif //FGGL_DATA_ASSIMP_MODULE_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/>.
*/
//
// Created by webpigeon on 11/06/22.
//
#ifndef FGGL_DATA_GEOM_HPP
#define FGGL_DATA_GEOM_HPP
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <filesystem>
#include "storage.hpp"
#include "model.hpp"
namespace fggl::data {
static inline math::vec3 aiVec3ToFggl(aiVector3D vec) {
return {vec.x, vec.y, vec.z};
}
static inline math::vec2 aiVec2ToFggl(aiVector3D vec) {
return {vec.x, vec.y};
}
static void process_mesh(aiMesh *mesh, data::Mesh &meshRecord) {
// process vertices
std::vector<data::Mesh::IndexType> indexList;
for (auto idx = 0; idx < mesh->mNumVertices; ++idx) {
auto idxVal = meshRecord.pushVertex({
.posititon = aiVec3ToFggl(mesh->mVertices[idx]),
.normal = aiVec3ToFggl(mesh->mNormals[idx]),
.texPos = aiVec2ToFggl(mesh->mTextureCoords[0][idx])
});
indexList.push_back(idxVal);
}
if (mesh->HasFaces()) {
// if there is face data, that's our index list
for (auto face = 0; face < mesh->mNumFaces; ++face) {
auto &facePtr = mesh->mFaces[face];
meshRecord.pushIndex(indexList[facePtr.mIndices[0]]);
meshRecord.pushIndex(indexList[facePtr.mIndices[1]]);
meshRecord.pushIndex(indexList[facePtr.mIndices[2]]);
}
}
}
static void process_model(aiScene *scene, aiNode *node, data::Mesh &mesh) {
auto ptr = node->mMeshes;
for (auto j = 0; j < node->mNumMeshes; ++j) {
for (int meshCount = 0; node->mNumMeshes; ++meshCount) {
auto aiMeshIdx = node->mMeshes[meshCount];
data::Mesh mesh;
auto *aiMesh = scene->mMeshes[aiMeshIdx];
process_mesh(aiMesh, mesh);
}
}
// process child meshes
for (int i = 0; i < node->mNumChildren; ++i) {
auto *child = node->mChildren[i];
process_model(child);
}
}
template<>
bool fggl_deserialize(std::filesystem::path &data, StaticMesh *out) {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(data.c_str(), aiProcess_Triangulate | aiProcess_FlipUVs);
// check if the import worked
if (scene == nullptr || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
return false;
}
// figure out the meshes
process_model(scene->mRootNode);
//TODO implement the rest of this
return false;
}
} // namespace fggl::data
#endif //FGGL_DATA_GEOM_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/>.
*/
//
// Created by webpigeon on 20/11/2021.
//
#ifndef FGGL_DATA_HEIGHTMAP_HPP
#define FGGL_DATA_HEIGHTMAP_HPP
#include <cstdint>
#include <array>
#include "fggl/data/model.hpp"
namespace fggl::data {
constexpr std::size_t heightMaxX = 255;
constexpr std::size_t heightMaxZ = 255;
constexpr float heightSeaLevel = 0.0f;
struct HeightMap {
constexpr static const char name[] = "Heightmap";
std::array<float, heightMaxX * heightMaxZ> heightValues;
void clear() {
for (float &heightValue : heightValues) {
heightValue = heightSeaLevel;
}
}
[[nodiscard]]
inline float getValue(std::size_t x, std::size_t z) const {
return heightValues.at(x * heightMaxZ + z);
}
inline void setValue(std::size_t x, std::size_t z, float value) {
heightValues.at(x * heightMaxZ + z) = value;
}
};
inline int idx(int x, int z, int zMax) {
return x * zMax + z;
}
void generateHeightMesh(const data::HeightMap &heights, data::Mesh &mesh);
}
#endif //FGGL_DATA_HEIGHTMAP_HPP
#ifndef FGGL_DATA_MODEL_H
#define FGGL_DATA_MODEL_H
/*
* 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_DATA_MODEL_HPP
#define FGGL_DATA_MODEL_HPP
#include <tuple>
#include <vector>
#include <string>
#include <climits>
#include <fggl/math/types.hpp>
namespace fggl::data {
constexpr math::vec3 ILLEGAL_NORMAL{0.0F, 0.0F, 0.F};
constexpr math::vec3 DEFAULT_COLOUR{1.0F, 1.0F, 1.0F};
struct Vertex {
math::vec3 posititon;
math::vec3 normal;
math::vec3 colour;
math::vec3 normal = ILLEGAL_NORMAL;
math::vec3 colour = DEFAULT_COLOUR;
math::vec2 texPos;
inline static Vertex from_pos(math::vec3 pos) {
return {
pos,
ILLEGAL_NORMAL,
DEFAULT_COLOUR,
{0.0F, 0.0F}
};
}
};
// comparison operators
inline bool operator<(const Vertex& lhs, const Vertex& rhs) {
return std::tie( lhs.posititon, lhs.normal, lhs.colour )
< std::tie( rhs.posititon, rhs.normal, rhs.colour );
inline bool operator<(const Vertex &lhs, const Vertex &rhs) {
return std::tie(lhs.posititon, lhs.normal, lhs.colour)
< std::tie(rhs.posititon, rhs.normal, rhs.colour);
}
inline bool operator==(const Vertex& lhs, const Vertex& rhs) {
inline bool operator==(const Vertex &lhs, const Vertex &rhs) {
return lhs.posititon == rhs.posititon
&& lhs.colour == rhs.colour
&& lhs.normal == rhs.normal;
}
inline bool operator!=(const Vertex& lhs, const Vertex& rhs) {
return !(lhs==rhs);
inline bool operator!=(const Vertex &lhs, const Vertex &rhs) {
return !(lhs == rhs);
}
struct Vertex2D {
fggl::math::vec2 position;
fggl::math::vec3 colour;
fggl::math::vec2 texPos;
};
struct Mesh2D {
std::vector<Vertex2D> vertexList;
std::vector<uint32_t> indexList;
inline std::size_t add_vertex(const Vertex2D &vertex) {
vertexList.push_back(vertex);
return vertexList.size() - 1;
}
inline void add_index(uint32_t idx) {
indexList.push_back(idx);
}
};
class Mesh {
public:
using IndexType = unsigned int;
constexpr static const IndexType INVALID_IDX = IndexType(UINT_MAX);
Mesh();
~Mesh() = default;
......@@ -43,10 +97,12 @@ namespace fggl::data {
*/
inline void push(Vertex vert) {
auto idx = indexOf(vert);
if ( idx == -1 )
pushVertex(vert);
else
if (idx == INVALID_IDX) {
idx = pushVertex(vert);
pushIndex(idx);
} else {
pushIndex(idx);
}
}
/**
......@@ -54,14 +110,14 @@ namespace fggl::data {
*
* Don't push non-existant indexes, it'll make the rendering backend sad.
*/
void pushIndex(unsigned int idx);
void pushIndex(IndexType idx);
/**
* Force push a vertex duplicate.
*
* If the vetex already exists, it will be duplicated.
*/
unsigned int pushVertex(Vertex vert);
IndexType pushVertex(Vertex vert);
/**
* remove Duplicate verticies
......@@ -76,44 +132,44 @@ namespace fggl::data {
*
* Returns the index of a vert, else returns -1.
*/
int indexOf(Vertex vert);
IndexType indexOf(Vertex vert);
inline std::vector<Vertex>& vertexList() {
inline const std::vector<Vertex> &vertexList() const {
return m_verts;
}
inline std::size_t vertexCount() {
inline std::size_t vertexCount() const {
return m_verts.size();
}
inline std::vector<uint32_t>& indexList() {
inline const std::vector<IndexType> &indexList() const {
return m_index;
}
inline std::size_t indexCount() {
inline std::size_t indexCount() const {
return m_index.size();
}
inline Vertex& vertex(int idx) {
inline Vertex &vertex(IndexType idx) {
return m_verts[idx];
}
IndexType restartVertex;
private:
std::vector<Vertex> m_verts;
std::vector<unsigned int> m_index;
std::vector<IndexType> m_index;
};
class Model {
public:
Model();
~Model();
struct StaticMesh {
constexpr static const char name[] = "StaticMesh";
constexpr static const util::GUID guid = util::make_guid("StaticMesh");
inline void append(const Mesh& mesh) {
m_meshes.push_back( mesh );
}
data::Mesh mesh;
std::string pipeline;
private:
std::vector<Mesh> m_meshes;
inline StaticMesh() = default;
inline StaticMesh(const data::Mesh &aMesh, std::string aPipeline) :
mesh(aMesh), pipeline(std::move(aPipeline)) {}
};
}
......
/*
* 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 27/06/22.
//
#ifndef FGGL_DATA_MODULE_HPP
#define FGGL_DATA_MODULE_HPP
#include "fggl/modules/module.hpp"
#include "fggl/data/storage.hpp"
#include "fggl/platform/paths.hpp"
namespace fggl::data {
struct LocalStorage {
constexpr static const char *name = "fggl::data::Storage";
constexpr static const std::array<modules::ServiceName, 1> provides = {
SERVICE_STORAGE
};
constexpr static const std::array<modules::ServiceName, 0> depends = {};
static bool factory(modules::ServiceName service, modules::Services &serviceManager);
};
} // namespace fggl::data
#endif //FGGL_DATA_MODULE_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_DATA_PROCEDURAL_HPP
#define FGGL_DATA_PROCEDURAL_HPP
#include "model.hpp"
#include "fggl/mesh/mesh.hpp"
namespace fggl::data {
constexpr math::mat4 OFFSET_NONE(1.0f);
// basic shapes 'building blocks'
Mesh make_triangle();
Mesh make_quad_xy();
Mesh make_quad_xz();
// platonic solids
void make_tetrahedron(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE);
mesh::Mesh3D make_cube(mesh::Mesh3D &mesh, const math::mat4 &offset = OFFSET_NONE);
void make_octahedron(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE);
void make_icosahedron(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE);
void make_dodecahedron(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE);
// useful shapes
void make_sphere_uv(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE);
void make_sphere_iso(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE);
// level block-out shapes
mesh::Mesh3D make_slope(mesh::Mesh3D &mesh, const math::mat4 &offset = OFFSET_NONE);
mesh::Mesh3D make_ditch(mesh::Mesh3D &mesh, const math::mat4 &offset = OFFSET_NONE);
mesh::Mesh3D make_point(mesh::Mesh3D &mesh, const math::mat4 &offset = OFFSET_NONE);
// other useful types people expect
void make_capsule(Mesh &mesh);
void make_sphere(mesh::Mesh3D &mesh, const math::mat4 &offset = OFFSET_NONE, uint32_t stacks = 16U, uint32_t slices = 16U);
}
#endif
\ No newline at end of file
/*
* 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_DATA_STORAGE_HPP
#define FGGL_DATA_STORAGE_HPP
#include <iostream>
#include <string>
#include <filesystem>
#include <utility>
#include <vector>
#include "fggl/debug/logging.hpp"
#include "fggl/modules/module.hpp"
#include "fggl/platform/paths.hpp"
namespace fggl::data {
template<typename T>
bool fggl_serialize(std::filesystem::path &data, const T *out);
template<typename T>
bool fggl_deserialize(std::filesystem::path &data, T *out);
enum StorageType { Data, Config, Cache };
constexpr const auto SERVICE_STORAGE = modules::make_service("fggl::data::Storage");
class Storage {
public:
constexpr static auto service = SERVICE_STORAGE;
Storage(fggl::platform::EnginePaths paths) : m_paths(std::move(paths)) {}
template<typename T>
bool load(StorageType pool, const std::string &name, T *out) {
auto path = resolvePath(pool, name);
if (!std::filesystem::exists(path)) {
debug::log(debug::Level::warning, "Attempted to load '{}', but it did not exist", path.string());
return false;
}
return fggl_deserialize<T>(path, out);
}
std::vector<std::filesystem::path> findResources(std::filesystem::path root, const std::string ext) {
if (!std::filesystem::exists(root)) {
return {};
}
std::vector<std::filesystem::path> paths;
for (const auto &entry : std::filesystem::recursive_directory_iterator(root)) {
if (entry.is_regular_file() && entry.path().extension() == ext) {
paths.push_back(entry);
}
}
return paths;
}
template<typename T>
void save(StorageType pool, const std::string &name, const T *out) {
auto path = resolvePath(pool, name, true);
fggl_serialize<T>(path, out);
}
inline std::filesystem::path resolvePath(StorageType pool,
const std::string &name,
bool createParents = false) {
std::filesystem::path path;
switch (pool) {
case Data: path = fggl::platform::locate_data(m_paths, name);
break;
case Config: path = fggl::platform::locate_config(m_paths, name);
break;
case Cache: path = fggl::platform::locate_cache(m_paths, name);
break;
}
if (createParents) {
if (!std::filesystem::exists(path.parent_path())) {
std::filesystem::create_directories(path.parent_path());
}
}
return path;
}
private:
fggl::platform::EnginePaths m_paths;
};
}
#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/10/22.
//
#ifndef FGGL_DATA_TEXTURE_HPP
#define FGGL_DATA_TEXTURE_HPP
#include "fggl/assets/module.hpp"
#include "fggl/math/types.hpp"
namespace fggl::data {
constexpr auto DATA_TEXTURE2D = assets::AssetType::make("gfx::texture");
struct Texture2D {
math::vec2i size;
int channels;
unsigned char* data;
Texture2D() = default;
Texture2D(const Texture2D& other) = delete;
inline ~Texture2D() {
delete data;
}
};
}
#endif //FGGL_DATA_TEXTURE_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_DEBUG_DEBUG_H
#define FGGL_DEBUG_DEBUG_H
#include <string>
#include <memory>
#include <utility>
#include <functional>
#include <unordered_map>
#include "fggl/display/glfw/window.hpp"
namespace fggl::debug {
using DebugUIDraw = std::function<void(bool *)>;
struct DebugWindow {
bool m_visible;
DebugUIDraw m_callback;
};
class DebugUI {
public:
explicit DebugUI(std::shared_ptr<display::glfw::Window> &window);
~DebugUI();
void frameStart();
void draw();
inline void addWindow(const std::string &name, DebugUIDraw window) {
m_windows[name] = DebugWindow{true, std::move(window)};
}
inline void visible(bool state) {
m_visible = state;
}
inline bool visible() const {
return m_visible;
}
private:
bool m_visible;
std::unordered_map<std::string, DebugWindow> m_windows;
};
}
#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/>.
*/
// ================================================================================================
// -*- C++ -*-
// File: debug_draw.hpp
// Author: Guilherme R. Lampert
// Brief: Debug Draw - an immediate-mode, renderer agnostic, lightweight debug drawing API.
// ================================================================================================
#ifndef DEBUG_DRAW_HPP
#define DEBUG_DRAW_HPP
// ========================================================
// Library Overview:
// ========================================================
//
// ---------
// LICENSE
// ---------
// This software is in the public domain. Where that dedication is not recognized,
// you are granted a perpetual, irrevocable license to copy, distribute, and modify
// this file as you see fit.
//
// The source code is provided "as is", without warranty of any kind, express or implied.
// No attribution is required, but a mention about the author(s) is appreciated.
//
// -------------
// QUICK SETUP
// -------------
// In *one* C++ source file, *before* including this file, do this:
//
// #define DEBUG_DRAW_IMPLEMENTATION
//
// To enable the implementation. Further includes of this
// file *should not* redefine DEBUG_DRAW_IMPLEMENTATION.
// Example:
//
// In my_program.cpp:
//
// #define DEBUG_DRAW_IMPLEMENTATION
// #include "debug_draw.hpp"
//
// In my_program.hpp:
//
// #include "debug_draw.hpp"
//
// ----------------------
// COMPILATION SWITCHES
// ----------------------
//
// DEBUG_DRAW_CXX11_SUPPORTED
// Enables the use of some C++11 features. If your compiler supports C++11
// or better, you should define this switch globally or before every inclusion
// of this file. If it is not defined, we try to guess it from the value of the
// '__cplusplus' built-in macro constant.
//
// DEBUG_DRAW_MAX_*
// Sizes of internal intermediate buffers, which are allocated on initialization
// by the implementation. If you need to draw more primitives than the sizes of
// these buffers, you need to redefine the macros and recompile.
//
// DEBUG_DRAW_VERTEX_BUFFER_SIZE
// Size in dd::DrawVertex elements of the intermediate vertex buffer used
// to batch primitives before sending them to dd::RenderInterface. A bigger
// buffer will reduce the number of calls to dd::RenderInterface when drawing
// large sets of debug primitives.
//
// DEBUG_DRAW_OVERFLOWED(message)
// An error handler called if any of the DEBUG_DRAW_MAX_* sizes overflow.
// By default it just prints a message to stderr.
//
// DEBUG_DRAW_USE_STD_MATH
// If defined to nonzero, uses cmath/math.h. If you redefine it to zero before
// the library implementation, it will force the use of local replacements
// for the Standard Library. This might be useful if you want to avoid the
// dependency. It is defined to zero by default (i.e. we use cmath by default).
//
// DEBUG_DRAW_*_TYPE_DEFINED
// The compound types used by Debug Draw can also be customized.
// By default, ddVec3 and ddMat4x4 are plain C-arrays, but you can
// redefine them to use your own classes or structures (see below).
// ddStr is by default a std::string, but you can redefine it to
// a custom string type if necessary. The only requirements are that
// it provides a 'c_str()' method returning a null terminated
// const char* string and an assignment operator (=).
//
// DEBUG_DRAW_STR_DEALLOC_FUNC(str)
// If you define a custom string type for ddStr and it requires some
// extra cleanup besides the class destructor, you might define this
// function macro to perform said cleanup. It is called by dd::clear()
// and dd::shutdown() on every instance of the internal DebugString buffer.
//
// DEBUG_DRAW_NO_DEFAULT_COLORS
// If defined, doesn't add the set of predefined color constants inside
// dd::colors:: namespace. Each color is a ddVec3, so you can define this
// to prevent adding more global data to the binary if you don't need them.
//
// DEBUG_DRAW_PER_THREAD_CONTEXT
// If defined, a per-thread global context will be created for Debug Draw.
// This allows having an instance of the library for each thread in
// your application. You must then call initialize/shutdown/flush/etc
// for each thread that wishes to use the library. If this is not
// defined it defaults to a single threaded global context.
//
// DEBUG_DRAW_EXPLICIT_CONTEXT
// If defined, each Debug Draw function will expect and additional argument
// (the first one) which is the library context instance. This is an alternative
// to DEBUG_DRAW_PER_THREAD_CONTEXT to allow having multiple instances of the
// library in the same application. This flag is mutually exclusive with
// DEBUG_DRAW_PER_THREAD_CONTEXT.
//
// -------------------
// MEMORY ALLOCATION
// -------------------
// Debug Draw will only perform a couple of memory allocations during startup to decompress
// the built-in glyph bitmap used for debug text rendering and to allocate the vertex buffers
// and intermediate draw/batch buffers and context data used internally.
//
// Memory allocation and deallocation for Debug Draw will be done via:
//
// DD_MALLOC(size)
// DD_MFREE(ptr)
//
// These two macros can be redefined if you'd like to supply you own memory allocator.
// By default, they are defined to use std::malloc and std::free, respectively.
// Note: If you redefine one, you must also provide the other.
//
// --------------------------------
// INTERFACING WITH YOUR RENDERER
// --------------------------------
// Debug Draw doesn't touch on any renderer-specific aspects or APIs, instead you provide
// the library with all of it's rendering needs via the dd::RenderInterface abstract class.
//
// See the declaration of dd::RenderInterface for details. Not all methods are
// required. In fact, you could also implement a full no-op RenderInterface that
// disables debug drawing by simply inheriting from dd::RenderInterface and not overriding
// any of the methods (or even easier, call dd::initialize(nullptr) to make everything a no-op).
//
// For examples on how to implement your own dd::RenderInterface, see the accompanying samples.
// You can also find them in the source code repository for this project:
// https://github.com/glampert/debug-draw
//
// ------------------
// CONVENTIONS USED
// ------------------
// Points and lines are always specified in world-space positions. This also
// applies to shapes drawn from lines, like boxes, spheres, cones, etc.
//
// 2D screen-text is in screen-space pixels (from 0,0 in the upper-left
// corner of the screen to screen_width-1 and screen_height-1).
// RenderInterface::drawGlyphList() also receives vertexes in screen-space.
//
// We make some usage of matrices for things like the projected text labels.
// Matrix layout used is column-major and vectors multiply as columns.
// This is the convention normally used by standard OpenGL.
//
// C++ Exceptions are not used. Little error checking is provided or
// done inside the library. We favor simpler, faster and easier to maintain
// code over more sophisticated error handling. The rationale is that a
// debug drawing API doesn't have to be very robust, since it won't make
// into the final release executable in most cases.
//
// ========================================================
// Configurable compilation switches:
// ========================================================
//
// If the user didn't specify if C++11 or above are supported, try to guess
// from the value of '__cplusplus'. It should be 199711L for pre-C++11 compilers
// and 201103L in those supporting C++11, but this is not a guarantee that all
// C++11 features will be available and stable, so again, we are making a guess.
// It is recommended to instead supply the DEBUG_DRAW_CXX11_SUPPORTED switch
// yourself before including this file.
//
#ifndef DEBUG_DRAW_CXX11_SUPPORTED
#if (__cplusplus > 199711L)
#define DEBUG_DRAW_CXX11_SUPPORTED 1
#endif // __cplusplus
#endif // DEBUG_DRAW_CXX11_SUPPORTED
//
// Max elements of each type at any given time.
// We supply these reasonable defaults, but you can provide your
// own tunned values to save memory or fit all of your debug data.
// These are hard constraints. If not enough, change and recompile.
//
#ifndef DEBUG_DRAW_MAX_STRINGS
#define DEBUG_DRAW_MAX_STRINGS 512
#endif // DEBUG_DRAW_MAX_STRINGS
#ifndef DEBUG_DRAW_MAX_POINTS
#define DEBUG_DRAW_MAX_POINTS 8192
#endif // DEBUG_DRAW_MAX_POINTS
#ifndef DEBUG_DRAW_MAX_LINES
#define DEBUG_DRAW_MAX_LINES 32768
#endif // DEBUG_DRAW_MAX_LINES
//
// Size in vertexes of a local buffer we use to sort elements
// drawn with and without depth testing before submitting them to
// the dd::RenderInterface. A larger buffer will require less flushes
// (e.g. dd::RenderInterface calls) when drawing large amounts of
// primitives. Less will obviously save more memory. Each DrawVertex
// is about 32 bytes in size, we keep a context-specific array
// with this many entries.
//
#ifndef DEBUG_DRAW_VERTEX_BUFFER_SIZE
#define DEBUG_DRAW_VERTEX_BUFFER_SIZE 4096
#endif // DEBUG_DRAW_VERTEX_BUFFER_SIZE
//
// This macro is called with an error message if any of the above
// sizes is overflowed during runtime. In a debug build, you might
// keep this enabled to be able to log and find out if more space
// is needed for the debug data arrays. Default output is stderr.
//
#ifndef DEBUG_DRAW_OVERFLOWED
#include <cstdio>
#define DEBUG_DRAW_OVERFLOWED(message) std::fprintf(stderr, "%s\n", message)
#endif // DEBUG_DRAW_OVERFLOWED
//
// Use <math.h> and <float.h> for trigonometry functions by default.
// If you wish to avoid those dependencies, DD provides local approximations
// of the required functions as a portable replacement. Just define
// DEBUG_DRAW_USE_STD_MATH to zero before including this file.
//
#ifndef DEBUG_DRAW_USE_STD_MATH
#define DEBUG_DRAW_USE_STD_MATH 1
#endif // DEBUG_DRAW_USE_STD_MATH
// ========================================================
// Overridable Debug Draw types:
// ========================================================
#include <cstddef>
#include <cstdint>
//
// Following typedefs are not members of the dd:: namespace to allow easy redefinition by the user.
// If you provide a custom implementation for them before including this file, be sure to
// also define the proper DEBUG_DRAW_*_TYPE_DEFINED switch to disable the default typedefs.
//
// The only requirement placed on the vector/matrix types is that they provide
// an array subscript operator [] and have the expected number of elements. Apart
// from that, they could be structs, classes, what-have-you. POD types are recommended
// but not mandatory.
//
#ifndef DEBUG_DRAW_VEC3_TYPE_DEFINED
// ddVec3:
// A small array of floats with at least three elements, but
// it could have more for alignment purposes, extra slots are ignored.
// A custom ddVec3 type must provide the array subscript operator.
typedef float ddVec3[3];
// ddVec3_In/ddVec3_Out:
// Since our default ddVec3 is a plain C-array, it decays to a pointer
// when passed as an input parameter to a function, so we can use it directly.
// If you change it to some structured type, it might be more efficient
// 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;
#define DEBUG_DRAW_VEC3_TYPE_DEFINED 1
#endif // DEBUG_DRAW_VEC3_TYPE_DEFINED
#ifndef DEBUG_DRAW_MAT4X4_TYPE_DEFINED
// ddMat4x4:
// Homogeneous matrix of 16 floats, representing rotations as well as
// translation/scaling and projections. The internal matrix layout used by this
// library is COLUMN-MAJOR, vectors multiplying as columns (usual OpenGL convention).
// Column-major matrix layout:
// c.0 c.1 c.2 c.3
// r.0 | 0.x 4.x 8.x 12.x |
// r.1 | 1.y 5.y 9.y 13.y |
// r.2 | 2.z 6.z 10.z 14.z |
// r.3 | 3.w 7.w 11.w 15.w |
// If your custom matrix type uses row-major format internally, you'll
// have to transpose them before passing your matrices to the DD functions.
// We use the array subscript operator internally, so it must also be provided.
typedef float ddMat4x4[4 * 4];
// ddMat4x4_In/ddMat4x4_Out:
// Since our default ddMat4x4 is a plain C-array, it decays to a pointer
// when passed as an input parameter to a function, so we can use it directly.
// 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;
#define DEBUG_DRAW_MAT4X4_TYPE_DEFINED 1
#endif // DEBUG_DRAW_MAT4X4_TYPE_DEFINED
#ifndef DEBUG_DRAW_STRING_TYPE_DEFINED
// ddStr:
// String type used internally to store the debug text strings.
// A custom string type must provide at least an assignment
// operator (=) and a 'c_str()' method that returns a
// 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;
#define DEBUG_DRAW_STRING_TYPE_DEFINED 1
#endif // DEBUG_DRAW_STRING_TYPE_DEFINED
namespace dd {
// ========================================================
// Optional built-in colors in RGB float format:
// ========================================================
#ifndef DEBUG_DRAW_NO_DEFAULT_COLORS
namespace colors {
extern const ddVec3 AliceBlue;
extern const ddVec3 AntiqueWhite;
extern const ddVec3 Aquamarine;
extern const ddVec3 Azure;
extern const ddVec3 Beige;
extern const ddVec3 Bisque;
extern const ddVec3 Black;
extern const ddVec3 BlanchedAlmond;
extern const ddVec3 Blue;
extern const ddVec3 BlueViolet;
extern const ddVec3 Brown;
extern const ddVec3 BurlyWood;
extern const ddVec3 CadetBlue;
extern const ddVec3 Chartreuse;
extern const ddVec3 Chocolate;
extern const ddVec3 Coral;
extern const ddVec3 CornflowerBlue;
extern const ddVec3 Cornsilk;
extern const ddVec3 Crimson;
extern const ddVec3 Cyan;
extern const ddVec3 DarkBlue;
extern const ddVec3 DarkCyan;
extern const ddVec3 DarkGoldenRod;
extern const ddVec3 DarkGray;
extern const ddVec3 DarkGreen;
extern const ddVec3 DarkKhaki;
extern const ddVec3 DarkMagenta;
extern const ddVec3 DarkOliveGreen;
extern const ddVec3 DarkOrange;
extern const ddVec3 DarkOrchid;
extern const ddVec3 DarkRed;
extern const ddVec3 DarkSalmon;
extern const ddVec3 DarkSeaGreen;
extern const ddVec3 DarkSlateBlue;
extern const ddVec3 DarkSlateGray;
extern const ddVec3 DarkTurquoise;
extern const ddVec3 DarkViolet;
extern const ddVec3 DeepPink;
extern const ddVec3 DeepSkyBlue;
extern const ddVec3 DimGray;
extern const ddVec3 DodgerBlue;
extern const ddVec3 FireBrick;
extern const ddVec3 FloralWhite;
extern const ddVec3 ForestGreen;
extern const ddVec3 Gainsboro;
extern const ddVec3 GhostWhite;
extern const ddVec3 Gold;
extern const ddVec3 GoldenRod;
extern const ddVec3 Gray;
extern const ddVec3 Green;
extern const ddVec3 GreenYellow;
extern const ddVec3 HoneyDew;
extern const ddVec3 HotPink;
extern const ddVec3 IndianRed;
extern const ddVec3 Indigo;
extern const ddVec3 Ivory;
extern const ddVec3 Khaki;
extern const ddVec3 Lavender;
extern const ddVec3 LavenderBlush;
extern const ddVec3 LawnGreen;
extern const ddVec3 LemonChiffon;
extern const ddVec3 LightBlue;
extern const ddVec3 LightCoral;
extern const ddVec3 LightCyan;
extern const ddVec3 LightGoldenYellow;
extern const ddVec3 LightGray;
extern const ddVec3 LightGreen;
extern const ddVec3 LightPink;
extern const ddVec3 LightSalmon;
extern const ddVec3 LightSeaGreen;
extern const ddVec3 LightSkyBlue;
extern const ddVec3 LightSlateGray;
extern const ddVec3 LightSteelBlue;
extern const ddVec3 LightYellow;
extern const ddVec3 Lime;
extern const ddVec3 LimeGreen;
extern const ddVec3 Linen;
extern const ddVec3 Magenta;
extern const ddVec3 Maroon;
extern const ddVec3 MediumAquaMarine;
extern const ddVec3 MediumBlue;
extern const ddVec3 MediumOrchid;
extern const ddVec3 MediumPurple;
extern const ddVec3 MediumSeaGreen;
extern const ddVec3 MediumSlateBlue;
extern const ddVec3 MediumSpringGreen;
extern const ddVec3 MediumTurquoise;
extern const ddVec3 MediumVioletRed;
extern const ddVec3 MidnightBlue;
extern const ddVec3 MintCream;
extern const ddVec3 MistyRose;
extern const ddVec3 Moccasin;
extern const ddVec3 NavajoWhite;
extern const ddVec3 Navy;
extern const ddVec3 OldLace;
extern const ddVec3 Olive;
extern const ddVec3 OliveDrab;
extern const ddVec3 Orange;
extern const ddVec3 OrangeRed;
extern const ddVec3 Orchid;
extern const ddVec3 PaleGoldenRod;
extern const ddVec3 PaleGreen;
extern const ddVec3 PaleTurquoise;
extern const ddVec3 PaleVioletRed;
extern const ddVec3 PapayaWhip;
extern const ddVec3 PeachPuff;
extern const ddVec3 Peru;
extern const ddVec3 Pink;
extern const ddVec3 Plum;
extern const ddVec3 PowderBlue;
extern const ddVec3 Purple;
extern const ddVec3 RebeccaPurple;
extern const ddVec3 Red;
extern const ddVec3 RosyBrown;
extern const ddVec3 RoyalBlue;
extern const ddVec3 SaddleBrown;
extern const ddVec3 Salmon;
extern const ddVec3 SandyBrown;
extern const ddVec3 SeaGreen;
extern const ddVec3 SeaShell;
extern const ddVec3 Sienna;
extern const ddVec3 Silver;
extern const ddVec3 SkyBlue;
extern const ddVec3 SlateBlue;
extern const ddVec3 SlateGray;
extern const ddVec3 Snow;
extern const ddVec3 SpringGreen;
extern const ddVec3 SteelBlue;
extern const ddVec3 Tan;
extern const ddVec3 Teal;
extern const ddVec3 Thistle;
extern const ddVec3 Tomato;
extern const ddVec3 Turquoise;
extern const ddVec3 Violet;
extern const ddVec3 Wheat;
extern const ddVec3 White;
extern const ddVec3 WhiteSmoke;
extern const ddVec3 Yellow;
extern const ddVec3 YellowGreen;
} // namespace colors
#endif // DEBUG_DRAW_NO_DEFAULT_COLORS
// ========================================================
// Optional explicit context mode:
// ========================================================
#ifdef DEBUG_DRAW_EXPLICIT_CONTEXT
struct OpaqueContextType { };
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
// ========================================================
// Debug Draw functions:
// - Durations are always in milliseconds.
// - Colors are RGB floats in the [0,1] range.
// - Positions are in world-space, unless stated otherwise.
// ========================================================
// Add a point in 3D space to the debug draw queue.
// Point is expressed in world-space coordinates.
// Note that not all renderer support configurable point
// size, so take the 'size' parameter as a hint only
void point(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddVec3_In pos,
ddVec3_In color,
float size = 1.0f,
int durationMillis = 0,
bool depthEnabled = true);
// Add a 3D line to the debug draw queue. Note that
// lines are expressed in world coordinates, and so are
// all wireframe primitives which are built from lines.
void line(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddVec3_In from,
ddVec3_In to,
ddVec3_In color,
int durationMillis = 0,
bool depthEnabled = true);
// Add a 2D text string as an overlay to the current view, using a built-in font.
// Position is in screen-space pixels, origin at the top-left corner of the screen.
// 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,
ddVec3_In pos,
ddVec3_In color,
float scaling = 1.0f,
int durationMillis = 0);
// Add a 3D text label centered at the given world position that
// gets projected to screen-space. The label always faces the viewer.
// 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,
ddVec3_In pos,
ddVec3_In color,
ddMat4x4_In vpMatrix,
int sx, int sy,
int sw, int sh,
float scaling = 1.0f,
int durationMillis = 0);
// Add a set of three coordinate axis depicting the position and orientation of the given transform.
// 'size' defines the size of the arrow heads. 'length' defines the length of the arrow's base line.
void axisTriad(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddMat4x4_In transform,
float size,
float length,
int durationMillis = 0,
bool depthEnabled = true);
// Add a 3D line with an arrow-like end to the debug draw queue.
// 'size' defines the arrow head size. 'from' and 'to' the length of the arrow's base line.
void arrow(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddVec3_In from,
ddVec3_In to,
ddVec3_In color,
float size,
int durationMillis = 0,
bool depthEnabled = true);
// Add an axis-aligned cross (3 lines converging at a point) to the debug draw queue.
// 'length' defines the length of the crossing lines.
// 'center' is the world-space point where the lines meet.
void cross(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddVec3_In center,
float length,
int durationMillis = 0,
bool depthEnabled = true);
// Add a wireframe circle to the debug draw queue.
void circle(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddVec3_In center,
ddVec3_In planeNormal,
ddVec3_In color,
float radius,
float numSteps,
int durationMillis = 0,
bool depthEnabled = true);
// Add a wireframe plane in 3D space to the debug draw queue.
// If 'normalVecScale' is not zero, a line depicting the plane normal is also draw.
void plane(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddVec3_In center,
ddVec3_In planeNormal,
ddVec3_In planeColor,
ddVec3_In normalVecColor,
float planeScale,
float normalVecScale,
int durationMillis = 0,
bool depthEnabled = true);
// Add a wireframe sphere to the debug draw queue.
void sphere(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddVec3_In center,
ddVec3_In color,
float radius,
int durationMillis = 0,
bool depthEnabled = true);
// Add a wireframe cone to the debug draw queue.
// The cone 'apex' is the point where all lines meet.
// The length of the 'dir' vector determines the thickness.
// 'baseRadius' & 'apexRadius' are in degrees.
void cone(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddVec3_In apex,
ddVec3_In dir,
ddVec3_In color,
float baseRadius,
float apexRadius,
int durationMillis = 0,
bool depthEnabled = true);
// Wireframe box from the eight points that define it.
void box(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
const ddVec3 points[8],
ddVec3_In color,
int durationMillis = 0,
bool depthEnabled = true);
// Add a wireframe box to the debug draw queue.
void box(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddVec3_In center,
ddVec3_In color,
float width,
float height,
float depth,
int durationMillis = 0,
bool depthEnabled = true);
// Add a wireframe Axis Aligned Bounding Box (AABB) to the debug draw queue.
void aabb(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddVec3_In mins,
ddVec3_In maxs,
ddVec3_In color,
int durationMillis = 0,
bool depthEnabled = true);
// Add a wireframe frustum pyramid to the debug draw queue.
// 'invClipMatrix' is the inverse of the matrix defining the frustum
// (AKA clip) volume, which normally consists of the projection * view matrix.
// E.g.: inverse(projMatrix * viewMatrix)
void frustum(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddMat4x4_In invClipMatrix,
ddVec3_In color,
int durationMillis = 0,
bool depthEnabled = true);
// Add a vertex normal for debug visualization.
// The normal vector 'normal' is assumed to be already normalized.
void vertexNormal(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddVec3_In origin,
ddVec3_In normal,
float length,
int durationMillis = 0,
bool depthEnabled = true);
// Add a "tangent basis" at a given point in world space.
// Color scheme used is: normal=WHITE, tangent=YELLOW, bi-tangent=MAGENTA.
// The normal vector, tangent and bi-tangent vectors are assumed to be already normalized.
void tangentBasis(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
ddVec3_In origin,
ddVec3_In normal,
ddVec3_In tangent,
ddVec3_In bitangent,
float lengths,
int durationMillis = 0,
bool depthEnabled = true);
// Makes a 3D square grid of lines along the X and Z planes.
// 'y' defines the height in the Y axis where the grid is placed.
// The grid will go from 'mins' to 'maxs' units in both the X and Z.
// 'step' defines the gap between each line of the grid.
void xzSquareGrid(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
float mins,
float maxs,
float y,
float step,
ddVec3_In color,
int durationMillis = 0,
bool depthEnabled = true);
// ========================================================
// Debug Draw vertex type:
// The only drawing type the user has to interface with.
// ========================================================
union DrawVertex {
struct {
float x, y, z;
float r, g, b;
float size;
} point;
struct {
float x, y, z;
float r, g, b;
} line;
struct {
float x, y;
float u, v;
float r, g, b;
} glyph;
};
//
// Opaque handle to a texture object.
// Used by the debug text drawing functions.
//
struct OpaqueTextureType {};
typedef OpaqueTextureType *GlyphTextureHandle;
// ========================================================
// Debug Draw rendering callbacks:
// Implementation is provided by the user so we don't
// tie this code directly to a specific rendering API.
// ========================================================
class RenderInterface {
public:
//
// These are called by dd::flush() before any drawing and after drawing is finished.
// User can override these to perform any common setup for subsequent draws and to
// cleanup afterwards. By default, no-ops stubs are provided.
//
virtual void beginDraw();
virtual void endDraw();
//
// Create/free the glyph bitmap texture used by the debug text drawing functions.
// The debug renderer currently only creates one of those on startup.
//
// You're not required to implement these two if you don't care about debug text drawing.
// Default no-op stubs are provided by default, which disable debug text rendering.
//
// Texture dimensions are in pixels, data format is always 8-bits per pixel (Grayscale/GL_RED).
// 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 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);
// User defined cleanup. Nothing by default.
virtual ~RenderInterface() = 0;
};
// ========================================================
// Housekeeping functions:
// ========================================================
// Flags for dd::flush()
enum FlushFlags {
FlushPoints = 1 << 1,
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);
// 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.
void shutdown(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx));
// Test if the Debug Draw library is currently initialized and has a render interface.
bool isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx));
// Test if there's data in the debug draw queue and dd::flush() should be called.
bool hasPendingDraws(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx));
// Manually removes all queued debug render data without drawing.
// This is not normally called. To draw stuff, call dd::flush() instead.
void clear(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx));
// Actually calls the dd::RenderInterface to consume the debug draw queues.
// Objects that have expired their lifetimes get removed. Pass the current
// application time in milliseconds to remove timed objects that have expired.
// Passing zero removes all objects after they get drawn, regardless of lifetime.
void flush(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,)
std::int64_t currTimeMillis = 0,
std::uint32_t flags = FlushAll);
} // namespace dd
// ================== End of header file ==================
#endif // DEBUG_DRAW_HPP
// ================== End of header file ==================
// ================================================================================================
//
// Debug Draw Implementation
//
// ================================================================================================
#ifdef DEBUG_DRAW_IMPLEMENTATION
#ifndef DD_MALLOC
#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>
#endif // DEBUG_DRAW_USE_STD_MATH
namespace dd
{
#if defined(FLT_EPSILON) && DEBUG_DRAW_USE_STD_MATH
static const float FloatEpsilon = FLT_EPSILON;
#else // !FLT_EPSILON || !DEBUG_DRAW_USE_STD_MATH
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);
#else // !M_PI || !DEBUG_DRAW_USE_STD_MATH
static const float PI = 3.1415926535897931f;
#endif // M_PI && DEBUG_DRAW_USE_STD_MATH
static const float HalfPI = PI * 0.5f;
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);
}
template<typename T, int Size>
static inline int arrayLength(const T (&)[Size])
{
return Size;
}
// ========================================================
// Built-in color constants:
// ========================================================
#ifndef DEBUG_DRAW_NO_DEFAULT_COLORS
namespace colors
{
const ddVec3 AliceBlue = {0.941176f, 0.972549f, 1.000000f};
const ddVec3 AntiqueWhite = {0.980392f, 0.921569f, 0.843137f};
const ddVec3 Aquamarine = {0.498039f, 1.000000f, 0.831373f};
const ddVec3 Azure = {0.941176f, 1.000000f, 1.000000f};
const ddVec3 Beige = {0.960784f, 0.960784f, 0.862745f};
const ddVec3 Bisque = {1.000000f, 0.894118f, 0.768627f};
const ddVec3 Black = {0.000000f, 0.000000f, 0.000000f};
const ddVec3 BlanchedAlmond = {1.000000f, 0.921569f, 0.803922f};
const ddVec3 Blue = {0.000000f, 0.000000f, 1.000000f};
const ddVec3 BlueViolet = {0.541176f, 0.168627f, 0.886275f};
const ddVec3 Brown = {0.647059f, 0.164706f, 0.164706f};
const ddVec3 BurlyWood = {0.870588f, 0.721569f, 0.529412f};
const ddVec3 CadetBlue = {0.372549f, 0.619608f, 0.627451f};
const ddVec3 Chartreuse = {0.498039f, 1.000000f, 0.000000f};
const ddVec3 Chocolate = {0.823529f, 0.411765f, 0.117647f};
const ddVec3 Coral = {1.000000f, 0.498039f, 0.313726f};
const ddVec3 CornflowerBlue = {0.392157f, 0.584314f, 0.929412f};
const ddVec3 Cornsilk = {1.000000f, 0.972549f, 0.862745f};
const ddVec3 Crimson = {0.862745f, 0.078431f, 0.235294f};
const ddVec3 Cyan = {0.000000f, 1.000000f, 1.000000f};
const ddVec3 DarkBlue = {0.000000f, 0.000000f, 0.545098f};
const ddVec3 DarkCyan = {0.000000f, 0.545098f, 0.545098f};
const ddVec3 DarkGoldenRod = {0.721569f, 0.525490f, 0.043137f};
const ddVec3 DarkGray = {0.662745f, 0.662745f, 0.662745f};
const ddVec3 DarkGreen = {0.000000f, 0.392157f, 0.000000f};
const ddVec3 DarkKhaki = {0.741176f, 0.717647f, 0.419608f};
const ddVec3 DarkMagenta = {0.545098f, 0.000000f, 0.545098f};
const ddVec3 DarkOliveGreen = {0.333333f, 0.419608f, 0.184314f};
const ddVec3 DarkOrange = {1.000000f, 0.549020f, 0.000000f};
const ddVec3 DarkOrchid = {0.600000f, 0.196078f, 0.800000f};
const ddVec3 DarkRed = {0.545098f, 0.000000f, 0.000000f};
const ddVec3 DarkSalmon = {0.913725f, 0.588235f, 0.478431f};
const ddVec3 DarkSeaGreen = {0.560784f, 0.737255f, 0.560784f};
const ddVec3 DarkSlateBlue = {0.282353f, 0.239216f, 0.545098f};
const ddVec3 DarkSlateGray = {0.184314f, 0.309804f, 0.309804f};
const ddVec3 DarkTurquoise = {0.000000f, 0.807843f, 0.819608f};
const ddVec3 DarkViolet = {0.580392f, 0.000000f, 0.827451f};
const ddVec3 DeepPink = {1.000000f, 0.078431f, 0.576471f};
const ddVec3 DeepSkyBlue = {0.000000f, 0.749020f, 1.000000f};
const ddVec3 DimGray = {0.411765f, 0.411765f, 0.411765f};
const ddVec3 DodgerBlue = {0.117647f, 0.564706f, 1.000000f};
const ddVec3 FireBrick = {0.698039f, 0.133333f, 0.133333f};
const ddVec3 FloralWhite = {1.000000f, 0.980392f, 0.941176f};
const ddVec3 ForestGreen = {0.133333f, 0.545098f, 0.133333f};
const ddVec3 Gainsboro = {0.862745f, 0.862745f, 0.862745f};
const ddVec3 GhostWhite = {0.972549f, 0.972549f, 1.000000f};
const ddVec3 Gold = {1.000000f, 0.843137f, 0.000000f};
const ddVec3 GoldenRod = {0.854902f, 0.647059f, 0.125490f};
const ddVec3 Gray = {0.501961f, 0.501961f, 0.501961f};
const ddVec3 Green = {0.000000f, 0.501961f, 0.000000f};
const ddVec3 GreenYellow = {0.678431f, 1.000000f, 0.184314f};
const ddVec3 HoneyDew = {0.941176f, 1.000000f, 0.941176f};
const ddVec3 HotPink = {1.000000f, 0.411765f, 0.705882f};
const ddVec3 IndianRed = {0.803922f, 0.360784f, 0.360784f};
const ddVec3 Indigo = {0.294118f, 0.000000f, 0.509804f};
const ddVec3 Ivory = {1.000000f, 1.000000f, 0.941176f};
const ddVec3 Khaki = {0.941176f, 0.901961f, 0.549020f};
const ddVec3 Lavender = {0.901961f, 0.901961f, 0.980392f};
const ddVec3 LavenderBlush = {1.000000f, 0.941176f, 0.960784f};
const ddVec3 LawnGreen = {0.486275f, 0.988235f, 0.000000f};
const ddVec3 LemonChiffon = {1.000000f, 0.980392f, 0.803922f};
const ddVec3 LightBlue = {0.678431f, 0.847059f, 0.901961f};
const ddVec3 LightCoral = {0.941176f, 0.501961f, 0.501961f};
const ddVec3 LightCyan = {0.878431f, 1.000000f, 1.000000f};
const ddVec3 LightGoldenYellow = {0.980392f, 0.980392f, 0.823529f};
const ddVec3 LightGray = {0.827451f, 0.827451f, 0.827451f};
const ddVec3 LightGreen = {0.564706f, 0.933333f, 0.564706f};
const ddVec3 LightPink = {1.000000f, 0.713726f, 0.756863f};
const ddVec3 LightSalmon = {1.000000f, 0.627451f, 0.478431f};
const ddVec3 LightSeaGreen = {0.125490f, 0.698039f, 0.666667f};
const ddVec3 LightSkyBlue = {0.529412f, 0.807843f, 0.980392f};
const ddVec3 LightSlateGray = {0.466667f, 0.533333f, 0.600000f};
const ddVec3 LightSteelBlue = {0.690196f, 0.768627f, 0.870588f};
const ddVec3 LightYellow = {1.000000f, 1.000000f, 0.878431f};
const ddVec3 Lime = {0.000000f, 1.000000f, 0.000000f};
const ddVec3 LimeGreen = {0.196078f, 0.803922f, 0.196078f};
const ddVec3 Linen = {0.980392f, 0.941176f, 0.901961f};
const ddVec3 Magenta = {1.000000f, 0.000000f, 1.000000f};
const ddVec3 Maroon = {0.501961f, 0.000000f, 0.000000f};
const ddVec3 MediumAquaMarine = {0.400000f, 0.803922f, 0.666667f};
const ddVec3 MediumBlue = {0.000000f, 0.000000f, 0.803922f};
const ddVec3 MediumOrchid = {0.729412f, 0.333333f, 0.827451f};
const ddVec3 MediumPurple = {0.576471f, 0.439216f, 0.858824f};
const ddVec3 MediumSeaGreen = {0.235294f, 0.701961f, 0.443137f};
const ddVec3 MediumSlateBlue = {0.482353f, 0.407843f, 0.933333f};
const ddVec3 MediumSpringGreen = {0.000000f, 0.980392f, 0.603922f};
const ddVec3 MediumTurquoise = {0.282353f, 0.819608f, 0.800000f};
const ddVec3 MediumVioletRed = {0.780392f, 0.082353f, 0.521569f};
const ddVec3 MidnightBlue = {0.098039f, 0.098039f, 0.439216f};
const ddVec3 MintCream = {0.960784f, 1.000000f, 0.980392f};
const ddVec3 MistyRose = {1.000000f, 0.894118f, 0.882353f};
const ddVec3 Moccasin = {1.000000f, 0.894118f, 0.709804f};
const ddVec3 NavajoWhite = {1.000000f, 0.870588f, 0.678431f};
const ddVec3 Navy = {0.000000f, 0.000000f, 0.501961f};
const ddVec3 OldLace = {0.992157f, 0.960784f, 0.901961f};
const ddVec3 Olive = {0.501961f, 0.501961f, 0.000000f};
const ddVec3 OliveDrab = {0.419608f, 0.556863f, 0.137255f};
const ddVec3 Orange = {1.000000f, 0.647059f, 0.000000f};
const ddVec3 OrangeRed = {1.000000f, 0.270588f, 0.000000f};
const ddVec3 Orchid = {0.854902f, 0.439216f, 0.839216f};
const ddVec3 PaleGoldenRod = {0.933333f, 0.909804f, 0.666667f};
const ddVec3 PaleGreen = {0.596078f, 0.984314f, 0.596078f};
const ddVec3 PaleTurquoise = {0.686275f, 0.933333f, 0.933333f};
const ddVec3 PaleVioletRed = {0.858824f, 0.439216f, 0.576471f};
const ddVec3 PapayaWhip = {1.000000f, 0.937255f, 0.835294f};
const ddVec3 PeachPuff = {1.000000f, 0.854902f, 0.725490f};
const ddVec3 Peru = {0.803922f, 0.521569f, 0.247059f};
const ddVec3 Pink = {1.000000f, 0.752941f, 0.796078f};
const ddVec3 Plum = {0.866667f, 0.627451f, 0.866667f};
const ddVec3 PowderBlue = {0.690196f, 0.878431f, 0.901961f};
const ddVec3 Purple = {0.501961f, 0.000000f, 0.501961f};
const ddVec3 RebeccaPurple = {0.400000f, 0.200000f, 0.600000f};
const ddVec3 Red = {1.000000f, 0.000000f, 0.000000f};
const ddVec3 RosyBrown = {0.737255f, 0.560784f, 0.560784f};
const ddVec3 RoyalBlue = {0.254902f, 0.411765f, 0.882353f};
const ddVec3 SaddleBrown = {0.545098f, 0.270588f, 0.074510f};
const ddVec3 Salmon = {0.980392f, 0.501961f, 0.447059f};
const ddVec3 SandyBrown = {0.956863f, 0.643137f, 0.376471f};
const ddVec3 SeaGreen = {0.180392f, 0.545098f, 0.341176f};
const ddVec3 SeaShell = {1.000000f, 0.960784f, 0.933333f};
const ddVec3 Sienna = {0.627451f, 0.321569f, 0.176471f};
const ddVec3 Silver = {0.752941f, 0.752941f, 0.752941f};
const ddVec3 SkyBlue = {0.529412f, 0.807843f, 0.921569f};
const ddVec3 SlateBlue = {0.415686f, 0.352941f, 0.803922f};
const ddVec3 SlateGray = {0.439216f, 0.501961f, 0.564706f};
const ddVec3 Snow = {1.000000f, 0.980392f, 0.980392f};
const ddVec3 SpringGreen = {0.000000f, 1.000000f, 0.498039f};
const ddVec3 SteelBlue = {0.274510f, 0.509804f, 0.705882f};
const ddVec3 Tan = {0.823529f, 0.705882f, 0.549020f};
const ddVec3 Teal = {0.000000f, 0.501961f, 0.501961f};
const ddVec3 Thistle = {0.847059f, 0.749020f, 0.847059f};
const ddVec3 Tomato = {1.000000f, 0.388235f, 0.278431f};
const ddVec3 Turquoise = {0.250980f, 0.878431f, 0.815686f};
const ddVec3 Violet = {0.933333f, 0.509804f, 0.933333f};
const ddVec3 Wheat = {0.960784f, 0.870588f, 0.701961f};
const ddVec3 White = {1.000000f, 1.000000f, 1.000000f};
const ddVec3 WhiteSmoke = {0.960784f, 0.960784f, 0.960784f};
const ddVec3 Yellow = {1.000000f, 1.000000f, 0.000000f};
const ddVec3 YellowGreen = {0.603922f, 0.803922f, 0.196078f};
} // namespace colors
#endif // DEBUG_DRAW_NO_DEFAULT_COLORS
// ========================================================
// Embedded bitmap font for debug text rendering:
// ========================================================
struct FontChar
{
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];
};
#if DEBUG_DRAW_CXX11_SUPPORTED
#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
#endif // DEBUG_DRAW_CXX11_SUPPORTED
//
// Data generated from font 'Monoid18' by font-tool.
// Command line: monoid-18.fnt monoid-18.png monoid-18.h Monoid18 --static --compress --structs --hex --encoding=lzw
//
// The Monoid font, copyright (c) 2015 Andreas Larsen and contributors,
// is released under the MIT license. See: https://github.com/larsenwork/monoid
//
// The following glyph bitmap is an LZW compressed graymap.
// ~7.55 KB of data.
//
// It is better to ensure it is aligned to a, say 16 bytes boundary,
// because we cast the first few bytes to uint32s.
//
// font-tool: https://github.com/glampert/font-tool
// LZW compression: https://github.com/glampert/compression-algorithms
//
DD_ALIGNED_BUFFER(s_fontMonoid18Bitmap) =
"\x2F\x1E\x00\x00\x78\xF1\x00\x00\x00\x00\x06\x14\x38\x90\x60\x41\x83\x07\x11\x26\x54\xB8"
"\x90\x61\x43\x87\x0F\x21\x46\x94\x38\x91\x62\x45\x8B\x17\x31\x66\xD4\x88\x70\x43\x13\x8C"
"\x15\x80\x0D\xD8\x98\x10\x0D\x98\x84\x4D\x36\x8C\x8C\x28\x40\xC1\x01\x95\x2F\x1F\x06\x10"
"\x10\x00\x26\xC0\x44\xFE\x5C\x5A\xA4\xF4\x8F\x4E\xCD\x81\x01\xF8\xFD\x4B\x70\x30\x40\x3F"
"\x3F\x3E\x17\x32\xFA\xB7\xB4\x5F\xCA\x8C\x67\xFC\xD1\x04\xB0\x2F\x0D\x52\x82\x74\x96\xF2"
"\x54\xD9\xC4\x5D\xD6\xA5\xBF\x10\x36\x5B\xBA\xE3\x60\x81\x7E\xFD\xA4\x2E\x3C\xC0\xE8\xDB"
"\xD2\x77\xC1\x2A\x34\xF4\xF2\xEF\x52\x42\x7E\x3D\xAD\x1E\x74\xB5\x34\x13\xB4\xA5\x11\x9E"
"\x46\x05\x78\x37\x6F\xC0\x25\x4B\xDB\xFE\xAB\xB2\x51\x0E\xE2\x7E\xFF\xDE\xF9\x73\xA6\xF7"
"\x9F\xBF\xA5\x14\x0E\xEE\xFC\xC2\xD0\x44\x56\x7F\xEF\xB2\xB2\x60\xA8\xCF\xDF\x50\x84\x84"
"\x0B\x13\x8C\xF0\xAF\x5F\xCE\x2F\xFF\xC0\x62\x84\x4A\x13\x28\xDE\xC2\xD8\xFC\xC5\xA5\xE0"
"\xAF\x5B\x40\x1A\x16\x0F\xF8\xF3\x67\x02\x40\x17\x7F\x09\x4B\xB0\x06\x30\xE4\x5F\x6F\x83"
"\xC1\xFB\x31\xDC\xF7\x4F\x4D\x5A\x2B\xFF\xD8\x2D\x9C\xF2\x8F\x94\x42\xD4\xA9\x05\x7E\xF8"
"\x57\x35\xE0\x3E\x6E\x19\x8D\x07\xE4\x47\xBE\xF0\xB2\xE3\x83\x9D\x2B\x1A\x5F\x91\xF9\x66"
"\x00\xB3\x11\x66\xFB\x07\x02\xA0\xB2\x7F\xA2\x0D\x12\xE5\x1F\x26\x14\x3A\xAC\xBB\x81\x70"
"\x4B\xEB\xA0\x7C\xFE\x01\xCC\x2E\xDB\x16\x6A\x86\xC0\x8C\xC4\x3B\x10\x80\x01\xFA\x71\xCE"
"\xA2\x59\xDA\x9A\xC5\xC3\x7F\xBE\x01\x46\x23\x09\x11\x4A\xE4\x1F\x6F\x9A\xE8\x86\x2E\x00"
"\xE4\x6B\xAD\xA2\xC6\x34\x00\x08\x3F\x83\x34\xF8\x67\x32\x80\x6A\xBC\xB1\x20\x08\xFE\xE1"
"\x47\x21\x34\xFE\xF9\x00\xA0\x06\xAC\x00\xC3\x0A\x67\x04\x43\x88\xB9\xD8\x1E\x74\xE8\x3B"
"\x8C\xA6\xC3\x0C\x00\x66\xB4\xB2\x28\x00\xCB\xBC\x5A\x2A\xC9\x81\x5A\x3A\xC0\xCB\x03\x14"
"\x10\xC0\x3B\x08\x0B\xDA\x26\xAB\x76\x00\x30\xB1\x1F\x91\x2A\x92\x61\xC5\xFB\xB6\x24\x48"
"\x97\x7F\x66\x10\xE8\x9E\x06\x0F\xDA\xE5\x1F\xB2\x10\x82\xCA\x00\x00\xD2\xC8\x32\xCE\x82"
"\xD2\xF9\x27\xC6\x31\x9D\x24\xD3\xA2\xC3\xFC\x51\xE3\xB1\xE8\x30\x12\xC0\x0F\x7F\x1A\x20"
"\xA0\x01\x7E\xFE\x10\x93\xA0\x2B\xB3\xFC\xE7\xA8\x26\x13\x32\x81\x08\xE2\xD4\xFC\xD3\x22"
"\x0C\xFF\xA9\x03\x00\x34\xDE\x7B\xCE\xC1\x80\x0E\x78\xB5\xA0\x0C\xFE\x41\xB3\xCF\xE3\x3C"
"\xA8\xAC\x89\x06\x14\xF8\x63\xD0\x81\xDC\xD4\x11\xD4\x83\x36\x88\x6B\x30\xF2\x04\xF8\x8D"
"\xA0\x06\x32\x99\x45\xD5\x80\x76\x90\xC5\xA0\x40\xB3\x7A\x46\x41\x8B\x66\x7C\x72\x20\x60"
"\x3C\xEC\x36\x98\x09\x4F\x53\xF4\x20\x4B\x58\x33\xF5\xA2\x1D\xBC\xF2\xC7\x8A\x8D\x96\xF9"
"\xEF\x56\x00\xCC\xD8\x33\x20\x57\x7E\x15\x48\x3F\xFE\x16\xD2\x96\x20\x7B\x2A\xAB\x03\x84"
"\x7E\x30\x31\x21\x98\xA5\x00\x0C\x08\xA8\xAC\x20\x05\xA0\x92\x5F\x47\xF1\x0A\x2F\x01\x08"
"\x90\x2D\x49\x7D\x21\x92\x49\x00\x01\x0E\xC0\x14\x80\x8B\x35\x35\x88\x5C\x17\x0D\xB2\x98"
"\xE3\x91\x3B\x26\x08\x24\xAF\xFA\x31\xED\xA2\xE4\x34\x2C\x08\x2A\x78\xF7\x0B\x28\x9B\x7A"
"\x01\x62\xB9\x21\x8A\x05\x02\x23\xB1\x4E\x4D\x71\x2A\xA0\x19\xFE\x29\x25\x02\x75\x60\x03"
"\x68\xE6\x6B\x19\xAD\x80\x07\xD6\x44\x92\x83\xE6\x87\xD2\x3B\xD6\xA2\xC6\x3A\xD5\xF2\xDA"
"\x80\x1A\x5E\x13\x21\xAA\xAB\x7E\x7A\xAA\x45\x2A\x50\xD1\xC7\x8C\xB4\x89\xD9\xA0\x97\xE3"
"\xAD\x13\x00\x1E\x9F\x6E\xB7\x60\x44\x15\x52\x00\x0C\x84\xF1\x25\xA8\x31\x21\x03\x98\x6E"
"\x16\x40\xB0\x23\xA8\xEC\x12\x00\x12\x70\xB2\x19\x29\x6A\x62\x16\x7F\xC0\x68\x02\x0C\x7F"
"\x80\xF1\x88\x22\x2A\xBE\x81\x46\x72\xC8\x24\x87\xC6\x9B\xAB\x01\xC8\x5A\x01\xC3\x67\x31"
"\x89\x20\xC8\x2B\x0F\xDD\x72\xCC\xD5\x23\xAF\x6C\x21\x31\x0A\xF6\xA0\x97\x55\xA8\x8C\x07"
"\x1E\x82\xA2\x99\x56\x5B\x19\xC2\x99\xA0\xE4\x96\x6A\x79\x20\xA7\xD9\x8C\x00\x4B\x79\x05"
"\x02\x2A\xBB\x80\xF4\xF9\x87\x08\x2A\xBC\x5E\x28\x80\xAA\x97\x22\x3D\xA2\xF5\x12\x6A\x18"
"\xA7\x4A\xBC\x8A\x45\x23\xD4\x56\x98\x2F\xA3\x42\x8D\x25\xE8\x8C\x7F\x20\x00\x60\xAF\xA5"
"\xDC\x09\x86\x66\x3D\xF9\xAC\x5D\x5C\x82\x50\x25\x42\x2C\x45\x0C\x8A\xA0\x85\xE0\x79\x20"
"\xA2\x81\x4D\xF9\x39\x2F\xA0\x1A\xAD\xB6\xA8\x85\x52\xFC\xC1\x83\x16\xF0\xA0\x1F\xA5\x98"
"\x5F\x41\x82\x92\xA5\xF5\x19\xCC\x76\x99\xAB\x4C\x02\x14\xC0\x9A\x0A\x54\x80\x1D\x86\xCA"
"\x08\x6A\x68\xB5\xC0\x88\xA0\xCB\x17\x06\x69\x0C\xE0\x00\xC0\x03\x40\xA8\x01\x00\x0D\x50"
"\x16\x41\x78\x34\xB6\x9B\x69\x30\x20\xED\xEA\x4E\x70\xFE\x41\x9C\x8A\xE8\xE7\x50\x00\x01"
"\x12\x6B\x9C\xE7\x90\xA8\x01\x00\x7A\x06\xD1\xC7\x3B\x80\x08\x44\x7F\xB0\xE7\x20\x0D\xDC"
"\x07\xA5\x00\x50\x23\x34\x00\xA4\x0B\x66\xB3\x12\x6A\x4C\xF4\xB6\x8B\x30\x48\x56\xD0\xE2"
"\x8E\x43\x04\x04\x2E\xF5\x29\x84\x02\xCA\x01\x08\x73\x84\x25\x11\x2A\x54\x46\x86\x00\x28"
"\x40\xEE\x24\x26\x95\x06\x56\xCC\x28\x08\x69\x80\x83\xC4\x53\x8A\xC0\xFD\x03\x84\x14\x51"
"\x63\x55\xAE\xE3\x0F\x73\x61\x64\x3B\x16\x1A\xC8\x74\x00\x51\xB2\x92\x09\x04\x3A\x10\x59"
"\x23\x40\x22\x20\xAB\x13\x56\xE4\x15\xE4\x6B\x96\x57\x0E\x58\x91\x6C\xB1\x10\x22\x28\x69"
"\x48\xF1\x26\x97\xB0\x89\xD4\xC8\x1F\x6D\xF9\xCC\x52\x86\xA0\x12\xD2\xA8\x4C\x20\x2D\xF0"
"\x0C\x68\x96\x82\xBA\x81\x68\xC6\x90\x94\xF4\x09\x0F\xBA\x92\x15\x40\x5C\x47\x93\x00\x00"
"\x81\x15\x9A\x90\x4B\x5D\xE6\x72\x5D\x67\x4B\x12\x55\xC0\x33\x90\x06\x54\xF0\x1F\xEE\xA8"
"\x62\x44\x28\xF0\x18\xAF\x78\xC3\x67\x1B\x99\xCB\x22\x0C\xA2\x80\x4C\x24\xE6\x33\xDF\xE8"
"\x5E\x40\xCC\x82\x96\x56\x06\xB3\x20\x04\xF0\x12\x42\xF6\xC0\xBC\xE6\x71\x53\x22\xDF\xD4"
"\x88\x02\xFC\x51\x97\x9A\x1C\x8C\x94\x10\xD9\x89\x2B\x09\x72\x48\x72\xAA\xA6\x05\x26\xB0"
"\xE7\x3D\xED\x19\xC9\x79\xEE\x13\x20\x96\xF4\xC9\x17\x3C\x37\x91\x0A\xCC\x82\x4D\x0F\x91"
"\x27\x3F\x11\x9A\x50\x85\xF2\x13\x98\x0B\x75\xE8\x43\x21\x1A\x51\x89\x4E\x94\xA2\x15\xB5"
"\xE8\x45\x31\x9A\x51\x8D\x6E\x94\xA3\x1D\xF5\xE8\x47\x41\x1A\x52\x91\x8E\x94\xA4\x25\x35"
"\xE9\x49\x51\x9A\x52\x95\xAE\x14\xA1\x8C\x00\xC3\xFD\x20\x42\x03\x46\xE8\x73\x20\x44\xB0"
"\xE9\x4D\x15\xB2\x81\x9B\xDA\xB4\x99\x03\xA1\x41\xDD\x20\x12\x50\x96\xA2\x94\x53\xFF\x80"
"\x66\x43\x2A\xA0\x4C\xF8\x15\x64\x79\xE9\xCA\x21\x00\xB8\xB6\x94\xA5\x16\x64\x3A\x47\x75"
"\x48\x04\x82\x42\xC4\xA1\x1E\x04\x62\x83\xAC\x68\x04\x16\x07\xBC\x85\x60\xE3\x1F\xC0\x30"
"\x41\x4E\x0A\x42\x03\xB5\xFE\x2F\x79\x50\x21\x82\x5A\x69\x70\xCD\x81\xF0\xC0\x32\x98\x70"
"\x08\x03\x1E\x03\x0C\xB4\x9E\xB4\xA1\x08\x69\x58\xB5\x8E\x39\x10\xF7\x30\x40\xA1\x6E\x82"
"\x27\x50\x54\xB8\x10\xE3\x3C\xF5\x65\x0C\x61\x40\x50\x16\xC3\x10\x06\x59\x35\xA5\x87\xC4"
"\x0D\x34\x4C\xA1\x4C\x98\x1A\x64\x8C\xEA\x44\x28\x86\xE0\xC9\xC3\xA9\x2E\x84\x70\xBE\x7C"
"\xAA\x40\xBA\xE8\x0F\xC2\x2A\x84\x7A\xD6\x63\xE9\x21\x4B\xA6\x86\x37\x1D\xE4\x88\x7B\x3C"
"\xC8\x25\x42\x3B\x90\x04\xB0\x03\xA8\x06\x39\x28\x0F\xB5\x9A\x90\xD2\xBA\x2C\x79\x03\xE9"
"\xAC\x42\x0A\xE0\x8F\x5A\x56\x36\xB7\x00\x00\x8A\xEE\xEC\xF6\x8F\xD1\x1A\x64\x00\xD3\xB1"
"\xEB\x43\x22\xF0\x98\xE0\xC6\x73\xBB\x06\xD9\x47\x6E\x87\xEB\xBD\xE2\xFE\x71\xB9\x9F\xD3"
"\x9E\x42\x86\x10\x8C\x6F\x2D\x24\xBD\xEB\x45\xE8\x6F\x01\x62\x16\xE8\x0E\x64\x00\xB3\x60"
"\x84\xF2\xD6\xF1\x0F\x69\x35\x04\x04\x96\x89\xAC\x5D\x98\x94\x10\x1E\x81\xB7\xAD\xE3\x15"
"\x88\x80\x7A\x4B\x90\x76\x05\xD6\x65\x4B\x71\xE5\xF7\xAA\xC4\x4F\xF8\x02\x60\x27\xDD\x9D"
"\x48\xA1\xC2\x78\x10\x53\x0E\x48\x79\xFC\xF0\x07\x83\x07\x22\x9F\x4F\x31\x24\xBC\x03\x29"
"\x71\x9F\x9C\x68\x10\x65\x18\x98\x6A\xAE\x6C\x71\x42\xE1\x8B\x2E\x7F\x14\x94\x6C\xB5\x7A"
"\x2A\x11\xC6\xD2\x10\x13\xFD\x23\x18\x8B\x1C\x08\x23\xBA\x82\xC4\x86\x9C\x38\x20\x44\x36"
"\x88\x0E\x52\x5C\x10\x65\x24\x36\x21\x2D\xA8\x43\x1D\x7C\x6C\x10\x27\x43\x39\xA1\xDC\xF8"
"\xAF\x42\xC4\x23\xD6\x8D\x50\x09\x64\x05\x79\x0D\x9D\x1C\x32\x80\xAC\x4C\xD7\x60\x58\xF2"
"\x23\x69\x0B\x7C\xDA\x81\xD0\x2A\xC1\x03\x51\x86\xFE\xB6\xEA\x10\x74\x44\x58\x25\xBD\xF0"
"\x54\xC8\x1E\xD3\x66\x84\x14\xCA\x1B\x34\xD8\xEB\x40\x36\x50\x87\x1C\x0F\x39\xCD\x0F\x19"
"\xA3\x9E\x03\xA2\x8C\xF2\xC6\x99\x28\xFC\x60\xF2\x46\x0A\x50\xC1\x05\x06\x85\x84\x0D\x41"
"\x2C\x43\x82\x40\x67\x85\x18\x19\x4E\x6A\x16\xC8\xF7\x10\x0D\x10\xF7\x78\x9A\xD1\x00\xD0"
"\x87\x85\x2D\xF2\xD8\xA2\x1D\x04\x04\x4B\xA1\xAC\x77\x3C\x9B\x90\xE4\x12\x98\xB1\x06\x4E"
"\xF4\x3F\xE4\x4A\x10\x3D\xA9\xB2\xD4\xF3\xCC\xEE\x3F\x5C\x8B\x10\x0D\xD4\xF5\x66\xA8\x1E"
"\x08\x7C\x39\xCD\x69\xFA\xF6\xE3\xD1\x04\x41\x32\x99\x2F\xE2\x05\x20\xDA\x27\x21\xD2\x7E"
"\x07\xB5\x53\x3A\xEC\x7F\x5C\x57\x21\xBF\x0E\xF6\xB0\xBC\x63\x6C\x13\x17\xBA\x21\x8D\x81"
"\xF6\xB2\x67\xBC\x10\x05\x80\x00\x04\x9B\x45\xB1\xA6\xCF\xE6\x60\x75\xB3\xDB\xDD\x19\x31"
"\x01\x23\x00\x31\x92\x56\x1B\xF5\xAE\x8F\x09\x30\x75\xFB\x11\x5A\x7E\x9C\xDB\xB4\xC2\xB5"
"\x75\x6A\x57\x9B\x10\xEA\xFD\xDB\x83\x4B\x19\xF1\xD6\xE4\xAD\x90\x17\x4B\xDC\xE1\x2A\x99"
"\xCE\x79\x2F\xC2\x80\xA5\xFC\x01\x22\x09\x08\x4A\x07\x11\x62\x58\x86\x00\x85\x76\x8A\x2D"
"\x70\x43\x32\xF6\x0F\x6C\x27\xA4\x78\x1C\x17\x6E\xC4\x13\xB2\x01\x2B\xF8\xC3\x95\x32\xA7"
"\x39\x69\x61\x9E\x91\x83\xDD\x59\x23\x7D\x68\x2E\x36\xD9\x51\x6F\x81\x34\xA0\x09\x8F\xA9"
"\xA3\x42\x0A\x35\x0B\x10\x44\xCC\x20\x34\x68\x41\x3D\x01\x58\x6B\x01\xB6\x20\xAE\x1A\x7E"
"\x8C\x1C\x1B\xE2\xBB\xA0\x31\x3D\xDE\x18\x07\xF7\xD7\xBB\x2E\xEE\x88\x34\x32\xC9\x13\x2D"
"\x2A\xD6\x19\xF2\x6B\xE9\x86\x4C\x50\x4F\x8D\xEA\xDA\x7D\xBB\x6D\x88\x24\xD5\xEB\x15\x01"
"\x1A\x3C\xEF\x9E\xD0\x03\x90\x3A\xA1\x8C\x50\x03\x88\x11\xC2\x83\x4C\x44\x39\x20\x3B\xB5"
"\x69\x4E\x0D\xDF\x53\x81\xF0\xA0\x8C\x10\xA9\x4E\x46\x98\xE3\x2E\xF4\x12\xAC\xD7\x95\xA7"
"\x68\x63\x2A\x4D\xF1\xCC\x5B\x9E\xF3\x0F\xF5\x2A\x42\x3E\xDF\x79\xD1\x8F\x9E\xF4\xA5\x37"
"\xFD\xE9\x51\x9F\x7A\xD5\xAF\x9E\xF5\xAD\x77\xFD\xEB\x61\x1F\x7B\xD9\xCF\x9E\xF6\xB5\x8F"
"\x08\x01\xB8\x9E\x10\x8E\x35\x04\xF7\x2F\xC9\xFD\x45\x03\x80\xFB\xD0\x97\xD4\x77\xDF\x5E"
"\xDD\x2F\x51\x8D\xE3\xA5\x3C\x83\xC6\x05\x29\x45\x56\xF4\x9A\x90\xB9\x2D\xC5\x1B\x42\xBF"
"\x75\xD5\x78\x5D\x10\xDC\x00\x7E\x2A\x9D\x42\x35\xAB\x4C\xD3\x57\x82\x5C\xDC\x2D\x75\x27"
"\xDE\x32\x73\x5D\x90\x06\x74\x43\x9B\x19\x15\x0F\x9C\x6F\xA5\xC6\x05\xF2\x2F\x32\xFF\x80"
"\x3F\x41\xFC\xE3\x0F\x68\x58\xA6\xD9\x01\xB9\x21\x34\x40\x83\x66\x7A\xE1\x33\x82\x08\x95"
"\x42\x8D\x05\x6A\xC5\x3B\x20\x23\x88\xCC\xAF\x85\x26\x66\x81\x7E\x08\x88\x12\xE3\xE1\x8E"
"\xED\x1B\x82\x61\x16\x02\xB0\x9D\x08\xE2\x3A\x2A\x83\xEF\x10\xAA\x46\xEE\xAF\xE0\x78\x68"
"\x81\xF4\x64\x31\x02\x80\x68\xD2\x0F\x00\xBA\xA8\xFD\xF4\x43\x6D\xA8\x2A\x37\x00\x62\x11"
"\x1A\x50\xFB\xD2\xED\x20\x0A\x25\xFB\x10\x28\xB7\xD0\x41\x85\xE4\x89\x01\x84\x83\xFB\x02"
"\x42\x3E\x62\xED\x27\x7E\xA1\x32\xBE\xC1\xD6\x12\x0A\x04\xD1\x4C\xFE\x68\x4B\x85\xF4\xA4"
"\xBB\x80\xE6\xBF\x92\x63\x7D\x6A\xC4\xB3\x92\x2B\x04\x0D\x22\x01\x80\x2D\xE4\x56\x0D\xEC"
"\x4E\x03\xCE\xE4\x49\x3F\xB4\xC8\x3B\xE6\x0B\x20\xA6\x43\x5A\x16\x4B\xA3\x94\x70\xD3\x1E"
"\xB0\x88\xD4\xA9\x8B\xE0\x0D\x20\x60\x24\x20\x40\xCB\x20\xC4\x83\x88\xCC\x83\xC4\x24\xCF"
"\x20\xE6\x4C\x4A\xBC\x30\x7E\x78\x6E\x04\x15\x62\x8C\x18\x0E\x21\x82\x03\x0B\x01\x42\x0D"
"\x94\xC5\xC8\x16\xA1\x5B\x3C\x04\x1A\x52\x50\x0D\x98\x4F\x20\xAA\xE0\x19\x34\x10\x00\x62"
"\x21\x18\xF4\xA9\x14\xA0\xC1\x05\x69\xC4\xFE\x96\x70\x30\x26\x8D\x3C\xC8\x6E\x7D\x40\x4D"
"\x20\xBE\xCB\x20\x7C\x90\x49\x78\xA4\x10\x09\x65\xD1\x70\x84\x35\x4C\x80\x06\xB8\x8F\x1F"
"\x36\x63\x80\x14\x6F\x20\x6A\x84\x3D\xD6\x28\x01\x94\xEB\x21\xFA\x20\x0E\xC5\x2B\x87\xCE"
"\xA1\x53\x8E\x4E\x20\xF8\x00\xCC\x02\x02\x1D\x6A\x70\x20\x80\x24\x61\x98\x43\x6B\x84\xCD"
"\x46\x42\x51\x10\x7D\xAB\x27\x78\x04\x10\xC4\x4F\x20\x80\xA4\x6E\x56\x63\x81\x8E\xE8\x80"
"\x60\xE1\x1F\x42\x89\x8B\x66\xAB\x20\xDE\x6E\xBF\xBC\xEB\x1B\x94\x89\xDF\x0E\x62\x2E\xEA"
"\x66\x8D\xF4\xA3\x1D\x88\xE0\xF7\x0E\x22\x38\xF4\xA8\x1A\x0D\xC2\x9B\x0E\xC0\x52\xCE\x82"
"\x74\x92\x63\xAA\x30\xA4\x0C\x01\xA0\x6C\xC8\x63\x3A\x3C\xF1\x13\x11\x71\x20\x98\xE0\x72"
"\x00\xC2\xCA\x0C\xA2\x8B\x7A\x42\x14\xFA\x41\x01\xF4\x21\xC3\x00\xE2\x30\x6C\x43\xE4\x0C"
"\x02\x01\xFF\x61\x16\xBA\x82\x20\x07\x02\x11\x82\x04\x21\xDA\xA5\x51\x04\x4D\x31\xF0\xAC"
"\x32\xAC\x80\x6F\xB4\x6C\x20\x92\xF1\x55\xD6\x08\x1F\x3C\xC3\xB6\x0C\x62\x2F\x28\xF0\xF8"
"\x3C\xAD\x31\xC6\x30\x78\xF8\x81\x76\x02\xF2\x20\x34\x2E\x2A\x1A\xC3\xF8\x42\x66\xC2\x12"
"\x42\x3C\xD0\xE0\x8C\x8E\x62\x1F\xCA\x90\x47\xFA\xE1\x7E\x08\x80\x68\xAE\x6C\x20\x78\xA4"
"\x7A\x46\x03\x16\xCD\xF0\xC3\x6A\xA6\x39\x0E\xA2\x0E\x80\x41\x2A\x96\xE4\x20\x46\x4D\x3D"
"\x58\x08\x63\x1A\x80\x1B\xE0\x6E\x1A\xB5\x72\xDC\xD4\x2C\x38\x86\x87\x2C\x4B\x03\x20\x92"
"\x51\x8A\x4C\x0C\x32\x70\x68\x8B\x32\xC2\x07\xC1\x60\x52\xFE\x84\x1F\xCE\x4C\x20\xEC\x0C"
"\x44\xB2\xC2\x0F\x09\x82\x41\x00\xE1\x00\x36\x60\x3A\x56\xAE\x20\xB2\x27\x27\xFF\xA8\x65"
"\xF2\xC1\xC0\xF6\xA1\xFF\x00\x00\x1F\x5A\xA6\x87\x90\x8B\x37\x14\x42\x0C\x09\x4D\xCD\xF6"
"\x22\xD4\x00\x60\x2E\x22\x4B\x1B\xF2\x11\x21\xA8\x44\x19\x17\x42\x1F\x1A\xB3\x22\xF6\x21"
"\x18\xFA\xA1\x3B\x80\x02\xD5\x32\x01\x65\x0E\x42\x3C\x58\xD1\x2B\x13\x62\x2F\x6A\x31\xD6"
"\x56\xCC\xD3\x7E\x93\xA9\x28\x0D\x0C\xC0\xA0\x0E\xFC\x21\x18\x84\xCA\x24\xBD\x06\x68\x4A"
"\x8E\x0D\x4F\x4B\x3C\x8C\x92\x20\x34\x6E\x32\xCC\x22\x2E\x0F\xA2\x31\x9A\x91\x9B\xA6\x83"
"\x2B\xAD\x31\x21\x98\x63\x08\x3F\x8D\x8E\xEC\xA5\x2D\x85\x27\xDC\x04\xA2\x2C\x17\x22\x38"
"\x37\xA5\x1D\xB3\x44\xCD\xEE\xC1\x6B\x18\xA4\xF1\x9C\x13\xE9\xE6\x52\x21\x98\x71\x00\x62"
"\x20\x10\x55\xD1\xCC\xF6\x29\x28\x62\x43\x22\x17\x62\xCE\x52\xF0\x3E\x2C\x28\xD1\xBC\xA6"
"\x3E\xBC\xC3\x8F\xD4\x53\x21\xD0\x93\x20\x4C\x00\x04\xEC\x09\x04\x5A\xA0\x80\x44\xD3\x01"
"\x9D\xA7\x89\x30\x32\xFE\x12\x42\x23\x19\x62\x3B\xC0\x40\x06\x71\x50\xB0\x8C\xE7\x6F\x16"
"\xE2\xDE\xF2\x4D\x23\xA2\x04\x20\x90\x4C\xDC\x6C\xE6\x20\x0E\xE3\xCA\xF2\xA1\x2D\xDD\xE3"
"\x12\x7D\xD2\x3A\x9D\x72\xE4\xF8\xC1\x3A\xED\x42\xEC\x06\xA3\x39\x7D\x12\x4F\x1C\xC2\xC8"
"\xF2\xA6\x2D\x7F\xD4\x8B\x0E\x82\x39\xB2\x23\x7B\x2C\x13\x15\x97\x82\x47\x19\x42\x4F\xEC"
"\x83\x41\x06\x54\x66\xF6\xB0\x20\x34\x2E\x65\x00\x02\x2B\xA2\x13\x56\x24\x63\x21\xF4\xE3"
"\x80\x98\x00\x14\x43\x46\x20\xFC\xE0\x1C\xFF\xD0\xB9\x04\x02\x2B\x02\x33\x20\x48\x12\x3C"
"\xE7\xF3\x20\x4C\x24\x7D\x16\x22\x18\xAF\x88\x28\xA6\x03\x84\xDA\x85\x85\x76\x0E\x32\x27"
"\x22\xCB\x66\xE1\x31\x16\x32\x20\x90\x4C\x47\x0B\x42\x40\xFE\xC1\x14\x54\x04\x48\xE3\xAD"
"\x27\x0B\x02\x5D\x78\xCC\x2F\xCA\x4E\x3D\xDE\xC2\x14\x62\xC9\xFA\xAE\xB1\x88\x26\x35\x96"
"\x0C\xF3\xD8\x10\x23\x88\xFC\xA1\x86\x74\xF2\x34\x2A\xC3\x53\xC5\x6D\x3A\xEC\x52\x20\xA8"
"\x47\x47\x38\xC9\x52\x03\x82\xEC\x2A\xF4\x21\x9A\xA0\x5A\x9A\xAF\x20\xC8\xE5\x18\x0D\xC2"
"\x25\x8B\x29\x17\xCF\x33\x3B\x11\xA2\x09\xB0\x24\x80\x10\xA2\x78\xB2\xC2\x1D\xA6\x74\xFC"
"\x2C\x4C\x58\xC9\x47\x57\xCD\xB0\x6A\x40\x55\x04\xBD\xAB\x53\x28\x49\x1F\xB4\xD2\x07\xFF"
"\xC1\x30\xF7\x62\x4D\x07\x62\xEF\x5E\xA2\x25\x1A\x82\x55\x09\x02\x63\xFE\x2C\x23\xCC\x29"
"\x21\x82\x6F\x5C\x75\x8E\x1F\x37\xAA\x4C\xDD\xD4\xF6\xD8\xF5\x21\x64\xE1\x31\xEA\xB3\x5D"
"\xE5\x15\x22\x1E\xE3\x19\x8A\x75\x5E\xF1\x95\x20\x0E\x60\xF8\xF2\xB5\x5F\xFD\xF5\x5F\x01"
"\x36\x60\x05\x76\x60\x09\xB6\x60\x0D\xF6\x60\x11\x36\x61\x15\x76\x61\x19\xB6\x61\x01\x62"
"\x26\x18\x82\x5F\x1D\x76\xA2\xB6\x91\xAA\x9C\x54\x50\x2B\xC3\x5B\x01\xA2\x50\xD6\x75\xA2"
"\x74\xAA\x61\x0F\x09\x29\xA9\xC6\x56\x7F\xE2\x31\x08\xB5\xA2\x9C\xC6\x03\xF1\x35\x64\x7F"
"\xAE\xBF\xDA\x61\x56\x09\x82\x14\x54\x6E\xA3\x76\x48\x61\x59\x16\xA1\x14\x41\x1D\x05\x82"
"\x06\xAC\x89\x20\x24\x91\xC6\x2A\x91\x46\x49\x24\x20\x86\x76\x20\xD2\x8B\x43\x3E\xD2\x43"
"\xDC\xCB\x20\xD4\xE0\x1B\xDE\xE1\x1B\x22\x87\x51\x01\xA2\x19\x42\x09\x10\x9C\xF6\x1D\xAA"
"\xB2\xA6\x9C\x56\x16\x0E\xE0\x17\xA4\x16\x00\x9A\x81\xF1\xDC\xE1\x1D\xB8\x36\x54\x9E\x01"
"\x1A\xBE\xE1\x44\x0F\xA2\x19\x6C\x0A\x6A\x4D\x01\x66\x97\x03\x39\xA1\x36\xAE\xBA\xE1\x1D"
"\xC2\xF2\x4C\xCD\x32\x20\x12\x20\x18\xA0\x01\xC4\x8E\x76\x16\x44\x04\x21\xB8\x65\x16\x96"
"\xD6\x20\x1A\x34\x20\x1A\x23\xC1\x82\xF1\xE8\x70\xC3\x26\x45\x51\x20\x28\x26\x9C\xAA\x86"
"\x4F\x01\xA0\x50\xB2\x84\x72\xF9\x01\x51\x1D\x66\x75\xA0\xB5\x88\xDA\xEE\x48\xB3\x64\x49"
"\x13\x08\x61\xBA\xAE\x6A\x32\x34\x6F\x41\xA5\xBA\xDC\xB1\xE1\xFA\x67\x25\x73\x4E\xC9\x7E"
"\xE5\x14\x07\x02\x68\xF0\xD0\x47\x51\xF7\x71\x09\xC2\x52\x0E\x40\x11\xFC\x61\x03\x1A\xE0"
"\x77\xED\x11\x20\xBA\xA8\xFA\x00\x22\x00\xB4\xF5\x59\xA9\x83\x36\x1A\xCD\x78\x00\xA0\x33"
"\x84\xB1\xFB\xC0\x82\x00\xCC\x04\x1C\xFF\xC1\x23\x08\x40\x2D\x2D\x6C\x3A\xD6\xE5\x00\xD4"
"\xB2\x1C\xEF\xB2\x27\x02\xA5\x35\x0A\xE0\x88\x72\xE8\x66\x03\x80\x41\xB0\x35\x20\x30\x46"
"\x52\xBC\xA6\x1F\x2F\x85\x85\x82\xF3\x00\x4C\xC1\x73\x64\x57\x20\x7C\x50\x47\xBA\x28\x7D"
"\x9F\xA4\x81\x1A\x2B\x21\x6A\x64\x0E\x1A\x62\x3A\xC4\x0E\x3A\xFD\xEF\x79\xC9\x17\x20\x34"
"\x0E\x11\x6B\xC4\x42\x0E\x91\xB6\x34\x44\x3C\xC4\xC5\x4F\xCC\xA8\x1F\x8E\x0A\x11\xBC\x26"
"\x64\x97\x88\x68\x5C\x91\xB8\xD4\x6C\x8D\x82\x13\xC9\x20\xE5\x7B\x42\x34\x46\x03\x62\x3B"
"\xBE\x76\x7F\x35\x48\xD9\x6A\xC4\x1D\x36\x40\xCD\xF6\x81\x48\x01\x62\x2E\x0A\x06\x40\xBD"
"\x4B\x58\xB2\xA1\xFD\xEE\x52\x4E\xD3\x81\x66\xBE\xA3\x36\x38\x58\x6A\x3A\x2D\x5C\xBC\x83"
"\x11\x8A\xF0\x74\x7F\x18\x6E\x8E\xEF\x4F\x42\xA1\x32\x08\xAB\x0F\xEA\x65\x2F\xA4\x84\x37"
"\x8B\x08\x42\xF8\x77\xBC\xDA\x45\x4B\x66\x01\xF0\xF2\x10\xE7\x54\x09\x82\x1D\x58\x20\x16"
"\x34\x3C\x79\x2D\x8C\x21\x97\x8A\x23\xB8\x0D\x83\x78\x8A\x15\x82\xFC\x68\x2D\x5F\x58\x48"
"\x84\x2F\x13\x94\x00\x60\x19\x70\x58\x20\x94\x60\x66\x4D\x4D\x2B\x51\x58\x43\x17\xC2\x0A"
"\x82\x01\x95\x70\x4D\x8D\x71\xAE\x6E\x7C\xD8\xB7\x1E\x8E\x8C\x65\x24\xC5\x10\x19\x77\x19"
"\xF9\xD3\xD0\xD8\xC8\x42\xD6\x33\x12\xEE\x4D\xF1\x16\x00\xC4\x83\x0A\xCE\xA8\x09\xD2\xA1"
"\x3B\xF2\xA1\x0C\x83\x63\x32\x68\x25\x7D\xBB\x73\x94\x87\xF1\x21\x04\x40\x06\x17\xF2\xA0"
"\xEA\x37\x83\x68\x4B\x58\x16\x19\x8E\x0D\x94\x74\xF6\xB8\x94\xAD\x11\x92\x29\xE9\x27\x01"
"\xE0\x8E\x8D\xD8\x2D\xDD\x38\x21\x82\x23\x0D\x68\xE0\x1F\x0E\x60\x12\xF8\x21\x63\x3A\xD6"
"\x34\x01\x20\x0E\x54\x13\x81\x88\xA8\x8A\x55\x16\x00\xD0\x01\x16\x0F\xEA\x6E\x02\x82\x95"
"\x6B\x18\x8C\xEB\xE5\x83\xEC\x85\x87\xCD\xF8\x88\x51\xC3\x96\xCD\xB3\x72\x5F\x32\x54\x91"
"\xF8\x20\xF4\x01\x18\x32\xC1\x47\x64\x00\x71\x54\xD2\x20\xE6\xC2\x0A\xB6\x41\x2B\x81\x82"
"\x49\x68\x38\xDE\x9A\x95\x21\x8E\xE6\x52\x17\x22\x39\x5C\x0E\x00\x90\x0C\x1C\x55\x08\x43"
"\x4E\x16\x00\x10\x70\xC4\x8A\x62\xA0\x47\x99\x62\x26\x49\x46\x2E\xF8\x96\xF1\x42\x3C\xEA"
"\x98\x9C\x2B\x19\x00\xF4\x64\x45\xCC\x62\x29\xEE\x99\x4B\x97\x42\x94\xA7\x02\x06\x01\x80"
"\x5C\x34\x68\x2E\xD0\xAE\x21\xC2\x14\xA1\x0F\x0A\x1F\x01\x43\x75\xA9\x97\x3C\xA8\x67\x5D"
"\x93\xCB\xA3\xC9\xA5\x63\x67\x99\x97\x6B\xF9\x69\x48\x0E\x89\x5B\xC1\x4C\xDD\x79\xBC\x0E"
"\x09\xC2\xD4\x66\xCE\x22\x7A\x20\x2A\x3A\xA3\x91\x71\x29\x66\xA1\x1D\x5E\x77\x20\x2E\xDA"
"\x1D\x4C\xE1\x1B\xB2\x96\x78\xA0\x61\x16\x9A\x7A\x29\x12\xB2\x8C\x1B\x62\x89\xFF\x61\xFF"
"\x92\xBA\x3C\x3A\x75\x29\x26\x79\x20\xE8\x34\x13\x54\x24\x5E\xE3\xA9\x9B\x69\xF9\x9B\xBD"
"\x66\xCE\xBE\x61\x16\xBC\xB7\xAC\xB1\x29\xCF\x14\x82\x5E\x3A\x98\x92\x52\x8B\x4D\x1A\x63"
"\x83\x01\x42\x3C\x76\x39\x06\xB5\x84\x07\x2A\x76\x20\x28\x28\x2B\xA8\x77\x58\x09\xAF\x3C"
"\x2E\x16\x13\x98\x02\x04\x52\xD1\xB7\xD4\x40\xB6\xC8\x28\x21\x1C\xFB\x44\xB4\x18\x0F\xB7"
"\x6B\x46\x1A\xCA\xC8\x1A\xE0\xE2\x28\x57\xFC\xE6\x62\xAF\x99\xD1\x85\x2F\x16\x26\x64\xE2"
"\x21\xBC\x49\x62\x83\x0F\x62\x54\xE2\x62\x1A\xF9\xD8\x00\x98\x00\x24\x96\x90\x82\x97\x9C"
"\xBC\xE4\x99\x11\xE2\x35\x02\x95\x60\x6D\x07\x29\x57\xAA\x1B\x82\xA1\x2F\x96\x42\x59\x0B"
"\x36\xB7\x7F\x6E\xA5\x46\xD7\x1F\x5E\x35\x60\x8B\x82\x4F\x93\x3B\xF5\x20\x46\x01\x26\xB6"
"\x9F\x94\xD5\x9F\x9E\x7B\xBA\xA9\xBB\xBA\xAD\xFB\xBA\xB1\x3B\xBB\xB5\x7B\xBB\xB9\xBB\xBB"
"\xBD\x1B\xA5\x84\xEF\xBB\x33\xEA\xAF\x33\x62\xFA\xB6\xFA\x22\xC8\x1B\x22\xD2\xDB\xBA\x75"
"\x9B\x21\xF6\xCD\x43\xDC\xA1\x35\x2F\xA2\xBD\xDD\xDA\xBB\xE9\x9B\xE2\xE4\xF4\x25\xEE\xDB"
"\xAA\xED\x7B\xB8\x49\x0C\x27\x90\x62\xBF\x6F\x97\xBB\xF9\x41\x0D\x88\xC0\x6D\xE1\x36\x20"
"\xB8\x82\x6C\xBB\xF6\x6B\xE1\xA4\x21\x78\x40\x72\xBE\x21\x13\xC2\x55\x6B\x43\xA4\x02\xF4"
"\x41\x83\x36\xC0\x14\x24\xB0\x67\x7D\xEB\x0B\x4C\xC0\x1D\xBE\xA1\x6C\x0F\x22\x02\xA0\x16"
"\x6A\xA1\xA1\xB0\xFF\x95\xFC\x98\x02\xE2\x14\xA8\x9B\x1A\x60\x77\x7B\xF7\x77\x2B\xE5\x20"
"\x6E\xC8\x33\x12\x1C\xC2\x36\xD7\x20\x80\x26\x4B\x48\x76\x59\x9D\xEA\x36\x3B\xE5\xB8\xDB"
"\x55\x7B\x01\x80\x7B\xC9\xF1\x28\xFF\xE1\x37\x36\xE0\x31\xC4\x45\x72\x3B\x85\x4F\x07\xC0"
"\xC3\xE6\x47\x00\x62\x41\x3F\x7F\x94\x08\x9A\xF7\xBC\x4D\xED\x1F\x02\x2A\xB6\x97\xF5\x17"
"\x82\x6F\x7A\x0D\x51\x2A\x02\x85\x72\xFB\xF5\x46\xF3\x5A\x18\x17\xB8\xC8\xE2\x70\x08\x4C"
"\x61\x9A\x0E\x35\x13\x32\xC1\x14\xDA\x3A\x3C\xD6\x12\x43\x10\x91\x80\x6D\x88\xCD\xBB\x70"
"\x8D\x93\x24\x81\x1D\xEB\x79\xFB\xB5\x87\x71\xA6\x89\xEA\xA6\x46\x34\xA8\x66\xDF\x11\xCC"
"\x02\x80\x26\xA2\x19\x73\x64\x38\x20\xEA\x79\x20\xE6\xA2\xAA\x5B\x79\x3C\x3D\x6D\xB1\x71"
"\xDB\xAC\xBD\x67\x0F\x7D\x30\x85\x8B\x4B\xC7\x83\xFC\xD3\xBB\x58\x18\x81\x24\x44\xBD\xAB"
"\x65\x16\x39\x11\x77\x86\xD0\xF3\x75\xA6\x9D\xD1\x89\x2A\x9D\x96\xDF\xED\xC4\xA1\xB6\x0C"
"\xEB\xB7\x90\x09\x82\x39\x88\x9C\x1F\x0E\x59\x00\xDD\x02\x6A\x61\x7D\x65\x3D\xDD\x68\x55"
"\xD3\x8B\x25\x9A\x73\x55\xFD\xD3\xEB\xC6\x9A\x4D\x3D\x80\x5D\xB9\x5E\x6A\xC4\x1B\x9C\xDB"
"\xB9\x04\xDC\xF5\x64\x1D\xB5\xC4\x54\x61\x08\x5D\xD9\xB4\x79\x21\xA8\x19\x20\xA0\x7D\xD6"
"\x7F\x9C\xAA\xA6\x1D\x73\xBE\xC7\x7B\x15\xDD\x60\xB7\x5D\x20\x18\x04\x13\x1A\xC0\xB1\x45"
"\xFD\xB4\x82\x72\x9F\xFF\xA1\x9F\xFF\xD9\x20\xEE\x7D\x21\x2A\x93\x0E\xFB\x61\xA0\xC3\x3D"
"\x97\x8B\x7D\x5E\xDF\x3D\x20\x36\xCC\xA8\x6F\x6E\xD9\x9F\xC3\x32\x5E\x15\xA5\x2F\x64\x3A"
"\xD6\xC7\x2C\x7A\x57\x3A\xB4\xA7\xA5\x8F\x6C\xD5\x0E\x40\xE2\xDD\xFD\xD8\x97\x05\x10\x32"
"\x81\x07\x1A\xE0\xDB\xC7\x2B\x19\x0F\xD5\x14\xFE\xD8\xC2\xB0\xFA\x70\xB8\xFC\x86\x2E\xD0"
"\x7C\xB2\x77\x29\xD0\xD6\xAB\xCD\xB9\x98\x66\x41\xB0\x0D\x56\xFC\xD6\x5B\x20\xF2\xB3\xDE"
"\x19\x42\x0D\x7E\x67\x06\x01\x00\xB1\x5D\x67\xBD\xC3\x2A\x2B\xB2\xB7\xB1\xB5\x44\x3E\x85"
"\x89\x98\xDA\x01\xC3\x43\x5B\x61\x07\xA0\x78\x88\x9C\x21\xFA\x11\x62\x75\xAF\xC2\xEF\xD1"
"\xB5\x19\xC2\x9B\xAC\x5E\xEB\xAB\x1B\x72\xA4\xDA\x32\xF0\x5A\xBC\x19\x8D\xD4\xB9\xCD\xEC"
"\x47\xAF\x5C\xC1\x5C\xED\xDD\xFE\xED\xE1\x3E\xEE\xE5\x7E\xEE\xE9\xBE\xEE\xED\xFE\xEE\xF1"
"\x3E\xEF\xF5\x7E\xEF\xF9\xBE\xEF\xFD\xFE\xEF\x01\x3F\xF0\x05\x7F\xF0\x09\xBF\xF0\x0D\xFF"
"\xF0\x11\x3F\xF1\x15\x7F\xF1\x19\xBF\xF1\x1D\xFF\xF1\x21\x3F\xF2\x25\x7F\xF2\x29\xBF\xF2"
"\x2D\xFF\xF2\x31\x3F\xF3\x35\x7F\xF3\x39\xBF\xF3\x3D\xFF\xF3\x41\x3F\xF4\x45\x7F\xF4\x37"
"\x4A\xF8\x68\x9B\xAB\x7A\x6F\x24\x20\xA6\xED\x1D\x8A\x7F\x4C\xD7\x6F\x4E\x53\x20\x1A\x23"
"\x21\x83\x51\xAB\x02\x80\x34\x6C\x8B\x22\xAF\x06\x2A\xAA\x48\xD1\x0A\xE2\xD0\x68\xDA\x91"
"\xAF\x85\xD4\xB3\x24\xA8\x01\x02\x76\xB2\xC4\x15\xC9\xEF\x2D\x78\xC0\x10\x65\xC1\x33\xB4"
"\x9C\xB6\xB2\x82\xF9\x9D\x1F\xFA\xE3\x2D\xBF\xD3\xD8\xCD\xAB\x5A\x1B\x8C\x7F\xE7\xF7\x67"
"\x01\x0B\x70\x29\x42\xB0\x31\x82\xEB\x88\x9A\x6F\x28\x69\xAC\x28\x15\x2C\x76\x93\x6C\x17"
"\x84\x0C\x55\x03\x59\xF8\x6D\x3D\x86\xC1\xDF\x2D\x80\xC8\xF8\x6F\xE8\x1B\x50\xFE\x08\x33"
"\x34\x28\x80\x28\x2B\x9C\x01\x08\x28\x30\x60\x03\x7F\xFF\xFC\x99\x82\xF6\xEF\x1F\xA6\x81"
"\x0E\xF9\xFD\x7B\xF7\x6E\xE1\x3F\x80\x0E\x09\x1A\x44\xA8\xF0\x1F\xA9\x8B\x00\xCE\xFC\xEB"
"\x77\xF1\x8C\xBF\x00\x0E\x41\xF2\x73\xA8\xE3\xA0\xC9\x8B\xFC\xE8\x78\x8C\xE9\x31\xDD\xBF"
"\x19\x17\x0B\xF8\x13\x39\x50\xC9\xBF\x4B\x1E\x13\xFD\x5B\x24\xB0\x40\x3F\x7F\x09\x2E\x2E"
"\x2B\x39\xF2\x1F\x08\x87\x5E\x6A\x5E\xD4\xA6\x73\x20\xC9\x96\x54\x95\xCA\x7C\x29\x33\x60"
"\x82\x83\x2D\xB6\x0E\xD4\x1A\x10\xC4\xBE\x9E\x1E\xB5\x31\x14\x08\xA2\xDF\x3F\x16\x31\xC5"
"\x02\x20\x6B\x36\xEA\xBF\x52\x6A\xD9\xBA\x3D\xB9\xF0\xCB\x49\xAC\x02\x41\xFE\x1B\x32\x10"
"\x1F\x4B\x8F\x70\xC1\x7A\xA4\x52\x31\xA6\xA8\xC0\x83\xFF\x31\xF0\x38\x80\x9F\xD1\x80\x94"
"\xFE\xFD\xF1\x28\xC7\xEF\x5F\x7F\x03\x1C\x7E\xC0\xEC\x30\x00\xBF\x6E\x23\x39\x07\xAC\xBA"
"\xF5\xB0\x47\x9E\x55\x10\x0B\x3C\x1C\x40\x9F\x3F\x03\x0E\x4B\x2C\x1E\x18\xDA\xB4\x61\x98"
"\x02\x67\xD7\x76\x08\xE2\x1F\x37\xD0\xFF\x78\x53\x5D\xD8\xCF\xEA\x47\xD4\x80\xA7\x0E\x59"
"\x88\x3A\x20\x6B\xD8\x01\x27\xFB\x8B\xEC\x91\xC1\xBF\x94\x01\x57\xC6\x92\xF9\x34\x3C\xF7"
"\xE5\x1E\x55\xF7\x65\x0E\x60\x40\x3F\xE4\x01\x35\x88\x4E\x7F\x5A\xFD\x43\xDF\x32\xE5\xFC"
"\xFB\x60\x1D\x00\xEB\x38\x4C\x1D\x22\xF2\x9F\x43\xBD\x54\xE6\x92\x7D\x01\xF9\xD7\xD4\x40"
"\x40\x29\x38\x10\x81\x47\x51\xD5\x0F\x18\xF1\xA5\xE6\x5C\x3F\x80\xFC\xF3\x1A\x00\xF9\xB8"
"\xC3\xC8\x74\xFC\x1D\xB8\x1F\x00\x8D\x85\xE8\x50\x25\xFF\x30\x11\x50\x3E\xD9\x6D\x95\xCF"
"\x3F\x11\x34\xA6\xE1\x69\x02\x45\x70\x40\x40\x5E\x4C\x87\x0E\x67\x5D\x40\x25\x9F\x8F\x6F"
"\x95\xE8\xD0\x0C\x73\xC1\xC6\xDA\x24\x02\x0A\xB4\xCC\x54\x03\xF1\x58\x42\x6F\x0E\x1D\xD9"
"\x60\x40\x4A\x7A\xD4\xE4\x49\xFD\x08\x90\x4D\x81\xCD\xA9\x77\x46\x3F\x07\xE4\x23\x12\x4F"
"\x43\x50\xF1\x61\x75\xB0\xC1\xB7\xA4\x47\x38\xF9\x03\x80\x19\x44\xC6\xB4\xD2\x42\xDE\x9D"
"\xF7\x4F\x05\x00\x84\xC6\x4E\x40\xAE\x4C\x07\x94\x06\x03\xED\xB2\x65\x67\xF4\xA1\x07\x24"
"\x58\xEC\xFD\x03\x06\x7D\x4F\x0E\x74\xE2\x9F\x03\x29\x33\x1D\x48\x52\xC6\x16\xA2\xA3\x0E"
"\x45\x4A\x28\x92\x15\xE2\xC9\xD1\xA0\x7D\x01\xC0\x13\x20\xEC\x88\x64\x86\x99\x41\x22\x46"
"\x93\x4D\x60\xE1\xF7\x8D\x3F\x9E\x21\x86\x56\x8F\x9A\xFD\x87\x46\x61\x49\xD1\x37\x64\x1A"
"\x03\xE9\x43\xE7\x55\x84\x7E\x58\x29\x62\x3B\x50\xF4\x4D\x26\x3C\xAC\x76\xE0\x70\x6A\x02"
"\x90\xE9\x52\x94\x52\xA7\x6C\x48\x17\x39\xAB\x17\xA5\xAA\x29\xF3\x0F\x05\x15\x76\xA9\x94"
"\x3E\x0B\xBD\x56\x68\x58\xA8\x6E\xA5\x98\x45\x88\xB9\x88\x22\x6C\xAD\x2C\xA4\x9D\x47\x2C"
"\xFC\xF3\x67\xB6\xF9\x35\xAB\xE7\x4D\xFD\x9C\x0B\x9F\x4F\xF3\xF1\x9B\x15\xB9\x03\x6D\x10"
"\x0C\x45\x21\x7D\x66\xD8\x1F\x01\x10\xD0\x80\x1A\x0B\xA5\xE8\xD0\x2B\xFF\xD8\x66\xAD\xC1"
"\x08\x2B\xCC\xF0\x45\x0F\x47\x9C\xDC\xB5\x4A\x51\x90\x9B\xB8\x5C\x02\xB0\xC4\xB4\x20\x1B"
"\x28\xE2\x7A\x45\xB9\x0B\x56\xC7\xBF\xC0\x06\x01\x45\xE1\xC5\x04\xDF\x6B\xFC\xD4\xA1\xCF"
"\x1C\x1B\x16\x77\x16\x56\xB6\xE6\xF5\xE3\xA7\xFE\x8A\x48\x80\x09\xDD\xFC\xD3\x4E\x4C\x65"
"\x0D\xFC\x4F\x90\x4A\x6A\x0A\x2D\x00\x48\x0F\xBC\xB4\x79\x12\xFF\x1A\x50\x63\x4E\x7E\x8C"
"\x1E\x08\x0D\x70\xCB\xE8\x7E\x24\x8A\xB8\xCF\xAE\x88\x65\x6B\xC5\x36\xF4\x7A\x44\x1A\x1A"
"\x11\xBC\xD8\x4A\x71\x67\x02\xC0\x73\x40\xDA\x7C\x48\x92\x47\x38\x2E\x2A\xAC\xC9\x00\xD0"
"\xF4\xE8\x43\xEE\xCC\x12\xCC\x2C\x07\xC5\x54\xAD\xC6\x86\x01\x2E\x78\x1D\x11\x14\x2E\xE9"
"\xA6\x24\x1F\xE0\x8F\xD1\xA6\x76\x0B\xEC\xA2\x70\xC7\x14\x1A\xB3\x60\x61\x2E\x10\x6E\x46"
"\xC7\x70\x5C\x4C\xA4\xA5\x51\x85\x48\x53\xF4\xD3\xC0\x3E\xA8\xB2\xED\x53\xE4\x2C\xD7\xC9"
"\xB8\x43\xA2\x04\x1B\xED\xDE\x00\xAC\xD4\xF0\x43\x63\x03\x70\xA2\x8C\x90\x62\x65\x15\x48"
"\xFA\xB9\xC4\xFB\x56\x86\xFF\xF5\x78\xA1\x01\xCE\x50\x66\xE5\xFD\xD6\x27\xA2\xAA\x26\x77"
"\x1E\x10\x4D\x79\x65\xF3\xCF\x0E\x31\x61\xF3\x0C\x30\x00\x75\x5C\xC7\x3F\x4E\xC6\x94\xCE"
"\xE9\x8E\x79\x64\x2B\xF9\x03\x61\x53\x3B\x88\xB7\x77\xEC\x87\xD7\x2E\xFB\x2A\x90\xAD\x82"
"\x05\xD0\x8F\x6F\xEA\x9C\x0A\xDB\xFD\x17\xE1\xC3\x39\x7E\x21\x4D\x3B\x00\x21\x40\xCB\x79"
"\x0D\x31\x8A\x81\x9D\x88\xAA\x17\x9D\x9C\x75\x6A\x73\x00\xE8\xC5\x5E\x02\xF2\xAD\x78\xC9"
"\xC4\x56\x2D\x60\x46\xB0\xA2\xD3\x91\x81\x44\x4E\x82\x7A\x33\x19\x50\x7A\x36\x2E\x48\xB5"
"\xE5\x22\x1D\x33\xDA\xFB\xE0\x75\xAE\xE8\x21\x66\x85\x0E\x71\x21\xF4\x3E\x22\x9D\xE7\xF9"
"\x6C\x84\x60\x21\x8A\xA0\xAC\x53\xBD\x6F\xF9\x0D\x00\xBB\xF8\x07\x5F\x96\x62\xA7\x80\x0C"
"\xB1\x76\x09\x30\x88\xA7\x64\x52\x16\x30\x08\x44\x01\xDC\x20\x22\xE7\xFE\x15\x10\xF5\x34"
"\xE1\x20\x19\x3B\xA1\x40\x56\x90\x1B\x87\x64\xAF\x14\x04\x10\x5B\x04\xF0\x62\x28\xD8\x64"
"\xAF\x21\x01\xD9\x80\x19\x73\xF8\xAD\x03\xD6\x50\x87\x5B\x01\xCC\x37\x24\x62\xC7\x77\x4C"
"\xED\x8C\x31\xC1\x0F\x03\x03\xE2\x32\x58\x09\x69\x64\x00\x50\x0C\x0B\x65\x42\x41\x0C\xCA"
"\x84\x58\x07\x31\x85\xC0\x8A\x86\x98\xEA\xC1\xE7\x1D\xD0\x98\x85\x29\xDC\xB1\x10\xC1\x24"
"\x10\x00\x84\x91\xDD\x40\x0A\x92\xB4\xB4\xE8\x11\x2C\x11\xC8\x48\x42\x16\x62\x97\x38\xF2"
"\x04\x8E\x39\x0C\xC8\x3E\xFC\x71\x47\x89\xE4\x31\x79\x9F\x54\x4E\xDE\x58\x69\xBC\x8B\xD0"
"\x26\x65\x01\x39\x51\x11\x07\xC2\x36\x06\xB2\x0D\x66\x32\x09\xCD\x0B\x63\x52\x01\x60\x50"
"\xC4\x1F\x50\x44\x8C\xD8\x54\xC6\x96\x81\x79\x63\x03\x4E\xBC\xA5\xC8\x6E\x19\x90\x03\x0C"
"\x6E\x21\xEF\x68\xC2\x56\x9A\x69\x1D\x6C\x26\x93\x9B\xE7\x99\x8E\xF9\x70\x58\xB5\x8B\x40"
"\x6D\x60\xB1\xBC\x1D\x3B\xDB\xE9\x4E\x99\x08\xE0\x00\x04\x78\x67\x4C\x04\x40\x80\x7B\xD2"
"\xD3\x23\x14\xB4\x26\x3D\xE3\x69\xA3\x7C\x02\x34\xA0\x02\x1D\x28\x41\x0B\xFA\xCE\x8E\xFD"
"\xE3\x1B\xA6\x78\x86\x41\x1B\xEA\xD0\x87\x42\x34\xA2\x12\xBD\x88\x09\xDA\x71\xC3\x89\x62"
"\x34\xA3\x1A\xDD\x28\x47\x05\x42\x80\x03\x08\xA0\xA3\x22\x1D\x29\x49\x4B\x6A\xD2\x93\xA2"
"\x34\xA5\x2A\x5D\x29\x4B\x5B\xEA\xD2\x97\xC2\x34\xA6\x32\x9D\x29\x4D\x6B\x6A\xD3\x9B\xE2"
"\x34\xA7\x3A\xDD\x29\x4F\x7B\xEA\xD3\x9F\x02\x35\xA8\x42\x1D\xEA\x46\xED\x49\x80\x90\xCE"
"\xF4\x9E\x47\x25\x6A\x41\x3F\x46\xC1\x55\xA1\x73\x96\xEB\x64\xE5\x27\xF9\xF9\x31\x92\x5D"
"\x84\x07\xDF\x48\x1A\x3F\x9F\x26\xD5\x45\xA5\x93\x22\xEB\x04\x8C\x74\x82\x21\xCE\xAD\x58"
"\xE1\x99\x2F\x33\xA2\x74\xA0\x71\xD6\xAD\x44\xA7\x90\x40\x15\x97\x25\x9A\xE8\x11\x7D\xBC"
"\x72\x22\x1F\xC2\xEB\x44\x22\xF2\x0F\xAB\x4A\xEA\x43\x67\xFB\x87\x3B\x4C\x31\x0B\x68\xF8"
"\xA3\x97\x0F\x89\xC8\x2B\xA7\xCA\x1F\xC6\xDE\x71\xAC\x90\x65\x62\x3F\x20\x74\x91\x00\xAC"
"\x63\x21\x0A\x3D\xAC\x3F\x20\x98\x3C\x3B\xAE\x15\x2C\x17\x54\xAC\x4F\x0B\x65\x2B\xF7\xC0"
"\xE6\xAA\x5C\x8C\xC9\x55\xAF\xDA\x18\x77\x48\xB3\x48\x56\x5C\xED\x1C\xFD\x12\x81\x66\x08"
"\xD2\x21\xAE\xF8\xC7\x33\xB8\x06\x96\x42\x55\xA0\x2C\xBA\xF3\x88\xC8\x66\xC1\x0F\x11\xE6"
"\x14\x3D\x2B\x41\x2E\x6B\xDD\xD7\xB9\xD6\xA2\x86\x6D\xCC\x85\xA1\x6C\x53\x8B\x1A\x45\xC0"
"\x29\x20\x1D\x9B\x6E\xD7\x04\xC2\x80\xCE\x6E\x25\x1D\x6D\xF2\xCF\xF6\xE6\xAA\x94\x0A\x1C"
"\xC4\xB2\xFB\x51\xAD\x1C\x57\xF9\xB1\xFF\xED\xA7\x7A\xB4\x95\xC9\x55\xF5\xC1\x2C\xF8\x22"
"\xE6\x63\xFB\xF0\xAC\x43\x32\xE0\xA9\xC8\x15\x93\xA7\x76\x2B\x00\x44\x9C\x96\x5F\xE7\xFE"
"\x0B\xBA\xEA\x41\xDE\x23\x67\xDB\xDE\x71\xD2\xE7\x52\xC0\xAB\xA5\xCF\x00\x2C\x93\x00\x11"
"\x2F\x52\x5B\x14\xF0\x97\xEE\x81\xBE\xBD\xB1\xD7\x76\xF4\x6D\xD3\x45\xBA\x80\x1A\x06\x73"
"\xAE\xAB\x86\x51\xF1\x39\x07\xF2\x14\x4A\xA1\xB8\xB9\x03\x09\x00\x6E\x49\x3B\x90\x30\x09"
"\x44\x64\xBF\x13\xF0\xC0\x0C\x7C\x60\x0A\x3F\xF7\x1F\x80\xA8\x03\x91\x89\xAC\x86\x60\x9C"
"\xD8\x2F\x3C\x30\x45\x26\x32\x61\x8A\xB7\x3C\x43\x0D\x45\xAE\x03\x0D\xB2\xF2\x0D\x46\x34"
"\xD9\xC9\x98\x74\xEF\xE3\x62\x5C\x27\x26\x67\x62\x16\x06\x09\xB0\x40\xE0\x23\x4C\xEE\x90"
"\x39\xB9\xEA\x64\x67\x88\xDF\x47\xDF\x59\x5E\x74\xC2\x02\xD9\x43\x32\xC1\xFA\x49\x45\x38"
"\xF1\xCE\x10\xBE\x08\x28\xBA\xEC\xBE\xE4\x7C\xD2\x1F\x06\xC6\xCF\x96\x35\x09\xC8\x9E\x82"
"\xC4\x34\xEC\x12\xA6\x88\xDA\xFC\x5C\x7F\xB4\x60\x03\x92\x96\x74\x05\x2E\x91\x64\xAB\x24"
"\xEC\x00\x8A\x30\x13\x26\x2A\x30\xE9\xAD\x65\x65\x11\x07\x68\x00\xA9\x1B\x30\xCF\x38\x02"
"\xE0\xC5\x98\xFA\x73\x67\x36\x50\xEA\x0A\x4C\xE8\x1F\xBE\x75\x48\x52\x0A\x16\x90\x00\xF9"
"\xD8\xA6\x24\xB1\x4D\x00\xCA\x52\x68\xEB\x38\x3A\xC1\x24\x76\x08\xE5\x68\x1D\xD8\xCB\xB1"
"\x78\x77\xD6\x6D\x9A\xB1\x29\x7C\x95\x8B\x10\x8B\xD1\x02\x29\x4B\x1D\xC0\x60\x6D\x2B\x64"
"\x36\xD9\x34\x45\x0F\x6E\x82\x23\xA2\x3E\x20\xB8\xB6\x08\x1C\x08\xED\x36\x8C\xD5\xF9\x26"
"\x6B\xD9\xD4\xF2\xC7\xAC\xAD\xE6\xED\x1F\xBB\x84\xBF\x00\x60\x1B\x9C\xA5\xAD\xD3\x42\x01"
"\x25\xCD\x32\xD1\x12\x90\x85\x3D\x6E\x81\xF0\xA8\x44\xAA\x95\xEF\x83\xE3\xC8\x9E\xFA\x0D"
"\x52\x69\xC0\x46\xCD\x00\xBE\xE8\xB9\xBA\x6C\xC0\x04\x12\x07\x01\x0D\xDA\xF3\x53\x71\xB9"
"\xC8\xC6\x01\xE1\xA4\x40\x5C\x26\xD7\x4C\xBA\x17\x35\x21\x44\xB5\x9B\xAB\x0B\xEF\x81\x2C"
"\xE3\x1F\xF2\x73\x48\x08\x9D\xDD\xDD\x8E\x83\x57\x2F\x50\x15\x08\xBF\x4B\xCB\x19\xF8\xBC"
"\xE8\x22\x5D\xF1\x06\x27\x23\x50\x16\x8D\xA3\xBB\xC5\x3F\x1B\x08\xBB\xBC\xD1\x6E\x34\xF4"
"\xCF\x87\x0E\x2E\x54\x04\x32\x2B\xC1\x50\xFC\x83\xE7\x27\x27\xC8\xB7\x86\xBB\x4B\x44\x02"
"\x8A\x45\x88\x46\x0D\x7E\x10\x7E\x80\x0B\xBE\x63\x16\x5B\x75\xB8\x43\xF8\x7A\x90\x77\xF8"
"\x03\xB0\xFF\x16\xC8\x00\x2E\x08\x8D\x30\x6F\x35\xE9\xB0\x69\x65\x5E\x25\x2B\x91\x3A\x4A"
"\x47\xBD\x33\x4E\xD7\x66\x83\x81\xC7\xA5\xDC\x91\x22\x64\xC6\xD5\xB3\x6C\x4E\x1F\xB4\xEC"
"\x38\x20\x4D\x18\x3B\x61\xAD\xD0\xCD\x4F\x06\x49\xC1\x31\x01\x83\x5A\x23\x22\x8B\xA3\x69"
"\x7B\xDA\x5F\xD5\x4B\x32\xDD\x8A\x98\x26\x58\x52\x9D\x5D\x4A\x9A\x3F\xBE\xB1\x4C\xA4\x4C"
"\x15\x24\x26\x60\xAA\x75\x02\x20\x00\xA4\x8E\xF4\xA3\x04\x70\x39\x4A\x65\x0F\x7B\xD6\xE3"
"\x3E\xF7\xBA\xDF\x3D\xEF\x7B\xEF\xFB\xDF\x03\x3F\xF8\xC2\x1F\x3E\xF1\x8B\x6F\xFC\xE3\x23"
"\x3F\xF9\xCA\x5F\x3E\xF3\x9B\xEF\xFC\xE7\x43\x3F\xFA\xD2\x9F\x3E\xF5\xAB\x6F\xFD\xEB\x63"
"\x3F\xFB\xDA\xDF\x3E\xF7\xBB\xEF\xFD\xEF\x83\x3F\xFC\xE2\x1F\x3F\xF9\xCB\x6F\xFE\xF3\xA3"
"\x3F\xFD\xEA\x5F\x3F\xFB\xDB\xEF\xFE\xF7\xC3\xFF\xF8";
#undef DD_ALIGNED_BUFFER
static const FontCharSet s_fontMonoid18CharSet = {
/* bitmap = */ s_fontMonoid18Bitmap,
/* bitmapWidth = */ 256,
/* bitmapHeight = */ 256,
/* bitmapColorChannels = */ 1,
/* bitmapDecompressSize = */ 65536,
/* charBaseHeight = */ 20,
/* charWidth = */ 17,
/* charHeight = */ 30,
/* charCount = */ 96,
{
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 150 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 60 }, { 17, 60 }, { 68, 0 },
{ 153, 0 }, { 119, 0 }, { 34, 0 }, { 204, 30 },
{ 119, 30 }, { 102, 30 }, { 0, 0 }, { 102, 0 },
{ 170, 30 }, { 136, 0 }, { 187, 30 }, { 221, 0 },
{ 34, 60 }, { 187, 60 }, { 170, 60 }, { 153, 60 },
{ 136, 60 }, { 119, 60 }, { 102, 60 }, { 85, 60 },
{ 68, 60 }, { 51, 60 }, { 136, 30 }, { 153, 30 },
{ 17, 30 }, { 85, 0 }, { 0, 30 }, { 221, 30 },
{ 204, 0 }, { 170, 210 }, { 153, 210 }, { 136, 210 },
{ 119, 210 }, { 102, 210 }, { 85, 210 }, { 68, 210 },
{ 51, 210 }, { 34, 210 }, { 17, 210 }, { 0, 210 },
{ 238, 180 }, { 221, 180 }, { 204, 180 }, { 187, 180 },
{ 170, 180 }, { 153, 180 }, { 136, 180 }, { 119, 180 },
{ 102, 180 }, { 85, 180 }, { 68, 180 }, { 51, 180 },
{ 34, 180 }, { 17, 180 }, { 0, 180 }, { 85, 30 },
{ 187, 0 }, { 68, 30 }, { 170, 0 }, { 51, 0 },
{ 238, 30 }, { 119, 120 }, { 102, 120 }, { 85, 120 },
{ 68, 120 }, { 51, 120 }, { 34, 120 }, { 17, 120 },
{ 0, 120 }, { 238, 90 }, { 221, 90 }, { 204, 90 },
{ 187, 90 }, { 170, 90 }, { 153, 90 }, { 136, 90 },
{ 119, 90 }, { 102, 90 }, { 85, 90 }, { 68, 90 },
{ 51, 90 }, { 34, 90 }, { 17, 90 }, { 0, 90 },
{ 238, 60 }, { 221, 60 }, { 204, 60 }, { 51, 30 },
{ 238, 0 }, { 34, 30 }, { 17, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }
}
};
// ========================================================
// LZW decompression helpers for the font bitmap:
// ========================================================
// These must match the font-tool encoder!
static const int LzwNil = -1;
static const int LzwMaxDictBits = 12;
static const int LzwStartBits = 9;
static const int LzwFirstCode = (1 << (LzwStartBits - 1)); // 256
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;
};
int size;
Entry entries[LzwMaxDictEntries];
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.
LzwBitStreamReader(const std::uint8_t * bitStream, int byteCount, int bitCount);
bool readNextBit(int & outBit);
int readBits(int bitCount);
};
// ========================================================
// LzwDictionary:
// ========================================================
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;
}
}
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;
}
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;
}
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;
}
// ========================================================
// LzwBitStreamReader:
// ========================================================
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)
{ }
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;
}
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;
}
// ========================================================
// lzwDecompress() and helpers:
// ========================================================
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;
}
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;
}
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;
}
// ========================================================
// Built-in font glyph bitmap decompression:
// ========================================================
// If you decide to change the font, these are the only things that
// need to be updated. The s_font* variables are never referenced
// directly in the code, these functions are used instead.
static inline const std::uint8_t * getRawFontBitmapData() { return s_fontMonoid18Bitmap; }
static inline const FontCharSet & getFontCharSet() { return s_fontMonoid18CharSet; }
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;
}
// ========================================================
// Internal Debug Draw queues and helper types/functions:
// ========================================================
struct DebugString
{
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;
};
struct DebugLine
{
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)
{ }
};
// ========================================================
// Library context mode selection:
// ========================================================
#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!"
#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)
#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
#else // Debug Draw context selection
//
// Global static context (single threaded operation)
//
static InternalContext * s_globalContext = nullptr;
#define DD_CONTEXT s_globalContext
#endif // Debug Draw context selection
// ========================================================
#if DEBUG_DRAW_USE_STD_MATH
static inline float floatAbs(float x) { return fabsf(x); }
static inline float floatSin(float radians) { return sinf(radians); }
static inline float floatCos(float radians) { return cosf(radians); }
static inline float floatInvSqrt(float x) { return (1.0f / sqrtf(x)); }
#else // !DEBUG_DRAW_USE_STD_MATH
// ========================================================
// Fast approximations of math functions used by Debug Draw
// ========================================================
union Float2UInt
{
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);
}
static inline float floatAbs(float x)
{
// 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;
}
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 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);
}
#endif // DEBUG_DRAW_USE_STD_MATH
// ========================================================
// ddVec3 helpers:
// ========================================================
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;
}
static inline void vecCopy(ddVec3_Out dest, ddVec3_In src)
{
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];
}
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];
}
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;
}
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;
}
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;
}
}
// ========================================================
// ddMat4x4 helpers:
// ========================================================
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];
}
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];
}
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;
}
// ========================================================
// Misc local functions for draw queue management:
// ========================================================
enum DrawMode
{
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;
}
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;
}
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];
}
static void pushGlyphVerts(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) const DrawVertex verts[4])
{
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);
}
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;
}
}
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;
}
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);
}
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);
}
}
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);
}
}
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;
}
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);
}
// ========================================================
// Public Debug Draw interface:
// ========================================================
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;
}
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
}
}
bool isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx))
{
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;
}
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;
}
// 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();
// 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();
// 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;
}
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);
}
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);
}
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);
}
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);
}
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
}
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);
}
}
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);
}
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)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 left, up;
ddVec3 point, lastPoint;
vecOrthogonalBasis(left, up, planeNormal);
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;
ddVec3 vs, vc;
vecScale(vs, left, floatSin(radians));
vecScale(vc, up, floatCos(radians));
vecAdd(point, center, vs);
vecAdd(point, point, vc);
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);
}
}
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);
}
}
}
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);
}
}
}
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);
}
}
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);
}
void aabb(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In mins, ddVec3_In maxs,
ddVec3_In color, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 bb[2];
ddVec3 points[8];
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];
}
// 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);
}
void vertexNormal(DD_EXPLICIT_CONTEXT_ONLY(ContextHandle ctx,) ddVec3_In origin, ddVec3_In normal,
const float length, const int durationMillis, const bool depthEnabled)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
ddVec3 normalVec;
ddVec3 normalColor;
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];
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)
{
if (!isInitialized(DD_EXPLICIT_CONTEXT_ONLY(ctx)))
{
return;
}
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
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];
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);
}
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);
}
}
// ========================================================
// RenderInterface stubs:
// ========================================================
RenderInterface::~RenderInterface() { }
void RenderInterface::beginDraw() { }
void RenderInterface::endDraw() { }
void RenderInterface::drawPointList(const DrawVertex *, int, bool) { }
void RenderInterface::drawLineList(const DrawVertex *, int, bool) { }
void RenderInterface::drawGlyphList(const DrawVertex *, int, GlyphTextureHandle) { }
void RenderInterface::destroyGlyphTexture(GlyphTextureHandle) { }
GlyphTextureHandle RenderInterface::createGlyphTexture(int, int, const void *) { return nullptr; }
} // namespace dd
#undef DD_CONTEXT
#undef DD_MALLOC
#undef DD_MFREE
// ================ End of implementation =================
#endif // DEBUG_DRAW_IMPLEMENTATION
// ================ End of implementation =================
\ No newline at end of file
/*
* 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 11/06/22.
//
#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>
#include <cstdio>
constexpr std::string_view CERR_FMT{"[{}] {}\n"};
namespace fggl::debug {
using FmtType = const std::string_view;
/**
* Logging levels
*/
enum class Level {
critical = 0,
error = 1,
warning = 2,
info = 3,
debug = 4,
trace = 5
};
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";
}
}
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_format_args(format, args...));
}
#define info_va(format, ...) \
logf(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__)
template<typename ...T>
void log(Level level, FmtType fmt, T &&...args) {
auto fmtStr = fmt::format(fmt::runtime(fmt), args...);
fmt::print(CERR_FMT, level_to_string(level), fmtStr);
}
// inlined, pre-set level versions of the log function above
template<typename ...T>
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>
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>
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>
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>
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_FMT_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/>.
*/
//
// Created by webpigeon on 08/06/22.
//
#ifndef FGGL_DEBUG_IMPL_LOGGING_SPDLOG_HPP
#define FGGL_DEBUG_IMPL_LOGGING_SPDLOG_HPP
#include <string>
#include <iostream>
#include <memory>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
namespace fggl::debug {
using FmtType = const std::string_view;
/**
* 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
};
template<typename ...T>
void error(const FmtType &fmt, T &&...args) {
spdlog::error(fmt, args...);
}
template<typename ...T>
void warning(const FmtType &fmt, T &&...args) {
spdlog::warn(fmt, args...);
}
template<typename ...T>
void info(const FmtType &fmt, T &&...args) {
spdlog::info(fmt, args...);
}
template<typename ...T>
void debug(const FmtType &fmt, T &&...args) {
spdlog::debug(fmt, args...);
}
template<typename ...T>
void trace(const FmtType &fmt, T &&...args) {
spdlog::trace(fmt, args...);
}
template<typename ...T>
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) {
spdlog::log(level, fmt, args...);
}
class Logger {
public:
Logger();
template<typename ...Args>
void log(Level level, const FmtType &fmt, Args &&...args) {
spdlog::log(level, fmt, args...);
}
};
}
#endif //FGGL_DEBUG_IMPL_LOGGING_SPDLOG_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/>.
*/
//
// Created by webpigeon on 11/06/22.
//
#ifndef FGGL_DEBUG_LOGGING_HPP
#define FGGL_DEBUG_LOGGING_HPP
#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);
template<typename ...T>
void warning(FmtType fmt, T &&...args);
template<typename ...T>
void info(FmtType fmt, T &&...args);
template<typename ...T>
void debug(FmtType fmt, T &&...args);
template<typename ...T>
void trace(FmtType fmt, T &&...args);
template<typename ...T>
void log(FmtType fmt, T &&...args);
template<typename ...T>
void log(Level level, FmtType fmt, T &&...args);
}
#include "fggl/debug/impl/logging_fmt.hpp"
#endif //FGGL_DEBUG_LOGGING_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/>.
*/
//
// Created by webpigeon on 27/06/22.
// see https://youtrack.jetbrains.com/issue/CPP-5059
#ifndef FGGL_DEBUG_PRAGMAS_HPP
#define FGGL_DEBUG_PRAGMAS_HPP
#if defined(__clang__)
#define PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push")
#elif defined(__GNUC__)
#define PRAGMA_DIAGNOSTIC_PUSH _Pragma("gcc diagnostic push")
#else
#define PRAGMA_DIAGNOSTIC_PUSH
#endif
#if defined(__clang__)
#define PRAGMA_DIAGNOSTIC_POP _Pragma("clang diagnostic pop")
#elif defined(__GNUC__)
#define PRAGMA_DIAGNOSTIC_POP _Pragma("gcc diagnostic pop")
#else
#define PRAGMA_DIAGNOSTIC_POP
#endif
#if defined(__JETBRAINS_IDE__)
#define PRAGMA_CLION_IGNORED(x) _Pragma("ide diagnostic ignored \"" x "\"")
#else
#define PRAGMA_CLION_IGNORED
#endif
#endif //FGGL_DEBUG_PRAGMAS_HPP