Add software

This commit is contained in:
RocketGod
2022-09-22 09:26:57 -07:00
parent fee0ab05fd
commit 957ea3d712
4511 changed files with 1943182 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,637 @@
#pragma once
/*
* DataElement/DataNode/DataTree -- structured serialization/deserialization system
* designed for the CoolMule project :)
*
Copyright (C) 2003 by Charles J. Cliffe
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <vector>
#include <map>
#include <set>
#include <string>
#include <sstream>
#include <stack>
#include <iostream>
#include "tinyxml.h"
using namespace std;
/* map comparison function */
struct string_less
{
bool operator()(const std::string& a,const std::string& b) const
{
return a.compare(b) < 0;
}
};
/* Data Exceptions */
class DataException : public exception
{
private:
string reason;
public:
explicit DataException(const char *why) : reason(why) {}
const char* what() const noexcept override { return reason.c_str(); }
explicit operator string() { return reason; }
};
class DataTypeMismatchException : public DataException
{
public:
explicit DataTypeMismatchException(const char *why) : DataException(why) { }
};
class DataInvalidChildException : public DataException
{
public:
explicit DataInvalidChildException(const char *why) : DataException(why) { }
};
class DataElement
{
public :
enum class Type {
DATA_NULL,
DATA_CHAR,
DATA_UCHAR,
DATA_INT,
DATA_UINT,
DATA_LONG,
DATA_ULONG,
DATA_LONGLONG,
DATA_FLOAT,
DATA_DOUBLE,
DATA_STRING,
DATA_STR_VECTOR,
DATA_CHAR_VECTOR,
DATA_UCHAR_VECTOR,
DATA_INT_VECTOR,
DATA_UINT_VECTOR,
DATA_LONG_VECTOR,
DATA_ULONG_VECTOR,
DATA_LONGLONG_VECTOR,
DATA_FLOAT_VECTOR,
DATA_DOUBLE_VECTOR,
DATA_VOID,
DATA_WSTRING
};
typedef vector<unsigned char> DataElementBuffer;
typedef vector< DataElementBuffer > DataElementBufferVector;
private:
Type data_type;
// raw buffer holding data_type element in bytes form.
DataElementBuffer data_val;
//keep the vector of types in a spearate vector of DataElementBuffer.
DataElementBufferVector data_val_vector;
//specializations to extract type: (need to be declared/done OUTSIDE of class scope else "Error: explicit specialization is not allowed in the current scope")
//this is apparently fixed in C++17...
// so we need to workaround it with a partial specialization using a fake Dummy parameter.
//if the exact right determineScalarDataType specialization was not used, throw exception at runtime.
template<typename U, typename Dummy = int >
Type determineScalarDataType(const U& /* type_in */) { throw DataTypeMismatchException("determineScalarDataType(U) usage with unsupported type !"); }
template< typename Dummy = int >
Type determineScalarDataType(const char& /* type_in */) { return DataElement::Type::DATA_CHAR; }
template< typename Dummy = int >
Type determineScalarDataType(const unsigned char& /* type_in */) { return DataElement::Type::DATA_UCHAR; }
template< typename Dummy = int >
Type determineScalarDataType(const int& /* type_in */) { return DataElement::Type::DATA_INT; }
template< typename Dummy = int >
Type determineScalarDataType(const unsigned int& /* type_in */) { return DataElement::Type::DATA_UINT; }
template< typename Dummy = int >
Type determineScalarDataType(const long& /* type_in */) { return DataElement::Type::DATA_LONG; }
template< typename Dummy = int >
Type determineScalarDataType(const unsigned long& /* type_in */) { return DataElement::Type::DATA_ULONG; }
template< typename Dummy = int >
Type determineScalarDataType(const long long& /* type_in */) { return DataElement::Type::DATA_LONGLONG; }
template< typename Dummy = int >
Type determineScalarDataType(const float& /* type_in */) { return DataElement::Type::DATA_FLOAT; }
template< typename Dummy = int >
Type determineScalarDataType(const double& /* type_in */) { return DataElement::Type::DATA_DOUBLE; }
//vector versions:
//if the exact right determineVectorDataType specialization was not used, throw exception at runtime.
template<typename V, typename Dummy = int >
Type determineVectorDataType(const vector<V>& /* type_in */) { throw DataTypeMismatchException("determineVectorDataType(V) usage with unsupported type !"); }
template< typename Dummy = int >
Type determineVectorDataType(const vector<char>& /* type_in */) { return DataElement::Type::DATA_CHAR_VECTOR; }
template< typename Dummy = int >
Type determineVectorDataType(const vector<unsigned char>& /* type_in */) { return DataElement::Type::DATA_UCHAR_VECTOR; }
template< typename Dummy = int >
Type determineVectorDataType(const vector<int>& /* type_in */) { return DataElement::Type::DATA_INT_VECTOR; }
template< typename Dummy = int >
Type determineVectorDataType(const vector<unsigned int>& /* type_in */) { return DataElement::Type::DATA_UINT_VECTOR; }
template< typename Dummy = int >
Type determineVectorDataType(const vector<long>& /* type_in */) { return DataElement::Type::DATA_LONG_VECTOR; }
template< typename Dummy = int >
Type determineVectorDataType(const vector<unsigned long>& /* type_in */) { return DataElement::Type::DATA_ULONG_VECTOR; }
template< typename Dummy = int >
Type determineVectorDataType(const vector<long long>& /* type_in */) { return DataElement::Type::DATA_LONGLONG_VECTOR; }
template< typename Dummy = int >
Type determineVectorDataType(const vector<float>& /* type_in */) { return DataElement::Type::DATA_FLOAT_VECTOR; }
template< typename Dummy = int >
Type determineVectorDataType(const vector<double>& /* type_in */) { return DataElement::Type::DATA_DOUBLE_VECTOR; }
public:
DataElement();
DataElement(DataElement &cloneFrom);
~DataElement();
Type getDataType();
char *getDataPointer();
size_t getDataSize();
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//set overloads :
// general templates : for scalars
template<typename T, typename Dummy = int >
void set(const T& scalar_in) {
data_type = determineScalarDataType<T>(scalar_in);
int unit_size = sizeof(T);
//copy in a temporary variable (needed ?)
T local_copy = scalar_in;
auto* local_copy_ptr = reinterpret_cast<unsigned char*>(&local_copy);
data_val.assign(local_copy_ptr, local_copy_ptr + unit_size);
}
// general templates : for vector of scalars
template<typename T, typename Dummy = int >
void set(const vector<T>& scalar_vector_in) {
data_type = determineVectorDataType<T>(scalar_vector_in);
int unit_size = sizeof(T);
data_val_vector.clear();
DataElementBuffer single_buffer;
for (auto single_element : scalar_vector_in) {
//copy in a temporary variable (needed ?)
T local_copy = single_element;
auto* local_copy_ptr = reinterpret_cast<unsigned char*>(&local_copy);
single_buffer.assign(local_copy_ptr, local_copy_ptr + unit_size);
data_val_vector.push_back(single_buffer);
}
}
//template specialization : for string
template< typename Dummy = int >
void set(const string& str_in) {
data_type = DataElement::Type::DATA_STRING;
data_val.assign(str_in.begin(), str_in.end());
}
//template specialization : for wstring
template< typename Dummy = int >
void set(const wstring& wstr_in) {
data_type = DataElement::Type::DATA_WSTRING;
//wchar_t is tricky, the terminating zero is actually a (wchar_t)0 !
//wchar_t is typically 16 bits on windows, and 32 bits on Unix, so use sizeof(wchar_t) everywhere.
size_t maxLenBytes = (wstr_in.length() + 1) * sizeof(wchar_t);
//be paranoid, zero the buffer
char *tmp_str = (char *)::calloc(maxLenBytes, sizeof(char));
//if something awful happens, the last sizeof(wchar_t) is at least zero...
::wcstombs(tmp_str, wstr_in.c_str(), maxLenBytes - sizeof(wchar_t));
data_val.assign(tmp_str, tmp_str + maxLenBytes - sizeof(wchar_t));
::free(tmp_str);
}
//template specialization : for vector<string>
template< typename Dummy = int >
void set(const vector<string>& vector_str_in) {
data_type = DataElement::Type::DATA_STR_VECTOR;
data_val_vector.clear();
DataElementBuffer single_buffer;
for (auto single_element : vector_str_in) {
single_buffer.assign(single_element.begin(), single_element.end());
data_val_vector.push_back(single_buffer);
}
}
///specific versions
void set(const std::set<string> &strset_in);
void set(const char *data_in, long size_in); /* voids, file chunks anyone? */
void set(const char *data_in); /* strings, stops at NULL, returns as string */
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* get overloads */
template<typename T, typename Dummy = int >
void get(T& scalar_out) {
if (getDataSize() == 0) {
throw DataException("Cannot get() the scalar, DataElement is empty !");
}
DataElement::Type storageType = getDataType();
//TODO: smarter way with templates ?
if (storageType == DataElement::Type::DATA_CHAR) {
char* storage_ptr = reinterpret_cast<char*>(&data_val[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_UCHAR) {
auto* storage_ptr = reinterpret_cast<unsigned char*>(&data_val[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_INT) {
int* storage_ptr = reinterpret_cast<int*>(&data_val[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_UINT) {
auto* storage_ptr = reinterpret_cast<unsigned int*>(&data_val[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_LONG) {
long* storage_ptr = reinterpret_cast<long*>(&data_val[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_ULONG) {
auto* storage_ptr = reinterpret_cast<unsigned long*>(&data_val[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_LONGLONG) {
auto* storage_ptr = reinterpret_cast<long long*>(&data_val[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_FLOAT) {
auto* storage_ptr = reinterpret_cast<float*>(&data_val[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_DOUBLE) {
auto* storage_ptr = reinterpret_cast<double*>(&data_val[0]);
//constructor-like
scalar_out = T(*storage_ptr);
}
}
// general templates : for vector of scalars
template<typename T, typename Dummy = int >
void get(vector<T>& scalar_vector_out) {
scalar_vector_out.clear();
DataElementBuffer single_buffer;
Type storageType = getDataType();
for (auto single_storage_element : data_val_vector) {
if (single_storage_element.empty()) {
throw DataException("Cannot get(vector<scalar>) on single element because it is empty!");
}
T scalar_out;
//TODO: smarter way with templates ?
if (storageType == DataElement::Type::DATA_CHAR_VECTOR) {
char* storage_ptr = reinterpret_cast<char*>(&single_storage_element[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_UCHAR_VECTOR) {
auto* storage_ptr = reinterpret_cast<unsigned char*>(&single_storage_element[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_INT_VECTOR) {
int* storage_ptr = reinterpret_cast<int*>(&single_storage_element[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_UINT_VECTOR) {
auto* storage_ptr = reinterpret_cast<unsigned int*>(&single_storage_element[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_LONG_VECTOR) {
long* storage_ptr = reinterpret_cast<long*>(&single_storage_element[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_ULONG_VECTOR) {
auto* storage_ptr = reinterpret_cast<unsigned long*>(&single_storage_element[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_LONGLONG_VECTOR) {
auto* storage_ptr = reinterpret_cast<long long*>(&single_storage_element[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_FLOAT_VECTOR) {
auto* storage_ptr = reinterpret_cast<float*>(&single_storage_element[0]);
//constructor-like
scalar_out = T(*storage_ptr);
} else if (storageType == DataElement::Type::DATA_DOUBLE_VECTOR) {
auto* storage_ptr = reinterpret_cast<double*>(&single_storage_element[0]);
//constructor-like
scalar_out = T(*storage_ptr);
}
scalar_vector_out.push_back(scalar_out);
} //end for.
}
//template specialization : for string or void* returned as string
template< typename Dummy = int >
void get(string& str_out) {
//reset
str_out.clear();
if (data_type == DataElement::Type::DATA_NULL) {
//it means TinyXML has parsed an empty tag,
//so return an empty string.
return;
}
if (data_type != DataElement::Type::DATA_STRING && data_type != DataElement::Type::DATA_VOID) {
throw(DataTypeMismatchException("Type mismatch, neither a STRING nor a VOID*"));
}
for (auto single_char : data_val) {
str_out.push_back((char)single_char);
}
}
//template specialization : for wstring
template< typename Dummy = int >
void get(wstring& wstr_out) {
//reset
wstr_out.clear();
if (data_type == DataElement::Type::DATA_NULL) {
//it means TinyXML has parsed an empty tag,
//so return an empty string.
return;
}
if (data_type != DataElement::Type::DATA_WSTRING) {
throw(DataTypeMismatchException("Type mismatch, not a WSTRING"));
}
if (getDataSize() >= sizeof(wchar_t)) {
//data_val is an array of bytes holding wchar_t characters, plus a terminating (wchar_t)0
//wchar_t is typically 16 bits on windows, and 32 bits on Unix, so use sizeof(wchar_t) everywhere.
size_t maxNbWchars = getDataSize() / sizeof(wchar_t);
//be paranoid, zero the buffer
auto *tmp_wstr = (wchar_t *)::calloc(maxNbWchars + 1, sizeof(wchar_t));
//the last wchar_t is actually zero if anything goes wrong...
::mbstowcs(tmp_wstr, (const char*)&data_val[0], maxNbWchars);
wstr_out.assign(tmp_wstr);
::free(tmp_wstr);
}
}
//template specialization : for vector<string>
template< typename Dummy = int >
void get(vector<string>& vector_str_out) {
if (data_type != DataElement::Type::DATA_STR_VECTOR) {
throw(DataTypeMismatchException("Type mismatch, not a STRING VECTOR"));
}
vector_str_out.clear();
string single_buffer;
for (auto single_element : data_val_vector) {
single_buffer.assign(single_element.begin(), single_element.end());
vector_str_out.push_back(single_buffer);
}
}
//special versions:
void get(DataElementBuffer& data_out); /* getting a void or string */
void get(std::set<string> &strset_out);
/* special get functions, saves creating unnecessary vars */
int getChar() { char i_get; get(i_get); return i_get; };
unsigned int getUChar() { unsigned char i_get; get(i_get); return i_get; };
int getInt() { int i_get; get(i_get); return i_get; };
unsigned int getUInt() { unsigned int i_get; get(i_get); return i_get; };
long getLong() { long l_get; get(l_get); return l_get; };
unsigned long getULong() { unsigned long l_get; get(l_get); return l_get; };
long long getLongLong() { long long l_get; get(l_get); return l_get; };
float getFloat() { float f_get; get(f_get); return f_get; };
double getDouble() { double d_get; get(d_get); return d_get; };
std::string toString();
};
///
class DataNode
{
private:
DataNode *parentNode;
vector<DataNode *> children;
map<string, vector<DataNode *>, string_less> childmap;
map<string, unsigned int, string_less> childmap_ptr;
string node_name;
DataElement *data_elem;
unsigned int ptr;
public:
DataNode();
explicit DataNode(const char *name_in);
DataNode(const char *name_in, DataElement &cloneFrom);
DataNode(const char *name_in, DataNode &cloneFrom);
~DataNode();
void setName(const char *name_in);
string &getName() { return node_name; }
DataNode *getParentNode() { return parentNode; };
void setParentNode(DataNode &parentNode_in) { parentNode = &parentNode_in; };
size_t numChildren(); /* Number of children */
size_t numChildren(const char *name_in); /* Number of children named 'name_in' */
DataElement *element(); /* DataElement at this node */
DataNode *newChild(const char *name_in);
DataNode *newChild(const char *name_in, DataNode *otherNode);
DataNode *newChildCloneFrom(const char *name_in, DataNode *cloneFrom);
DataNode *child(const char *name_in, int index = 0);
DataNode *child(int index);
bool hasAnother(const char *name_in); /* useful for while() loops in conjunction with getNext() */
bool hasAnother();
DataNode *getNext(const char *name_in); /* get next of specified name */
DataNode *getNext(); /* get next child */
void rewind(const char *name_in); /* rewind specific */
void rewind(); /* rewind generic */
void rewindAll();
void findAll(const char *name_in, vector<DataNode *> &node_list_out);
explicit operator string () { string s; element()->get(s); return s; }
explicit operator const char * () { if (element()->getDataType() == DataElement::Type::DATA_STRING) { return element()->getDataPointer(); } else { return nullptr; } }
explicit operator char () { char v=0; element()->get(v); return v; }
explicit operator unsigned char () { unsigned char v=0; element()->get(v); return v; }
explicit operator int () { int v=0; element()->get(v); return v; }
explicit operator unsigned int () { unsigned int v=0; element()->get(v); return v; }
explicit operator long () { long v=0; element()->get(v); return v; }
explicit operator unsigned long () { unsigned long v=0; element()->get(v); return v; }
explicit operator long long () { long long v=0; element()->get(v); return v; }
explicit operator float () { float v=0; element()->get(v); return v; }
explicit operator double () { double v=0; element()->get(v); return v; }
explicit operator vector<char> () { vector<char> v; element()->get(v); return v; }
explicit operator vector<unsigned char> () { vector<unsigned char> v; element()->get(v); return v; }
explicit operator vector<int> () { vector<int> v; element()->get(v); return v; }
explicit operator vector<unsigned int> () { vector<unsigned int> v; element()->get(v); return v; }
explicit operator vector<long> () { vector<long> v; element()->get(v); return v; }
explicit operator vector<unsigned long> () { vector<unsigned long> v; element()->get(v); return v; }
explicit operator vector<float> () { vector<float> v; element()->get(v); return v; }
explicit operator vector<double> () { vector<double> v; element()->get(v); return v; }
const string &operator= (const string &s) { element()->set(s); return s; }
const wstring &operator= (const wstring &s) { element()->set(s); return s; }
char operator= (char i) { element()->set(i); return i; }
unsigned char operator= (unsigned char i) { element()->set(i); return i; }
int operator= (int i) { element()->set(i); return i; }
unsigned int operator= (unsigned int i) { element()->set(i); return i; }
long operator= (long i) { element()->set(i); return i; }
unsigned long operator= (unsigned long i) { element()->set(i); return i; }
long long operator= (long long i) { element()->set(i); return i; }
float operator= (float i) { element()->set(i); return i; }
double operator= (double i) { element()->set(i); return i; }
vector<char> &operator= (vector<char> &v) { element()->set(v); return v; }
vector<unsigned char> &operator= (vector<unsigned char> &v) { element()->set(v); return v; }
vector<int> &operator= (vector<int> &v) { element()->set(v); return v; }
vector<unsigned int> &operator= (vector<unsigned int> &v) { element()->set(v); return v; }
vector<long> &operator= (vector<long> &v) { element()->set(v); return v; }
vector<unsigned long> &operator= (vector<unsigned long> &v) { element()->set(v); return v; }
vector<float> &operator= (vector<float> &v) { element()->set(v); return v; }
vector<double> &operator= (vector<double> &v) { element()->set(v); return v; }
DataNode *operator[] (const char *name_in) { return getNext(name_in); }
DataNode *operator[] (int idx) { return child(idx); }
bool operator() (const char *name_in) { return hasAnother(name_in); }
bool operator() () { return hasAnother(); }
DataNode *operator ^(const char *name_in) { return newChild(name_in); }
};
typedef vector<DataNode *> DataNodeList;
enum DT_FloatingPointPolicy {
USE_FLOAT,
USE_DOUBLE
};
class DataTree
{
private:
DataNode dn_root;
string wsEncode(const wstring& wstr);
wstring wsDecode(const string& str);
public:
explicit DataTree(const char *name_in);
DataTree();
~DataTree();
DataNode *rootNode();
void nodeToXML(DataNode *elem, TiXmlElement *elxml);
void setFromXML(DataNode *elem, TiXmlNode *elxml, bool root_node=true, DT_FloatingPointPolicy fpp=USE_FLOAT);
void decodeXMLText(DataNode *elem, const char *in_text, DT_FloatingPointPolicy fpp);
void printXML(); /* print datatree as XML */
bool LoadFromFileXML(const std::string& filename, DT_FloatingPointPolicy fpp=USE_FLOAT);
bool SaveToFileXML(const std::string& filename);
};

View File

@ -0,0 +1,104 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#include "GLExt.h"
#include <iostream>
#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#endif
#if defined(__linux__) || defined(__FreeBSD__)
#include <GL/glx.h>
#include <dlfcn.h>
#endif
#ifdef _WIN32
PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT = NULL;
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = NULL;
bool GLExtSupported(const char *extension_name) {
const GLubyte *extensions = glGetString(GL_EXTENSIONS);
return (std::strstr((const char *)extensions, extension_name) != NULL);
}
#endif
bool GLExt_initialized = false;
void initGLExtensions() {
if (GLExt_initialized) {
return;
}
// const GLubyte *extensions = glGetString(GL_EXTENSIONS);
// std::cout << std::endl << "Supported GL Extensions: " << std::endl << extensions << std::endl << std::endl;
#ifdef __APPLE__
const GLint interval = 1;
#else
const GLint interval = 2;
#endif
#ifdef _WIN32
if (GLExtSupported("WGL_EXT_swap_control")) {
std::cout << "Initializing WGL swap control extensions.." << std::endl << std::flush;
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT");
wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) wglGetProcAddress("wglGetSwapIntervalEXT");
wglSwapIntervalEXT(interval);
}
#endif
#ifdef __APPLE__
// OSX is just ON / OFF
CGLSetParameter (CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
#endif
#if defined(__linux__) || defined(__FreeBSD__)
dlopen("libglx.so",RTLD_LAZY);
void (*glxSwapIntervalEXTFunc) (Display *dpy, GLXDrawable drawable, int interval) = nullptr;
int (*glxSwapIntervalMESAFunc)(unsigned int interval) = nullptr;
int (*glxSwapIntervalSGIFunc) (int interval) = nullptr;
void (*DRI2SwapIntervalFunc) (Display *dpy, XID drawable, int interval) = nullptr;
glxSwapIntervalEXTFunc = (void (*) (Display *dpy, GLXDrawable drawable, int interval)) dlsym(RTLD_DEFAULT,"glXSwapIntervalEXT");
glxSwapIntervalMESAFunc = (int (*)(unsigned int interval)) dlsym(RTLD_DEFAULT,"glXSwapIntervalMESA");
glxSwapIntervalSGIFunc = (int (*) (int interval)) dlsym(RTLD_DEFAULT,"glXSwapIntervalSGI");
DRI2SwapIntervalFunc = (void (*) (Display *dpy, XID drawable, int interval)) dlsym(RTLD_DEFAULT,"DRI2SwapInterval");
std::cout << "Available vertical sync SwapInterval functions: " << std::endl;
std::cout << "\tglxSwapIntervalEXT: " << ((glxSwapIntervalEXTFunc != nullptr)?"Yes":"No") << std::endl;
std::cout << "\tDRI2SwapInterval: " << ((DRI2SwapIntervalFunc != nullptr)?"Yes":"No") << std::endl;
std::cout << "\tglxSwapIntervalMESA: " << ((glxSwapIntervalMESAFunc != nullptr)?"Yes":"No") << std::endl;
std::cout << "\tglxSwapIntervalSGI: " << ((glxSwapIntervalSGIFunc != nullptr)?"Yes":"No") << std::endl;
if (glxSwapIntervalEXTFunc) {
Display *dpy = glXGetCurrentDisplay();
GLXDrawable drawable = glXGetCurrentDrawable();
glxSwapIntervalEXTFunc(dpy, drawable, interval);
std::cout << "Using glxSwapIntervalEXT." << std::endl << std::endl;
} else if (DRI2SwapIntervalFunc) {
Display *dpy = glXGetCurrentDisplay();
GLXDrawable drawable = glXGetCurrentDrawable();
DRI2SwapIntervalFunc(dpy, drawable, interval);
std::cout << "Using DRI2SwapInterval." << std::endl << std::endl;
} else if (glxSwapIntervalMESAFunc) {
glxSwapIntervalMESAFunc(interval);
std::cout << "Using glxSwapIntervalMESA." << std::endl << std::endl;
} else if (glxSwapIntervalSGIFunc) {
glxSwapIntervalSGIFunc(interval);
std::cout << "Using glxSwapIntervalSGI." << std::endl << std::endl;
} else {
std::cout << "No vertical sync swap interval functions available." << std::endl;
}
#endif
GLExt_initialized = true;
}

View File

@ -0,0 +1,27 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#pragma once
#include "wx/glcanvas.h"
#ifdef _WIN32
#include <windows.h>
#ifdef __MINGW32__
#include <gl/wglext.h>
#else
#include "wglext.h"
#endif
extern PFNWGLGETEXTENSIONSSTRINGEXTPROC _wglGetExtensionsStringEXT;
extern PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
extern PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT;
bool GLExtSupported(const char *extension_name);
#endif
extern bool GLExt_initialized;
void initGLExtensions();

View File

@ -0,0 +1,920 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#include "GLFont.h"
#include <wx/string.h>
#include <iostream>
#include <fstream>
#include <algorithm>
#include "cubic_math.h"
#ifdef _OSX_APP_
#include "CoreFoundation/CoreFoundation.h"
#endif
#ifndef RES_FOLDER
#define RES_FOLDER L""
#endif
#define GC_DRAW_COUNT_PERIOD 50
#define GC_DRAW_COUNT_LIMIT 10
GLFontStringCache::GLFontStringCache() {
gc = 0;
}
//Static initialization of all available fonts,
//using aggregate syntax (Cx11+)
//Fonts must be listed in increasing size for Drawer to work !
GLFont GLFont::fonts[GLFont::GLFontSize::GLFONT_SIZE_MAX] = {
{ GLFont::GLFontSize::GLFONT_SIZE12, L"fonts/vera_sans_mono12.fnt" },
{ GLFont::GLFontSize::GLFONT_SIZE16, L"fonts/vera_sans_mono16.fnt" },
{ GLFont::GLFontSize::GLFONT_SIZE18, L"fonts/vera_sans_mono18.fnt" },
{ GLFont::GLFontSize::GLFONT_SIZE24, L"fonts/vera_sans_mono24.fnt" },
{ GLFont::GLFontSize::GLFONT_SIZE27, L"fonts/vera_sans_mono27.fnt" },
{ GLFont::GLFontSize::GLFONT_SIZE32, L"fonts/vera_sans_mono32.fnt" },
{ GLFont::GLFontSize::GLFONT_SIZE36, L"fonts/vera_sans_mono36.fnt" },
{ GLFont::GLFontSize::GLFONT_SIZE48, L"fonts/vera_sans_mono48.fnt" },
{ GLFont::GLFontSize::GLFONT_SIZE64, L"fonts/vera_sans_mono64.fnt" },
{ GLFont::GLFontSize::GLFONT_SIZE72, L"fonts/vera_sans_mono72.fnt" },
{ GLFont::GLFontSize::GLFONT_SIZE96, L"fonts/vera_sans_mono96.fnt" },
};
std::atomic<GLFont::GLFontScale> GLFont::currentScale{ GLFont::GLFontScale::GLFONT_SCALE_NORMAL };
GLFontChar::GLFontChar() :
id(0), x(0), y(0), width(0), height(0), xoffset(0), yoffset(0), xadvance(0), aspect(1), index(0) {
}
GLFontChar::~GLFontChar() = default;
void GLFontChar::setId(int idval) {
id = idval;
}
int GLFontChar::getId() const {
return id;
}
void GLFontChar::setXOffset(int xofs) {
xoffset = xofs;
}
int GLFontChar::getXOffset() {
return xoffset;
}
void GLFontChar::setYOffset(int yofs) {
yoffset = yofs;
}
int GLFontChar::getYOffset() {
return yoffset;
}
void GLFontChar::setX(int xpos) {
x = xpos;
}
int GLFontChar::getX() {
return x;
}
void GLFontChar::setY(int ypos) {
y = ypos;
}
int GLFontChar::getY() {
return y;
}
void GLFontChar::setWidth(int w) {
width = w;
if (width && height) {
aspect = (float) width / (float) height;
}
}
int GLFontChar::getWidth() {
return width;
}
void GLFontChar::setHeight(int h) {
height = h;
if (width && height) {
aspect = (float) width / (float) height;
}
}
int GLFontChar::getHeight() const {
return height;
}
void GLFontChar::setXAdvance(int xadv) {
xadvance = xadv;
}
int GLFontChar::getXAdvance() {
return xadvance;
}
float GLFontChar::getAspect() const {
return aspect;
}
void GLFontChar::setIndex(unsigned int idx) {
index = idx;
}
int GLFontChar::getIndex() const {
return index;
}
GLFont::GLFont(GLFontSize size, std::wstring defFileName):
lineHeight(0), base(0), imageWidth(0), imageHeight(0), loaded(false), texId(0), gcCounter(0) {
fontSizeClass = size;
fontDefFileSource = defFileName;
}
GLFont::~GLFont() = default;
std::wstring GLFont::nextParam(std::wistringstream &str) {
std::wstring param_str;
str >> param_str;
if (param_str.find(L'"') != std::wstring::npos) {
std::wstring rest;
while (!str.eof() && (std::count(param_str.begin(), param_str.end(), L'"') % 2)) {
str >> rest;
param_str.append(L" " + rest);
}
}
return param_str;
}
std::wstring GLFont::getParamKey(const std::wstring& param_str) {
std::wstring keyName;
size_t eqpos = param_str.find(L'=');
if (eqpos != std::wstring::npos) {
keyName = param_str.substr(0, eqpos);
}
return keyName;
}
std::wstring GLFont::getParamValue(const std::wstring& param_str) {
std::wstring value;
size_t eqpos = param_str.find(L'=');
if (eqpos != std::wstring::npos) {
value = param_str.substr(eqpos + 1);
}
if (value[0] == L'"' && value[value.length() - 1] == L'"') {
value = value.substr(1, value.length() - 2);
}
return value;
}
void GLFont::loadFontOnce() {
if (loaded) {
return;
}
#if _OSX_APP_
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
char path[PATH_MAX];
if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
{
// error!
}
CFRelease(resourcesURL);
wxString resourceFolder = std::string(path) + "/";
#else
wxString resourceFolder = RES_FOLDER;
#endif
wxFileName exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath());
//1) First try : RES_FOLDER/fonts/*
wxFileName fontDefFileName = wxFileName(resourceFolder + L"/" + fontDefFileSource);
bool fontFilePathFound = fontDefFileName.Exists();
// 2) Second try: [Cubic exe path]/RES_FOLDER/fonts/*
if (!fontFilePathFound) {
fontDefFileName = wxFileName(exePath.GetPath() + L"/" + RES_FOLDER + L"/" + fontDefFileSource);
fontFilePathFound = fontDefFileName.Exists();
}
// 3) Third try: [Cubic exe path]/fonts/*
if (!fontFilePathFound) {
fontDefFileName = wxFileName(exePath.GetPath() + L"/" + fontDefFileSource);
fontFilePathFound = fontDefFileName.Exists();
}
if (fontFilePathFound) {
if (!fontDefFileName.IsFileReadable()) {
std::cout << "Font file " << fontDefFileName.GetFullPath() << " is not readable?" << std::endl;
return;
}
}
else {
if (!fontDefFileName.FileExists()) {
std::cout << "Font file " << fontDefFileName.GetFullPath() << " does not exist?" << std::endl;
return;
}
}
//Re-compute the resource dir.
resourceFolder = fontDefFileName.GetPath();
std::string fontDefFileNamePath = fontDefFileName.GetFullPath(wxPATH_NATIVE).ToStdString();
std::wifstream input;
input.open(fontDefFileNamePath, std::ios::in);
std::wstring op;
while (!input.eof()) {
input >> op;
if (op == L"info") {
std::wstring info_param_str;
getline(input, info_param_str);
std::wistringstream info_param(info_param_str);
while (!info_param.eof()) {
std::wstring param = nextParam(info_param);
std::wstring paramKey = getParamKey(param);
if (paramKey == L"face") {
fontName = getParamValue(param);
}
param = nextParam(info_param);
paramKey = getParamKey(param);
if (paramKey == L"size") {
std::wistringstream paramValue(getParamValue(param));
paramValue >> pixHeight;
}
// std::cout << "[" << paramKey << "] = '" << paramValue << "'" << std::endl;
}
} else if (op == L"common") {
std::wstring common_param_str;
getline(input, common_param_str);
std::wistringstream common_param(common_param_str);
while (!common_param.eof()) {
std::wstring param = nextParam(common_param);
std::wstring paramKey = getParamKey(param);
std::wistringstream paramValue(getParamValue(param));
if (paramKey == L"lineHeight") {
paramValue >> lineHeight;
} else if (paramKey == L"base") {
paramValue >> base;
} else if (paramKey == L"scaleW") {
paramValue >> imageWidth;
} else if (paramKey == L"scaleH") {
paramValue >> imageHeight;
}
// std::cout << "[" << paramKey << "] = '" << getParamValue(param) << "'" << std::endl;
}
} else if (op == L"page") {
std::wstring page_param_str;
getline(input, page_param_str);
std::wistringstream page_param(page_param_str);
while (!page_param.eof()) {
std::wstring param = nextParam(page_param);
std::wstring paramKey = getParamKey(param);
std::wstring paramValue = getParamValue(param);
if (paramKey == L"file") {
wxFileName imgFileName = wxFileName(resourceFolder, paramValue);
imageFile = imgFileName.GetFullPath(wxPATH_NATIVE).ToStdWstring();
}
// std::cout << "[" << paramKey << "] = '" << paramValue << "'" << std::endl;
}
} else if (op == L"char") {
std::wstring char_param_str;
getline(input, char_param_str);
std::wistringstream char_param(char_param_str);
auto *newChar = new GLFontChar;
while (!char_param.eof()) {
std::wstring param = nextParam(char_param);
std::wstring paramKey = getParamKey(param);
std::wistringstream paramValue(getParamValue(param));
int val;
if (paramKey == L"id") {
paramValue >> val;
newChar->setId(val);
} else if (paramKey == L"x") {
paramValue >> val;
newChar->setX(val);
} else if (paramKey == L"y") {
paramValue >> val;
newChar->setY(val);
} else if (paramKey == L"width") {
paramValue >> val;
newChar->setWidth(val);
} else if (paramKey == L"height") {
paramValue >> val;
newChar->setHeight(val);
} else if (paramKey == L"xoffset") {
paramValue >> val;
newChar->setXOffset(val);
} else if (paramKey == L"yoffset") {
paramValue >> val;
newChar->setYOffset(val);
} else if (paramKey == L"xadvance") {
paramValue >> val;
newChar->setXAdvance(val);
}
// std::cout << "[" << paramKey << "] = '" << getParamValue(param) << "'" << std::endl;
}
characters[newChar->getId()] = newChar;
} else {
std::wstring dummy;
getline(input, dummy);
}
}
if (!imageFile.empty() && imageWidth && imageHeight && !characters.empty()) {
// Load file and decode image.
std::vector<unsigned char> image;
unsigned int imgWidth = imageWidth, imgHeight = imageHeight;
//1) First load the raw file to memory using wstring filenames
wxFile png_file(imageFile);
int png_size = png_file.Length();
auto* raw_image = new unsigned char[png_size];
if (png_size > 0) {
int nbRead = png_file.Read((void*)raw_image, png_size);
if (png_size != nbRead) {
std::cout << "Error loading the full PNG image file in memory: '" << imageFile << "'" << std::endl;
}
}
//2) then load from memory
unsigned error = lodepng::decode(image, imgWidth, imgHeight, raw_image, png_size);
delete[] raw_image;
png_file.Close();
if (error) {
std::cout << "Error decoding PNG image file: '" << imageFile << "'" << std::endl;
}
glGenTextures(1, &texId);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texId);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, 4, imageWidth, imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, error?nullptr:(&image[0]));
glDisable(GL_TEXTURE_2D);
std::map<int, GLFontChar *>::iterator char_i;
gl_vertices.resize(characters.size() * 8); // one quad per char
gl_uv.resize(characters.size() * 8);
unsigned int ofs = 0;
for (char_i = characters.begin(); char_i != characters.end(); char_i++) {
// int charId = (*char_i).first;
GLFontChar *fchar = (*char_i).second;
float faspect = fchar->getAspect();
float uv_xpos = (float) fchar->getX() / (float) imageWidth;
float uv_ypos = ((float) fchar->getY() / (float) imageHeight);
float uv_xofs = (float) fchar->getWidth() / (float) imageWidth;
float uv_yofs = ((float) fchar->getHeight() / (float) imageHeight);
gl_vertices[ofs] = 0;
gl_vertices[ofs + 1] = 0;
gl_uv[ofs] = uv_xpos;
gl_uv[ofs + 1] = uv_ypos + uv_yofs;
gl_vertices[ofs + 2] = faspect;
gl_vertices[ofs + 3] = 0;
gl_uv[ofs + 2] = uv_xpos + uv_xofs;
gl_uv[ofs + 3] = uv_ypos + uv_yofs;
gl_vertices[ofs + 4] = faspect;
gl_vertices[ofs + 5] = 1;
gl_uv[ofs + 4] = uv_xpos + uv_xofs;
gl_uv[ofs + 5] = uv_ypos;
gl_vertices[ofs + 6] = 0;
gl_vertices[ofs + 7] = 1;
gl_uv[ofs + 6] = uv_xpos;
gl_uv[ofs + 7] = uv_ypos;
fchar->setIndex(ofs);
ofs += 8;
}
std::cout << "Loaded font '" << fontName << "' from '" << imageFile << "', parsed " << characters.size() << " characters." << std::endl;
loaded = true;
} else {
std::cout << "Error loading font file " << imageFile << std::endl;
}
input.close();
loaded = true;
}
float GLFont::getStringWidth(const std::wstring& str, float size, float viewAspect) {
float scalex = size / viewAspect;
float width = 0;
for (int charId : str) {
if (characters.find(charId) == characters.end()) {
continue;
}
GLFontChar *fchar = characters[charId];
float ofsx = (float) fchar->getXOffset() / (float) imageWidth;
float advx = (float) fchar->getXAdvance() / (float) imageWidth;
if (charId == 32) {
advx = characters[L'_']->getAspect();
}
width += fchar->getAspect() + advx + ofsx;
}
width *= scalex;
return width;
}
// Draw string, immediate
void GLFont::drawString(const std::wstring& str, int pxHeight, float xpos, float ypos, Align hAlign, Align vAlign, int vpx, int vpy, bool cacheable) {
pxHeight *= 2;
if (!vpx || !vpy) {
GLint vp[4];
glGetIntegerv( GL_VIEWPORT, vp);
vpx = vp[2];
vpy = vp[3];
}
if (cacheable) {
gcCounter++;
std::lock_guard<SpinMutex> lock(cache_busy);
if (gcCounter > GC_DRAW_COUNT_PERIOD) {
doCacheGC();
gcCounter = 0;
}
GLFontStringCache *fc = nullptr;
std::map<std::wstring, GLFontStringCache * >::iterator cache_iter;
std::wstringstream sscacheIdx;
sscacheIdx << vpx << "." << vpy << "." << pxHeight << "." << str;
std::wstring cacheIdx(sscacheIdx.str());
cache_iter = stringCache.find(cacheIdx);
if (cache_iter != stringCache.end()) {
fc = cache_iter->second;
fc->gc = 0;
}
if (fc == nullptr) {
// std::cout << "cache miss" << std::endl;
fc = cacheString(str, pxHeight, vpx, vpy);
stringCache[cacheIdx] = fc;
}
drawCacheString(fc, xpos, ypos, hAlign, vAlign);
return;
}
float size = (float) pxHeight / (float) vpy;
float viewAspect = (float) vpx / (float) vpy;
float msgWidth = getStringWidth(str, size, viewAspect);
glPushMatrix();
glTranslatef(xpos, ypos, 0.0f);
switch (vAlign) {
case GLFONT_ALIGN_TOP:
glTranslatef(0.0, -size, 0.0);
break;
case GLFONT_ALIGN_CENTER:
glTranslatef(0.0, -size/2.0, 0.0);
break;
default:
break;
}
switch (hAlign) {
case GLFONT_ALIGN_RIGHT:
glTranslatef(-msgWidth, 0.0, 0.0);
break;
case GLFONT_ALIGN_CENTER:
glTranslatef(-msgWidth / 2.0, 0.0, 0.0);
break;
default:
break;
}
glPushMatrix();
glScalef(size / viewAspect, size, 1.0f);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texId);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, &gl_vertices[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &gl_uv[0]);
for (int charId : str) {
if (characters.find(charId) == characters.end()) {
continue;
}
GLFontChar *fchar = characters[charId];
float ofsx = (float) fchar->getXOffset() / (float) imageWidth;
float advx = (float) fchar->getXAdvance() / (float) imageWidth;
if (charId == 32) {
advx = characters[L'_']->getAspect();
}
glTranslatef(ofsx, 0.0, 0.0);
glDrawArrays(GL_QUADS, fchar->getIndex() / 2, 4);
glTranslatef(fchar->getAspect() + advx, 0.0, 0.0);
}
glVertexPointer(2, GL_FLOAT, 0, nullptr);
glTexCoordPointer(2, GL_FLOAT, 0, nullptr);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glPopMatrix();
glPopMatrix();
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}
// Draw string, immediate, 8 bit version
void GLFont::drawString(const std::string& str, int pxHeight, float xpos, float ypos, Align hAlign, Align vAlign, int vpx, int vpy, bool cacheable) {
//Displayed string is wstring, so use wxString to do the heavy lifting of converting str...
wxString wsTmp;
wsTmp.assign(str);
drawString(wsTmp.ToStdWstring(), pxHeight, xpos, ypos, hAlign, vAlign, vpx, vpy, cacheable);
}
// Draw cached GLFontCacheString
void GLFont::drawCacheString(GLFontStringCache *fc, float xpos, float ypos, Align hAlign, Align vAlign) const {
float size = (float) fc->pxHeight / (float) fc->vpy;
glPushMatrix();
glTranslatef(xpos, ypos, 0.0f);
switch (vAlign) {
case GLFONT_ALIGN_TOP:
glTranslatef(0.0, -size, 0.0);
break;
case GLFONT_ALIGN_CENTER:
glTranslatef(0.0, -size/2.0, 0.0);
break;
default:
break;
}
switch (hAlign) {
case GLFONT_ALIGN_RIGHT:
glTranslatef(-fc->msgWidth, 0.0, 0.0);
break;
case GLFONT_ALIGN_CENTER:
glTranslatef(-fc->msgWidth / 2.0, 0.0, 0.0);
break;
default:
break;
}
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texId);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, &fc->gl_vertices[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &fc->gl_uv[0]);
glDrawArrays(GL_QUADS, 0, 4 * fc->drawlen);
glVertexPointer(2, GL_FLOAT, 0, nullptr);
glTexCoordPointer(2, GL_FLOAT, 0, nullptr);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glPopMatrix();
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}
// Compile optimized GLFontCacheString
GLFontStringCache *GLFont::cacheString(const std::wstring& str, int pxHeight, int vpx, int vpy) {
auto *fc = new GLFontStringCache;
fc->pxHeight = pxHeight;
fc->vpx = vpx;
fc->vpy = vpy;
float size = (float) pxHeight / (float) vpy;
float viewAspect = (float) vpx / (float) vpy;
fc->msgWidth = getStringWidth(str, size, viewAspect);
int nChar = 0;
for (int charId : str) {
if (characters.find(charId) == characters.end()) {
continue;
}
nChar++;
}
fc->drawlen = nChar;
fc->gl_vertices.resize(nChar*8);
fc->gl_uv.resize(nChar*8);
CubicVR::mat4 trans = CubicVR::mat4::scale(size / viewAspect, size, 1.0f);
int c = 0;
for (int charId : str) {
if (characters.find(charId) == characters.end()) {
continue;
}
GLFontChar *fchar = characters[charId];
float ofsx = (float) fchar->getXOffset() / (float) imageWidth;
float advx = (float) fchar->getXAdvance() / (float) imageWidth;
if (charId == 32) {
advx = characters[L'_']->getAspect();
}
// freeze transform to buffer
trans *= CubicVR::mat4::translate(ofsx, 0.0, 0.0);
int charIdx = fchar->getIndex();
for (int j = 0; j < 8; j+=2) {
CubicVR::vec3 pt(gl_vertices[charIdx + j],gl_vertices[charIdx + j + 1], 0.0);
pt = CubicVR::mat4::multiply(trans, pt, true);
fc->gl_vertices[c * 8 + j] = pt[0];
fc->gl_vertices[c * 8 + j + 1] = pt[1];
fc->gl_uv[c * 8 + j] = gl_uv[charIdx + j];
fc->gl_uv[c * 8 + j + 1] = gl_uv[charIdx + j + 1];
}
trans *= CubicVR::mat4::translate(fchar->getAspect() + advx, 0.0, 0.0);
c++;
}
return fc;
}
void GLFont::doCacheGC() {
std::map<std::wstring, GLFontStringCache * >::iterator cache_iter;
bool flushDone = false;
//do aging and remove in one pass.
cache_iter = stringCache.begin();
while (cache_iter != stringCache.end()) {
//aging
cache_iter->second->gc--;
//only flush 1 element per call
if (!flushDone && cache_iter->second->gc < -GC_DRAW_COUNT_LIMIT) {
delete cache_iter->second;
cache_iter = stringCache.erase(cache_iter);
flushDone = true;
}
else {
cache_iter++;
}
} //end while
}
void GLFont::clearCache() {
std::lock_guard<SpinMutex> lock(cache_busy);
std::map<std::wstring, GLFontStringCache * >::iterator cache_iter;
cache_iter = stringCache.begin();
while (cache_iter != stringCache.end()) {
delete cache_iter->second;
cache_iter = stringCache.erase(cache_iter);
}
}
void GLFont::clearAllCaches() {
for (int i = 0; i < GLFont::GLFONT_SIZE_MAX; i++) {
fonts[i].clearCache();
}
}
GLFont::Drawer GLFont::getFont(int requestedSize, double scaleFactor) {
return GLFont::Drawer(requestedSize, scaleFactor);
}
void GLFont::setScale(GLFontScale scale) {
//safety vs. inputs
if (scale < GLFONT_SCALE_NORMAL || scale > GLFONT_SCALE_LARGE) {
scale = GLFontScale::GLFONT_SCALE_NORMAL;
}
currentScale.store(scale);
//Flush all the GC stuff
clearAllCaches();
}
GLFont::GLFontScale GLFont::getScale() {
return currentScale.load();
}
double GLFont::getScaleFactor() {
GLFontScale scale = currentScale.load();
if (scale == GLFONT_SCALE_MEDIUM) {
return 1.5;
}
else if (scale == GLFONT_SCALE_LARGE) {
return 2.0;
}
return 1.0;
}
int GLFont::getScaledPx(int basicFontSize, double scaleFactor) {
//try to align on an integer pixel size if the targetSize font is available
int targetSize = round(basicFontSize * scaleFactor);
int resultIndex = 0;
fonts[0].loadFontOnce();
for (int i = 0; i < GLFONT_SIZE_MAX - 1; i++) {
fonts[i + 1].loadFontOnce();
if (fonts[i + 1].pixHeight <= targetSize) {
resultIndex = i + 1;
}
else {
break;
}
} //end for
// return font height px
return fonts[resultIndex].pixHeight;
}
GLFont::Drawer::Drawer(int basicFontSize, double scaleFactor) {
//Selection of the final font: scan GLFont::fonts to find the biggest font such as
// its pixHeight <= basicFontSize * scaleFactor.
//then compute finalScaleFactor the zooming factor of renderingFont to reach a
//final font size of basicFontSize* scaleFactor:
renderingFontIndex = 0;
//try to align on an integer pixel size if the targetSize font is available
int targetSize = round(basicFontSize * scaleFactor);
fonts[0].loadFontOnce();
for (int i = 0; i < GLFONT_SIZE_MAX - 1; i++) {
fonts[i + 1].loadFontOnce();
if (fonts[i + 1].pixHeight <= targetSize) {
renderingFontIndex = i + 1;
}
else {
break;
}
} //end for
//
int rawSize = fonts[renderingFontIndex].pixHeight;
//targetSize may not be reached yet, so the effective rendering font: fonts[renderingFontIndex] must be scaled up a bit.
renderingFontScaleFactor = (double) targetSize / rawSize;
}
void GLFont::Drawer::drawString(const std::wstring& str, float xpos, float ypos, Align hAlign, Align vAlign, int vpx, int vpy, bool cacheable) const {
GLFont& appliedFont = fonts[renderingFontIndex];
appliedFont.drawString(str, round(appliedFont.pixHeight * renderingFontScaleFactor), xpos, ypos, hAlign, vAlign, vpx, vpy, cacheable);
}
//Public drawing font, 8 bit char version.
void GLFont::Drawer::drawString(const std::string& str, float xpos, float ypos, Align hAlign, Align vAlign, int vpx, int vpy, bool cacheable) const {
GLFont& appliedFont = fonts[renderingFontIndex];
appliedFont.drawString(str, round(appliedFont.pixHeight * renderingFontScaleFactor), xpos, ypos, hAlign, vAlign, vpx, vpy, cacheable);
}

View File

@ -0,0 +1,209 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#pragma once
#include <map>
#include <string>
#include <sstream>
#include <mutex>
#include <atomic>
#include "lodepng.h"
#include "wx/glcanvas.h"
#include "wx/filename.h"
#include "wx/stdpaths.h"
#include "SpinMutex.h"
class GLFontStringCache {
public:
GLFontStringCache();
int drawlen;
int vpx, vpy;
int pxHeight = 0;
float msgWidth = 0.0f;
std::atomic_int gc;
std::vector<float> gl_vertices;
std::vector<float> gl_uv;
};
class GLFontChar {
public:
GLFontChar();
~GLFontChar();
void setId(int idval);
// Returns the code point of the 16bit character, supposely Unicode.
int getId() const;
void setXOffset(int xofs);
int getXOffset();
void setYOffset(int yofs);
int getYOffset();
void setX(int xpos);
int getX();
void setY(int ypos);
int getY();
void setWidth(int w);
int getWidth();
void setHeight(int h);
int getHeight() const;
void setXAdvance(int xadv);
int getXAdvance();
float getAspect() const;
void setIndex(unsigned int idx);
int getIndex() const;
private:
// this is the code point of the 16bit character, supposly Unicode.
int id;
int x, y, width, height;
int xoffset, yoffset;
int xadvance;
float aspect;
int index;
};
class GLFont {
public:
enum Align {
GLFONT_ALIGN_LEFT, GLFONT_ALIGN_RIGHT, GLFONT_ALIGN_CENTER, GLFONT_ALIGN_TOP, GLFONT_ALIGN_BOTTOM
};
enum GLFontSize {
GLFONT_SIZE12,
GLFONT_SIZE16,
GLFONT_SIZE18,
GLFONT_SIZE24,
GLFONT_SIZE27, //new
GLFONT_SIZE32,
GLFONT_SIZE36, //new
GLFONT_SIZE48,
GLFONT_SIZE64, //new
GLFONT_SIZE72, //new
GLFONT_SIZE96, //new
GLFONT_SIZE_MAX
};
enum GLFontScale {
GLFONT_SCALE_NORMAL,
GLFONT_SCALE_MEDIUM, // x1.5
GLFONT_SCALE_LARGE, // x2
GLFONT_SCALE_MAX
};
GLFont(GLFontSize size, std::wstring fontFileName);
~GLFont();
//Called to change the scale of the rendered fonts
static void setScale(GLFontScale scale);
static GLFontScale getScale();
//Mean current scale factor: 1.0 in normal, 1.5 medium, 2.0 for large
static double getScaleFactor();
//Return a valid font px height given the font size and scale factor
static int getScaledPx(int basicFontSize, double scaleFactor);
private:
std::wstring nextParam(std::wistringstream &str);
std::wstring getParamKey(const std::wstring& param_str);
std::wstring getParamValue(const std::wstring& param_str);
//Repository of all loaded fonts
static GLFont fonts[GLFontSize::GLFONT_SIZE_MAX];
static std::atomic<GLFontScale> currentScale;
//load a given font file, (lazy loading)
void loadFontOnce();
//private drawing font, 16 bit char version, called by Drawer object
void drawString(const std::wstring& str, int pxHeight, float xpos, float ypos, Align hAlign = GLFONT_ALIGN_LEFT, Align vAlign = GLFONT_ALIGN_TOP, int vpx = 0, int vpy = 0, bool cacheable = false);
//private drawing font, 8 bit char version, called by Drawer object
void drawString(const std::string& str, int pxHeight, float xpos, float ypos, Align hAlign = GLFONT_ALIGN_LEFT, Align vAlign = GLFONT_ALIGN_TOP, int vpx = 0, int vpy = 0, bool cacheable = false);
GLFontStringCache *cacheString(const std::wstring& str, int pxHeight, int vpx, int vpy);
void drawCacheString(GLFontStringCache *fc, float xpos, float ypos, Align hAlign, Align vAlign) const;
void doCacheGC();
void clearCache();
//force GC of all available fonts
static void clearAllCaches();
float getStringWidth(const std::wstring& str, float size, float viewAspect);
//the string cache is per-front (internal font)
std::map<std::wstring, GLFontStringCache * > stringCache;
int lineHeight;
int base;
int imageWidth, imageHeight, pixHeight;
bool loaded;
GLFontSize fontSizeClass;
std::map<int, GLFontChar *> characters;
std::vector<float> gl_vertices;
std::vector<float> gl_uv;
//The font name as written in the def file.
std::wstring fontName;
//The full path font PNG filename
std::wstring imageFile;
//the real path location of the font definition file
std::wstring fontDefFileSource;
GLuint texId;
int gcCounter;
SpinMutex cache_busy;
public:
//Proxy class computing and caching the selection of the underlying fonts
//depending of the user input and requested scale for the fonts.
class Drawer {
private:
//result of the computation
int renderingFontIndex = 0;
double renderingFontScaleFactor = 1.0;
public:
Drawer(int basicFontSize, double scaleFactor);
//Public drawing font, 16 bit char version.
void drawString(const std::wstring& str, float xpos, float ypos, Align hAlign = GLFONT_ALIGN_LEFT, Align vAlign = GLFONT_ALIGN_TOP, int vpx = 0, int vpy = 0, bool cacheable = false) const;
//Public drawing font, 8 bit char version.
void drawString(const std::string& str, float xpos, float ypos, Align hAlign = GLFONT_ALIGN_LEFT, Align vAlign = GLFONT_ALIGN_TOP, int vpx = 0, int vpy = 0, bool cacheable = false) const;
}; //end class Drawer
//The User request a font of size requestedSize to display, with an additional
//optional scale factor scaleFactor.
static GLFont::Drawer getFont(int requestedSize, double scaleFactor = 1.0);
};

View File

@ -0,0 +1,87 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#include "Gradient.h"
#include <cstddef>
Gradient::Gradient() = default;
void Gradient::clear() {
colors.clear();
}
void Gradient::addColor(GradientColor c) {
colors.push_back(c);
}
void Gradient::addColors(const std::vector<GradientColor>& color_list) {
for (auto single_color : color_list) {
colors.push_back(single_color);
}
}
std::vector<float> &Gradient::getRed() {
return r_val;
}
std::vector<float> &Gradient::getGreen() {
return g_val;
}
std::vector<float> &Gradient::getBlue() {
return b_val;
}
void Gradient::generate(unsigned int len) {
size_t chunk_size = len / (colors.size() - 1);
size_t p = 0;
r_val.resize(len);
g_val.resize(len);
b_val.resize(len);
for (size_t j = 0, jMax = colors.size() - 1; j < jMax; j++) {
if ((chunk_size * (jMax)) < len && (j == jMax - 1)) {
chunk_size += len - chunk_size * (jMax);
}
for (size_t i = 0; i < chunk_size; i++) {
float idx = (float) (i) / (float) chunk_size;
float r1 = colors[j].r;
float g1 = colors[j].g;
float b1 = colors[j].b;
float r2 = colors[j + 1].r;
float g2 = colors[j + 1].g;
float b2 = colors[j + 1].b;
float r = r1 + (r2 - r1) * idx;
float g = g1 + (g2 - g1) * idx;
float b = b1 + (b2 - b1) * idx;
if (r < 0.0)
r = 0.0;
if (r > 1.0)
r = 1.0;
if (g < 0.0)
g = 0.0;
if (g > 1.0)
g = 1.0;
if (b < 0.0)
b = 0.0;
if (b > 1.0)
b = 1.0;
r_val[p] = r;
g_val[p] = g;
b_val[p] = b;
p++;
}
}
}
Gradient::~Gradient() = default;

View File

@ -0,0 +1,41 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#pragma once
#include <vector>
class GradientColor {
public:
float r, g, b;
float w;
//should work with both float and double inputs
GradientColor(double r_in, double g_in, double b_in) :
r((float)r_in), g((float)g_in), b((float)b_in), w(1) {
}
};
class Gradient {
public:
Gradient();
void addColor(GradientColor c);
void clear();
void addColors(const std::vector<GradientColor>& color_list);
std::vector<float> &getRed();
std::vector<float> &getGreen();
std::vector<float> &getBlue();
void generate(unsigned int len);
~Gradient();
private:
std::vector<GradientColor> colors;
std::vector<float> r_val;
std::vector<float> g_val;
std::vector<float> b_val;
};

View File

@ -0,0 +1,183 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#include "MouseTracker.h"
MouseTracker::MouseTracker(wxWindow *target) :
mouseX(0), mouseY(0), lastMouseX(0), lastMouseY(0), originMouseX(0), originMouseY(0), deltaMouseX(0), deltaMouseY(0), vertDragLock(false), horizDragLock(
false), isMouseDown(false), isMouseRightDown(false), isMouseInView(false), target(target) {
}
MouseTracker::MouseTracker() :
MouseTracker(nullptr) {
}
void MouseTracker::OnMouseMoved(wxMouseEvent& event) {
if (target == nullptr) {
return;
}
const wxSize ClientSize = target->GetClientSize();
mouseX = (float) event.m_x / (float) ClientSize.x;
mouseY = 1.0 - (float) event.m_y / (float) ClientSize.y;
deltaMouseX = mouseX - lastMouseX;
deltaMouseY = mouseY - lastMouseY;
if (isMouseDown || isMouseRightDown) {
#ifndef __APPLE__
#if !defined(__linux__) && !defined(__FreeBSD__)
if (horizDragLock && vertDragLock) {
target->WarpPointer(originMouseX * ClientSize.x, (1.0 - originMouseY) * ClientSize.y);
mouseX = originMouseX;
mouseY = originMouseY;
} else if (vertDragLock && mouseY != lastMouseY) {
target->WarpPointer(event.m_x, (1.0 - originMouseY) * ClientSize.y);
mouseY = originMouseY;
} else if (horizDragLock && mouseX != lastMouseX) {
target->WarpPointer(originMouseX * ClientSize.x, event.m_y);
mouseX = originMouseX;
}
#endif
#endif
}
lastMouseX = mouseX;
lastMouseY = mouseY;
}
void MouseTracker::OnMouseWheelMoved(wxMouseEvent& /* event */) {
// std::cout << "wheel?" << std::endl;
}
void MouseTracker::OnMouseDown(wxMouseEvent& event) {
if (isMouseRightDown || target == nullptr) {
return;
}
const wxSize ClientSize = target->GetClientSize();
mouseX = lastMouseX = (float) event.m_x / (float) ClientSize.x;
mouseY = lastMouseY = 1.0 - (float) event.m_y / (float) ClientSize.y;
originMouseX = mouseX;
originMouseY = mouseY;
isMouseDown = true;
}
void MouseTracker::OnMouseReleased(wxMouseEvent& /* event */) {
isMouseDown = false;
}
void MouseTracker::OnMouseRightDown(wxMouseEvent& event) {
if (isMouseDown || target == nullptr) {
return;
}
const wxSize ClientSize = target->GetClientSize();
mouseX = lastMouseX = (float) event.m_x / (float) ClientSize.x;
mouseY = lastMouseY = 1.0 - (float) event.m_y / (float) ClientSize.y;
originMouseX = mouseX;
originMouseY = mouseY;
isMouseRightDown = true;
}
void MouseTracker::OnMouseRightReleased(wxMouseEvent& /* event */) {
isMouseRightDown = false;
}
void MouseTracker::OnMouseEnterWindow(wxMouseEvent& /* event */) {
isMouseInView = true;
isMouseDown = false;
isMouseRightDown = false;
}
void MouseTracker::OnMouseLeftWindow(wxMouseEvent& /* event */) {
isMouseDown = false;
isMouseRightDown = false;
isMouseInView = false;
}
float MouseTracker::getOriginMouseX() const {
return originMouseX;
}
float MouseTracker::getOriginMouseY() const {
return originMouseY;
}
float MouseTracker::getOriginDeltaMouseX() const {
return mouseX - originMouseX;
}
float MouseTracker::getOriginDeltaMouseY() const {
return mouseY - originMouseY;
}
float MouseTracker::getDeltaMouseX() const {
return deltaMouseX;
}
float MouseTracker::getDeltaMouseY() const {
return deltaMouseY;
}
float MouseTracker::getLastMouseX() const {
return lastMouseX;
}
float MouseTracker::getLastMouseY() const {
return lastMouseY;
}
CubicVR::vec2 MouseTracker::getGLXY() const {
return CubicVR::vec2((getMouseX()-0.5)*2.0, (getMouseY()-0.5)*2.0);
}
float MouseTracker::getMouseX() const {
return mouseX;
}
float MouseTracker::getMouseY() const {
return mouseY;
}
void MouseTracker::setVertDragLock(bool dragLock) {
vertDragLock = dragLock;
}
void MouseTracker::setHorizDragLock(bool dragLock) {
horizDragLock = dragLock;
}
bool MouseTracker::getVertDragLock() const {
return vertDragLock;
}
bool MouseTracker::getHorizDragLock() const {
return horizDragLock;
}
bool MouseTracker::mouseDown() const {
return isMouseDown;
}
bool MouseTracker::mouseInView() const {
return isMouseInView;
}
void MouseTracker::setTarget(wxWindow *target_in) {
target = target_in;
}
bool MouseTracker::mouseRightDown() const {
return isMouseRightDown;
}

View File

@ -0,0 +1,53 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#pragma once
#include "wx/window.h"
#include "cubic_math.h"
class MouseTracker {
public:
MouseTracker(wxWindow *target);
MouseTracker();
void OnMouseMoved(wxMouseEvent& event);
void OnMouseWheelMoved(wxMouseEvent& event);
void OnMouseDown(wxMouseEvent& event);
void OnMouseReleased(wxMouseEvent& event);
void OnMouseRightDown(wxMouseEvent& event);
void OnMouseRightReleased(wxMouseEvent& event);
void OnMouseEnterWindow(wxMouseEvent& event);
void OnMouseLeftWindow(wxMouseEvent& event);
float getOriginMouseX() const;
float getOriginMouseY() const;
float getOriginDeltaMouseX() const;
float getOriginDeltaMouseY() const;
float getDeltaMouseX() const;
float getDeltaMouseY() const;
float getLastMouseX() const;
float getLastMouseY() const;
CubicVR::vec2 getGLXY() const;
float getMouseX() const;
float getMouseY() const;
void setVertDragLock(bool dragLock);
void setHorizDragLock(bool dragLock);
bool getVertDragLock() const;
bool getHorizDragLock() const;
bool mouseDown() const;
bool mouseRightDown() const;
bool mouseInView() const;
void setTarget(wxWindow *target_in);
private:
float mouseX, mouseY;
float lastMouseX, lastMouseY;
float originMouseX, originMouseY;
float deltaMouseX, deltaMouseY;
bool vertDragLock, horizDragLock;
bool isMouseDown, isMouseRightDown, isMouseInView;
wxWindow *target;
};

View File

@ -0,0 +1,27 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#pragma once
#include <atomic>
// A non-recursive Mutex implemented as a spin-lock, implementing the Lockable requirement
class SpinMutex {
public:
SpinMutex() = default;
SpinMutex(const SpinMutex&) = delete;
SpinMutex& operator=(const SpinMutex&) = delete;
~SpinMutex() { lock_state.clear(std::memory_order_release); }
void lock() { while (lock_state.test_and_set(std::memory_order_acquire)); }
bool try_lock() {return !lock_state.test_and_set(std::memory_order_acquire); }
void unlock() { lock_state.clear(std::memory_order_release); }
private:
std::atomic_flag lock_state = ATOMIC_FLAG_INIT;
};

View File

@ -0,0 +1,4 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#include <ThreadBlockingQueue.h>

View File

@ -0,0 +1,229 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#pragma once
#include <deque>
#include <mutex>
#include <thread>
#include <cstdint>
#include <cstddef>
#include <condition_variable>
#include <typeinfo>
#include <iostream>
#include "SpinMutex.h"
#define MIN_ITEM_NB (1)
//use this timeout constant in either pop() or push() calls to indicate
// a non-blocking operation, so respectively equivalent to try_pop() and try_push()
#define NON_BLOCKING_TIMEOUT (100)
//use this timeout constant in either pop() or push() calls to indicate
//an indefnite timeout duration.
#define BLOCKING_INFINITE_TIMEOUT (0)
class ThreadQueueBase {
};
typedef std::shared_ptr<ThreadQueueBase> ThreadQueueBasePtr;
/** A thread-safe asynchronous blocking queue */
template<typename T>
class ThreadBlockingQueue : public ThreadQueueBase {
typedef typename std::deque<T>::value_type value_type;
typedef typename std::deque<T>::size_type size_type;
public:
/*! Create safe blocking queue. */
ThreadBlockingQueue() {
//at least 1 (== Java SynchronizedQueue)
m_max_num_items = MIN_ITEM_NB;
};
//Forbid Copy construction.
ThreadBlockingQueue(const ThreadBlockingQueue& sq) = delete;
/*! Forbid copy assignment. */
ThreadBlockingQueue& operator=(const ThreadBlockingQueue& sq) = delete;
/*! Destroy safe queue. */
~ThreadBlockingQueue() {
std::lock_guard < SpinMutex > lock(m_mutex);
}
/**
* Sets the maximum number of items in the queue. Real value is clamped
* to 1 on the lower bound.
* \param[in] nb max of items
*/
void set_max_num_items(unsigned int max_num_items) {
std::lock_guard < SpinMutex > lock(m_mutex);
if (max_num_items > m_max_num_items) {
//Only raise the existing max size, never reduce it
//for simplification sake at runtime.
m_max_num_items = max_num_items;
m_cond_not_full.notify_all();
}
}
/**
* Pushes the item into the queue. If the queue is full, waits until room
* is available, for at most timeout microseconds.
* \param[in] item An item.
* \param[in] timeout a max waiting timeout in microseconds for an item to be pushed.
* by default, = 0 means indefinite wait.
* \param[in] errorMessage if != nullptr (is nullptr by default) an error message written on std::cout in case of the timeout wait
* \return true if an item was pushed into the queue, else a timeout has occurred.
*/
bool push(const value_type& item, std::uint64_t timeout = BLOCKING_INFINITE_TIMEOUT,const char* errorMessage = nullptr) {
std::unique_lock < SpinMutex > lock(m_mutex);
if (timeout == BLOCKING_INFINITE_TIMEOUT) {
m_cond_not_full.wait(lock, [this]() // Lambda funct
{
return m_queue.size() < m_max_num_items;
});
} else if (timeout <= NON_BLOCKING_TIMEOUT && m_queue.size() >= m_max_num_items) {
// if the value is below a threshold, consider it is a try_push()
return false;
}
else if (false == m_cond_not_full.wait_for(lock, std::chrono::microseconds(timeout),
[this]() { return m_queue.size() < m_max_num_items; })) {
if (errorMessage != nullptr) {
std::thread::id currentThreadId = std::this_thread::get_id();
std::cout << "WARNING: Thread 0x" << std::hex << currentThreadId << std::dec <<
" (" << currentThreadId << ") executing {" << typeid(*this).name() << "}.push() has failed with timeout > " <<
(timeout * 0.001) << " ms, message: '" << errorMessage << "'" << std::endl << std::flush;
}
return false;
}
m_queue.push_back(item);
m_cond_not_empty.notify_all();
return true;
}
/**
* Try to pushes the item into the queue, immediately, without waiting. If the queue is full, the item
* is not inserted and the function returns false.
* \param[in] item An item.
*/
bool try_push(const value_type& item) {
std::lock_guard < SpinMutex > lock(m_mutex);
if (m_queue.size() >= m_max_num_items) {
return false;
}
m_queue.push_back(item);
m_cond_not_empty.notify_all();
return true;
}
/**
* Pops item from the queue. If the queue is empty, blocks for timeout microseconds, or until item becomes available.
* \param[in] timeout The number of microseconds to wait. O (default) means indefinite wait.
* \param[in] errorMessage if != nullptr (is nullptr by default) an error message written on std::cout in case of the timeout wait
* \return true if get an item from the queue, false if no item is received before the timeout.
*/
bool pop(value_type& item, std::uint64_t timeout = BLOCKING_INFINITE_TIMEOUT, const char* errorMessage = nullptr) {
std::unique_lock < SpinMutex > lock(m_mutex);
if (timeout == BLOCKING_INFINITE_TIMEOUT) {
m_cond_not_empty.wait(lock, [this]() // Lambda funct
{
return !m_queue.empty();
});
} else if (timeout <= NON_BLOCKING_TIMEOUT && m_queue.empty()) {
// if the value is below a threshold, consider it is try_pop()
return false;
}
else if (false == m_cond_not_empty.wait_for(lock, std::chrono::microseconds(timeout),
[this]() { return !m_queue.empty(); })) {
if (errorMessage != nullptr) {
std::thread::id currentThreadId = std::this_thread::get_id();
std::cout << "WARNING: Thread 0x" << std::hex << currentThreadId << std::dec <<
" (" << currentThreadId << ") executing {" << typeid(*this).name() << "}.pop() has failed with timeout > " <<
(timeout * 0.001) << " ms, message: '" << errorMessage << "'" << std::endl << std::flush;
}
return false;
}
item = m_queue.front();
m_queue.pop_front();
m_cond_not_full.notify_all();
return true;
}
/**
* Tries to pop item from the queue.
* \param[out] item The item.
* \return False is returned if no item is available.
*/
bool try_pop(value_type& item) {
std::lock_guard < SpinMutex > lock(m_mutex);
if (m_queue.empty()) {
return false;
}
item = m_queue.front();
m_queue.pop_front();
m_cond_not_full.notify_all();
return true;
}
/**
* Gets the number of items in the queue.
* \return Number of items in the queue.
*/
size_type size() const {
std::lock_guard < SpinMutex > lock(m_mutex);
return m_queue.size();
}
/**
* Check if the queue is empty.
* \return true if queue is empty.
*/
bool empty() const {
std::lock_guard < SpinMutex > lock(m_mutex);
return m_queue.empty();
}
/**
* Check if the queue is full.
* \return true if queue is full.
*/
bool full() const {
std::lock_guard < SpinMutex > lock(m_mutex);
return (m_queue.size() >= m_max_num_items);
}
/**
* Remove any items in the queue.
*/
void flush() {
std::lock_guard < SpinMutex > lock(m_mutex);
m_queue.clear();
m_cond_not_full.notify_all();
}
private:
std::deque<T> m_queue;
mutable SpinMutex m_mutex;
std::condition_variable_any m_cond_not_empty;
std::condition_variable_any m_cond_not_full;
size_t m_max_num_items = MIN_ITEM_NB;
};

View File

@ -0,0 +1,182 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#include <iostream>
#include "Timer.h"
Timer::Timer() : time_elapsed(0), system_milliseconds(0), start_time(0), end_time(0), last_update(0), num_updates(0), paused_time(0), offset(0), paused_state(false), lock_state(false), lock_rate(0)
{
#ifdef _WIN32
// According to Microsoft, QueryPerformanceXXX API is perfectly
//fine for Windows 7+ systems, and use the highest appropriate counter.
//this only need to be done once.
::QueryPerformanceFrequency(&win_frequency);
#endif
}
void Timer::start()
{
update();
num_updates = 0;
start_time = system_milliseconds;
last_update = start_time;
paused_state = false;
lock_state = false;
lock_rate = 0;
paused_time = 0;
offset = 0;
}
void Timer::stop()
{
end_time = system_milliseconds;
}
void Timer::reset()
{
start();
}
void Timer::lockFramerate(float f_rate)
{
lock_rate = 1.0f/f_rate;
lock_state = true;
}
void Timer::unlock()
{
unsigned long msec_tmp = system_milliseconds;
lock_state = false;
update();
last_update = system_milliseconds-(unsigned long)lock_rate;
offset += msec_tmp-system_milliseconds;
lock_rate = 0;
}
bool Timer::locked() const
{
return lock_state;
}
void Timer::update()
{
num_updates++;
last_update = system_milliseconds;
if (lock_state)
{
system_milliseconds += (unsigned long)(lock_rate*1000.0);
}
else
{
#ifdef _WIN32
//Use QuaryPerformanceCounter, imune to problems sometimes
//multimedia timers have.
LARGE_INTEGER win_current_count;
::QueryPerformanceCounter(&win_current_count);
system_milliseconds = (unsigned long)(win_current_count.QuadPart * 1000.0 / win_frequency.QuadPart);
#else
gettimeofday(&time_val,&time_zone);
system_milliseconds = (unsigned long)time_val.tv_usec;
system_milliseconds /= 1000;
system_milliseconds += (unsigned long)(time_val.tv_sec*1000);
#endif
}
if (paused_state) paused_time += system_milliseconds-last_update;
time_elapsed = system_milliseconds-start_time-paused_time+offset;
}
unsigned long Timer::getMilliseconds() const
{
return time_elapsed;
}
double Timer::getSeconds() const
{
return ((double)getMilliseconds())/1000.0;
}
void Timer::setMilliseconds(unsigned long milliseconds_in)
{
offset -= (system_milliseconds-start_time-paused_time+offset)-milliseconds_in;
}
void Timer::setSeconds(double seconds_in)
{
setMilliseconds((long)(seconds_in*1000.0));
}
double Timer::lastUpdateSeconds() const
{
return ((double)lastUpdateMilliseconds())/1000.0;
}
unsigned long Timer::lastUpdateMilliseconds() const
{
return system_milliseconds-last_update;
}
unsigned long Timer::totalMilliseconds() const
{
return system_milliseconds-start_time;
}
double Timer::totalSeconds() const
{
return totalMilliseconds()/1000.0;
}
unsigned long Timer::getNumUpdates() const
{
return num_updates;
}
void Timer::paused(bool pause_in)
{
paused_state = pause_in;
}
bool Timer::paused() const
{
return paused_state;
}
void Timer::timerTestFunc() {
update();
if (getNumUpdates() % 120 == 0) {
std::cout << getNumUpdates() << "," << getSeconds() << " Rate: " << ((double)getNumUpdates()/getSeconds()) << "/sec" << std::endl;
}
if (getNumUpdates() >= 600) {
reset();
}
}

View File

@ -0,0 +1,171 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
#ifndef TIMER_H
#define TIMER_H
#ifdef WIN32
#include <windows.h>
#else
#include <sys/time.h>
#endif
/// Timer Class, high resolution timer
/**
* Class provides high resolution timing and useful time control functions
*/
class Timer {
private:
unsigned long time_elapsed;
unsigned long system_milliseconds;
unsigned long start_time;
unsigned long end_time;
unsigned long last_update;
unsigned long num_updates;
unsigned long paused_time;
unsigned long offset;
#ifndef _WIN32
struct timeval time_val;
struct timezone time_zone;
#else
LARGE_INTEGER win_frequency;
#endif
bool paused_state;
bool lock_state;
float lock_rate;
public:
/// Constructor
Timer();
/// Start the timer
/**
* Resets the timer to 0 and begins timing
*/
void start();
/// Stop the timer
/**
* Stops the timer and records the end time
*/
void stop();
/// Locks the timer to a specified framerate (for recording / benchmarking purposes typically)
/**
* Locks the timer to a specified framerate (for recording / benchmarking purposes typically)
*/
void lockFramerate(float f_rate);
/// Unlock any framerate lock that's been applied
/**
* Unlock any framerate lock that's been applied
*/
void unlock();
/// Check locked state
/**
* Check locked state
*/
bool locked() const;
/// Reset the timer counter
/**
* Resetting the timer will reset the current time to 0
*/
void reset();
/// Timer update
/**
* Calling the update command will bring the timer value up to date, this is meant
* to be called at the beginning of the frame to establish the time index which is being drawn.
*/
void update();
/// Get the total time elapsed since the timer start, not counting paused time
/**
* Returns the total time elapsed in since the timer start() to the last update() but
* does not count the time elapsed while the timer is paused().
* \return Total time elapsed since the timer start() to the last update() excluding time paused() in milliseconds
*/
unsigned long getMilliseconds() const;
/// Alias of getMilliseconds() which returns time in seconds
/**
* \return Total time elapsed since the timer start() to the last update() excluding time paused() in seconds
*/
double getSeconds() const;
/// Get the total time elapsed since the timer start
/**
* Returns the total time elapsed in since the timer start() to the last update()
* this includes any time accumulated during updates while paused()
* \return Total time elapsed since the timer start() to the last update() including time paused() in milliseconds
*/
unsigned long totalMilliseconds() const;
/// Alias of totalMilliseconds() which returns time in seconds
/**
* \return Total time elapsed since the timer start() to the last update() including time paused() in seconds
*/
double totalSeconds() const;
/// Set the amount of time elapsed
/**
* Force the timer duration to a specific value, useful for rolling forward or back in a system
* based upon the timer.
* \param milliseconds_in Time to set timer to in milliseconds
*/
void setMilliseconds(unsigned long milliseconds_in);
/// alias of setMilliseconds() which accepts time in seconds
/**
* \param seconds_in Time to set timer to in seconds
*/
void setSeconds(double seconds_in);
/// Get the amount of times the update() command has been called
/**
* By using the number of times the update() command has been called you can easily determine
* an average frame rate. Also useful for merely determining how many frames have been drawn.
* \return Number of times update() has been called
*/
unsigned long getNumUpdates() const;
/// Get the timer duration during the last update
/**
* Useful for determining the amount of time which elapsed during the last update
* can be used to accurately control values with a per-second rate or determine the current frame rate.
* \return Duration of time between the last two calls to update() in milliseconds
*/
unsigned long lastUpdateMilliseconds() const;
/// Alias of lastUpdateMilliseconds() which returns time in seconds
/**
* \return Duration of time between the last two calls to update() in seconds
*/
double lastUpdateSeconds() const;
/// Set the timer pause state
/**
* Pause the timer, allowing for continued update() calls without an increment in timing but
* maintaining the update and total time count, useful for pausing a scene but allowing frame
* timing to resume.
* \param pause_in Value to set the current pause state to
*/
void paused(bool pause_in);
/// Check if the timer is currently in a paused state
/**
* \return Current pause state, true if paused, false otherwise
*/
bool paused() const;
void timerTestFunc();
};
#endif