mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-22 01:19:19 +02:00
Merge branch 'copilot/rebuild-files-structure' into ecs
This commit is contained in:
94
utilities/Classes.h
Normal file
94
utilities/Classes.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef ClassesH
|
||||
#define ClassesH
|
||||
//---------------------------------------------------------------------------
|
||||
// Ra: zestaw klas do robienia wskaźników, aby uporządkować nagłówki
|
||||
//---------------------------------------------------------------------------
|
||||
class opengl_renderer;
|
||||
class opengl33_renderer;
|
||||
class TTrack; // odcinek trajektorii
|
||||
class basic_event;
|
||||
class TTrain; // pojazd sterowany
|
||||
class TDynamicObject; // pojazd w scenerii
|
||||
struct material_data;
|
||||
class TAnimModel; // opakowanie egzemplarz modelu
|
||||
class TAnimContainer; // fragment opakowania egzemplarza modelu
|
||||
class TModel3d; //siatka modelu wspólna dla egzemplarzy
|
||||
class TSubModel; // fragment modelu (tu do wyświetlania terenu)
|
||||
class TMemCell; // komórka pamięci
|
||||
class cParser;
|
||||
class sound_source;
|
||||
class TEventLauncher;
|
||||
class TTraction; // drut
|
||||
class TTractionPowerSource; // zasilanie drutów
|
||||
class TCamera;
|
||||
class scenario_time;
|
||||
class TMoverParameters;
|
||||
class ui_layer;
|
||||
class editor_ui;
|
||||
class itemproperties_panel;
|
||||
class event_manager;
|
||||
class memory_table;
|
||||
class powergridsource_table;
|
||||
class instance_table;
|
||||
class vehicle_table;
|
||||
class train_table;
|
||||
struct light_array;
|
||||
class particle_manager;
|
||||
struct dictionary_source;
|
||||
class trainset_desc;
|
||||
class scenery_desc;
|
||||
|
||||
namespace plc {
|
||||
using element_handle = short;
|
||||
class basic_controller;
|
||||
}
|
||||
|
||||
namespace scene {
|
||||
struct node_data;
|
||||
class basic_node;
|
||||
using group_handle = std::size_t;
|
||||
}
|
||||
|
||||
namespace Mtable
|
||||
{
|
||||
class TTrainParameters; // rozkład jazdy
|
||||
class TMtableTime; // czas dla danego posterunku
|
||||
};
|
||||
|
||||
class TController; // obiekt sterujący pociągiem (AI)
|
||||
|
||||
enum class TCommandType
|
||||
{ // binarne odpowiedniki komend w komórce pamięci
|
||||
cm_Unknown, // ciąg nierozpoznany (nie jest komendą)
|
||||
cm_Ready, // W4 zezwala na odjazd, ale semafor może zatrzymać
|
||||
cm_SetVelocity, // prędkość pociągowa zadawana na semaforze
|
||||
cm_RoadVelocity, // prędkość drogowa
|
||||
cm_SectionVelocity, //ograniczenie prędkości na odcinku
|
||||
cm_ShuntVelocity, // prędkość manewrowa na semaforze
|
||||
cm_SetProximityVelocity, // informacja wstępna o ograniczeniu
|
||||
cm_ChangeDirection,
|
||||
cm_PassengerStopPoint,
|
||||
cm_OutsideStation,
|
||||
// cm_Shunt, // unused?
|
||||
cm_EmergencyBrake,
|
||||
cm_SecuritySystemMagnet,
|
||||
cm_Command // komenda pobierana z komórki
|
||||
};
|
||||
|
||||
using material_handle = int;
|
||||
using texture_handle = int;
|
||||
|
||||
struct invalid_scenery_exception : std::runtime_error {
|
||||
invalid_scenery_exception() : std::runtime_error("cannot load scenery") {}
|
||||
};
|
||||
|
||||
#endif
|
||||
49
utilities/Float3d.cpp
Normal file
49
utilities/Float3d.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "utilities/Float3d.h"
|
||||
#include "scene/sn_utils.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void float4x4::deserialize_float32(std::istream &s)
|
||||
{
|
||||
for (size_t i = 0; i < 16; i++)
|
||||
e[i] = sn_utils::ld_float32(s);
|
||||
}
|
||||
|
||||
void float4x4::deserialize_float64(std::istream &s)
|
||||
{
|
||||
for (size_t i = 0; i < 16; i++)
|
||||
e[i] = (float)sn_utils::ld_float64(s);
|
||||
}
|
||||
|
||||
void float4x4::serialize_float32(std::ostream &s)
|
||||
{
|
||||
for (size_t i = 0; i < 16; i++)
|
||||
sn_utils::ls_float32(s, e[i]);
|
||||
}
|
||||
|
||||
void float4x4::Quaternion(float4 *q)
|
||||
{ // konwersja kwaternionu obrotu na macierz obrotu
|
||||
float xx = q->x * q->x, yy = q->y * q->y, zz = q->z * q->z;
|
||||
float xy = q->x * q->y, xz = q->x * q->z, yz = q->y * q->z;
|
||||
float wx = q->w * q->x, wy = q->w * q->y, wz = q->w * q->z;
|
||||
e[0] = 1.0f - yy - yy - zz - zz;
|
||||
e[1] = xy + xy + wz + wz;
|
||||
e[2] = xz + xz - wy - wy;
|
||||
e[4] = xy + xy - wz - wz;
|
||||
e[5] = 1.0f - xx - xx - zz - zz;
|
||||
e[6] = yz + yz + wx + wx;
|
||||
e[8] = xz + xz + wy + wy;
|
||||
e[9] = yz + yz - wx - wx;
|
||||
e[10] = 1.0f - xx - xx - yy - yy;
|
||||
// czwartej kolumny i czwartego wiersza nie ruszamy
|
||||
};
|
||||
356
utilities/Float3d.h
Normal file
356
utilities/Float3d.h
Normal file
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class float3
|
||||
{ // wapółrzędne wierchołka 3D o pojedynczej precyzji
|
||||
public:
|
||||
float x, y, z;
|
||||
float3(void){};
|
||||
float3(float a, float b, float c)
|
||||
{
|
||||
x = a;
|
||||
y = b;
|
||||
z = c;
|
||||
};
|
||||
float Length() const;
|
||||
float LengthSquared() const;
|
||||
|
||||
operator glm::vec3() const
|
||||
{
|
||||
return glm::vec3(x, y, z);
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const float3 &v1, const float3 &v2)
|
||||
{
|
||||
return (v1.x == v2.x && v1.y == v2.y && v1.z == v2.z);
|
||||
};
|
||||
inline float3 &operator+=(float3 &v1, const float3 &v2)
|
||||
{
|
||||
v1.x += v2.x;
|
||||
v1.y += v2.y;
|
||||
v1.z += v2.z;
|
||||
return v1;
|
||||
};
|
||||
inline float3 operator-(const float3 &v)
|
||||
{
|
||||
return float3(-v.x, -v.y, -v.z);
|
||||
};
|
||||
inline float3 operator-(const float3 &v1, const float3 &v2)
|
||||
{
|
||||
return float3(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
|
||||
};
|
||||
inline float3 operator+(const float3 &v1, const float3 &v2)
|
||||
{
|
||||
return float3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
|
||||
};
|
||||
inline float float3::Length() const
|
||||
{
|
||||
return std::sqrt(LengthSquared());
|
||||
};
|
||||
inline float float3::LengthSquared() const {
|
||||
return ( x * x + y * y + z * z );
|
||||
}
|
||||
inline float3 operator*( float3 const &v, float const k ) {
|
||||
return float3( v.x * k, v.y * k, v.z * k );
|
||||
};
|
||||
inline float3 operator/( float3 const &v, float const k )
|
||||
{
|
||||
return float3(v.x / k, v.y / k, v.z / k);
|
||||
};
|
||||
inline float3 SafeNormalize(const float3 &v)
|
||||
{ // bezpieczna normalizacja (wektor długości 1.0)
|
||||
auto const l = v.Length();
|
||||
float3 retVal;
|
||||
if (l == 0)
|
||||
retVal.x = retVal.y = retVal.z = 0;
|
||||
else
|
||||
retVal = v / l;
|
||||
return retVal;
|
||||
};
|
||||
inline float3 CrossProduct( float3 const &v1, float3 const &v2 )
|
||||
{
|
||||
return float3(v1.y * v2.z - v1.z * v2.y, v2.x * v1.z - v2.z * v1.x, v1.x * v2.y - v1.y * v2.x);
|
||||
}
|
||||
inline float DotProduct( float3 const &v1, float3 const &v2 ) {
|
||||
|
||||
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
|
||||
}
|
||||
|
||||
inline float3 Interpolate( float3 const &First, float3 const &Second, float const Factor ) {
|
||||
|
||||
return ( First * ( 1.0f - Factor ) ) + ( Second * Factor );
|
||||
}
|
||||
|
||||
class float4
|
||||
{ // kwaternion obrotu
|
||||
public:
|
||||
float x, y, z, w;
|
||||
float4()
|
||||
{
|
||||
x = y = z = 0.f;
|
||||
w = 1.f;
|
||||
};
|
||||
float4(float a, float b, float c, float d)
|
||||
{
|
||||
x = a;
|
||||
y = b;
|
||||
z = c;
|
||||
w = d;
|
||||
};
|
||||
float inline LengthSquared() const
|
||||
{
|
||||
return x * x + y * y + z * z + w * w;
|
||||
};
|
||||
float inline Length() const
|
||||
{
|
||||
return sqrt(x * x + y * y + z * z + w * w);
|
||||
};
|
||||
};
|
||||
inline float4 operator*(const float4 &q1, const float4 &q2)
|
||||
{ // mnożenie to prawie jak mnożenie macierzy
|
||||
return float4(q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y,
|
||||
q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z,
|
||||
q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x,
|
||||
q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z);
|
||||
}
|
||||
inline float4 operator-(const float4 &q)
|
||||
{ // sprzężony; odwrotny tylko dla znormalizowanych!
|
||||
return float4(-q.x, -q.y, -q.z, q.w);
|
||||
};
|
||||
inline float4 operator-(const float4 &q1, const float4 &q2)
|
||||
{ // z odejmowaniem nie ma lekko
|
||||
return (-q1) * q2; // inwersja tylko dla znormalizowanych!
|
||||
};
|
||||
inline float4 operator+(const float4 &v1, const float4 &v2)
|
||||
{
|
||||
return float4(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z, v1.w + v2.w);
|
||||
};
|
||||
inline float4 operator/(const float4 &v, float const k)
|
||||
{
|
||||
return float4(v.x / k, v.y / k, v.z / k, v.w / k);
|
||||
};
|
||||
inline float4 Normalize(const float4 &v)
|
||||
{ // bezpieczna normalizacja (wektor długości 1.0)
|
||||
auto const lengthsquared = v.LengthSquared();
|
||||
if (lengthsquared == 1.0)
|
||||
return v;
|
||||
if (lengthsquared == 0.0)
|
||||
return float4(); // wektor zerowy, w=1
|
||||
else
|
||||
return v / std::sqrt(lengthsquared); // pierwiastek liczony tylko jeśli trzeba wykonać dzielenia
|
||||
};
|
||||
inline
|
||||
float Dot(const float4 &q1, const float4 &q2)
|
||||
{ // iloczyn skalarny
|
||||
return q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
|
||||
}
|
||||
inline float4 &operator*=(float4 &v1, float const d)
|
||||
{ // mnożenie przez skalar, jaki ma sens?
|
||||
v1.x *= d;
|
||||
v1.y *= d;
|
||||
v1.z *= d;
|
||||
v1.w *= d;
|
||||
return v1;
|
||||
};
|
||||
inline float4 Slerp(const float4 &q0, const float4 &q1, float t)
|
||||
// void Slerp(QUATERNION *Out, const QUATERNION &q0, const QUATERNION &q1, float t)
|
||||
{ // interpolacja sweryczna
|
||||
float cosOmega = Dot(q0, q1);
|
||||
float4 new_q1(q1);
|
||||
if (cosOmega < 0.0f)
|
||||
{ // jeżeli są niezgodne kierunki, jeden z nich trzeba zanegować
|
||||
new_q1.x = -new_q1.x;
|
||||
new_q1.y = -new_q1.y;
|
||||
new_q1.z = -new_q1.z;
|
||||
new_q1.w = -new_q1.w;
|
||||
cosOmega = -cosOmega;
|
||||
}
|
||||
float k0, k1;
|
||||
if (cosOmega > 0.9999f)
|
||||
{ // jeśli jesteśmy z (t) na maksimum kosinusa, to tam prawie liniowo jest
|
||||
k0 = 1.0f - t;
|
||||
k1 = t;
|
||||
}
|
||||
else
|
||||
{ // a w ogólnym przypadku trzeba liczyć na trygonometrię
|
||||
auto const sinOmega = std::sqrt(1.0f - cosOmega * cosOmega); // sinus z jedynki tryg.
|
||||
auto const omega = std::atan2(sinOmega, cosOmega); // wyznaczenie kąta
|
||||
auto const oneOverSinOmega = 1.0f / sinOmega; // odwrotność sinusa, bo sinus w mianowniku
|
||||
k0 = sin((1.0f - t) * omega) * oneOverSinOmega;
|
||||
k1 = sin(t * omega) * oneOverSinOmega;
|
||||
}
|
||||
return float4(q0.x * k0 + new_q1.x * k1, q0.y * k0 + new_q1.y * k1, q0.z * k0 + new_q1.z * k1,
|
||||
q0.w * k0 + new_q1.w * k1);
|
||||
}
|
||||
|
||||
struct float8
|
||||
{ // wierchołek 3D z wektorem normalnym i mapowaniem, pojedyncza precyzja
|
||||
public:
|
||||
float3 Point;
|
||||
float3 Normal;
|
||||
float tu, tv;
|
||||
};
|
||||
|
||||
class float4x4
|
||||
{ // macierz transformacji pojedynczej precyzji
|
||||
public:
|
||||
float e[16];
|
||||
|
||||
void deserialize_float32(std::istream&);
|
||||
void deserialize_float64(std::istream&);
|
||||
void serialize_float32(std::ostream&);
|
||||
float4x4(void){};
|
||||
float4x4(const float f[16])
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
e[i] = f[i];
|
||||
};
|
||||
float * operator()(int i)
|
||||
{
|
||||
return &e[i << 2];
|
||||
}
|
||||
const float * readArray(void) const
|
||||
{
|
||||
return e;
|
||||
}
|
||||
void Identity()
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
e[i] = 0;
|
||||
e[0] = e[5] = e[10] = e[15] = 1.0f;
|
||||
}
|
||||
const float *operator[](int i) const
|
||||
{
|
||||
return &e[i << 2];
|
||||
};
|
||||
void InitialRotate()
|
||||
{ // taka specjalna rotacja, nie ma co ciągać trygonometrii
|
||||
float f;
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
e[i] = -e[i]; // zmiana znaku X
|
||||
f = e[i + 1];
|
||||
e[i + 1] = e[i + 2];
|
||||
e[i + 2] = f; // zamiana Y i Z
|
||||
}
|
||||
};
|
||||
inline float4x4 &Rotation(float const angle, float3 const &axis);
|
||||
inline bool IdentityIs()
|
||||
{ // sprawdzenie jednostkowości
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (e[i] != ((i % 5) ? 0.0 : 1.0)) // jedynki tylko na 0, 5, 10 i 15
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
void Quaternion(float4 *q);
|
||||
inline float3 *TranslationGet()
|
||||
{
|
||||
return (float3 *)(e + 12);
|
||||
}
|
||||
};
|
||||
|
||||
inline float3 operator*(const float4x4 &m, const float3 &v)
|
||||
{ // mnożenie wektora przez macierz
|
||||
return float3(v.x * m[0][0] + v.y * m[1][0] + v.z * m[2][0] + m[3][0],
|
||||
v.x * m[0][1] + v.y * m[1][1] + v.z * m[2][1] + m[3][1],
|
||||
v.x * m[0][2] + v.y * m[1][2] + v.z * m[2][2] + m[3][2]);
|
||||
}
|
||||
|
||||
inline glm::vec3 operator*( const float4x4 &m, const glm::vec3 &v ) { // mnożenie wektora przez macierz
|
||||
return glm::vec3(
|
||||
v.x * m[ 0 ][ 0 ] + v.y * m[ 1 ][ 0 ] + v.z * m[ 2 ][ 0 ] + m[ 3 ][ 0 ],
|
||||
v.x * m[ 0 ][ 1 ] + v.y * m[ 1 ][ 1 ] + v.z * m[ 2 ][ 1 ] + m[ 3 ][ 1 ],
|
||||
v.x * m[ 0 ][ 2 ] + v.y * m[ 1 ][ 2 ] + v.z * m[ 2 ][ 2 ] + m[ 3 ][ 2 ] );
|
||||
}
|
||||
|
||||
inline float4x4 &float4x4::Rotation(float const Angle, float3 const &Axis)
|
||||
{
|
||||
auto const c = std::cos(Angle);
|
||||
auto const s = std::sin(Angle);
|
||||
// One minus c (short name for legibility of formulai)
|
||||
auto const omc = (1.f - c);
|
||||
auto const axis = SafeNormalize(Axis);
|
||||
auto const xs = axis.x * s;
|
||||
auto const ys = axis.y * s;
|
||||
auto const zs = axis.z * s;
|
||||
auto const xyomc = axis.x * axis.y * omc;
|
||||
auto const xzomc = axis.x * axis.z * omc;
|
||||
auto const yzomc = axis.y * axis.z * omc;
|
||||
e[0] = axis.x * axis.x * omc + c;
|
||||
e[1] = xyomc + zs;
|
||||
e[2] = xzomc - ys;
|
||||
e[3] = 0;
|
||||
e[4] = xyomc - zs;
|
||||
e[5] = axis.y * axis.y * omc + c;
|
||||
e[6] = yzomc + xs;
|
||||
e[7] = 0;
|
||||
e[8] = xzomc + ys;
|
||||
e[9] = yzomc - xs;
|
||||
e[10] = axis.z * axis.z * omc + c;
|
||||
e[11] = 0;
|
||||
e[12] = 0;
|
||||
e[13] = 0;
|
||||
e[14] = 0;
|
||||
e[15] = 1;
|
||||
return *this;
|
||||
};
|
||||
|
||||
inline bool operator==(const float4x4& v1, const float4x4& v2)
|
||||
{
|
||||
for (size_t i = 0; i < 16; i++)
|
||||
{
|
||||
if (v1.e[i] != v2.e[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline float4x4 operator*(const float4x4 &m1, const float4x4 &m2)
|
||||
{ // iloczyn macierzy
|
||||
float4x4 retVal;
|
||||
for (int x = 0; x < 4; ++x)
|
||||
for (int y = 0; y < 4; ++y)
|
||||
{
|
||||
retVal(x)[y] = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
retVal(x)[y] += m1[i][y] * m2[x][i];
|
||||
}
|
||||
return retVal;
|
||||
};
|
||||
|
||||
// From code in Graphics Gems; p. 766
|
||||
inline float Det2x2(float a, float b, float c, float d)
|
||||
{ // obliczenie wyznacznika macierzy 2×2
|
||||
return a * d - b * c;
|
||||
};
|
||||
|
||||
inline float Det3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2,
|
||||
float c3)
|
||||
{ // obliczenie wyznacznika macierzy 3×3
|
||||
return +a1 * Det2x2(b2, b3, c2, c3) - b1 * Det2x2(a2, a3, c2, c3) + c1 * Det2x2(a2, a3, b2, b3);
|
||||
};
|
||||
|
||||
inline
|
||||
float Det(const float4x4 &m)
|
||||
{ // obliczenie wyznacznika macierzy 4×4
|
||||
float a1 = m[0][0], a2 = m[1][0], a3 = m[2][0], a4 = m[3][0];
|
||||
float b1 = m[0][1], b2 = m[1][1], b3 = m[2][1], b4 = m[3][1];
|
||||
float c1 = m[0][2], c2 = m[1][2], c3 = m[2][2], c4 = m[3][2];
|
||||
float d1 = m[0][3], d2 = m[1][3], d3 = m[2][3], d4 = m[3][3];
|
||||
return +a1 * Det3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4) -
|
||||
b1 * Det3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4) +
|
||||
c1 * Det3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4) -
|
||||
d1 * Det3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
|
||||
};
|
||||
1515
utilities/Globals.cpp
Normal file
1515
utilities/Globals.cpp
Normal file
File diff suppressed because it is too large
Load Diff
380
utilities/Globals.h
Normal file
380
utilities/Globals.h
Normal file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utilities/Classes.h"
|
||||
#include "vehicle/Camera.h"
|
||||
#include "utilities/dumb3d.h"
|
||||
#include "utilities/Float3d.h"
|
||||
#include "rendering/light.h"
|
||||
#include "utilities/utilities.h"
|
||||
#include "utilities/motiontelemetry.h"
|
||||
#include "entt/entity/registry.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
#ifdef WITH_UART
|
||||
#include "utilities/uart.h"
|
||||
#endif
|
||||
#ifdef WITH_ZMQ
|
||||
#include "input/zmq_input.h"
|
||||
#endif
|
||||
|
||||
struct global_settings {
|
||||
// members
|
||||
// data items
|
||||
// TODO: take these out of the settings
|
||||
|
||||
std::chrono::steady_clock::time_point startTimestamp;
|
||||
|
||||
/// EnTT variables
|
||||
entt::registry world;
|
||||
|
||||
/// <summary>
|
||||
/// Mapa z watkami w formacie <std::string nazwa, std::thread watek>
|
||||
/// </summary>
|
||||
std::map<std::string, std::thread> threads = {};
|
||||
bool shiftState{ false }; //m7todo: brzydko
|
||||
bool ctrlState{ false };
|
||||
bool altState{ false };
|
||||
std::mt19937 random_engine;
|
||||
std::mt19937 local_random_engine;
|
||||
bool ready_to_load{ false };
|
||||
std::time_t starting_timestamp = 0; // starting time, in local timezone
|
||||
uint32_t random_seed = 0;
|
||||
TCamera pCamera; // parametry kamery
|
||||
TCamera pDebugCamera;
|
||||
std::array<Math3D::vector3, 10> FreeCameraInit; // pozycje kamery
|
||||
std::array<Math3D::vector3, 10> FreeCameraInitAngle;
|
||||
int iCameraLast{ -1 };
|
||||
int iSlowMotion{ 0 }; // info o malym FPS: 0-OK, 1-wyłączyć multisampling, 3-promień 1.5km, 7-1km
|
||||
basic_light DayLight;
|
||||
float SunAngle{ 0.f }; // angle of the sun relative to horizon
|
||||
int trainThreads{0};
|
||||
double fLuminance{ 1.0 }; // jasność światła do automatycznego zapalania
|
||||
double fTimeAngleDeg{ 0.0 }; // godzina w postaci kąta
|
||||
float fClockAngleDeg[ 6 ]; // kąty obrotu cylindrów dla zegara cyfrowego
|
||||
std::string LastGLError;
|
||||
float ZoomFactor{ 1.f }; // determines current camera zoom level. TODO: move it to the renderer
|
||||
bool CabWindowOpen{ false }; // controls sound attenuation between cab and outside
|
||||
bool ControlPicking{ true }; // indicates controls pick mode is active
|
||||
bool DLFont{ false }; // switch indicating presence of basic font
|
||||
bool bActive{ true }; // czy jest aktywnym oknem
|
||||
int iPause{ 0 }; // globalna pauza ruchu: b0=start,b1=klawisz,b2=tło,b3=lagi,b4=wczytywanie
|
||||
float AirTemperature{ 15.f };
|
||||
std::string asCurrentSceneryPath{ "scenery/" };
|
||||
std::string asCurrentTexturePath{ szTexturePath };
|
||||
std::string asCurrentDynamicPath;
|
||||
int CurrentMaxTextureSize{ 4096 };
|
||||
bool UpdateMaterials{ true };
|
||||
// settings
|
||||
// filesystem
|
||||
bool bLoadTraction{ true };
|
||||
bool bSmoothTraction{ true };
|
||||
bool CreateSwitchTrackbeds{ true };
|
||||
std::string szTexturesTGA{ ".tga" }; // lista tekstur od TGA
|
||||
std::string szTexturesDDS{ ".dds" }; // lista tekstur od DDS
|
||||
std::string szDefaultExt{ szTexturesDDS };
|
||||
std::string SceneryFile;
|
||||
std::string local_start_vehicle{ "EU07-424" };
|
||||
int iConvertModels{ 0 }; // tworzenie plików binarnych
|
||||
int iConvertIndexRange{ 1000 }; // range of duplicate vertex scan
|
||||
bool file_binary_terrain{ true }; // enable binary terrain (de)serialization
|
||||
bool file_binary_terrain_state{true};
|
||||
// logs
|
||||
bool priorityLoadText3D{false}; // ladowanie T3D priorytetowo
|
||||
int iWriteLogEnabled{ 3 }; // maska bitowa: 1-zapis do pliku, 2-okienko, 4-nazwy torów
|
||||
bool MultipleLogs{ false };
|
||||
unsigned int DisabledLogTypes{ 0 };
|
||||
bool ParserLogIncludes{ true };
|
||||
// simulation
|
||||
bool RealisticControlMode{ false }; // controls ability to steer the vehicle from outside views
|
||||
bool bEnableTraction{ true };
|
||||
float fFriction{ 1.f }; // mnożnik tarcia - KURS90
|
||||
float FrictionWeatherFactor{ 1.f };
|
||||
bool bLiveTraction{ true };
|
||||
float Overcast{ 0.1f }; // NOTE: all this weather stuff should be moved elsewhere
|
||||
glm::vec3 FogColor = { 0.6f, 0.7f, 0.8f };
|
||||
float fFogEnd{ 7500 };
|
||||
float fTurbidity{ 128 };
|
||||
std::string Season{}; // season of the year, based on simulation date
|
||||
std::string Weather{ "cloudy:" }; // current weather
|
||||
std::string Period{}; // time of the day, based on sun position
|
||||
bool FullPhysics{ true }; // full calculations performed for each simulation step
|
||||
bool bnewAirCouplers{ true };
|
||||
float fMoveLight{ 0.f }; // numer dnia w roku albo -1
|
||||
bool FakeLight{ false }; // toggle between fixed and dynamic daylight
|
||||
double fTimeSpeed{ 1.0 }; // przyspieszenie czasu, zmienna do testów
|
||||
double default_timespeed { 1.0 }; // timescale loaded from config
|
||||
double fLatitudeDeg{ 52.0 }; // szerokość geograficzna
|
||||
float ScenarioTimeOverride{ std::numeric_limits<float>::quiet_NaN() }; // requested scenario start time
|
||||
float ScenarioTimeOffset{ 0.f }; // time shift (in hours) applied to train timetables
|
||||
bool ScenarioTimeCurrent{ false }; // automatic time shift to match scenario time with local clock
|
||||
bool bInactivePause{ true }; // automatyczna pauza, gdy okno nieaktywne
|
||||
int iSlowMotionMask{ -1 }; // maska wyłączanych właściwości
|
||||
bool bHideConsole{ false }; // hunter-271211: ukrywanie konsoli
|
||||
bool bRollFix{ true }; // czy wykonać przeliczanie przechyłki
|
||||
bool bJoinEvents{ false }; // czy grupować eventy o tych samych nazwach
|
||||
int iHiddenEvents{ 1 }; // czy łączyć eventy z torami poprzez nazwę toru
|
||||
bool AITrainman{ true }; // virtual assistant performing consist coupling/decoupling and other maintenance tasks
|
||||
// ui
|
||||
int PythonScreenUpdateRate{ 200 }; // delay between python-based screen updates, in milliseconds
|
||||
int iTextMode{ 0 }; // tryb pracy wyświetlacza tekstowego
|
||||
glm::vec4 UITextColor{ glm::vec4( 225.f / 255.f, 225.f / 255.f, 225.f / 255.f, 1.f ) }; // base color of UI text
|
||||
float UIBgOpacity{ 0.65f }; // opacity of ui windows
|
||||
std::string asLang{ "pl" }; // domyślny język - http://tools.ietf.org/html/bcp47
|
||||
// gfx
|
||||
glm::ivec2 window_size; // main window size in platform-specific virtual pixels
|
||||
glm::ivec2 cursor_pos; // cursor position in platform-specific virtual pixels
|
||||
glm::ivec2 fb_size; // main window framebuffer size
|
||||
float ShakingMultiplierBF {1.f}; // mnożnik bujania kamera przod/tyl
|
||||
float ShakingMultiplierRL {1.f}; // mnożnik bujania kamera lewo/prawo
|
||||
float ShakingMultiplierUD {1.f}; // mnożnik bujania kamera gora/dol
|
||||
float fDistanceFactor{ 1.f }; // baza do przeliczania odległości dla LoD
|
||||
float targetfps{ 0.0f };
|
||||
bool bFullScreen{ false };
|
||||
bool VSync{ false };
|
||||
bool bWireFrame{ false };
|
||||
bool bAdjustScreenFreq{ true };
|
||||
float BaseDrawRange{ 2500.f };
|
||||
int DynamicLightCount{ 7 };
|
||||
bool ScaleSpecularValues{ true };
|
||||
std::string GfxRenderer{ "default" };
|
||||
bool LegacyRenderer{ false };
|
||||
bool NvRenderer{ false };
|
||||
bool BasicRenderer{ false };
|
||||
bool RenderShadows{ true };
|
||||
bool applicationQuitOrder{false};
|
||||
int RenderCabShadowsRange{ 0 };
|
||||
struct shadowtune_t {
|
||||
unsigned int map_size{ 2048 };
|
||||
float range{ 250.f };
|
||||
} shadowtune;
|
||||
struct reflectiontune_t {
|
||||
double update_interval{ 300.0 };
|
||||
int fidelity{ 0 }; // 0: sections, 1: +static models, 2: +vehicles
|
||||
float range_instances{ 750.f };
|
||||
float range_vehicles{ 250.f };
|
||||
} reflectiontune;
|
||||
bool bUseVBO{ true }; // czy jest VBO w karcie graficznej (czy użyć)
|
||||
float AnisotropicFiltering{ 8.f }; // requested level of anisotropic filtering. TODO: move it to renderer object
|
||||
float FieldOfView{ 45.f }; // vertical field of view for the camera. TODO: move it to the renderer
|
||||
GLint iMaxTextureSize{ 4096 }; // maksymalny rozmiar tekstury
|
||||
GLint iMaxCabTextureSize{ 4096 }; // largest allowed texture in vehicle cab
|
||||
int iMultisampling{ 2 }; // tryb antyaliasingu: 0=brak,1=2px,2=4px,3=8px,4=16px
|
||||
float SplineFidelity{ 1.f }; // determines segment size during conversion of splines to geometry
|
||||
bool Smoke{ true }; // toggles smoke simulation and visualization
|
||||
float SmokeFidelity{ 1.f }; // determines amount of generated smoke particles
|
||||
bool ResourceSweep{ true }; // gfx resource garbage collection
|
||||
bool ResourceMove{ false }; // gfx resources are moved between cpu and gpu side instead of sending a copy
|
||||
bool compress_tex{ true }; // all textures are compressed on gpu side
|
||||
std::string asSky{ "1" };
|
||||
float fFpsAverage{ 0.f }; // oczekiwana wartosć FPS
|
||||
float fFpsDeviation{ 5.f }; // odchylenie standardowe FPS
|
||||
double fFpsMin{ 30.0 }; // dolna granica FPS, przy której promień scenerii będzie zmniejszany
|
||||
double fFpsMax{ 65.0 }; // górna granica FPS, przy której promień scenerii będzie zwiększany
|
||||
// audio
|
||||
bool bSoundEnabled{ true };
|
||||
float AudioVolume{ 1.f };
|
||||
float DefaultRadioVolume{ 0.75f };
|
||||
float VehicleVolume{ 1.0f };
|
||||
float EnvironmentPositionalVolume{ 1.0f };
|
||||
float EnvironmentAmbientVolume{ 1.0f };
|
||||
int audio_max_sources = 30;
|
||||
std::string AudioRenderer;
|
||||
// input
|
||||
float fMouseXScale{ 1.5f };
|
||||
float fMouseYScale{ 0.2f };
|
||||
int iFeedbackMode{ 1 }; // tryb pracy informacji zwrotnej
|
||||
int iFeedbackPort{ 0 }; // dodatkowy adres dla informacji zwrotnych
|
||||
bool InputGamepad{ true }; // whether gamepad support is enabled
|
||||
bool InputMouse{ true }; // whether control pick mode can be activated
|
||||
double fBrakeStep { 1.0 }; // krok zmiany hamulca innych niż FV4a dla klawiszy [Num3] i [Num9]
|
||||
double brake_speed { 3.0 }; // prędkość przesuwu hamulca dla FV4a
|
||||
// parametry kalibracyjne wejść z pulpitu
|
||||
double fCalibrateIn[ 6 ][ 6 ] = {
|
||||
{ 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 } };
|
||||
// parametry kalibracyjne wyjść dla pulpitu
|
||||
double fCalibrateOut[ 7 ][ 6 ] = {
|
||||
{ 0, 1, 0, 0, 0, 0 },
|
||||
{ 0, 1, 0, 0, 0, 0 },
|
||||
{ 0, 1, 0, 0, 0, 0 },
|
||||
{ 0, 1, 0, 0, 0, 0 },
|
||||
{ 0, 1, 0, 0, 0, 0 },
|
||||
{ 0, 1, 0, 0, 0, 0 },
|
||||
{ 0, 1, 0, 0, 0, 0 } };
|
||||
// wartości maksymalne wyjść dla pulpitu
|
||||
double fCalibrateOutMax[ 7 ] = {
|
||||
0, 0, 0, 0, 0, 0, 0 };
|
||||
int iCalibrateOutDebugInfo{ -1 }; // numer wyjścia kalibrowanego dla którego wyświetlać informacje podczas kalibracji
|
||||
int iPoKeysPWM[ 7 ] = { 0, 1, 2, 3, 4, 5, 6 }; // numery wejść dla PWM
|
||||
#ifdef WITH_UART
|
||||
uart_input::conf_t uart_conf;
|
||||
std::map<std::string, bool *> uartfeatures_map = {
|
||||
{"main", &uart_conf.mainenable},
|
||||
{"scnd", &uart_conf.scndenable},
|
||||
{"train", &uart_conf.trainenable},
|
||||
{"local", &uart_conf.localenable},
|
||||
{"radiovolume", &uart_conf.radiovolumeenable},
|
||||
{"radiochannel", &uart_conf.radiochannelenable}
|
||||
};
|
||||
#endif
|
||||
#ifdef WITH_ZMQ
|
||||
std::string zmq_address;
|
||||
#endif
|
||||
// multiplayer
|
||||
int iMultiplayer{ 0 }; // blokada działania niektórych eventów na rzecz kominikacji
|
||||
bool bIsolatedTrainName{ false }; //wysyłanie zajęcia odcinka izolowanego z nazwą pociągu
|
||||
// other
|
||||
std::string AppName{ "MaSzyna" };
|
||||
std::string asVersion; // z opisem
|
||||
motiontelemetry::conf_t motiontelemetry_conf;
|
||||
std::string screenshot_dir;
|
||||
bool loading_log = false;
|
||||
bool dds_upper_origin = false;
|
||||
bool captureonstart = true;
|
||||
bool render_cab = true;
|
||||
bool crash_damage = true;
|
||||
bool gui_defaultwindows = true;
|
||||
bool gui_showtranscripts = true;
|
||||
bool gui_trainingdefault = false;
|
||||
|
||||
std::string extcam_cmd;
|
||||
std::string extcam_rec;
|
||||
glm::ivec2 extcam_res{800, 600};
|
||||
|
||||
std::chrono::duration<float> minframetime {0.0f};
|
||||
std::string fullscreen_monitor;
|
||||
bool fullscreen_windowed{ false };
|
||||
|
||||
bool python_enabled = true;
|
||||
bool python_mipmaps = true;
|
||||
bool python_displaywindows = false;
|
||||
bool python_threadedupload = true;
|
||||
bool python_vsync = true;
|
||||
bool python_sharectx = true;
|
||||
bool python_uploadmain = true;
|
||||
std::chrono::duration<float> python_minframetime {0.01f};
|
||||
|
||||
bool gfx_skiprendering = false;
|
||||
int gfx_framebuffer_width = -1;
|
||||
int gfx_framebuffer_height = -1;
|
||||
bool gfx_shadowmap_enabled = true;
|
||||
bool gfx_envmap_enabled = true;
|
||||
bool gfx_postfx_motionblur_enabled = true;
|
||||
float gfx_postfx_motionblur_shutter = 0.01f;
|
||||
GLenum gfx_postfx_motionblur_format = GL_RG16F;
|
||||
GLenum gfx_format_color = GL_RGB16F;
|
||||
GLenum gfx_format_depth = GL_DEPTH_COMPONENT32F;
|
||||
bool gfx_postfx_chromaticaberration_enabled = true;
|
||||
bool gfx_skippipeline = false;
|
||||
bool gfx_extraeffects = true;
|
||||
bool gfx_shadergamma = false;
|
||||
bool gfx_usegles = false;
|
||||
std::string gfx_angleplatform;
|
||||
bool gfx_gldebug = false;
|
||||
bool vr = false;
|
||||
std::string vr_backend;
|
||||
|
||||
float gfx_distance_factor_max { 3.f };
|
||||
float gfx_shadow_angle_min { -0.2f };
|
||||
int gfx_shadow_rank_cutoff { 3 };
|
||||
|
||||
float ui_fontsize = 13.0f;
|
||||
float ui_scale = 1.0f;
|
||||
|
||||
float map_highlight_distance = 3000.0f;
|
||||
|
||||
std::string exec_on_exit;
|
||||
std::string prepend_scn;
|
||||
|
||||
struct extraviewport_config {
|
||||
std::string monitor;
|
||||
|
||||
int width, height;
|
||||
float draw_range;
|
||||
|
||||
viewport_proj_config projection;
|
||||
};
|
||||
std::vector<extraviewport_config> extra_viewports;
|
||||
|
||||
struct pythonviewport_config {
|
||||
std::string surface;
|
||||
|
||||
std::string monitor;
|
||||
glm::ivec2 size;
|
||||
|
||||
glm::vec2 offset;
|
||||
glm::vec2 scale;
|
||||
};
|
||||
std::vector<pythonviewport_config> python_viewports;
|
||||
|
||||
struct headtrack_config {
|
||||
std::string joy;
|
||||
|
||||
bool magic_window = false;
|
||||
|
||||
glm::ivec3 move_axes;
|
||||
glm::vec3 move_mul;
|
||||
|
||||
glm::ivec3 rot_axes;
|
||||
glm::vec3 rot_mul;
|
||||
};
|
||||
headtrack_config headtrack_conf;
|
||||
|
||||
glm::vec3 viewport_move;
|
||||
glm::mat3 viewport_rotate;
|
||||
|
||||
bool map_manualswitchcontrol = false;;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> network_servers;
|
||||
std::optional<std::pair<std::string, std::string>> network_client;
|
||||
float desync = 0.0f;
|
||||
|
||||
std::unordered_map<int, std::string> trainset_overrides;
|
||||
|
||||
float m_skysaturationcorrection{ 1.65f };
|
||||
float m_skyhuecorrection{ 0.5f };
|
||||
|
||||
// methods
|
||||
void LoadIniFile( std::string asFileName );
|
||||
void ConfigParse( cParser &parser );
|
||||
bool ConfigParse_gfx( cParser &parser, std::string_view const Token );
|
||||
// sends basic content of the class in legacy (text) format to provided stream
|
||||
void
|
||||
export_as_text( std::ostream &Output ) const;
|
||||
template <typename Type_>
|
||||
void
|
||||
export_as_text( std::ostream &Output, std::string const Key, Type_ const &Value ) const;
|
||||
};
|
||||
|
||||
template <typename Type_>
|
||||
void
|
||||
global_settings::export_as_text( std::ostream &Output, std::string const Key, Type_ const &Value ) const {
|
||||
|
||||
Output << Key << " " << Value << "\n";
|
||||
}
|
||||
|
||||
template <>
|
||||
void
|
||||
global_settings::export_as_text( std::ostream &Output, std::string const Key, std::string const &Value ) const;
|
||||
|
||||
template <>
|
||||
void
|
||||
global_settings::export_as_text( std::ostream &Output, std::string const Key, bool const &Value ) const;
|
||||
|
||||
extern global_settings& GetGlobalSettings();
|
||||
|
||||
#define Global (GetGlobalSettings())
|
||||
261
utilities/Logs.cpp
Normal file
261
utilities/Logs.cpp
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
#include "utilities/Globals.h"
|
||||
#include "winheaders.h"
|
||||
#include "utilities/utilities.h"
|
||||
#include "application/uilayer.h"
|
||||
#include <deque>
|
||||
|
||||
std::ofstream output; // standardowy "log.txt", można go wyłączyć
|
||||
std::ofstream errors; // lista błędów "errors.txt", zawsze działa
|
||||
std::ofstream comms; // lista komunikatow "comms.txt", można go wyłączyć
|
||||
char logbuffer[ 256 ];
|
||||
|
||||
char endstring[10] = "\n";
|
||||
|
||||
std::deque<std::string> log_scrollback;
|
||||
|
||||
std::string filename_date() {
|
||||
::SYSTEMTIME st;
|
||||
|
||||
#ifdef __unix__
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
tm *tms = localtime(&ts.tv_sec);
|
||||
st.wYear = tms->tm_year;
|
||||
st.wMonth = tms->tm_mon;
|
||||
st.wDayOfWeek = tms->tm_wday;
|
||||
st.wDay = tms->tm_mday;
|
||||
st.wHour = tms->tm_hour;
|
||||
st.wMinute = tms->tm_min;
|
||||
st.wSecond = tms->tm_sec;
|
||||
st.wMilliseconds = ts.tv_nsec / 1000000;
|
||||
#elif _WIN32
|
||||
::GetLocalTime( &st );
|
||||
#endif
|
||||
|
||||
std::snprintf(
|
||||
logbuffer,
|
||||
sizeof(logbuffer),
|
||||
"%d%02d%02d_%02d%02d%03d",
|
||||
st.wYear,
|
||||
st.wMonth,
|
||||
st.wDay,
|
||||
st.wHour,
|
||||
st.wMinute,
|
||||
st.wMilliseconds);
|
||||
|
||||
return std::string( logbuffer );
|
||||
}
|
||||
|
||||
std::string filename_scenery() {
|
||||
|
||||
auto extension = Global.SceneryFile.rfind( '.' );
|
||||
if( extension != std::string::npos ) {
|
||||
return Global.SceneryFile.substr( 0, extension );
|
||||
}
|
||||
else {
|
||||
return Global.SceneryFile;
|
||||
}
|
||||
}
|
||||
|
||||
// log service stacks
|
||||
std::deque < std::pair<std::string, bool>> InfoStack;
|
||||
std::deque<std::string> ErrorStack;
|
||||
|
||||
// lock for log stacks
|
||||
std::mutex logMutex;
|
||||
|
||||
|
||||
void LogService()
|
||||
{
|
||||
// prevent crash if mutex is not initialized
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
logMutex.lock();
|
||||
break;
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
logMutex.unlock();
|
||||
|
||||
while (!Global.applicationQuitOrder)
|
||||
{
|
||||
{
|
||||
// --- Obsługa InfoStack ---
|
||||
while (!InfoStack.empty())
|
||||
{
|
||||
logMutex.lock();
|
||||
std::string msg = InfoStack.front().first;
|
||||
bool isError = InfoStack.front().second;
|
||||
InfoStack.pop_front();
|
||||
logMutex.unlock();
|
||||
|
||||
// log to file
|
||||
if (Global.iWriteLogEnabled & 1)
|
||||
{
|
||||
if (!output.is_open())
|
||||
{
|
||||
std::string filename = (Global.MultipleLogs ? "logs/log (" + filename_scenery() + ") " + filename_date() + ".txt" : "log.txt");
|
||||
output.open(filename, std::ios::trunc);
|
||||
}
|
||||
output << msg << "\n";
|
||||
output.flush();
|
||||
}
|
||||
|
||||
// log to scrollback imgui
|
||||
log_scrollback.emplace_back(msg);
|
||||
if (log_scrollback.size() > 200)
|
||||
log_scrollback.pop_front();
|
||||
|
||||
// log to console
|
||||
if (Global.iWriteLogEnabled & 2)
|
||||
{
|
||||
if (isError)
|
||||
printf("\033[1;37;41m%s\033[0m\n", msg.c_str());
|
||||
else
|
||||
printf("\033[32m%s\033[0m\n", msg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// --- Obsługa ErrorStack ---
|
||||
while (!ErrorStack.empty())
|
||||
{
|
||||
logMutex.lock();
|
||||
std::string msg = ErrorStack.front();
|
||||
ErrorStack.pop_front();
|
||||
logMutex.unlock();
|
||||
|
||||
if (!(Global.iWriteLogEnabled & 1))
|
||||
continue;
|
||||
|
||||
if (!errors.is_open())
|
||||
{
|
||||
std::string filename = (Global.MultipleLogs ? "logs/errors (" + filename_scenery() + ") " + filename_date() + ".txt" : "errors.txt");
|
||||
errors.open(filename, std::ios::trunc);
|
||||
errors << "EU07.EXE " + Global.asVersion << "\n";
|
||||
}
|
||||
|
||||
errors << msg << "\n";
|
||||
errors.flush();
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WriteLog(const char *str, logtype const Type, bool isError)
|
||||
{
|
||||
if (!str || *str == '\0')
|
||||
return;
|
||||
if (TestFlag(Global.DisabledLogTypes, static_cast<unsigned int>(Type)))
|
||||
return;
|
||||
|
||||
// time calculation
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto elapsed = now - Global.startTimestamp;
|
||||
double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(elapsed).count();
|
||||
|
||||
// time format
|
||||
std::ostringstream oss;
|
||||
oss << "[ " << std::fixed << std::setprecision(3) << seconds << " ] ";
|
||||
|
||||
// wyrownanie do np. 10 znaków długości + dwie tabulacje
|
||||
std::ostringstream final;
|
||||
final << std::setw(10) << oss.str() << "\t\t" << str;
|
||||
|
||||
|
||||
logMutex.lock();
|
||||
InfoStack.emplace_back(final.str(), isError);
|
||||
logMutex.unlock();
|
||||
}
|
||||
|
||||
void ErrorLog(const char *str, logtype const Type)
|
||||
{
|
||||
if (!str || *str == '\0')
|
||||
return;
|
||||
if (TestFlag(Global.DisabledLogTypes, static_cast<unsigned int>(Type)))
|
||||
return;
|
||||
|
||||
// time calculation
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto elapsed = now - Global.startTimestamp;
|
||||
double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(elapsed).count();
|
||||
|
||||
// time format
|
||||
std::ostringstream oss;
|
||||
oss << "[ " << std::fixed << std::setprecision(3) << seconds << " ] ";
|
||||
|
||||
// wyrownanie do np. 10 znaków długości + dwie tabulacje
|
||||
std::ostringstream final;
|
||||
final << std::setw(10) << oss.str() << "\t\t" << str;
|
||||
|
||||
logMutex.lock();
|
||||
ErrorStack.emplace_back(final.str());
|
||||
logMutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
void Error(const std::string &asMessage, bool box)
|
||||
{
|
||||
// if (box)
|
||||
// MessageBox(NULL, asMessage.c_str(), string("EU07 " + Global.asRelease).c_str(), MB_OK);
|
||||
ErrorLog(asMessage.c_str());
|
||||
}
|
||||
|
||||
void Error(const char *&asMessage, bool box)
|
||||
{
|
||||
// if (box)
|
||||
// MessageBox(NULL, asMessage, string("EU07 " + Global.asRelease).c_str(), MB_OK);
|
||||
ErrorLog(asMessage);
|
||||
WriteLog(asMessage);
|
||||
}
|
||||
|
||||
void ErrorLog(const std::string &str, logtype const Type )
|
||||
{
|
||||
ErrorLog( str.c_str(), Type );
|
||||
WriteLog( str.c_str(), Type, true );
|
||||
}
|
||||
|
||||
void WriteLog(const std::string &str, logtype const Type )
|
||||
{ // Ra: wersja z AnsiString jest zamienna z Error()
|
||||
WriteLog( str.c_str(), Type );
|
||||
};
|
||||
|
||||
void CommLog(const char *str)
|
||||
{ // Ra: warunkowa rejestracja komunikatów
|
||||
WriteLog(str);
|
||||
/* if (Global.iWriteLogEnabled & 4)
|
||||
{
|
||||
if (!comms.is_open())
|
||||
{
|
||||
comms.open("comms.txt", std::ios::trunc);
|
||||
comms << AnsiString("EU07.EXE " + Global.asRelease).c_str() << "\n";
|
||||
}
|
||||
if (str)
|
||||
comms << str;
|
||||
comms << "\n";
|
||||
comms.flush();
|
||||
}*/
|
||||
};
|
||||
|
||||
void CommLog(const std::string &str)
|
||||
{ // Ra: wersja z AnsiString jest zamienna z Error()
|
||||
WriteLog(str);
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
35
utilities/Logs.h
Normal file
35
utilities/Logs.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
enum class logtype : unsigned int {
|
||||
|
||||
generic = ( 1 << 0 ),
|
||||
file = ( 1 << 1 ),
|
||||
model = ( 1 << 2 ),
|
||||
texture = ( 1 << 3 ),
|
||||
lua = ( 1 << 4 ),
|
||||
material = ( 1 << 5 ),
|
||||
shader = ( 1 << 6 ),
|
||||
net = ( 1 << 7 ),
|
||||
sound = ( 1 << 8 ),
|
||||
traction = ( 1 << 9 ),
|
||||
powergrid = ( 1 << 10 ),
|
||||
};
|
||||
void LogService();
|
||||
void WriteLog( const char *str, logtype const Type = logtype::generic, bool isError = false );
|
||||
void Error( const std::string &asMessage, bool box = false );
|
||||
void Error( const char* &asMessage, bool box = false );
|
||||
void ErrorLog( const std::string &str, logtype const Type = logtype::generic );
|
||||
void WriteLog( const std::string &str, logtype const Type = logtype::generic );
|
||||
void CommLog( const char *str );
|
||||
void CommLog( const std::string &str );
|
||||
|
||||
extern std::deque<std::string> log_scrollback;
|
||||
108
utilities/Names.h
Normal file
108
utilities/Names.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
template <typename Type_>
|
||||
class basic_table {
|
||||
|
||||
public:
|
||||
// destructor
|
||||
~basic_table() {
|
||||
for( auto *item : m_items ) {
|
||||
if (item)
|
||||
delete item; } }
|
||||
// methods
|
||||
// adds provided item to the collection. returns: true if there's no duplicate with the same name, false otherwise
|
||||
bool
|
||||
insert( Type_ *Item, std::string itemname ) {
|
||||
m_items.emplace_back( Item );
|
||||
if( ( true == itemname.empty() ) || ( itemname == "none" ) ) {
|
||||
return true;
|
||||
}
|
||||
auto const itemhandle { m_items.size() - 1 };
|
||||
// add item name to the map
|
||||
auto mapping = m_itemmap.emplace( itemname, itemhandle );
|
||||
if( true == mapping.second ) {
|
||||
return true;
|
||||
}
|
||||
// item with this name already exists; update mapping to point to the new one, for backward compatibility
|
||||
mapping.first->second = itemhandle;
|
||||
return false; }
|
||||
bool insert (Type_ *Item)
|
||||
{
|
||||
return insert(Item, Item->name());
|
||||
}
|
||||
void purge (std::string const &Name)
|
||||
{
|
||||
auto lookup = m_itemmap.find( Name );
|
||||
if (lookup == m_itemmap.end())
|
||||
return;
|
||||
delete m_items[lookup->second];
|
||||
|
||||
detach(Name);
|
||||
}
|
||||
void detach (std::string const &Name)
|
||||
{
|
||||
auto lookup = m_itemmap.find( Name );
|
||||
if (lookup == m_itemmap.end())
|
||||
return;
|
||||
|
||||
m_items[lookup->second] = nullptr;
|
||||
// TBD, TODO: remove from m_items?
|
||||
|
||||
m_itemmap.erase(lookup);
|
||||
}
|
||||
uint32_t find_id( std::string const &Name) const {
|
||||
auto lookup = m_itemmap.find( Name );
|
||||
return (
|
||||
lookup != m_itemmap.end() ?
|
||||
lookup->second :
|
||||
-1 );
|
||||
}
|
||||
void purge (Type_ *Item)
|
||||
{
|
||||
for (auto it = m_items.begin(); it != m_items.end(); it++) {
|
||||
if (*it == Item) {
|
||||
delete *it;
|
||||
*it = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// locates item with specified name. returns pointer to the item, or nullptr
|
||||
Type_ *
|
||||
find( std::string const &Name ) const {
|
||||
auto lookup = m_itemmap.find( Name );
|
||||
return (
|
||||
lookup != m_itemmap.end() ?
|
||||
m_items[ lookup->second ] :
|
||||
nullptr ); }
|
||||
|
||||
protected:
|
||||
// types
|
||||
using type_sequence = std::deque<Type_ *>;
|
||||
using index_map = std::unordered_map<std::string, std::size_t>;
|
||||
// members
|
||||
type_sequence m_items;
|
||||
index_map m_itemmap;
|
||||
|
||||
public:
|
||||
// data access
|
||||
type_sequence &
|
||||
sequence() {
|
||||
return m_items; }
|
||||
type_sequence const &
|
||||
sequence() const {
|
||||
return m_items; }
|
||||
|
||||
};
|
||||
113
utilities/Timer.cpp
Normal file
113
utilities/Timer.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "utilities/Timer.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "winheaders.h"
|
||||
|
||||
namespace Timer {
|
||||
|
||||
subsystem_stopwatches subsystem;
|
||||
|
||||
double DeltaTime = 0.0, DeltaRenderTime = 0.0;
|
||||
double fFPS{ 0.0f };
|
||||
double fLastTime{ 0.0f };
|
||||
DWORD dwFrames{ 0 };
|
||||
double fSimulationTime{ 0.0 };
|
||||
double fRenderTime{ 0.0 };
|
||||
double fSoundTimer{ 0.0 };
|
||||
double fSinceStart{ 0.0 };
|
||||
double override_delta = -1.0f;
|
||||
|
||||
double GetTime()
|
||||
{
|
||||
return fSimulationTime;
|
||||
}
|
||||
|
||||
double GetRenderTime() {
|
||||
return fRenderTime;
|
||||
}
|
||||
|
||||
double GetDeltaTime()
|
||||
{ // czas symulacji (stoi gdy pauza)
|
||||
if (override_delta != -1.0f)
|
||||
return override_delta;
|
||||
return DeltaTime;
|
||||
}
|
||||
|
||||
double GetDeltaRenderTime()
|
||||
{ // czas renderowania (do poruszania się)
|
||||
return DeltaRenderTime;
|
||||
}
|
||||
|
||||
void set_delta_override(double t)
|
||||
{
|
||||
override_delta = t;
|
||||
}
|
||||
|
||||
void ResetTimers()
|
||||
{
|
||||
UpdateTimers( Global.iPause != 0 );
|
||||
DeltaTime = 0.0;
|
||||
DeltaRenderTime = 0.0;
|
||||
}
|
||||
|
||||
uint64_t fr, count, oldCount;
|
||||
|
||||
void UpdateTimers(bool pause)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
QueryPerformanceFrequency((LARGE_INTEGER *)&fr);
|
||||
QueryPerformanceCounter((LARGE_INTEGER *)&count);
|
||||
#elif __unix__
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
count = (uint64_t)ts.tv_sec * 1000000000 + (uint64_t)ts.tv_nsec;
|
||||
fr = 1000000000;
|
||||
#endif
|
||||
DeltaRenderTime = double(count - oldCount) / double(fr);
|
||||
fRenderTime += DeltaRenderTime;
|
||||
if (!pause)
|
||||
{
|
||||
DeltaTime = Global.fTimeSpeed * DeltaRenderTime;
|
||||
fSoundTimer += DeltaTime;
|
||||
if (fSoundTimer > 0.1)
|
||||
fSoundTimer = 0.0;
|
||||
|
||||
if (DeltaTime > 1.0)
|
||||
DeltaTime = 1.0;
|
||||
|
||||
fSimulationTime += GetDeltaTime();
|
||||
}
|
||||
else
|
||||
DeltaTime = 0.0; // wszystko stoi, bo czas nie płynie
|
||||
|
||||
oldCount = count;
|
||||
// Keep track of the time lapse and frame count
|
||||
#if __unix__
|
||||
double fTime = (double)(count / 1000000000);
|
||||
#elif _WIN32_WINNT >= _WIN32_WINNT_VISTA
|
||||
double fTime = ::GetTickCount64() * 0.001f; // Get current time in seconds
|
||||
#elif _WIN32
|
||||
double fTime = ::GetTickCount() * 0.001f; // Get current time in seconds
|
||||
#endif
|
||||
++dwFrames; // licznik ramek
|
||||
// update the frame rate once per second
|
||||
if (fTime - fLastTime > 1.0f)
|
||||
{
|
||||
fFPS = dwFrames / (fTime - fLastTime);
|
||||
fLastTime = fTime;
|
||||
dwFrames = 0L;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace timer
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
73
utilities/Timer.h
Normal file
73
utilities/Timer.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Timer {
|
||||
|
||||
double GetTime();
|
||||
double GetRenderTime();
|
||||
|
||||
double GetDeltaTime();
|
||||
double GetDeltaRenderTime();
|
||||
|
||||
void set_delta_override(double v);
|
||||
|
||||
void ResetTimers();
|
||||
|
||||
void UpdateTimers(bool pause);
|
||||
|
||||
class stopwatch {
|
||||
|
||||
public:
|
||||
// constructors
|
||||
stopwatch() = default;
|
||||
// methods
|
||||
void
|
||||
start() {
|
||||
m_start = std::chrono::steady_clock::now(); }
|
||||
std::chrono::duration<float, std::milli>
|
||||
stop() {
|
||||
m_last = std::chrono::duration_cast<std::chrono::microseconds>( ( std::chrono::steady_clock::now() - m_start ) );
|
||||
m_accumulator = 0.95f * m_accumulator + m_last.count() / 1000.f;
|
||||
return m_last; }
|
||||
float
|
||||
average() const {
|
||||
return m_accumulator / 20.f;}
|
||||
std::chrono::microseconds
|
||||
last() const {
|
||||
return m_last; }
|
||||
|
||||
private:
|
||||
// members
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_start { std::chrono::steady_clock::now() };
|
||||
float m_accumulator { 1000.f / 30.f * 20.f }; // 20 last samples, initial 'neutral' rate of 30 fps
|
||||
std::chrono::microseconds m_last;
|
||||
};
|
||||
|
||||
struct subsystem_stopwatches {
|
||||
stopwatch gfx_total;
|
||||
stopwatch gfx_color;
|
||||
stopwatch gfx_shadows;
|
||||
stopwatch gfx_reflections;
|
||||
stopwatch gfx_swap;
|
||||
stopwatch gfx_gui;
|
||||
stopwatch gfx_animate;
|
||||
stopwatch sim_total;
|
||||
stopwatch sim_dynamics;
|
||||
stopwatch sim_events;
|
||||
stopwatch sim_ai;
|
||||
stopwatch mainloop_total;
|
||||
};
|
||||
|
||||
extern subsystem_stopwatches subsystem;
|
||||
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
128
utilities/color.h
Normal file
128
utilities/color.h
Normal file
@@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
namespace colors {
|
||||
|
||||
glm::vec4 const none{ 0.f, 0.f, 0.f, 1.f };
|
||||
glm::vec4 const white{ 1.f, 1.f, 1.f, 1.f };
|
||||
glm::vec4 const shadow{ 0.25f, 0.30f, 0.35f, 1.f };
|
||||
|
||||
glm::vec4 const uitextred{ 164.0f / 255.0f, 84.0f / 255.0f, 84.0f / 255.0f, 1.f };
|
||||
glm::vec4 const uitextorange{ 164.0f / 255.0f, 132.0f / 255.0f, 84.0f / 255.0f, 1.f };
|
||||
glm::vec4 const uitextgreen{ 84.0f / 255.0f, 164.0f / 255.0f, 132.0f / 255.0f, 1.f };
|
||||
|
||||
inline
|
||||
glm::vec3
|
||||
XYZtoRGB( glm::vec3 const &XYZ ) {
|
||||
|
||||
// M^-1 for Adobe RGB from http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html
|
||||
float const mi[ 3 ][ 3 ] = { 2.041369f, -0.969266f, 0.0134474f, -0.5649464f, 1.8760108f, -0.1183897f, -0.3446944f, 0.041556f, 1.0154096f };
|
||||
// m^-1 for sRGB:
|
||||
// float const mi[ 3 ][ 3 ] = { 3.240479f, -0.969256f, 0.055648f, -1.53715f, 1.875991f, -0.204043f, -0.49853f, 0.041556f, 1.057311f };
|
||||
|
||||
return glm::vec3{
|
||||
XYZ.x*mi[ 0 ][ 0 ] + XYZ.y*mi[ 1 ][ 0 ] + XYZ.z*mi[ 2 ][ 0 ],
|
||||
XYZ.x*mi[ 0 ][ 1 ] + XYZ.y*mi[ 1 ][ 1 ] + XYZ.z*mi[ 2 ][ 1 ],
|
||||
XYZ.x*mi[ 0 ][ 2 ] + XYZ.y*mi[ 1 ][ 2 ] + XYZ.z*mi[ 2 ][ 2 ] };
|
||||
}
|
||||
|
||||
inline
|
||||
glm::vec3
|
||||
RGBtoHSV( glm::vec3 const &RGB ) {
|
||||
|
||||
glm::vec3 hsv;
|
||||
|
||||
float const max = std::max( std::max( RGB.r, RGB.g ), RGB.b );
|
||||
float const min = std::min( std::min( RGB.r, RGB.g ), RGB.b );
|
||||
float const delta = max - min;
|
||||
|
||||
hsv.z = max; // v
|
||||
if( delta < 0.00001 ) {
|
||||
hsv.y = 0;
|
||||
hsv.x = 0; // undefined, maybe nan?
|
||||
return hsv;
|
||||
}
|
||||
if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash
|
||||
hsv.y = ( delta / max ); // s
|
||||
}
|
||||
else {
|
||||
// if max is 0, then r = g = b = 0
|
||||
// s = 0, v is undefined
|
||||
hsv.y = 0.0;
|
||||
hsv.x = NAN; // its now undefined
|
||||
return hsv;
|
||||
}
|
||||
if( RGB.r >= max ) // > is bogus, just keeps compilor happy
|
||||
hsv.x = ( RGB.g - RGB.b ) / delta; // between yellow & magenta
|
||||
else
|
||||
if( RGB.g >= max )
|
||||
hsv.x = 2.f + ( RGB.g - RGB.r ) / delta; // between cyan & yellow
|
||||
else
|
||||
hsv.x = 4.f + ( RGB.r - RGB.g ) / delta; // between magenta & cyan
|
||||
|
||||
hsv.x *= 60.0; // degrees
|
||||
|
||||
if( hsv.x < 0.0 )
|
||||
hsv.x += 360.0;
|
||||
|
||||
return hsv;
|
||||
}
|
||||
|
||||
inline
|
||||
glm::vec3
|
||||
HSVtoRGB( glm::vec3 const &HSV ) {
|
||||
|
||||
glm::vec3 rgb;
|
||||
|
||||
if( HSV.y <= 0.0 ) { // < is bogus, just shuts up warnings
|
||||
rgb.r = HSV.z;
|
||||
rgb.g = HSV.z;
|
||||
rgb.b = HSV.z;
|
||||
return rgb;
|
||||
}
|
||||
float hh = HSV.x;
|
||||
if( hh >= 360.0 ) hh = 0.0;
|
||||
hh /= 60.0;
|
||||
int const i = (int)hh;
|
||||
float const ff = hh - i;
|
||||
float const p = HSV.z * ( 1.f - HSV.y );
|
||||
float const q = HSV.z * ( 1.f - ( HSV.y * ff ) );
|
||||
float const t = HSV.z * ( 1.f - ( HSV.y * ( 1.f - ff ) ) );
|
||||
|
||||
switch( i ) {
|
||||
case 0:
|
||||
rgb.r = HSV.z;
|
||||
rgb.g = t;
|
||||
rgb.b = p;
|
||||
break;
|
||||
case 1:
|
||||
rgb.r = q;
|
||||
rgb.g = HSV.z;
|
||||
rgb.b = p;
|
||||
break;
|
||||
case 2:
|
||||
rgb.r = p;
|
||||
rgb.g = HSV.z;
|
||||
rgb.b = t;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
rgb.r = p;
|
||||
rgb.g = q;
|
||||
rgb.b = HSV.z;
|
||||
break;
|
||||
case 4:
|
||||
rgb.r = t;
|
||||
rgb.g = p;
|
||||
rgb.b = HSV.z;
|
||||
break;
|
||||
case 5:
|
||||
default:
|
||||
rgb.r = HSV.z;
|
||||
rgb.g = p;
|
||||
rgb.b = q;
|
||||
break;
|
||||
}
|
||||
return rgb;
|
||||
}
|
||||
|
||||
} // namespace colors
|
||||
78
utilities/comparison.h
Normal file
78
utilities/comparison.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
enum class comparison_operator {
|
||||
equal, // ==
|
||||
not_equal, // !=
|
||||
lt, // <
|
||||
gt, // >
|
||||
lt_eq, // <=
|
||||
gt_eq // >=
|
||||
};
|
||||
|
||||
enum class comparison_pass {
|
||||
all, // &&
|
||||
any, // ||
|
||||
none // !
|
||||
};
|
||||
|
||||
template <typename Type_>
|
||||
bool compare( Type_ const Left, Type_ const Right, comparison_operator const Operator ) {
|
||||
switch( Operator ) {
|
||||
case comparison_operator::equal: { return Left == Right; }
|
||||
case comparison_operator::not_equal: { return Left != Right; }
|
||||
case comparison_operator::lt: { return Left < Right; }
|
||||
case comparison_operator::gt: { return Left > Right; }
|
||||
case comparison_operator::lt_eq: { return Left <= Right; }
|
||||
case comparison_operator::gt_eq: { return Left >= Right; }
|
||||
default: { return false; }
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
std::string
|
||||
to_string( comparison_operator const Operator ) {
|
||||
switch( Operator ) {
|
||||
case comparison_operator::equal: { return "=="; }
|
||||
case comparison_operator::not_equal: { return "!="; }
|
||||
case comparison_operator::lt: { return "<"; }
|
||||
case comparison_operator::gt: { return ">"; }
|
||||
case comparison_operator::lt_eq: { return "<="; }
|
||||
case comparison_operator::gt_eq: { return ">"; }
|
||||
default: { return "??"; }
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
comparison_pass
|
||||
comparison_pass_from_string( std::string const Input ) {
|
||||
if( Input == "all" ) { return comparison_pass::all; }
|
||||
else if( Input == "any" ) { return comparison_pass::any; }
|
||||
else if( Input == "none" ) { return comparison_pass::none; }
|
||||
|
||||
return comparison_pass::all; // legacy default
|
||||
}
|
||||
|
||||
inline
|
||||
comparison_operator
|
||||
comparison_operator_from_string( std::string const Input ) {
|
||||
if( Input == "==" ) { return comparison_operator::equal; }
|
||||
else if( Input == "!=" ) { return comparison_operator::not_equal; }
|
||||
else if( Input == "<" ) { return comparison_operator::lt; }
|
||||
else if( Input == ">" ) { return comparison_operator::gt; }
|
||||
else if( Input == "<=" ) { return comparison_operator::lt_eq; }
|
||||
else if( Input == ">=" ) { return comparison_operator::gt_eq; }
|
||||
|
||||
return comparison_operator::equal; // legacy default
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
196
utilities/crashreporter.cpp
Normal file
196
utilities/crashreporter.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
#include <client/crash_report_database.h>
|
||||
#include <client/settings.h>
|
||||
#include <client/crashpad_client.h>
|
||||
#include <client/annotation.h>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include "version_info.h"
|
||||
|
||||
#if defined __has_attribute
|
||||
# if __has_attribute (init_priority)
|
||||
# define INITPRIO_CLASS __attribute__ ((init_priority (5000)))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef INITPRIO_CLASS
|
||||
# ifdef _MSC_VER
|
||||
#pragma init_seg(lib)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef INITPRIO_CLASS
|
||||
# define INITPRIO_CLASS
|
||||
#endif
|
||||
|
||||
class crash_reporter
|
||||
{
|
||||
crashpad::CrashpadClient client;
|
||||
std::unique_ptr<crashpad::CrashReportDatabase> database;
|
||||
|
||||
public:
|
||||
std::string provider = "";
|
||||
bool autoupload = false;
|
||||
|
||||
crash_reporter();
|
||||
void set_autoupload();
|
||||
void upload_reject();
|
||||
void upload_accept();
|
||||
bool upload_pending();
|
||||
};
|
||||
|
||||
crash_reporter::crash_reporter()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!std::filesystem::exists("crashdumps/crashpad_handler.exe"))
|
||||
return;
|
||||
#else
|
||||
if (!std::filesystem::exists("crashdumps/crashpad_handler"))
|
||||
return;
|
||||
#endif
|
||||
|
||||
autoupload = std::filesystem::exists("crashdumps/autoupload_enabled.conf");
|
||||
|
||||
std::string url, token, prov;
|
||||
|
||||
std::ifstream conf("crashdumps/crashpad.conf");
|
||||
|
||||
std::string line, param, value;
|
||||
while (std::getline(conf, line)) {
|
||||
std::istringstream linestream(line);
|
||||
|
||||
if (!std::getline(linestream, param, '='))
|
||||
continue;
|
||||
if (!std::getline(linestream, value, '='))
|
||||
continue;
|
||||
|
||||
if (param == "URL")
|
||||
url = value;
|
||||
else if (param == "TOKEN")
|
||||
token = value;
|
||||
else if (param == "PROVIDER")
|
||||
prov = value;
|
||||
}
|
||||
|
||||
if (token.empty())
|
||||
return;
|
||||
if (url.empty())
|
||||
return;
|
||||
if (prov.empty())
|
||||
return;
|
||||
|
||||
#ifdef _WIN32
|
||||
base::FilePath db(L"crashdumps");
|
||||
base::FilePath handler(L"crashdumps/crashpad_handler.exe");
|
||||
#else
|
||||
base::FilePath db("crashdumps");
|
||||
base::FilePath handler("crashdumps/crashpad_handler");
|
||||
#endif
|
||||
|
||||
std::map<std::string, std::string> annotations;
|
||||
annotations["git_hash"] = GIT_HASH;
|
||||
annotations["src_date"] = SRC_DATE;
|
||||
annotations["format"] = "minidump";
|
||||
annotations["token"] = token;
|
||||
|
||||
std::vector<std::string> arguments;
|
||||
arguments.push_back("--no-rate-limit");
|
||||
|
||||
database = crashpad::CrashReportDatabase::Initialize(db);
|
||||
|
||||
if (database == nullptr || database->GetSettings() == NULL)
|
||||
return;
|
||||
|
||||
std::vector<crashpad::CrashReportDatabase::Report> reports;
|
||||
database->GetCompletedReports(&reports);
|
||||
for (auto const &report : reports)
|
||||
if (report.uploaded)
|
||||
database->DeleteReport(report.uuid);
|
||||
|
||||
database->GetSettings()->SetUploadsEnabled(autoupload);
|
||||
|
||||
if (!client.StartHandler(handler, db, db, url, annotations, arguments, true, true))
|
||||
return;
|
||||
|
||||
provider = prov;
|
||||
}
|
||||
|
||||
void crash_reporter::set_autoupload()
|
||||
{
|
||||
autoupload = true;
|
||||
database->GetSettings()->SetUploadsEnabled(true);
|
||||
std::ofstream flag("crashdumps/autoupload_enabled.conf");
|
||||
flag << "y" << std::endl;
|
||||
flag.close();
|
||||
}
|
||||
|
||||
void crash_reporter::upload_accept()
|
||||
{
|
||||
std::vector<crashpad::CrashReportDatabase::Report> reports;
|
||||
database->GetCompletedReports(&reports);
|
||||
for (auto const &report : reports)
|
||||
if (!report.uploaded)
|
||||
database->RequestUpload(report.uuid);
|
||||
}
|
||||
|
||||
void crash_reporter::upload_reject()
|
||||
{
|
||||
std::vector<crashpad::CrashReportDatabase::Report> reports;
|
||||
database->GetCompletedReports(&reports);
|
||||
for (auto const &report : reports)
|
||||
if (!report.uploaded)
|
||||
database->DeleteReport(report.uuid);
|
||||
}
|
||||
|
||||
bool crash_reporter::upload_pending()
|
||||
{
|
||||
if (autoupload)
|
||||
return false;
|
||||
|
||||
std::vector<crashpad::CrashReportDatabase::Report> reports;
|
||||
database->GetCompletedReports(&reports);
|
||||
for (auto const &report : reports)
|
||||
if (!report.uploaded && !report.upload_explicitly_requested)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
crash_reporter crash_reporter_inst INITPRIO_CLASS;
|
||||
|
||||
const std::string& crashreport_get_provider()
|
||||
{
|
||||
return crash_reporter_inst.provider;
|
||||
}
|
||||
|
||||
void crashreport_add_info(const char *name, const std::string &value)
|
||||
{
|
||||
char *copy = new char[value.size() + 1];
|
||||
strcpy(copy, value.c_str());
|
||||
|
||||
crashpad::Annotation *annotation = new crashpad::Annotation(crashpad::Annotation::Type::kString, name, copy);
|
||||
annotation->SetSize(value.size() + 1);
|
||||
}
|
||||
|
||||
void crashreport_set_autoupload()
|
||||
{
|
||||
crash_reporter_inst.set_autoupload();
|
||||
}
|
||||
|
||||
bool crashreport_is_pending()
|
||||
{
|
||||
if (crash_reporter_inst.provider.empty())
|
||||
return false;
|
||||
return crash_reporter_inst.upload_pending();
|
||||
}
|
||||
|
||||
void crashreport_upload_reject()
|
||||
{
|
||||
crash_reporter_inst.upload_reject();
|
||||
}
|
||||
|
||||
void crashreport_upload_accept()
|
||||
{
|
||||
crash_reporter_inst.upload_accept();
|
||||
}
|
||||
15
utilities/crashreporter.h
Normal file
15
utilities/crashreporter.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifdef WITH_CRASHPAD
|
||||
void crashreport_add_info(const char *name, const std::string &value);
|
||||
const std::string& crashreport_get_provider();
|
||||
void crashreport_set_autoupload();
|
||||
bool crashreport_is_pending();
|
||||
void crashreport_upload_reject();
|
||||
void crashreport_upload_accept();
|
||||
#else
|
||||
#define crashreport_add_info(a,b)
|
||||
#define crashreport_get_provider() (std::string(""))
|
||||
#define crashreport_set_autoupload()
|
||||
#define crashreport_is_pending() (false)
|
||||
#define crashreport_upload_reject()
|
||||
#define crashreport_upload_accept()
|
||||
#endif
|
||||
50
utilities/dictionary.cpp
Normal file
50
utilities/dictionary.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "utilities/dictionary.h"
|
||||
|
||||
#include "simulation/simulation.h"
|
||||
#include "utilities/utilities.h"
|
||||
#include "vehicle/DynObj.h"
|
||||
#include "vehicle/Driver.h"
|
||||
#include "world/mtable.h"
|
||||
|
||||
dictionary_source::dictionary_source( std::string const &Input ) {
|
||||
|
||||
auto const keyvaluepairs { Split( Input, '&' ) };
|
||||
|
||||
for( auto const &keyvaluepair : keyvaluepairs ) {
|
||||
|
||||
auto const valuepos { keyvaluepair.find( '=' ) };
|
||||
if( keyvaluepair[ 0 ] != '$' ) {
|
||||
// regular key, value pairs
|
||||
insert(
|
||||
ToLower( keyvaluepair.substr( 0, valuepos ) ),
|
||||
keyvaluepair.substr( valuepos + 1 ) );
|
||||
}
|
||||
else {
|
||||
// special case, $key indicates request to include certain dataset clarified by value
|
||||
auto const key { ToLower( keyvaluepair.substr( 0, valuepos ) ) };
|
||||
auto const value { keyvaluepair.substr( valuepos + 1 ) };
|
||||
|
||||
if( key == "$timetable" ) {
|
||||
// timetable pulled from (preferably) the owner/direct controller of specified vehicle
|
||||
auto const *vehicle { simulation::Vehicles.find( value ) };
|
||||
auto const *controller { (
|
||||
vehicle == nullptr ? nullptr :
|
||||
vehicle->ctOwner == nullptr ? vehicle->Mechanik :
|
||||
vehicle->ctOwner ) };
|
||||
if( controller != nullptr ) {
|
||||
controller->TrainTimetable().serialize( this );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
utilities/dictionary.h
Normal file
33
utilities/dictionary.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// collection of keyword-value pairs
|
||||
// NOTE: since our python dictionary operates on a few types, most of the class was hardcoded for simplicity
|
||||
struct dictionary_source {
|
||||
// types
|
||||
template <typename Type_>
|
||||
using keyvaluepair_sequence = std::vector<std::pair<std::string, Type_>>;
|
||||
// members
|
||||
keyvaluepair_sequence<double> floats;
|
||||
keyvaluepair_sequence<int> integers;
|
||||
keyvaluepair_sequence<bool> bools;
|
||||
keyvaluepair_sequence<std::string> strings;
|
||||
keyvaluepair_sequence<std::vector<glm::vec2>> vec2_lists;
|
||||
// constructors
|
||||
dictionary_source() = default;
|
||||
dictionary_source( std::string const &Input );
|
||||
// methods
|
||||
inline void insert( std::string const &Key, double const Value ) { floats.emplace_back( Key, Value ); }
|
||||
inline void insert( std::string const &Key, int const Value ) { integers.emplace_back( Key, Value ); }
|
||||
inline void insert( std::string const &Key, bool const Value ) { bools.emplace_back( Key, Value ); }
|
||||
inline void insert( std::string const &Key, std::string const Value ) { strings.emplace_back( Key, Value ); }
|
||||
inline void insert( std::string const &Key, std::vector<glm::vec2> const Value ) { vec2_lists.emplace_back( Key, Value ); }
|
||||
};
|
||||
414
utilities/dumb3d.cpp
Normal file
414
utilities/dumb3d.cpp
Normal file
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "utilities/dumb3d.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace Math3D
|
||||
{
|
||||
|
||||
void vector3::RotateX(double angle)
|
||||
{
|
||||
double ty = y;
|
||||
y = (cos(angle) * y - z * sin(angle));
|
||||
z = (z * cos(angle) + sin(angle) * ty);
|
||||
};
|
||||
void vector3::RotateY(double angle)
|
||||
{
|
||||
double tx = x;
|
||||
x = (cos(angle) * x + z * sin(angle));
|
||||
z = (z * cos(angle) - sin(angle) * tx);
|
||||
};
|
||||
void vector3::RotateZ(double angle)
|
||||
{
|
||||
double ty = y;
|
||||
y = (cos(angle) * y + x * sin(angle));
|
||||
x = (x * cos(angle) - sin(angle) * ty);
|
||||
};
|
||||
|
||||
void inline vector3::SafeNormalize()
|
||||
{
|
||||
double l = Length();
|
||||
if (l == 0)
|
||||
{
|
||||
x = y = z = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
x /= l;
|
||||
y /= l;
|
||||
z /= l;
|
||||
}
|
||||
}
|
||||
|
||||
// From code in Graphics Gems; p. 766
|
||||
inline scalar_t det2x2(scalar_t a, scalar_t b, scalar_t c, scalar_t d)
|
||||
{
|
||||
return a * d - b * c;
|
||||
}
|
||||
|
||||
inline scalar_t det3x3(scalar_t a1, scalar_t a2, scalar_t a3, scalar_t b1, scalar_t b2, scalar_t b3,
|
||||
scalar_t c1, scalar_t c2, scalar_t c3)
|
||||
{
|
||||
return a1 * det2x2(b2, b3, c2, c3) - b1 * det2x2(a2, a3, c2, c3) + c1 * det2x2(a2, a3, b2, b3);
|
||||
}
|
||||
|
||||
scalar_t Determinant(const matrix4x4 &m)
|
||||
{
|
||||
scalar_t a1 = m[0][0];
|
||||
scalar_t a2 = m[1][0];
|
||||
scalar_t a3 = m[2][0];
|
||||
scalar_t a4 = m[3][0];
|
||||
scalar_t b1 = m[0][1];
|
||||
scalar_t b2 = m[1][1];
|
||||
scalar_t b3 = m[2][1];
|
||||
scalar_t b4 = m[3][1];
|
||||
scalar_t c1 = m[0][2];
|
||||
scalar_t c2 = m[1][2];
|
||||
scalar_t c3 = m[2][2];
|
||||
scalar_t c4 = m[3][2];
|
||||
scalar_t d1 = m[0][3];
|
||||
scalar_t d2 = m[1][3];
|
||||
scalar_t d3 = m[2][3];
|
||||
scalar_t d4 = m[3][3];
|
||||
|
||||
return a1 * det3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4) -
|
||||
b1 * det3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4) +
|
||||
c1 * det3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4) -
|
||||
d1 * det3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
|
||||
}
|
||||
|
||||
matrix4x4 Adjoint(const matrix4x4 &m)
|
||||
{
|
||||
scalar_t a1 = m[0][0];
|
||||
scalar_t a2 = m[0][1];
|
||||
scalar_t a3 = m[0][2];
|
||||
scalar_t a4 = m[0][3];
|
||||
scalar_t b1 = m[1][0];
|
||||
scalar_t b2 = m[1][1];
|
||||
scalar_t b3 = m[1][2];
|
||||
scalar_t b4 = m[1][3];
|
||||
scalar_t c1 = m[2][0];
|
||||
scalar_t c2 = m[2][1];
|
||||
scalar_t c3 = m[2][2];
|
||||
scalar_t c4 = m[2][3];
|
||||
scalar_t d1 = m[3][0];
|
||||
scalar_t d2 = m[3][1];
|
||||
scalar_t d3 = m[3][2];
|
||||
scalar_t d4 = m[3][3];
|
||||
|
||||
// Adjoint(x,y) = -1^(x+y) * a(y,x)
|
||||
// Where a(i,j) is the 3x3 determinant of m with row i and col j removed
|
||||
matrix4x4 retVal;
|
||||
retVal(0)[0] = det3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4);
|
||||
retVal(0)[1] = -det3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4);
|
||||
retVal(0)[2] = det3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4);
|
||||
retVal(0)[3] = -det3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
|
||||
|
||||
retVal(1)[0] = -det3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4);
|
||||
retVal(1)[1] = det3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4);
|
||||
retVal(1)[2] = -det3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4);
|
||||
retVal(1)[3] = det3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4);
|
||||
|
||||
retVal(2)[0] = det3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4);
|
||||
retVal(2)[1] = -det3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4);
|
||||
retVal(2)[2] = det3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4);
|
||||
retVal(2)[3] = -det3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4);
|
||||
|
||||
retVal(3)[0] = -det3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3);
|
||||
retVal(3)[1] = det3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3);
|
||||
retVal(3)[2] = -det3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3);
|
||||
retVal(3)[3] = det3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
matrix4x4 Inverse(const matrix4x4 &m)
|
||||
{
|
||||
matrix4x4 retVal = Adjoint(m);
|
||||
scalar_t det = Determinant(m);
|
||||
assert(det);
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
retVal(i)[j] /= det;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
//**************************************
|
||||
// Testing from here on.
|
||||
//**************************************
|
||||
#ifdef TEST_MATH3D
|
||||
#include <iostream>
|
||||
|
||||
using namespace Math3D;
|
||||
using namespace std;
|
||||
|
||||
static int failures = 0;
|
||||
|
||||
void ReportFailure(const char *className, const char *testName, bool passed)
|
||||
{
|
||||
cout << className;
|
||||
if (passed)
|
||||
cout << " passed test ";
|
||||
else
|
||||
{
|
||||
cout << " FAILED test ";
|
||||
++failures;
|
||||
}
|
||||
cout << testName << "." << endl;
|
||||
}
|
||||
|
||||
const char *vector3Name = "vector3";
|
||||
const char *matrix4x4Name = "matrix4x4";
|
||||
|
||||
void Testvector3Constructors(void)
|
||||
{
|
||||
// Default ctor... just make sure it compiles
|
||||
vector3 defaultCtorTest;
|
||||
|
||||
// Initializer ctor test (3 param)
|
||||
vector3 initCtorTest(1, 2, 3);
|
||||
ReportFailure(vector3Name, "initialized ctor (3 parameter version)",
|
||||
(initCtorTest[0] == 1 && initCtorTest[1] == 2 && initCtorTest[2] == 3 &&
|
||||
initCtorTest[3] == 1));
|
||||
|
||||
// Initializer ctor test (4 param)
|
||||
vector3 initCtorTest2(1, 2, 3, 4);
|
||||
ReportFailure(vector3Name, "initialized ctor (4 parameter version)",
|
||||
(initCtorTest2[0] == 1 && initCtorTest2[1] == 2 && initCtorTest2[2] == 3 &&
|
||||
initCtorTest2[3] == 4));
|
||||
|
||||
scalar_t initArray[] = {1, 2, 3, 4};
|
||||
vector3 initCtorArrayTest3(initArray);
|
||||
ReportFailure(vector3Name, "array initialized ctor (3 parameter version)",
|
||||
(initCtorArrayTest3[0] == 1 && initCtorArrayTest3[1] == 2 &&
|
||||
initCtorArrayTest3[2] == 3 && initCtorArrayTest3[3] == 1));
|
||||
|
||||
vector3 initCtorArrayTest4(initArray, 4);
|
||||
ReportFailure(vector3Name, "array initialized ctor (4 parameter version)",
|
||||
(initCtorArrayTest4[0] == 1 && initCtorArrayTest4[1] == 2 &&
|
||||
initCtorArrayTest4[2] == 3 && initCtorArrayTest4[3] == 4));
|
||||
|
||||
// Copy ctor test
|
||||
vector3 copyCtorTest(initCtorTest2);
|
||||
ReportFailure(vector3Name, "copy ctor", (copyCtorTest[0] == 1 && copyCtorTest[1] == 2 &&
|
||||
copyCtorTest[2] == 3 && copyCtorTest[3] == 4));
|
||||
}
|
||||
|
||||
void Testvector3Comparison(void)
|
||||
{
|
||||
vector3 alpha(1, 1, 1);
|
||||
vector3 beta(alpha);
|
||||
vector3 gamma(2, 3, 4);
|
||||
|
||||
ReportFailure(vector3Name, "equivalence operator test 1", (alpha == beta));
|
||||
ReportFailure(vector3Name, "equivalence operator test 2", (!(alpha == gamma)));
|
||||
ReportFailure(vector3Name, "comparison operator test 1", !(alpha < beta));
|
||||
ReportFailure(vector3Name, "comparison operator test 2", (alpha < gamma));
|
||||
ReportFailure(vector3Name, "comparison operator test 3", !(gamma < beta));
|
||||
}
|
||||
|
||||
void Testvector3Assignment(void)
|
||||
{
|
||||
vector3 alpha(1, 1, 1, 1);
|
||||
vector3 beta(10, 10, 10, 10);
|
||||
alpha = beta;
|
||||
ReportFailure(vector3Name, "assignment operator", (alpha == beta));
|
||||
}
|
||||
|
||||
void Testvector3UnaryOps(void)
|
||||
{
|
||||
vector3 alpha(10, 10, 10, 10);
|
||||
vector3 beta(-10, -10, -10, -10);
|
||||
alpha = -alpha;
|
||||
ReportFailure(vector3Name, "negation operator", (alpha == beta));
|
||||
|
||||
ReportFailure(vector3Name, "length squared 3 element version", LengthSquared3(alpha) == 300);
|
||||
ReportFailure(vector3Name, "length 3 element version", Length3(alpha) == SQRT_FUNCTION(300));
|
||||
ReportFailure(vector3Name, "length squared 4 element version", LengthSquared4(alpha) == 400);
|
||||
ReportFailure(vector3Name, "length 4 element version", Length4(alpha) == SQRT_FUNCTION(400));
|
||||
|
||||
// Manually normalize beta
|
||||
// Done without /= on vector3, as we want to be independant of failure of /=
|
||||
// Earlier failures should be resolved before later ones (just like C++)
|
||||
beta = alpha;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
beta(i) /= SQRT_FUNCTION(300);
|
||||
beta(3) = 1;
|
||||
ReportFailure(vector3Name, "normalize 3 element version", Normalize3(alpha) == beta);
|
||||
|
||||
beta = alpha;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
beta(i) /= SQRT_FUNCTION(400);
|
||||
ReportFailure(vector3Name, "normalize 4 element version", Normalize4(alpha) == beta);
|
||||
}
|
||||
|
||||
void Testvector3BinaryOps(void)
|
||||
{
|
||||
// Vector * Matrix is tested in Testmatrix4x4BinaryOps
|
||||
vector3 testVec(1, 1, 1, 1);
|
||||
vector3 deltaVec(1, 2, 3, 4);
|
||||
vector3 crossVec(1, -2, 1, 1); // testVec x deltaVec
|
||||
|
||||
vector3 factorVec(10, 10, 10, 10);
|
||||
vector3 sumVec(2, 3, 4, 5);
|
||||
vector3 difVec(0, -1, -2, -3);
|
||||
vector3 testVec2;
|
||||
|
||||
ReportFailure(vector3Name, "scalar multiply 1", (testVec * 10) == factorVec);
|
||||
ReportFailure(vector3Name, "scalar multiply 2", (10 * testVec) == factorVec);
|
||||
testVec2 = testVec;
|
||||
ReportFailure(vector3Name, "scalar multiply and store", (testVec2 *= 10) == factorVec);
|
||||
|
||||
ReportFailure(vector3Name, "scalar divide", (factorVec / 10) == testVec);
|
||||
testVec2 = factorVec;
|
||||
ReportFailure(vector3Name, "scalar divide and store", (testVec2 /= 10) == testVec);
|
||||
|
||||
ReportFailure(vector3Name, "vector addition", (testVec + deltaVec) == sumVec);
|
||||
testVec2 = testVec;
|
||||
ReportFailure(vector3Name, "vector addition and store", (testVec2 += deltaVec) == sumVec);
|
||||
|
||||
ReportFailure(vector3Name, "vector subtraction", (testVec - deltaVec) == difVec);
|
||||
testVec2 = testVec;
|
||||
ReportFailure(vector3Name, "vector subtraction and store", (testVec2 -= deltaVec) == difVec);
|
||||
|
||||
ReportFailure(vector3Name, "3 element dot product", 6 == DotProduct3(testVec, deltaVec));
|
||||
ReportFailure(vector3Name, "4 element dot product", 10 == DotProduct4(testVec, deltaVec));
|
||||
|
||||
ReportFailure(vector3Name, "cross product", crossVec == CrossProduct(testVec, deltaVec));
|
||||
}
|
||||
|
||||
void Testvector3(void)
|
||||
{
|
||||
// Accessors cannot be tested effectively...
|
||||
// They are really trivial, and so don't really need testing,
|
||||
// but more importantly, how do you test the ctors without assuming
|
||||
// the accessors work? Conversely, how do you test the acccessors
|
||||
// without assuming the ctors work? Chicken and egg problem, and I
|
||||
// decided on testing the ctors, not the accessors.
|
||||
Testvector3Constructors();
|
||||
Testvector3Comparison();
|
||||
Testvector3Assignment();
|
||||
Testvector3UnaryOps();
|
||||
Testvector3BinaryOps();
|
||||
}
|
||||
|
||||
void Testmatrix4x4Constructors(void)
|
||||
{
|
||||
// Check if default ctor compiles
|
||||
matrix4x4 defaultTest;
|
||||
|
||||
scalar_t initArray[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
matrix4x4 arrayTest;
|
||||
arrayTest.C_Matrix(initArray);
|
||||
bool passedTest = true;
|
||||
for (int x = 0; x < 4; ++x)
|
||||
for (int y = 0; y < 4; ++y)
|
||||
if (arrayTest[x][y] != initArray[(y << 2) + x])
|
||||
passedTest = false;
|
||||
ReportFailure(matrix4x4Name, "array constructor", passedTest);
|
||||
|
||||
matrix4x4 copyTest(arrayTest);
|
||||
passedTest = true;
|
||||
for (int x = 0; x < 4; ++x)
|
||||
for (int y = 0; y < 4; ++y)
|
||||
if (arrayTest[x][y] != copyTest[x][y])
|
||||
passedTest = false;
|
||||
ReportFailure(matrix4x4Name, "copy constructor", passedTest);
|
||||
}
|
||||
|
||||
void Testmatrix4x4Comparison(void)
|
||||
{
|
||||
scalar_t initArray[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
scalar_t initArray2[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
matrix4x4 alpha, beta, gamma;
|
||||
alpha.C_Matrix(initArray);
|
||||
beta.C_Matrix(initArray);
|
||||
gamma.C_Matrix(initArray2);
|
||||
|
||||
ReportFailure(matrix4x4Name, "equality test 1", alpha == beta);
|
||||
ReportFailure(matrix4x4Name, "equality test 2", !(alpha == gamma));
|
||||
ReportFailure(matrix4x4Name, "comparison test 1", alpha < gamma);
|
||||
ReportFailure(matrix4x4Name, "comparison test 2", !(gamma < alpha));
|
||||
ReportFailure(matrix4x4Name, "comparison test 3", !(alpha < beta));
|
||||
}
|
||||
|
||||
void Testmatrix4x4BinaryOps(void)
|
||||
{
|
||||
scalar_t initVector[] = {0, 1, 2, 3};
|
||||
scalar_t initMatrix[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
scalar_t resultVector[] = {0 * 0 + 1 * 1 + 2 * 2 + 3 * 3, 0 * 4 + 1 * 5 + 2 * 6 + 3 * 7,
|
||||
0 * 8 + 1 * 9 + 2 * 10 + 3 * 11, 0 * 12 + 1 * 13 + 2 * 14 + 3 * 15};
|
||||
|
||||
vector3 vector1(initVector, 4);
|
||||
matrix4x4 matrix1;
|
||||
matrix1.C_Matrix(initMatrix);
|
||||
vector3 vectorTest(resultVector, 4);
|
||||
ReportFailure(matrix4x4Name, "matrix * vector", vectorTest == matrix1 * vector1);
|
||||
|
||||
scalar_t initMatrix2[] = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
|
||||
|
||||
matrix4x4 matrix2;
|
||||
matrix2.C_Matrix(initMatrix2);
|
||||
matrix4x4 resultMatrix;
|
||||
for (int x = 0; x < 4; ++x)
|
||||
for (int y = 0; y < 4; ++y)
|
||||
{
|
||||
resultMatrix(x)[y] = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
resultMatrix(x)[y] += matrix1[i][y] * matrix2[x][i];
|
||||
}
|
||||
ReportFailure(matrix4x4Name, "matrix * matrix", resultMatrix == matrix1 * matrix2);
|
||||
}
|
||||
|
||||
void Testmatrix4x4(void)
|
||||
{
|
||||
Testmatrix4x4Constructors();
|
||||
Testmatrix4x4Comparison();
|
||||
Testmatrix4x4BinaryOps();
|
||||
}
|
||||
|
||||
int main(int, char *[])
|
||||
{
|
||||
int vectorFailures = 0;
|
||||
int matrixFailures = 0;
|
||||
Testvector3();
|
||||
vectorFailures = failures;
|
||||
failures = 0;
|
||||
|
||||
Testmatrix4x4();
|
||||
matrixFailures = failures;
|
||||
|
||||
cout << endl
|
||||
<< "****************************************" << endl;
|
||||
cout << "* *" << endl;
|
||||
if (vectorFailures + matrixFailures == 0)
|
||||
cout << "* No failures detected in Math3D *" << endl;
|
||||
else
|
||||
{
|
||||
cout << "* FAILURES DETECTED IN MATH3D! *" << endl;
|
||||
cout << "* Total vector3 failures: " << vectorFailures << " *" << endl;
|
||||
cout << "* Total matrix4x4 failures: " << matrixFailures << " *" << endl;
|
||||
cout << "* Total Failures in Math3D: " << vectorFailures + matrixFailures << " *"
|
||||
<< endl;
|
||||
}
|
||||
cout << "* *" << endl;
|
||||
cout << "****************************************" << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
655
utilities/dumb3d.h
Normal file
655
utilities/dumb3d.h
Normal file
@@ -0,0 +1,655 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace Math3D
|
||||
{
|
||||
|
||||
// Define this to have Math3D.cp generate a main which tests these classes
|
||||
//#define TEST_MATH3D
|
||||
|
||||
// Define this to allow streaming output of vectors and matrices
|
||||
// Automatically enabled by TEST_MATH3D
|
||||
//#define OSTREAM_MATH3D
|
||||
|
||||
// definition of the scalar type
|
||||
typedef double scalar_t;
|
||||
// inline pass-throughs to various basic math functions
|
||||
// written in this style to allow for easy substitution with more efficient versions
|
||||
inline scalar_t SINE_FUNCTION(scalar_t x)
|
||||
{
|
||||
return std::sin(x);
|
||||
}
|
||||
inline scalar_t COSINE_FUNCTION(scalar_t x)
|
||||
{
|
||||
return std::cos(x);
|
||||
}
|
||||
inline scalar_t SQRT_FUNCTION(scalar_t x)
|
||||
{
|
||||
return std::sqrt(x);
|
||||
}
|
||||
|
||||
// 2 element vector
|
||||
class vector2
|
||||
{
|
||||
public:
|
||||
vector2(void) :
|
||||
x(0.0), y(0.0)
|
||||
{
|
||||
}
|
||||
vector2(scalar_t a, scalar_t b)
|
||||
{
|
||||
x = a;
|
||||
y = b;
|
||||
}
|
||||
double x;
|
||||
union
|
||||
{
|
||||
double y;
|
||||
double z;
|
||||
};
|
||||
};
|
||||
// 3 element vector
|
||||
class vector3
|
||||
{
|
||||
public:
|
||||
vector3(void) :
|
||||
x(0.0), y(0.0), z(0.0)
|
||||
{}
|
||||
vector3( scalar_t X, scalar_t Y, scalar_t Z ) :
|
||||
x( X ), y( Y ), z( Z )
|
||||
{}
|
||||
template <typename Type_, glm::precision Precision_>
|
||||
vector3( glm::tvec3<Type_, Precision_> const &Vector ) :
|
||||
x( Vector.x ), y( Vector.y ), z( Vector.z )
|
||||
{}
|
||||
template <typename Type_, glm::precision Precision_>
|
||||
operator glm::tvec3<Type_, Precision_>() const {
|
||||
return glm::tvec3<Type_, Precision_>{ x, y, z }; }
|
||||
// The int parameter is the number of elements to copy from initArray (3 or 4)
|
||||
// explicit vector3(scalar_t* initArray, int arraySize = 3)
|
||||
// { for (int i = 0;i<arraySize;++i) e[i] = initArray[i]; }
|
||||
|
||||
void RotateX(double angle);
|
||||
void RotateY(double angle);
|
||||
void RotateZ(double angle);
|
||||
|
||||
void inline Normalize();
|
||||
void inline SafeNormalize();
|
||||
double inline Length() const;
|
||||
double inline LengthSquared() const;
|
||||
void inline Zero()
|
||||
{
|
||||
x = y = z = 0.0;
|
||||
};
|
||||
|
||||
// [] is to read, () is to write (const correctness)
|
||||
// const scalar_t& operator[] (int i) const { return e[i]; }
|
||||
// scalar_t& operator() (int i) { return e[i]; }
|
||||
|
||||
// Provides access to the underlying array; useful for passing this class off to C APIs
|
||||
const scalar_t *readArray(void)
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
scalar_t *getArray(void)
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
|
||||
double x, y, z;
|
||||
|
||||
bool inline Equal(vector3 *v)
|
||||
{ // sprawdzenie odległości punktów
|
||||
if (std::fabs(x - v->x) > 0.02)
|
||||
return false; // sześcian zamiast kuli
|
||||
if (std::fabs(z - v->z) > 0.02)
|
||||
return false;
|
||||
if (std::fabs(y - v->y) > 0.02)
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
operator glm::dvec3() const
|
||||
{
|
||||
return glm::dvec3(x, y, z);
|
||||
}
|
||||
private:
|
||||
};
|
||||
|
||||
// 4 element matrix
|
||||
class matrix4x4
|
||||
{
|
||||
public:
|
||||
matrix4x4(void)
|
||||
{
|
||||
memset( e, 0, sizeof( e ) );
|
||||
}
|
||||
|
||||
// When defining matrices in C arrays, it is easiest to define them with
|
||||
// the column increasing fastest. However, some APIs (OpenGL in particular) do this
|
||||
// backwards, hence the "constructor" from C matrices, or from OpenGL matrices.
|
||||
// Note that matrices are stored internally in OpenGL format.
|
||||
void C_Matrix(scalar_t const *initArray)
|
||||
{
|
||||
int i = 0;
|
||||
for (int y = 0; y < 4; ++y)
|
||||
for (int x = 0; x < 4; ++x)
|
||||
(*this)(x)[y] = initArray[i++];
|
||||
}
|
||||
template <typename Type_>
|
||||
void OpenGL_Matrix(Type_ const *initArray)
|
||||
{
|
||||
int i = 0;
|
||||
for (int x = 0; x < 4; ++x)
|
||||
for (int y = 0; y < 4; ++y)
|
||||
(*this)(x)[y] = initArray[i++];
|
||||
}
|
||||
|
||||
// [] is to read, () is to write (const correctness)
|
||||
// m[x][y] or m(x)[y] is the correct form
|
||||
const scalar_t *operator[](int i) const
|
||||
{
|
||||
return &e[i << 2];
|
||||
}
|
||||
scalar_t *operator()(int i)
|
||||
{
|
||||
return &e[i << 2];
|
||||
}
|
||||
|
||||
// Low-level access to the array.
|
||||
const scalar_t *readArray(void) const
|
||||
{
|
||||
return e;
|
||||
}
|
||||
scalar_t *getArray(void)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
|
||||
// Construct various matrices; REPLACES CURRENT CONTENTS OF THE MATRIX!
|
||||
// Written this way to work in-place and hence be somewhat more efficient
|
||||
void Identity(void)
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
e[i] = 0;
|
||||
e[0] = 1;
|
||||
e[5] = 1;
|
||||
e[10] = 1;
|
||||
e[15] = 1;
|
||||
}
|
||||
inline matrix4x4 &Rotation(scalar_t angle, vector3 axis);
|
||||
inline matrix4x4 &Translation(const vector3 &translation);
|
||||
inline matrix4x4 &Scale(scalar_t x, scalar_t y, scalar_t z);
|
||||
inline matrix4x4 &BasisChange(const vector3 &v, const vector3 &n);
|
||||
inline matrix4x4 &BasisChange(const vector3 &u, const vector3 &v, const vector3 &n);
|
||||
inline matrix4x4 &ProjectionMatrix(bool perspective, scalar_t l, scalar_t r, scalar_t t,
|
||||
scalar_t b, scalar_t n, scalar_t f);
|
||||
void InitialRotate()
|
||||
{ // taka specjalna rotacja, nie ma co ciągać trygonometrii
|
||||
double f;
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
e[i] = -e[i]; // zmiana znaku X
|
||||
f = e[i + 1];
|
||||
e[i + 1] = e[i + 2];
|
||||
e[i + 2] = f; // zamiana Y i Z
|
||||
}
|
||||
};
|
||||
inline bool IdentityIs()
|
||||
{ // sprawdzenie jednostkowości
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (e[i] != ((i % 5) ? 0.0 : 1.0)) // jedynki tylko na 0, 5, 10 i 15
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
operator glm::dmat4() const
|
||||
{
|
||||
return glm::make_mat4(e);
|
||||
}
|
||||
|
||||
private:
|
||||
scalar_t e[16];
|
||||
};
|
||||
|
||||
// Scalar operations
|
||||
|
||||
// Returns false if there are 0 solutions
|
||||
inline bool SolveQuadratic(scalar_t a, scalar_t b, scalar_t c, scalar_t *x1, scalar_t *x2);
|
||||
|
||||
// Vector operations
|
||||
inline bool operator==(const vector3 &, const vector3 &);
|
||||
inline bool operator<(const vector3 &, const vector3 &);
|
||||
|
||||
inline vector3 operator-(const vector3 &);
|
||||
inline vector3 operator*(const vector3 &, scalar_t);
|
||||
inline vector3 operator*(scalar_t, const vector3 &);
|
||||
inline vector3 &operator*=(vector3 &, scalar_t);
|
||||
inline vector3 operator/(const vector3 &, scalar_t);
|
||||
inline vector3 &operator/=(vector3 &, scalar_t);
|
||||
|
||||
inline vector3 operator+(const vector3 &, const vector3 &);
|
||||
inline vector3 &operator+=(vector3 &, const vector3 &);
|
||||
inline vector3 operator-(const vector3 &, const vector3 &);
|
||||
inline vector3 &operator-=(vector3 &, const vector3 &);
|
||||
|
||||
// X3 is the 3 element version of a function, X4 is four element
|
||||
inline scalar_t LengthSquared3(const vector3 &);
|
||||
inline scalar_t LengthSquared4(const vector3 &);
|
||||
inline scalar_t Length3(const vector3 &);
|
||||
inline scalar_t Length4(const vector3 &);
|
||||
inline vector3 Normalize(const vector3 &);
|
||||
inline vector3 Normalize4(const vector3 &);
|
||||
inline scalar_t DotProduct(const vector3 &, const vector3 &);
|
||||
inline scalar_t DotProduct4(const vector3 &, const vector3 &);
|
||||
// Cross product is only defined for 3 elements
|
||||
inline vector3 CrossProduct(const vector3 &, const vector3 &);
|
||||
|
||||
inline vector3 operator*(const matrix4x4 &, const vector3 &);
|
||||
|
||||
// Matrix operations
|
||||
inline bool operator==(const matrix4x4 &, const matrix4x4 &);
|
||||
inline bool operator<(const matrix4x4 &, const matrix4x4 &);
|
||||
|
||||
inline matrix4x4 operator*(const matrix4x4 &, const matrix4x4 &);
|
||||
|
||||
inline matrix4x4 Transpose(const matrix4x4 &);
|
||||
scalar_t Determinant(const matrix4x4 &);
|
||||
matrix4x4 Adjoint(const matrix4x4 &);
|
||||
matrix4x4 Inverse(const matrix4x4 &);
|
||||
|
||||
// Inline implementations follow
|
||||
inline bool SolveQuadratic(scalar_t a, scalar_t b, scalar_t c, scalar_t *x1, scalar_t *x2)
|
||||
{
|
||||
// If a == 0, solve a linear equation
|
||||
if (a == 0)
|
||||
{
|
||||
if (b == 0)
|
||||
return false;
|
||||
*x1 = c / b;
|
||||
*x2 = *x1;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
scalar_t det = b * b - 4 * a * c;
|
||||
if (det < 0)
|
||||
return false;
|
||||
det = SQRT_FUNCTION(det) / (2 * a);
|
||||
scalar_t prefix = -b / (2 * a);
|
||||
*x1 = prefix + det;
|
||||
*x2 = prefix - det;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool operator==(const vector3 &v1, const vector3 &v2)
|
||||
{
|
||||
return (v1.x == v2.x && v1.y == v2.y && v1.z == v2.z);
|
||||
}
|
||||
|
||||
inline bool operator<(const vector3 &v1, const vector3 &v2)
|
||||
{
|
||||
// for (int i=0;i<4;++i)
|
||||
// if (v1[i] < v2[i]) return true;
|
||||
// else if (v1[i] > v2[i]) return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline vector3 operator-(const vector3 &v)
|
||||
{
|
||||
return vector3(-v.x, -v.y, -v.z);
|
||||
}
|
||||
|
||||
inline vector3 operator*(const vector3 &v, scalar_t k)
|
||||
{
|
||||
return vector3(k * v.x, k * v.y, k * v.z);
|
||||
}
|
||||
|
||||
inline vector3 operator*(scalar_t k, const vector3 &v)
|
||||
{
|
||||
return v * k;
|
||||
}
|
||||
|
||||
inline vector3 &operator*=(vector3 &v, scalar_t k)
|
||||
{
|
||||
v.x *= k;
|
||||
v.y *= k;
|
||||
v.z *= k;
|
||||
return v;
|
||||
};
|
||||
|
||||
inline vector3 operator/(const vector3 &v, scalar_t k)
|
||||
{
|
||||
return vector3(v.x / k, v.y / k, v.z / k);
|
||||
}
|
||||
|
||||
inline vector3 &operator/=(vector3 &v, scalar_t k)
|
||||
{
|
||||
v.x /= k;
|
||||
v.y /= k;
|
||||
v.z /= k;
|
||||
return v;
|
||||
}
|
||||
|
||||
inline scalar_t LengthSquared3(const vector3 &v)
|
||||
{
|
||||
return DotProduct(v, v);
|
||||
}
|
||||
inline scalar_t LengthSquared4(const vector3 &v)
|
||||
{
|
||||
return DotProduct4(v, v);
|
||||
}
|
||||
|
||||
inline scalar_t Length3(const vector3 &v)
|
||||
{
|
||||
return SQRT_FUNCTION(LengthSquared3(v));
|
||||
}
|
||||
inline scalar_t Length4(const vector3 &v)
|
||||
{
|
||||
return SQRT_FUNCTION(LengthSquared4(v));
|
||||
}
|
||||
|
||||
inline vector3 Normalize(const vector3 &v)
|
||||
{
|
||||
vector3 retVal = v / Length3(v);
|
||||
return retVal;
|
||||
}
|
||||
inline vector3 SafeNormalize(const vector3 &v)
|
||||
{
|
||||
double l = Length3(v);
|
||||
vector3 retVal;
|
||||
if (l == 0)
|
||||
retVal.x = retVal.y = retVal.z = 0;
|
||||
else
|
||||
retVal = v / l;
|
||||
return retVal;
|
||||
}
|
||||
inline vector3 Normalize4(const vector3 &v)
|
||||
{
|
||||
return v / Length4(v);
|
||||
}
|
||||
|
||||
inline vector3 operator+(const vector3 &v1, const vector3 &v2)
|
||||
{
|
||||
return vector3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
|
||||
}
|
||||
|
||||
inline vector3 &operator+=(vector3 &v1, const vector3 &v2)
|
||||
{
|
||||
v1.x += v2.x;
|
||||
v1.y += v2.y;
|
||||
v1.z += v2.z;
|
||||
return v1;
|
||||
}
|
||||
|
||||
inline vector3 operator-(const vector3 &v1, const vector3 &v2)
|
||||
{
|
||||
return vector3(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
|
||||
}
|
||||
|
||||
inline vector3 &operator-=(vector3 &v1, const vector3 &v2)
|
||||
{
|
||||
v1.x -= v2.x;
|
||||
v1.y -= v2.y;
|
||||
v1.z -= v2.z;
|
||||
return v1;
|
||||
}
|
||||
|
||||
inline scalar_t DotProduct(const vector3 &v1, const vector3 &v2)
|
||||
{
|
||||
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
|
||||
}
|
||||
|
||||
inline scalar_t DotProduct4(const vector3 &v1, const vector3 &v2)
|
||||
{
|
||||
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
|
||||
}
|
||||
|
||||
inline vector3 CrossProduct(const vector3 &v1, const vector3 &v2)
|
||||
{
|
||||
return vector3(v1.y * v2.z - v1.z * v2.y, v2.x * v1.z - v2.z * v1.x, v1.x * v2.y - v1.y * v2.x);
|
||||
}
|
||||
|
||||
inline vector3 Interpolate( vector3 const &First, vector3 const &Second, float const Factor ) {
|
||||
|
||||
return ( First * ( 1.0f - Factor ) ) + ( Second * Factor );
|
||||
}
|
||||
|
||||
inline vector3 operator*(const matrix4x4 &m, const vector3 &v)
|
||||
{
|
||||
return vector3(v.x * m[0][0] + v.y * m[1][0] + v.z * m[2][0] + m[3][0],
|
||||
v.x * m[0][1] + v.y * m[1][1] + v.z * m[2][1] + m[3][1],
|
||||
v.x * m[0][2] + v.y * m[1][2] + v.z * m[2][2] + m[3][2]);
|
||||
}
|
||||
|
||||
void inline vector3::Normalize()
|
||||
{
|
||||
double il = 1 / Length();
|
||||
x *= il;
|
||||
y *= il;
|
||||
z *= il;
|
||||
}
|
||||
|
||||
double inline vector3::Length() const
|
||||
{
|
||||
return SQRT_FUNCTION(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
double inline vector3::LengthSquared() const {
|
||||
|
||||
return ( x * x + y * y + z * z );
|
||||
}
|
||||
|
||||
inline bool operator==(const matrix4x4 &m1, const matrix4x4 &m2)
|
||||
{
|
||||
for (int x = 0; x < 4; ++x)
|
||||
for (int y = 0; y < 4; ++y)
|
||||
if (m1[x][y] != m2[x][y])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool operator<(const matrix4x4 &m1, const matrix4x4 &m2)
|
||||
{
|
||||
for (int x = 0; x < 4; ++x)
|
||||
for (int y = 0; y < 4; ++y)
|
||||
if (m1[x][y] < m2[x][y])
|
||||
return true;
|
||||
else if (m1[x][y] > m2[x][y])
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline matrix4x4 operator*(const matrix4x4 &m1, const matrix4x4 &m2)
|
||||
{
|
||||
matrix4x4 retVal;
|
||||
for (int x = 0; x < 4; ++x)
|
||||
for (int y = 0; y < 4; ++y)
|
||||
{
|
||||
retVal(x)[y] = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
retVal(x)[y] += m1[i][y] * m2[x][i];
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
inline matrix4x4 Transpose(const matrix4x4 &m)
|
||||
{
|
||||
matrix4x4 retVal;
|
||||
for (int x = 0; x < 4; ++x)
|
||||
for (int y = 0; y < 4; ++y)
|
||||
retVal(x)[y] = m[y][x];
|
||||
return retVal;
|
||||
}
|
||||
|
||||
inline matrix4x4 &matrix4x4::Rotation(scalar_t angle, vector3 axis)
|
||||
{
|
||||
scalar_t c = COSINE_FUNCTION(angle);
|
||||
scalar_t s = SINE_FUNCTION(angle);
|
||||
// One minus c (short name for legibility of formulai)
|
||||
scalar_t omc = (1 - c);
|
||||
|
||||
if (LengthSquared3(axis) != 1)
|
||||
axis = Normalize(axis);
|
||||
|
||||
scalar_t x = axis.x;
|
||||
scalar_t y = axis.y;
|
||||
scalar_t z = axis.z;
|
||||
scalar_t xs = x * s;
|
||||
scalar_t ys = y * s;
|
||||
scalar_t zs = z * s;
|
||||
scalar_t xyomc = x * y * omc;
|
||||
scalar_t xzomc = x * z * omc;
|
||||
scalar_t yzomc = y * z * omc;
|
||||
|
||||
e[0] = x * x * omc + c;
|
||||
e[1] = xyomc + zs;
|
||||
e[2] = xzomc - ys;
|
||||
e[3] = 0;
|
||||
|
||||
e[4] = xyomc - zs;
|
||||
e[5] = y * y * omc + c;
|
||||
e[6] = yzomc + xs;
|
||||
e[7] = 0;
|
||||
|
||||
e[8] = xzomc + ys;
|
||||
e[9] = yzomc - xs;
|
||||
e[10] = z * z * omc + c;
|
||||
e[11] = 0;
|
||||
|
||||
e[12] = 0;
|
||||
e[13] = 0;
|
||||
e[14] = 0;
|
||||
e[15] = 1;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix4x4 &matrix4x4::Translation(const vector3 &translation)
|
||||
{
|
||||
Identity();
|
||||
e[12] = translation.x;
|
||||
e[13] = translation.y;
|
||||
e[14] = translation.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix4x4 &matrix4x4::Scale(scalar_t x, scalar_t y, scalar_t z)
|
||||
{
|
||||
Identity();
|
||||
e[0] = x;
|
||||
e[5] = y;
|
||||
e[10] = z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix4x4 &matrix4x4::BasisChange(const vector3 &u, const vector3 &v, const vector3 &n)
|
||||
{
|
||||
e[0] = u.x;
|
||||
e[1] = v.x;
|
||||
e[2] = n.x;
|
||||
e[3] = 0;
|
||||
|
||||
e[4] = u.y;
|
||||
e[5] = v.y;
|
||||
e[6] = n.y;
|
||||
e[7] = 0;
|
||||
|
||||
e[8] = u.z;
|
||||
e[9] = v.z;
|
||||
e[10] = n.z;
|
||||
e[11] = 0;
|
||||
|
||||
e[12] = 0;
|
||||
e[13] = 0;
|
||||
e[14] = 0;
|
||||
e[15] = 1;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix4x4 &matrix4x4::BasisChange(const vector3 &v, const vector3 &n)
|
||||
{
|
||||
vector3 u = CrossProduct(v, n);
|
||||
return BasisChange(u, v, n);
|
||||
}
|
||||
|
||||
inline matrix4x4 &matrix4x4::ProjectionMatrix(bool perspective, scalar_t left_plane,
|
||||
scalar_t right_plane, scalar_t top_plane,
|
||||
scalar_t bottom_plane, scalar_t near_plane,
|
||||
scalar_t far_plane)
|
||||
{
|
||||
scalar_t A = (right_plane + left_plane) / (right_plane - left_plane);
|
||||
scalar_t B = (top_plane + bottom_plane) / (top_plane - bottom_plane);
|
||||
scalar_t C = (far_plane + near_plane) / (far_plane - near_plane);
|
||||
|
||||
Identity();
|
||||
if (perspective)
|
||||
{
|
||||
e[0] = 2 * near_plane / (right_plane - left_plane);
|
||||
e[5] = 2 * near_plane / (top_plane - bottom_plane);
|
||||
e[8] = A;
|
||||
e[9] = B;
|
||||
e[10] = C;
|
||||
e[11] = -1;
|
||||
e[14] = 2 * far_plane * near_plane / (far_plane - near_plane);
|
||||
}
|
||||
else
|
||||
{
|
||||
e[0] = 2 / (right_plane - left_plane);
|
||||
e[5] = 2 / (top_plane - bottom_plane);
|
||||
e[10] = -2 / (far_plane - near_plane);
|
||||
e[12] = A;
|
||||
e[13] = B;
|
||||
e[14] = C;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
double inline SquareMagnitude(const vector3 &v)
|
||||
{
|
||||
return v.x * v.x + v.y * v.y + v.z * v.z;
|
||||
}
|
||||
|
||||
} // close namespace
|
||||
|
||||
// If we're testing, then we need OSTREAM support
|
||||
#ifdef TEST_MATH3D
|
||||
#define OSTREAM_MATH3D
|
||||
#endif
|
||||
|
||||
#ifdef OSTREAM_MATH3D
|
||||
#include <ostream>
|
||||
// Streaming support
|
||||
std::ostream &operator<<(std::ostream &os, const Math3D::vector3 &v)
|
||||
{
|
||||
os << '[';
|
||||
for (int i = 0; i < 4; ++i)
|
||||
os << ' ' << v[i];
|
||||
return os << ']';
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const Math3D::matrix4x4 &m)
|
||||
{
|
||||
for (int y = 0; y < 4; ++y)
|
||||
{
|
||||
os << '[';
|
||||
for (int x = 0; x < 4; ++x)
|
||||
os << ' ' << m[x][y];
|
||||
os << " ]" << std::endl;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
#endif // OSTREAM_MATH3D
|
||||
63
utilities/headtrack.cpp
Normal file
63
utilities/headtrack.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "stdafx.h"
|
||||
#include "utilities/headtrack.h"
|
||||
#include "utilities/Globals.h"
|
||||
|
||||
headtrack::headtrack()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void headtrack::find_joy() {
|
||||
for (size_t i = GLFW_JOYSTICK_1; i <= GLFW_JOYSTICK_LAST; i++) {
|
||||
if (!glfwJoystickPresent(i))
|
||||
continue;
|
||||
|
||||
std::string name(glfwGetJoystickName(i));
|
||||
if (name == Global.headtrack_conf.joy) {
|
||||
joy_id = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
joy_id = -1;
|
||||
}
|
||||
|
||||
float headtrack::get_axis(const float *data, int count, int axis, float mul) {
|
||||
if (axis < 0)
|
||||
return 0.0f;
|
||||
if (axis >= count)
|
||||
return 0.0f;
|
||||
|
||||
return data[axis] * mul;
|
||||
}
|
||||
|
||||
void headtrack::update()
|
||||
{
|
||||
if (joy_id == -1 || !glfwJoystickPresent(joy_id)) {
|
||||
Global.viewport_move = glm::vec3();
|
||||
Global.viewport_rotate = glm::mat3();
|
||||
find_joy();
|
||||
return;
|
||||
}
|
||||
|
||||
int count;
|
||||
const float* axes = glfwGetJoystickAxes(joy_id, &count);
|
||||
|
||||
auto const &move_axes = Global.headtrack_conf.move_axes;
|
||||
auto const &rot_axes = Global.headtrack_conf.rot_axes;
|
||||
auto const &move_mul = Global.headtrack_conf.move_mul;
|
||||
auto const &rot_mul = Global.headtrack_conf.rot_mul;
|
||||
|
||||
glm::vec3 move;
|
||||
move.x = get_axis(axes, count, move_axes.x, move_mul.x);
|
||||
move.y = get_axis(axes, count, move_axes.y, move_mul.y);
|
||||
move.z = get_axis(axes, count, move_axes.z, move_mul.z);
|
||||
|
||||
glm::vec3 rotate;
|
||||
rotate.x = get_axis(axes, count, rot_axes.x, rot_mul.x);
|
||||
rotate.y = get_axis(axes, count, rot_axes.y, rot_mul.y);
|
||||
rotate.z = get_axis(axes, count, rot_axes.z, rot_mul.z);
|
||||
|
||||
Global.viewport_move = move;
|
||||
Global.viewport_rotate = glm::orientate3(rotate);
|
||||
}
|
||||
14
utilities/headtrack.h
Normal file
14
utilities/headtrack.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
class headtrack
|
||||
{
|
||||
int joy_id = -1;
|
||||
|
||||
void find_joy();
|
||||
float get_axis(const float *data, int count, int axis, float mul);
|
||||
|
||||
public:
|
||||
headtrack();
|
||||
|
||||
void update();
|
||||
};
|
||||
161
utilities/motiontelemetry.cpp
Normal file
161
utilities/motiontelemetry.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include "stdafx.h"
|
||||
#include "utilities/motiontelemetry.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include "vehicle/Train.h"
|
||||
#include "utilities/Timer.h"
|
||||
#include "vehicle/Driver.h"
|
||||
#include "simulation/simulation.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
motiontelemetry::motiontelemetry()
|
||||
{
|
||||
conf = Global.motiontelemetry_conf;
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA wsd;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsd))
|
||||
throw std::runtime_error("failed to init winsock");
|
||||
#endif
|
||||
|
||||
struct addrinfo hints, *res;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = (conf.proto == "tcp" ? SOCK_STREAM : SOCK_DGRAM);
|
||||
|
||||
if (getaddrinfo(conf.address.c_str(), conf.port.c_str(), &hints, &res))
|
||||
throw std::runtime_error("motiontelemetry: getaddrinfo failed");
|
||||
|
||||
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
|
||||
if (sock == -1)
|
||||
throw std::runtime_error("motiontelemetry: failed to create socket");
|
||||
|
||||
if (connect(sock, res->ai_addr, res->ai_addrlen) == -1)
|
||||
throw std::runtime_error("motiontelemetry: failed to connect socket");
|
||||
|
||||
WriteLog("motiontelemetry: socket connected");
|
||||
}
|
||||
|
||||
motiontelemetry::~motiontelemetry()
|
||||
{
|
||||
shutdown(sock, 2);
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
void motiontelemetry::update()
|
||||
{
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
if (std::chrono::duration<float>(now - last_update).count() < conf.updatetime)
|
||||
return;
|
||||
last_update = now;
|
||||
|
||||
if (Global.iPause)
|
||||
return;
|
||||
|
||||
const TTrain *t = simulation::Train;
|
||||
if (!t)
|
||||
return;
|
||||
|
||||
double dt = Timer::GetDeltaTime();
|
||||
|
||||
glm::dvec3 front = t->Dynamic()->VectorFront();
|
||||
glm::dvec3 up = t->Dynamic()->VectorUp();
|
||||
glm::dvec3 left = t->Dynamic()->VectorLeft();
|
||||
|
||||
glm::dvec3 pos = t->Dynamic()->GetPosition();
|
||||
glm::dvec3 vel = (pos - last_pos) / dt;
|
||||
glm::dvec3 acc = (vel - last_vel) / dt;
|
||||
|
||||
glm::dvec3 local_acc;
|
||||
|
||||
if (conf.includegravity)
|
||||
{
|
||||
glm::dvec3 gravity(0.0, 9.81, 0.0);
|
||||
local_acc = glm::dvec3(-glm::dot(gravity, left), glm::dot(gravity, up), glm::dot(gravity, front));
|
||||
}
|
||||
|
||||
if (conf.latposbased)
|
||||
local_acc.x -= glm::dot(acc, left);
|
||||
else
|
||||
local_acc.x -= t->Occupied()->AccN;
|
||||
|
||||
local_acc.y += glm::dot(acc, up) + t->Occupied()->AccVert * conf.axlebumpscale;
|
||||
|
||||
if (conf.fwdposbased)
|
||||
local_acc.z += glm::dot(acc, front);
|
||||
else
|
||||
local_acc.z += t->Occupied()->AccSVBased;
|
||||
|
||||
local_acc /= 9.81;
|
||||
|
||||
// roll calculation, maybe too complicated?
|
||||
|
||||
// transform to left-handed
|
||||
front.z *= -1;
|
||||
up.z *= -1;
|
||||
|
||||
// make sure that vectors are orthonormal
|
||||
glm::dvec3 oright = glm::normalize(glm::cross(up, front));
|
||||
glm::dvec3 oup = glm::cross(front, oright);
|
||||
|
||||
// right and up vector without roll
|
||||
glm::dvec3 right0 = glm::normalize(glm::cross(glm::dvec3(0.0, 1.0, 0.0), front));
|
||||
glm::dvec3 up0 = glm::cross(front, right0);
|
||||
|
||||
double cosroll = glm::dot(up0, oup);
|
||||
double sinroll;
|
||||
|
||||
if (abs(right0.x) > abs(right0.y) && abs(right0.x) > abs(right0.z))
|
||||
sinroll = (up0.x * cosroll - oup.x) / right0.x;
|
||||
else if (abs(right0.y) > abs(right0.x) && abs(right0.y) > abs(right0.z))
|
||||
sinroll = (up0.y * cosroll - oup.y) / right0.y;
|
||||
else
|
||||
sinroll = (up0.z * cosroll - oup.z) / right0.z;
|
||||
|
||||
glm::dvec3 rot(asin(-front.y), atan2(front.x, front.z), asin(sinroll));
|
||||
|
||||
double velocity = t->Occupied()->V;
|
||||
double yaw_vel = (rot.y - last_yaw) / dt;
|
||||
|
||||
last_yaw = rot.y;
|
||||
last_pos = pos;
|
||||
last_vel = vel;
|
||||
|
||||
if (t->Occupied()->CabActive < 0)
|
||||
{
|
||||
velocity *= -1;
|
||||
local_acc.x *= -1;
|
||||
local_acc.z *= -1;
|
||||
yaw_vel *= -1;
|
||||
rot *= -1;
|
||||
}
|
||||
|
||||
float buffer[12] = { 0 };
|
||||
buffer[0] = Timer::GetTime();
|
||||
buffer[1] = velocity;
|
||||
buffer[2] = local_acc.y;
|
||||
buffer[3] = local_acc.z;
|
||||
buffer[4] = local_acc.x;
|
||||
buffer[5] = glm::degrees(rot.x);
|
||||
buffer[6] = glm::degrees(rot.z);
|
||||
buffer[7] = glm::degrees(rot.y);
|
||||
buffer[8] = yaw_vel;
|
||||
buffer[9] = 1.0f;
|
||||
|
||||
if (send(sock, (char*)buffer, sizeof(buffer), 0) == -1)
|
||||
WriteLog("motiontelemetry: socket send failed");
|
||||
}
|
||||
34
utilities/motiontelemetry.h
Normal file
34
utilities/motiontelemetry.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
class motiontelemetry
|
||||
{
|
||||
public:
|
||||
struct conf_t
|
||||
{
|
||||
bool enable;
|
||||
std::string proto;
|
||||
std::string address;
|
||||
std::string port;
|
||||
float updatetime;
|
||||
bool includegravity;
|
||||
bool fwdposbased;
|
||||
bool latposbased;
|
||||
float axlebumpscale;
|
||||
};
|
||||
|
||||
private:
|
||||
int sock;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> last_update;
|
||||
conf_t conf;
|
||||
glm::dvec3 last_pos;
|
||||
glm::dvec3 last_vel;
|
||||
double last_yaw;
|
||||
|
||||
public:
|
||||
motiontelemetry();
|
||||
~motiontelemetry();
|
||||
void update();
|
||||
};
|
||||
603
utilities/parser.cpp
Normal file
603
utilities/parser.cpp
Normal file
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "utilities/parser.h"
|
||||
#include "utilities/utilities.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
#include "scene/scenenodegroups.h"
|
||||
|
||||
/*
|
||||
MaSzyna EU07 locomotive simulator parser
|
||||
Copyright (C) 2003 TOLARIS
|
||||
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cParser -- generic class for parsing text data.
|
||||
|
||||
namespace
|
||||
{
|
||||
inline std::array<bool, 256> makeBreakTable(const char *brk)
|
||||
{
|
||||
std::array<bool, 256> arr{};
|
||||
for (unsigned char c : std::string_view(brk ? brk : ""))
|
||||
{
|
||||
arr[c] = true;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
inline char toLowerChar(char c)
|
||||
{
|
||||
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||
}
|
||||
|
||||
inline bool startsWithBOM(const std::string &s)
|
||||
{
|
||||
return s.size() >= 3
|
||||
&& static_cast<unsigned char>(s[0]) == 0xEF
|
||||
&& static_cast<unsigned char>(s[1]) == 0xBB
|
||||
&& static_cast<unsigned char>(s[2]) == 0xBF;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// constructors
|
||||
cParser::cParser(std::string const &Stream, buffertype const Type, std::string Path, bool const Loadtraction, std::vector<std::string> Parameters, bool allowRandom)
|
||||
: allowRandomIncludes(allowRandom), LoadTraction(Loadtraction), mPath(Path)
|
||||
{
|
||||
// store to calculate sub-sequent includes from relative path
|
||||
if (Type == buffertype::buffer_FILE)
|
||||
{
|
||||
mFile = Stream;
|
||||
}
|
||||
// reset pointers and attach proper type of buffer
|
||||
switch (Type)
|
||||
{
|
||||
case buffer_FILE:
|
||||
{
|
||||
Path.append(Stream);
|
||||
mStream = std::make_shared<std::ifstream>(Path, std::ios_base::binary);
|
||||
// content of *.inc files is potentially grouped together
|
||||
if ((Stream.size() >= 4) && (ToLower(Stream.substr(Stream.size() - 4)) == ".inc"))
|
||||
{
|
||||
mIncFile = true;
|
||||
scene::Groups.create();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case buffer_TEXT:
|
||||
{
|
||||
mStream = std::make_shared<std::istringstream>(Stream);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// calculate stream size
|
||||
if (mStream)
|
||||
{
|
||||
if (true == mStream->fail())
|
||||
{
|
||||
ErrorLog("Failed to open file \"" + Path + "\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
mSize = mStream->rdbuf()->pubseekoff(0, std::ios_base::end);
|
||||
mStream->rdbuf()->pubseekoff(0, std::ios_base::beg);
|
||||
mLine = 1;
|
||||
}
|
||||
}
|
||||
// set parameter set if one was provided
|
||||
if (false == Parameters.empty())
|
||||
{
|
||||
parameters.swap(Parameters);
|
||||
}
|
||||
}
|
||||
|
||||
// destructor
|
||||
cParser::~cParser()
|
||||
{
|
||||
|
||||
if (true == mIncFile)
|
||||
{
|
||||
// wrap up the node group holding content of processed file
|
||||
scene::Groups.close();
|
||||
}
|
||||
}
|
||||
|
||||
template <> glm::vec3 cParser::getToken(bool const ToLower, char const *Break)
|
||||
{
|
||||
// NOTE: this specialization ignores default arguments
|
||||
getTokens(3, false, "\n\r\t ,;[]");
|
||||
glm::vec3 output;
|
||||
*this >> output.x >> output.y >> output.z;
|
||||
return output;
|
||||
};
|
||||
|
||||
template <> cParser &cParser::operator>>(std::string &Right)
|
||||
{
|
||||
|
||||
if (true == this->tokens.empty())
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
Right = this->tokens.front();
|
||||
this->tokens.pop_front();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <> cParser &cParser::operator>>(bool &Right)
|
||||
{
|
||||
|
||||
if (true == this->tokens.empty())
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
Right = ((this->tokens.front() == "true") || (this->tokens.front() == "yes") || (this->tokens.front() == "1"));
|
||||
this->tokens.pop_front();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <> bool cParser::getToken<bool>(bool const ToLower, const char *Break)
|
||||
{
|
||||
|
||||
auto const token = getToken<std::string>(true, Break);
|
||||
return ((token == "true") || (token == "yes") || (token == "1"));
|
||||
}
|
||||
|
||||
// methods
|
||||
cParser &cParser::autoclear(bool const Autoclear)
|
||||
{
|
||||
|
||||
m_autoclear = Autoclear;
|
||||
if (mIncludeParser)
|
||||
{
|
||||
mIncludeParser->autoclear(Autoclear);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool cParser::getTokens(unsigned int Count, bool ToLower, const char *Break)
|
||||
{
|
||||
if (true == m_autoclear)
|
||||
{
|
||||
// legacy parser behaviour
|
||||
tokens.clear();
|
||||
}
|
||||
/*
|
||||
if (LoadTraction==true)
|
||||
trtest="niemaproblema"; //wczytywać
|
||||
else
|
||||
trtest="x"; //nie wczytywać
|
||||
*/
|
||||
/*
|
||||
int i;
|
||||
this->str("");
|
||||
this->clear();
|
||||
*/
|
||||
for (unsigned int i = tokens.size(); i < Count; ++i)
|
||||
{
|
||||
std::string token = readToken(ToLower, Break);
|
||||
if (true == token.empty())
|
||||
{
|
||||
// no more tokens
|
||||
break;
|
||||
}
|
||||
// collect parameters
|
||||
tokens.emplace_back(token);
|
||||
/*
|
||||
if (i == 0)
|
||||
this->str(token);
|
||||
else
|
||||
{
|
||||
std::string temp = this->str();
|
||||
temp.append("\n");
|
||||
temp.append(token);
|
||||
this->str(temp);
|
||||
}
|
||||
*/
|
||||
}
|
||||
if (tokens.size() < Count)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string cParser::readTokenFromDelegate(bool ToLower, const char *Break)
|
||||
{
|
||||
if (!mIncludeParser)
|
||||
return {};
|
||||
std::string token = mIncludeParser->readToken(ToLower, Break);
|
||||
if (token.empty())
|
||||
{
|
||||
mIncludeParser = nullptr;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
std::string cParser::readTokenFromStream(bool ToLower, const char *Break)
|
||||
{
|
||||
std::string token;
|
||||
token.reserve(64);
|
||||
|
||||
const auto breakTable = makeBreakTable(Break);
|
||||
char c = 0;
|
||||
|
||||
|
||||
while (token.empty() && mStream->peek() != EOF) {
|
||||
while (mStream->peek() != EOF) {
|
||||
c = static_cast<char>(mStream->get());
|
||||
|
||||
if (c == '\n') {
|
||||
++mLine;
|
||||
}
|
||||
|
||||
const unsigned char uc = static_cast<unsigned char>(c);
|
||||
if (breakTable[uc]) {
|
||||
// separator ends token (or continues skipping if token empty)
|
||||
if (!token.empty())
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ToLower) c = toLowerChar(c);
|
||||
token.push_back(c);
|
||||
|
||||
if (findQuotes(token)) {
|
||||
continue; // glue quoted content
|
||||
}
|
||||
if (skipComments && trimComments(token)) {
|
||||
break; // don't glue tokens separated by comment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
void cParser::stripFirstTokenBOM(std::string& token, bool ToLower, const char* Break) {
|
||||
if (!mFirstToken) return;
|
||||
mFirstToken = false;
|
||||
|
||||
if (startsWithBOM(token)) {
|
||||
token.erase(0, 3);
|
||||
}
|
||||
|
||||
// if first "token" was standalone BOM, read the next real token (avoid recursion)
|
||||
while (token.empty() && mStream->peek() != EOF) {
|
||||
token = readToken(ToLower, Break);
|
||||
// readToken will not re-enter BOM stripping because mFirstToken is now false
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void cParser::substituteParameters(std::string& token, bool ToLower) {
|
||||
if (parameters.empty()) return;
|
||||
|
||||
// Replace occurrences of "(pN)" anywhere in token.
|
||||
// Keep behavior: if missing parameter -> "none".
|
||||
size_t pos = 0;
|
||||
while ((pos = token.find("(p", pos)) != std::string::npos) {
|
||||
const size_t close = token.find(')', pos);
|
||||
if (close == std::string::npos) break; // malformed -> stop like old behavior (it would substr weirdly)
|
||||
|
||||
const std::string idxStr = token.substr(pos + 2, close - (pos + 2));
|
||||
token.erase(pos, (close - pos) + 1);
|
||||
|
||||
const size_t nr = static_cast<size_t>(std::atoi(idxStr.c_str()));
|
||||
const std::string repl = (nr >= 1 && (nr - 1) < parameters.size())
|
||||
? parameters[nr - 1]
|
||||
: std::string("none");
|
||||
|
||||
const size_t insertPos = pos;
|
||||
token.insert(insertPos, repl);
|
||||
|
||||
if (ToLower) {
|
||||
// Lowercase only what we inserted (same intent as original)
|
||||
for (size_t i = insertPos; i < insertPos + repl.size(); ++i) {
|
||||
token[i] = toLowerChar(token[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pos = insertPos + repl.size(); // continue after inserted text
|
||||
}
|
||||
}
|
||||
|
||||
void cParser::skipIncludeBlock() {
|
||||
// mimic original: while token != "end" readToken(true)
|
||||
std::string t;
|
||||
do {
|
||||
t = readToken(true);
|
||||
} while (t != "end" && !t.empty());
|
||||
}
|
||||
|
||||
void cParser::startIncludeFromParser(cParser& srcParser, bool ToLower, std::string includefile) {
|
||||
replace_slashes(includefile);
|
||||
|
||||
const bool allowTraction =
|
||||
(true == LoadTraction) ||
|
||||
((false == contains(includefile, "tr/")) && (false == contains(includefile, "tra/")));
|
||||
|
||||
if (!allowTraction) {
|
||||
// skip include block until "end" (original behavior in token-mode include)
|
||||
skipIncludeBlock();
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isTerrain = contains(includefile, "_ter.scm");
|
||||
if (isTerrain && true == Global.file_binary_terrain_state) {
|
||||
WriteLog("SBT found, ignoring: " + includefile);
|
||||
readParameters(srcParser); // preserve original side-effect: still consume parameters
|
||||
return;
|
||||
}
|
||||
|
||||
if (Global.ParserLogIncludes) {
|
||||
if (isTerrain) WriteLog("including terrain: " + includefile);
|
||||
else {
|
||||
// WriteLog("including: " + includefile);
|
||||
}
|
||||
}
|
||||
|
||||
mIncludeParser = std::make_shared<cParser>(
|
||||
includefile, /*buffer_FILE*/ static_cast<buffertype>(/*buffer_FILE*/ 0), mPath, LoadTraction, readParameters(srcParser)
|
||||
);
|
||||
mIncludeParser->allowRandomIncludes = allowRandomIncludes;
|
||||
mIncludeParser->autoclear(m_autoclear);
|
||||
|
||||
if (mIncludeParser->mSize <= 0) {
|
||||
ErrorLog("Bad include: can't open file \"" + includefile + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
bool cParser::handleIncludeIfPresent(std::string& token, bool ToLower, const char* Break) {
|
||||
// token-mode include: token == "include"
|
||||
if (expandIncludes && token == "include") {
|
||||
std::string includefile =
|
||||
allowRandomIncludes ? deserialize_random_set(*this) : readToken(ToLower);
|
||||
|
||||
startIncludeFromParser(*this, ToLower, std::move(includefile));
|
||||
|
||||
// after processing include, return next token from current parser
|
||||
token = readToken(ToLower, Break);
|
||||
return true;
|
||||
}
|
||||
|
||||
// line-mode HACK: Break == "\n\r" and line begins with "include"
|
||||
if ((std::strcmp(Break, "\n\r") == 0) && token.compare(0, 7, "include") == 0) {
|
||||
cParser includeparser(token.substr(7));
|
||||
std::string includefile =
|
||||
allowRandomIncludes ? deserialize_random_set(includeparser) : includeparser.readToken(ToLower);
|
||||
|
||||
startIncludeFromParser(includeparser, ToLower, std::move(includefile));
|
||||
|
||||
token = readToken(ToLower, Break);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string cParser::readToken(bool ToLower, const char *Break)
|
||||
{
|
||||
std::string token;
|
||||
|
||||
token = readTokenFromDelegate(ToLower, Break);
|
||||
if (token.empty())
|
||||
{
|
||||
token = readTokenFromStream(ToLower, Break);
|
||||
}
|
||||
|
||||
stripFirstTokenBOM(token, ToLower, Break);
|
||||
|
||||
substituteParameters(token, ToLower);
|
||||
|
||||
handleIncludeIfPresent(token, ToLower, Break);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
std::vector<std::string> cParser::readParameters(cParser &Input)
|
||||
{
|
||||
|
||||
std::vector<std::string> includeparameters;
|
||||
std::string parameter = Input.readToken(false); // w parametrach nie zmniejszamy
|
||||
while ((parameter.empty() == false) && (parameter != "end"))
|
||||
{
|
||||
includeparameters.emplace_back(parameter);
|
||||
parameter = Input.readToken(false);
|
||||
}
|
||||
return includeparameters;
|
||||
}
|
||||
|
||||
std::string cParser::readQuotes(char const Quote)
|
||||
{ // read the stream until specified char or stream end
|
||||
std::string token = "";
|
||||
char c{0};
|
||||
bool escaped = false;
|
||||
while (mStream->peek() != EOF)
|
||||
{ // get all chars until the quote mark
|
||||
c = mStream->get();
|
||||
|
||||
if (escaped)
|
||||
{
|
||||
escaped = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '\\')
|
||||
{
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
else if (c == Quote)
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
++mLine; // update line counter
|
||||
token += c;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
void cParser::skipComment(std::string const &Endmark)
|
||||
{ // pobieranie znaków aż do znalezienia znacznika końca
|
||||
std::string input = "";
|
||||
char c{0};
|
||||
auto const endmarksize = Endmark.size();
|
||||
while (mStream->peek() != EOF)
|
||||
{
|
||||
// o ile nie koniec pliku
|
||||
c = mStream->get(); // pobranie znaku
|
||||
if (c == '\n')
|
||||
{
|
||||
// update line counter
|
||||
++mLine;
|
||||
}
|
||||
input += c;
|
||||
if (input == Endmark) // szukanie znacznika końca
|
||||
break;
|
||||
if (input.size() >= endmarksize)
|
||||
{
|
||||
// keep the read text short, to avoid pointless string re-allocations on longer comments
|
||||
input = input.substr(1);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool cParser::findQuotes(std::string &String)
|
||||
{
|
||||
|
||||
if (String.back() == '\"')
|
||||
{
|
||||
|
||||
String.pop_back();
|
||||
String += readQuotes();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cParser::trimComments(std::string &String)
|
||||
{
|
||||
for (auto const &comment : mComments)
|
||||
{
|
||||
if (String.size() < comment.first.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (String.compare(String.size() - comment.first.size(), comment.first.size(), comment.first) == 0)
|
||||
{
|
||||
skipComment(comment.second);
|
||||
String.resize(String.rfind(comment.first));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cParser::injectString(const std::string &str)
|
||||
{
|
||||
if (mIncludeParser)
|
||||
{
|
||||
mIncludeParser->injectString(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
mIncludeParser = std::make_shared<cParser>(str, buffer_TEXT, "", LoadTraction, std::vector<std::string>(), allowRandomIncludes);
|
||||
mIncludeParser->autoclear(m_autoclear);
|
||||
}
|
||||
}
|
||||
|
||||
int cParser::getProgress() const
|
||||
{
|
||||
return static_cast<int>(mStream->rdbuf()->pubseekoff(0, std::ios_base::cur) * 100 / mSize);
|
||||
}
|
||||
|
||||
int cParser::getFullProgress() const
|
||||
{
|
||||
|
||||
int progress = getProgress();
|
||||
if (mIncludeParser)
|
||||
return progress + ((100 - progress) * (mIncludeParser->getProgress()) / 100);
|
||||
else
|
||||
return progress;
|
||||
}
|
||||
|
||||
std::size_t cParser::countTokens(std::string const &Stream, std::string Path)
|
||||
{
|
||||
|
||||
return cParser(Stream, buffer_FILE, Path).count();
|
||||
}
|
||||
|
||||
std::size_t cParser::count()
|
||||
{
|
||||
|
||||
std::string token;
|
||||
size_t count{0};
|
||||
do
|
||||
{
|
||||
token = "";
|
||||
token = readToken(false);
|
||||
++count;
|
||||
} while (false == token.empty());
|
||||
|
||||
return count - 1;
|
||||
}
|
||||
|
||||
void cParser::addCommentStyle(std::string const &Commentstart, std::string const &Commentend)
|
||||
{
|
||||
|
||||
mComments.insert(commentmap::value_type(Commentstart, Commentend));
|
||||
}
|
||||
|
||||
// returns name of currently open file, or empty string for text type stream
|
||||
std::string cParser::Name() const
|
||||
{
|
||||
|
||||
if (mIncludeParser)
|
||||
{
|
||||
return mIncludeParser->Name();
|
||||
}
|
||||
else
|
||||
{
|
||||
return mPath + mFile;
|
||||
}
|
||||
}
|
||||
|
||||
// returns number of currently processed line
|
||||
std::size_t cParser::Line() const
|
||||
{
|
||||
|
||||
if (mIncludeParser)
|
||||
{
|
||||
return mIncludeParser->Line();
|
||||
}
|
||||
else
|
||||
{
|
||||
return mLine;
|
||||
}
|
||||
}
|
||||
|
||||
int cParser::LineMain() const
|
||||
{
|
||||
return mIncludeParser ? -1 : mLine;
|
||||
}
|
||||
161
utilities/parser.h
Normal file
161
utilities/parser.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cParser -- generic class for parsing text data, either from file or provided string
|
||||
|
||||
class cParser //: public std::stringstream
|
||||
{
|
||||
public:
|
||||
// parameters:
|
||||
enum buffertype
|
||||
{
|
||||
buffer_FILE,
|
||||
buffer_TEXT
|
||||
};
|
||||
// constructors:
|
||||
cParser(std::string const &Stream, buffertype const Type = buffer_TEXT, std::string Path = "", bool const Loadtraction = true, std::vector<std::string> Parameters = std::vector<std::string>(), bool allowRandom = false );
|
||||
// destructor:
|
||||
virtual ~cParser();
|
||||
// methods:
|
||||
template <typename Type_>
|
||||
cParser &
|
||||
operator>>( Type_ &Right );
|
||||
template <typename Output_>
|
||||
Output_
|
||||
getToken( bool const ToLower = true, char const *Break = "\n\r\t ;" ) {
|
||||
getTokens( 1, ToLower, Break );
|
||||
Output_ output;
|
||||
*this >> output;
|
||||
return output; };
|
||||
inline
|
||||
void
|
||||
ignoreToken() {
|
||||
readToken(); };
|
||||
inline
|
||||
bool
|
||||
expectToken( std::string const &Value ) {
|
||||
return readToken() == Value; };
|
||||
inline
|
||||
bool
|
||||
eof() {
|
||||
return mStream->eof(); };
|
||||
inline
|
||||
bool
|
||||
ok() {
|
||||
return ( !mStream->fail() ); };
|
||||
cParser &
|
||||
autoclear( bool const Autoclear );
|
||||
inline
|
||||
bool
|
||||
autoclear() const {
|
||||
return m_autoclear; }
|
||||
bool
|
||||
getTokens( unsigned int Count = 1, bool ToLower = true, char const *Break = "\n\r\t ;" );
|
||||
std::string readTokenFromDelegate(bool ToLower, const char *Break);
|
||||
std::string readTokenFromStream(bool ToLower, const char *Break);
|
||||
void stripFirstTokenBOM(std::string &token, bool ToLower, const char *Break);
|
||||
void substituteParameters(std::string &token, bool ToLower);
|
||||
void skipIncludeBlock();
|
||||
// returns next incoming token, if any, without removing it from the set
|
||||
inline
|
||||
std::string
|
||||
peek() const {
|
||||
return (
|
||||
false == tokens.empty() ?
|
||||
tokens.front() :
|
||||
"" ); }
|
||||
// inject string as internal include
|
||||
void injectString(const std::string &str);
|
||||
|
||||
// returns percentage of file processed so far
|
||||
int getProgress() const;
|
||||
int getFullProgress() const;
|
||||
//
|
||||
static std::size_t countTokens( std::string const &Stream, std::string Path = "" );
|
||||
// add custom definition of text which should be ignored when retrieving tokens
|
||||
void addCommentStyle( std::string const &Commentstart, std::string const &Commentend );
|
||||
// returns name of currently open file, or empty string for text type stream
|
||||
std::string Name() const;
|
||||
// returns number of currently processed line
|
||||
std::size_t Line() const;
|
||||
// returns number of currently processed line in main file, -1 if inside include
|
||||
int LineMain() const;
|
||||
bool expandIncludes = true;
|
||||
bool allowRandomIncludes = false;
|
||||
bool skipComments = true;
|
||||
|
||||
private:
|
||||
void startIncludeFromParser(cParser &srcParser, bool ToLower, std::string includefile);
|
||||
bool handleIncludeIfPresent(std::string &token, bool ToLower, const char *Break);
|
||||
// methods:
|
||||
std::string readToken(bool ToLower = true, const char *Break = "\n\r\t ;");
|
||||
static std::vector<std::string> readParameters( cParser &Input );
|
||||
std::string readQuotes( char const Quote = '\"' );
|
||||
void skipComment( std::string const &Endmark );
|
||||
bool findQuotes( std::string &String );
|
||||
bool trimComments( std::string &String );
|
||||
std::size_t count();
|
||||
// members:
|
||||
bool m_autoclear { true }; // unretrieved tokens are discarded when another read command is issued (legacy behaviour)
|
||||
bool LoadTraction { true }; // load traction?
|
||||
std::shared_ptr<std::istream> mStream; // relevant kind of buffer is attached on creation.
|
||||
std::string mFile; // name of the open file, if any
|
||||
std::string mPath; // path to open stream, for relative path lookups.
|
||||
std::streamoff mSize { 0 }; // size of open stream, for progress report.
|
||||
std::size_t mLine { 0 }; // currently processed line
|
||||
bool mIncFile { false }; // the parser is processing an *.inc file
|
||||
bool mFirstToken { true }; // processing first token in the current file; helper used when checking for utf bom
|
||||
typedef std::map<std::string, std::string> commentmap;
|
||||
commentmap mComments {
|
||||
commentmap::value_type( "/*", "*/" ),
|
||||
commentmap::value_type( "//", "\n" ) };
|
||||
std::shared_ptr<cParser> mIncludeParser; // child class to handle include directives.
|
||||
std::vector<std::string> parameters; // parameter list for included file.
|
||||
std::deque<std::string> tokens;
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
glm::vec3
|
||||
cParser::getToken( bool const ToLower, const char *Break );
|
||||
|
||||
|
||||
template<typename Type_>
|
||||
cParser&
|
||||
cParser::operator>>( Type_ &Right ) {
|
||||
|
||||
if( true == this->tokens.empty() ) { return *this; }
|
||||
|
||||
std::stringstream converter( this->tokens.front() );
|
||||
converter >> Right;
|
||||
this->tokens.pop_front();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<>
|
||||
cParser&
|
||||
cParser::operator>>( std::string &Right );
|
||||
|
||||
template<>
|
||||
cParser&
|
||||
cParser::operator>>( bool &Right );
|
||||
|
||||
template<>
|
||||
bool
|
||||
cParser::getToken<bool>( bool const ToLower, const char *Break );
|
||||
383
utilities/translation.cpp
Normal file
383
utilities/translation.cpp
Normal file
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
/*
|
||||
MaSzyna EU07 locomotive simulator
|
||||
Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "utilities/translation.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "MOVER.h"
|
||||
|
||||
void locale::init()
|
||||
{
|
||||
std::fstream stream("lang/" + Global.asLang + ".po", std::ios_base::in | std::ios_base::binary);
|
||||
|
||||
if (!stream.is_open()) {
|
||||
WriteLog("translation: cannot open lang file: lang/" + Global.asLang + ".po");
|
||||
return;
|
||||
}
|
||||
|
||||
crashreport_add_info("translation", Global.asLang);
|
||||
|
||||
while (parse_translation(stream));
|
||||
|
||||
WriteLog("translation: " + std::to_string(lang_mapping.size()) + " strings loaded");
|
||||
}
|
||||
|
||||
const std::string& locale::lookup_s(const std::string &msg, bool constant)
|
||||
{
|
||||
if (constant) {
|
||||
auto it = pointer_cache.find(&msg);
|
||||
if (it != pointer_cache.end())
|
||||
return *((const std::string*)(it->second));
|
||||
}
|
||||
|
||||
auto it = lang_mapping.find(msg);
|
||||
if (it != lang_mapping.end()) {
|
||||
if (constant)
|
||||
pointer_cache.emplace(&msg, &(it->second));
|
||||
return it->second;
|
||||
}
|
||||
|
||||
if (constant)
|
||||
pointer_cache.emplace(&msg, &msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
const char* locale::lookup_c(const char *msg, bool constant)
|
||||
{
|
||||
if (constant) {
|
||||
auto it = pointer_cache.find(msg);
|
||||
if (it != pointer_cache.end())
|
||||
return (const char*)(it->second);
|
||||
}
|
||||
|
||||
auto it = lang_mapping.find(std::string(msg));
|
||||
if (it != lang_mapping.end()) {
|
||||
if (constant)
|
||||
pointer_cache.emplace(msg, it->second.c_str());
|
||||
return it->second.c_str();
|
||||
}
|
||||
|
||||
if (constant)
|
||||
pointer_cache.emplace(msg, msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool locale::parse_translation(std::istream &stream)
|
||||
{
|
||||
std::string line;
|
||||
|
||||
std::string msgid;
|
||||
std::string msgstr;
|
||||
std::string msgctxt;
|
||||
char last = 'x';
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
if (line.size() > 0 && line[0] == '#')
|
||||
continue;
|
||||
|
||||
if (string_starts_with(line, "msgid"))
|
||||
last = 'i';
|
||||
else if (string_starts_with(line, "msgstr"))
|
||||
last = 's';
|
||||
else if (string_starts_with(line, "msgctxt"))
|
||||
last = 'c';
|
||||
|
||||
if (line.size() > 1 && last != 'x') {
|
||||
if (last == 'i')
|
||||
msgid += parse_c_literal(line);
|
||||
else if (last == 's')
|
||||
msgstr += parse_c_literal(line);
|
||||
else if (last == 'c')
|
||||
msgctxt += parse_c_literal(line);
|
||||
}
|
||||
else {
|
||||
if (!msgid.empty() && !msgstr.empty()) {
|
||||
// TODO: context support is incomplete
|
||||
if (!msgctxt.empty())
|
||||
lang_mapping.emplace(msgctxt + "\x1d" + msgid, msgstr);
|
||||
else
|
||||
lang_mapping.emplace(msgid, msgstr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string locale::parse_c_literal(const std::string &str)
|
||||
{
|
||||
std::istringstream stream(str);
|
||||
std::string out;
|
||||
|
||||
bool active = false;
|
||||
bool escape = false;
|
||||
|
||||
char c;
|
||||
while ((c = stream.get()) != std::char_traits<char>::eof()) {
|
||||
if (!escape && c == '"')
|
||||
active = !active;
|
||||
else if (active && !escape && c == '\\')
|
||||
escape = true;
|
||||
else if (active && escape) {
|
||||
if (c == 'r')
|
||||
out += '\r';
|
||||
else if (c == 'n')
|
||||
out += '\n';
|
||||
else if (c == 't')
|
||||
out += '\t';
|
||||
else if (c == '\\')
|
||||
out += '\\';
|
||||
else if (c == '?')
|
||||
out += '?';
|
||||
else if (c == '\'')
|
||||
out += '\'';
|
||||
else if (c == '"')
|
||||
out += '"';
|
||||
else if (c == 'x') {
|
||||
char n1 = stream.get() - 48;
|
||||
char n2 = stream.get() - 48;
|
||||
if (n1 > 9)
|
||||
n1 -= 7;
|
||||
if (n1 > 16)
|
||||
n1 -= 32;
|
||||
if (n2 > 9)
|
||||
n2 -= 7;
|
||||
if (n2 > 16)
|
||||
n2 -= 32;
|
||||
out += ((n1 << 4) | n2);
|
||||
}
|
||||
escape = false;
|
||||
}
|
||||
else if (active)
|
||||
out += c;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string locale::label_cab_control(std::string const &Label)
|
||||
{
|
||||
if( Label.empty() ) { return ""; }
|
||||
|
||||
static std::unordered_map<std::string, std::string> cabcontrols_labels = {
|
||||
{ "mainctrl:", STRN("master controller") },
|
||||
{ "jointctrl:", STRN("master controller") },
|
||||
{ "scndctrl:", STRN("second controller") },
|
||||
{ "shuntmodepower:", STRN("shunt mode power") },
|
||||
{ "tempomat_sw:", STRN("cruise control") },
|
||||
{ "tempomatoff_sw:", STRN("cruise control") },
|
||||
{ "speedinc_bt:", STRN("cruise control (speed)") },
|
||||
{ "speeddec_bt:", STRN("cruise control (speed)") },
|
||||
{ "speedctrlpowerinc_bt:", STRN("cruise control (power)") },
|
||||
{ "speedctrlpowerdec_bt:", STRN("cruise control (power)") },
|
||||
{ "speedbutton0:", STRN("cruise control (speed)") },
|
||||
{ "speedbutton1:", STRN("cruise control (speed)") },
|
||||
{ "speedbutton2:", STRN("cruise control (speed)") },
|
||||
{ "speedbutton3:", STRN("cruise control (speed)") },
|
||||
{ "speedbutton4:", STRN("cruise control (speed)") },
|
||||
{ "speedbutton5:", STRN("cruise control (speed)") },
|
||||
{ "speedbutton6:", STRN("cruise control (speed)") },
|
||||
{ "speedbutton7:", STRN("cruise control (speed)") },
|
||||
{ "speedbutton8:", STRN("cruise control (speed)") },
|
||||
{ "speedbutton9:", STRN("cruise control (speed)") },
|
||||
{ "distancecounter_sw:", STRN("distance counter") },
|
||||
{ "dirkey:", STRN("reverser") },
|
||||
{ "dirbackward_bt:", STRN("reverser") },
|
||||
{ "dirforward_bt:", STRN("reverser") },
|
||||
{ "dirneutral_bt:", STRN("reverser") },
|
||||
{ "brakectrl:", STRN("train brake") },
|
||||
{ "localbrake:", STRN("independent brake") },
|
||||
{ "manualbrake:", STRN("manual brake") },
|
||||
{ "alarmchain:", STRN("emergency brake") },
|
||||
{ "brakeprofile_sw:", STRN("brake acting speed") },
|
||||
{ "brakeprofileg_sw:", STRN("brake acting speed: cargo") },
|
||||
{ "brakeprofiler_sw:", STRN("brake acting speed: rapid") },
|
||||
{ "brakeopmode_sw", STRN("brake operation mode") },
|
||||
{ "maxcurrent_sw:", STRN("motor overload relay threshold") },
|
||||
{ "waterpump_sw:", STRN("water pump") },
|
||||
{ "waterpumpbreaker_sw:", STRN("water pump breaker") },
|
||||
{ "waterheater_sw:", STRN("water heater") },
|
||||
{ "waterheaterbreaker_sw:", STRN("water heater breaker") },
|
||||
{ "watercircuitslink_sw:", STRN("water circuits link") },
|
||||
{ "fuelpump_sw:", STRN("fuel pump") },
|
||||
{ "oilpump_sw:", STRN("oil pump") },
|
||||
{ "motorblowersfront_sw:", STRN("motor blowers A") },
|
||||
{ "motorblowersrear_sw:", STRN("motor blowers B") },
|
||||
{ "motorblowersalloff_sw:", STRN("all motor blowers") },
|
||||
{ "coolingfans_sw:", STRN("cooling fans") },
|
||||
{ "main_off_bt:", STRN("line breaker") },
|
||||
{ "main_on_bt:", STRN("line breaker") },
|
||||
{ "security_reset_bt:", STRN("alerter") },
|
||||
{ "shp_reset_bt:", STRN("shp") },
|
||||
{ "releaser_bt:", STRN("independent brake releaser") },
|
||||
{ "sand_bt:", STRN("sandbox") },
|
||||
{ "antislip_bt:", STRN("wheelspin brake") },
|
||||
{ "horn_bt:", STRN("horn") },
|
||||
{ "hornlow_bt:", STRN("low tone horn") },
|
||||
{ "hornhigh_bt:", STRN("high tone horn") },
|
||||
{ "whistle_bt:", STRN("whistle") },
|
||||
{ "fuse_bt:", STRN("motor overload relay reset") },
|
||||
{ "converterfuse_bt:", STRN("converter overload relay reset") },
|
||||
{ "stlinoff_bt:", STRN("motor connectors") },
|
||||
{ "doorleftpermit_sw:", STRN("left door (permit)") },
|
||||
{ "doorrightpermit_sw:", STRN("right door (permit)") },
|
||||
{ "doorpermitpreset_sw:", STRN("door (permit)") },
|
||||
{ "door_left_sw:", STRN("left door") },
|
||||
{ "door_right_sw:", STRN("right door") },
|
||||
{ "doorlefton_sw:", STRN("left door (open)") },
|
||||
{ "doorrighton_sw:", STRN("right door (open)") },
|
||||
{ "doorleftoff_sw:", STRN("left door (close)") },
|
||||
{ "doorrightoff_sw:", STRN("right door (close)") },
|
||||
{ "doorallon_sw:", STRN("all doors (open)") },
|
||||
{ "dooralloff_sw:", STRN("all doors (close)") },
|
||||
{ "doorstep_sw:", STRN("doorstep") },
|
||||
{ "doormode_sw", STRN("door control mode") },
|
||||
{ "departure_signal_bt:", STRN("departure signal") },
|
||||
{ "upperlight_sw:", STRN("upper headlight") },
|
||||
{ "leftlight_sw:", STRN("left headlight") },
|
||||
{ "rightlight_sw:", STRN("right headlight") },
|
||||
{ "dimheadlights_sw:", STRN("headlights dimmer") },
|
||||
{ "moderndimmer_sw:", STRN("headlights dimmer") },
|
||||
{ "leftend_sw:", STRN("left marker light") },
|
||||
{ "rightend_sw:", STRN("right marker light") },
|
||||
{ "lights_sw:", STRN("light pattern") },
|
||||
{ "rearupperlight_sw:", STRN("rear upper headlight") },
|
||||
{ "rearleftlight_sw:", STRN("rear left headlight") },
|
||||
{ "rearrightlight_sw:", STRN("rear right headlight") },
|
||||
{ "rearleftend_sw:", STRN("rear left marker light") },
|
||||
{ "rearrightend_sw:", STRN("rear right marker light") },
|
||||
{ "compressor_sw:", STRN("compressor") },
|
||||
{ "compressorlocal_sw:", STRN("local compressor") },
|
||||
{ "converter_sw:", STRN("converter") },
|
||||
{ "converterlocal_sw:", STRN("local converter") },
|
||||
{ "converteroff_sw:", STRN("converter") },
|
||||
{ "main_sw:", STRN("line breaker") },
|
||||
{ "radio_sw:", STRN("radio") },
|
||||
{ "radiochannel_sw:", STRN("radio channel") },
|
||||
{ "radiochannelprev_sw:", STRN("radio channel") },
|
||||
{ "radiochannelnext_sw:", STRN("radio channel") },
|
||||
{ "radiotest_sw:", STRN("radiostop test") },
|
||||
{ "radiostop_sw:", STRN("radiostop") },
|
||||
{ "radiocall3_sw:", STRN("radio call 3") },
|
||||
{ "radiovolume_sw:", STRN("radio volume") },
|
||||
{ "radiovolumenext_sw:", STRN("radio volume") },
|
||||
{ "radiovolumeprev_sw:", STRN("radio volume") },
|
||||
{ "pantfront_sw:", STRN("pantograph A") },
|
||||
{ "pantrear_sw:", STRN("pantograph B") },
|
||||
{ "pantfrontoff_sw:", STRN("pantograph A") },
|
||||
{ "pantrearoff_sw:", STRN("pantograph B") },
|
||||
{ "pantalloff_sw:", STRN("all pantographs") },
|
||||
{ "pantselected_sw:", STRN("selected pantograph") },
|
||||
{ "pantselectedoff_sw:", STRN("selected pantograph") },
|
||||
{ "pantselect_sw:", STRN("selected pantograph") },
|
||||
{ "pantvalves_sw:", STRN("selected pantograph") },
|
||||
{ "pantvalvesoff_bt:", STRN("all pantographs down") },
|
||||
{ "pantvalvesupdate_bt:", STRN("selected pantographs up") },
|
||||
{ "pantcompressor_sw:", STRN("pantograph compressor") },
|
||||
{ "pantcompressorvalve_sw:", STRN("pantograph 3 way valve") },
|
||||
{ "trainheating_sw:", STRN("heating") },
|
||||
{ "signalling_sw:", STRN("braking indicator") },
|
||||
{ "door_signalling_sw:", STRN("door locking") },
|
||||
{ "nextcurrent_sw:", STRN("current indicator source") },
|
||||
{ "instrumentlight_sw:", STRN("instrument light") },
|
||||
{ "dashboardlight_sw:", STRN("dashboard light") },
|
||||
{ "timetablelight_sw:", STRN("timetable light") },
|
||||
{ "cablight_sw:", STRN("interior light") },
|
||||
{ "cablightdim_sw:", STRN("interior light dimmer") },
|
||||
{ "compartmentlights_sw:", STRN("compartment lights") },
|
||||
{ "compartmentlightsoff_sw:", STRN("compartment lights") },
|
||||
{ "compartmentlightson_sw:", STRN("compartment lights") },
|
||||
{ "couplingdisconnect_sw:", STRN("coupling") },
|
||||
{ "battery_sw:", STRN("battery") },
|
||||
{ "batteryon_sw:", STRN("battery") },
|
||||
{ "batteryoff_sw:", STRN("battery") },
|
||||
{ "epbrake_bt:", STRN("ep brake") },
|
||||
{ "relayreset1_bt:", STRN("relay reset") },
|
||||
{ "relayreset2_bt:", STRN("relay reset") },
|
||||
{ "relayreset3_bt:", STRN("relay reset") },
|
||||
{ "springbrakeoff_bt:", STRN("spring brake") },
|
||||
{ "springbrakeon_bt:", STRN("spring brake") },
|
||||
{ "springbraketoggle_bt:", STRN("spring brake") },
|
||||
{ "universalbrake1_bt:", STRN("train brake") },
|
||||
{ "universalbrake2_bt:", STRN("train brake") },
|
||||
{ "universalbrake3_bt:", STRN("train brake") },
|
||||
{"universal0:", STRN("interactive part")},
|
||||
{"universal1:", STRN("interactive part")},
|
||||
{"universal2:", STRN("interactive part")},
|
||||
{"universal3:", STRN("interactive part")},
|
||||
{"universal4:", STRN("interactive part")},
|
||||
{"universal5:", STRN("interactive part")},
|
||||
{"universal6:", STRN("interactive part")},
|
||||
{"universal7:", STRN("interactive part")},
|
||||
{"universal8:", STRN("interactive part")},
|
||||
{"universal9:", STRN("interactive part")},
|
||||
{"universal10:", STRN("interactive part")},
|
||||
{"universal11:", STRN("interactive part")},
|
||||
{"universal12:", STRN("interactive part")},
|
||||
{"universal13:", STRN("interactive part")},
|
||||
{"universal14:", STRN("interactive part")},
|
||||
{"universal15:", STRN("interactive part")},
|
||||
{"universal16:", STRN("interactive part")},
|
||||
{"universal17:", STRN("interactive part")},
|
||||
{"universal18:", STRN("interactive part")},
|
||||
{"universal19:", STRN("interactive part")},
|
||||
{"universal20:", STRN("interactive part")},
|
||||
{"universal21:", STRN("interactive part")},
|
||||
{"universal22:", STRN("interactive part")},
|
||||
{"universal23:", STRN("interactive part")},
|
||||
{"universal24:", STRN("interactive part")},
|
||||
{"universal25:", STRN("interactive part")},
|
||||
{"universal26:", STRN("interactive part")},
|
||||
{"universal27:", STRN("interactive part")},
|
||||
{"universal28:", STRN("interactive part")},
|
||||
{"universal29:", STRN("interactive part")},
|
||||
{ "wipers_sw:", STRN("wipers mode selector") },
|
||||
};
|
||||
|
||||
auto const it = cabcontrols_labels.find( Label );
|
||||
return (
|
||||
it != cabcontrols_labels.end() ?
|
||||
lookup_s(it->second) :
|
||||
"" );
|
||||
}
|
||||
|
||||
const std::string& locale::coupling_name(int c)
|
||||
{
|
||||
static std::unordered_map<coupling, std::string> coupling_names =
|
||||
{
|
||||
{ coupling::faux, STRN("faux") },
|
||||
{ coupling::coupler, STRN("coupler") },
|
||||
{ coupling::brakehose, STRN("brake hose") },
|
||||
{ coupling::control, STRN("control") },
|
||||
{ coupling::highvoltage, STRN("high voltage") },
|
||||
{ coupling::gangway, STRN("gangway") },
|
||||
{ coupling::mainhose, STRN("main hose") },
|
||||
{ coupling::heating, STRN("heating") },
|
||||
{ coupling::permanent, STRN("permanent") },
|
||||
{ coupling::power24v, STRN("power 24V") },
|
||||
{ coupling::power110v, STRN("power 110V") },
|
||||
{ coupling::power3x400v, STRN("power 3x400V") },
|
||||
};
|
||||
|
||||
static std::string unknown(STRN("unknown"));
|
||||
|
||||
auto it = coupling_names.find(static_cast<coupling>(c));
|
||||
if (it != coupling_names.end())
|
||||
return lookup_s(it->second);
|
||||
else
|
||||
return lookup_s(unknown);
|
||||
}
|
||||
|
||||
locale Translations;
|
||||
38
utilities/translation.h
Normal file
38
utilities/translation.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class locale {
|
||||
public:
|
||||
void init();
|
||||
std::string label_cab_control(const std::string &Label);
|
||||
const std::string &coupling_name(int c);
|
||||
|
||||
const char* lookup_c(const char *msg, bool constant = false);
|
||||
const std::string& lookup_s(const std::string &msg, bool constant = false);
|
||||
|
||||
private:
|
||||
bool parse_translation(std::istream &stream);
|
||||
std::string parse_c_literal(const std::string &str);
|
||||
|
||||
std::unordered_map<std::string, std::string> lang_mapping;
|
||||
std::unordered_map<const void*, const void*> pointer_cache;
|
||||
};
|
||||
|
||||
extern locale Translations;
|
||||
|
||||
#define STR(x) Translations.lookup_s(x, false)
|
||||
#define STR_C(x) Translations.lookup_c(x, true)
|
||||
#define STRN(x) x
|
||||
|
||||
//find . -name "*.cpp" | xargs xgettext --from-code="UTF-8" -k --keyword=STR --keyword=STR_C --keyword=STRN
|
||||
659
utilities/uart.cpp
Normal file
659
utilities/uart.cpp
Normal file
@@ -0,0 +1,659 @@
|
||||
#include "stdafx.h"
|
||||
#include "utilities/uart.h"
|
||||
|
||||
#include "utilities/Globals.h"
|
||||
#include "simulation/simulation.h"
|
||||
#include "vehicle/Train.h"
|
||||
#include "utilities/parser.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include "simulation/simulationtime.h"
|
||||
#include "application/application.h"
|
||||
|
||||
const char* uart_baudrates_list[] = {
|
||||
"300",
|
||||
"1200",
|
||||
"2400",
|
||||
"4800",
|
||||
"9600",
|
||||
"19200",
|
||||
"38400",
|
||||
"57600",
|
||||
"74880",
|
||||
"115200",
|
||||
"230400",
|
||||
"250000",
|
||||
"500000",
|
||||
"1000000",
|
||||
"2000000"
|
||||
};
|
||||
|
||||
const size_t uart_baudrates_list_num = (
|
||||
sizeof(uart_baudrates_list)/sizeof(uart_baudrates_list[0])
|
||||
);
|
||||
|
||||
void uart_status::reset_stats() {
|
||||
packets_sent = 0;
|
||||
packets_received = 0;
|
||||
}
|
||||
|
||||
uart_status UartStatus;
|
||||
|
||||
uart_input::uart_input()
|
||||
{
|
||||
conf = Global.uart_conf;
|
||||
uart_status *status = &UartStatus;
|
||||
|
||||
status->enabled = conf.enable;
|
||||
status->port_name = conf.port;
|
||||
status->baud = conf.baud;
|
||||
|
||||
const std::string baudStr = std::to_string(status->baud);
|
||||
|
||||
for(int i=0;i<uart_baudrates_list_num;i++) {
|
||||
if(baudStr == uart_baudrates_list[i]) {
|
||||
status->selected_baud_index = i;
|
||||
status->active_port_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
old_packet.fill(0);
|
||||
last_update = std::chrono::high_resolution_clock::now();
|
||||
last_setup = std::chrono::high_resolution_clock::now();
|
||||
|
||||
find_ports();
|
||||
}
|
||||
|
||||
void uart_input::find_ports() {
|
||||
uart_status *status = &UartStatus;
|
||||
|
||||
struct sp_port **ports;
|
||||
if (sp_list_ports(&ports) == SP_OK) {
|
||||
status->available_ports.clear();
|
||||
status->active_port_index = -1;
|
||||
status->selected_port_index = -1;
|
||||
for (int i=0; ports[i]; i++) {
|
||||
std::string newport = std::string(sp_get_port_name(ports[i]));
|
||||
status->available_ports.emplace_back(newport);
|
||||
if(newport == status->port_name) {
|
||||
status->active_port_index = i;
|
||||
status->selected_port_index = i;
|
||||
}
|
||||
}
|
||||
if(status->selected_port_index > status->available_ports.size()) {
|
||||
status->selected_port_index = -1;
|
||||
}
|
||||
sp_free_port_list(ports);
|
||||
} else {
|
||||
WriteLog("uart: cannot read serial ports list");
|
||||
}
|
||||
last_port_find = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
bool uart_input::setup_port()
|
||||
{
|
||||
uart_status *status = &UartStatus;
|
||||
|
||||
if(!port) {
|
||||
find_ports();
|
||||
}
|
||||
if (port) {
|
||||
sp_close(port);
|
||||
sp_free_port(port);
|
||||
port = nullptr;
|
||||
}
|
||||
|
||||
last_setup = std::chrono::high_resolution_clock::now();
|
||||
|
||||
if (sp_get_port_by_name(status->port_name.c_str(), &port) != SP_OK) {
|
||||
if(!error_notified) {
|
||||
status->is_connected = false;
|
||||
ErrorLog("uart: cannot find specified port '"+conf.port+"'");
|
||||
find_ports();
|
||||
}
|
||||
error_notified = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sp_open(port, (sp_mode)(SP_MODE_READ | SP_MODE_WRITE)) != SP_OK) {
|
||||
if(!error_notified) {
|
||||
status->is_connected = false;
|
||||
ErrorLog("uart: cannot open port '"+status->port_name+"'");
|
||||
find_ports();
|
||||
}
|
||||
error_notified = true;
|
||||
port = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
sp_port_config *config;
|
||||
|
||||
if (sp_new_config(&config) != SP_OK ||
|
||||
sp_set_config_baudrate(config, status->baud) != SP_OK ||
|
||||
sp_set_config_flowcontrol(config, SP_FLOWCONTROL_NONE) != SP_OK ||
|
||||
sp_set_config_bits(config, 8) != SP_OK ||
|
||||
sp_set_config_stopbits(config, 1) != SP_OK ||
|
||||
sp_set_config_parity(config, SP_PARITY_NONE) != SP_OK ||
|
||||
sp_set_config(port, config) != SP_OK) {
|
||||
if(!error_notified) {
|
||||
status->is_connected = false;
|
||||
ErrorLog("uart: cannot set config");
|
||||
}
|
||||
error_notified = true;
|
||||
port = nullptr;
|
||||
find_ports();
|
||||
return false;
|
||||
}
|
||||
|
||||
sp_free_config(config);
|
||||
|
||||
if (sp_flush(port, SP_BUF_BOTH) != SP_OK) {
|
||||
if(!error_notified) {
|
||||
status->is_connected = false;
|
||||
ErrorLog("uart: cannot flush");
|
||||
}
|
||||
error_notified = true;
|
||||
port = nullptr;
|
||||
find_ports();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(error_notified || ! status->is_connected) {
|
||||
error_notified = false;
|
||||
ErrorLog("uart: connected to '"+status->port_name+"'");
|
||||
status->reset_stats();
|
||||
status->is_connected = true;
|
||||
data_pending = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uart_input::~uart_input()
|
||||
{
|
||||
if (!port)
|
||||
return;
|
||||
|
||||
std::array<std::uint8_t, 52> buffer = { 0 };
|
||||
buffer[0] = 0xEF;
|
||||
buffer[1] = 0xEF;
|
||||
buffer[2] = 0xEF;
|
||||
buffer[3] = 0xEF;
|
||||
sp_blocking_write(port, (void*)buffer.data(), buffer.size(), 0);
|
||||
sp_drain(port);
|
||||
|
||||
sp_close(port);
|
||||
sp_free_port(port);
|
||||
}
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
bool
|
||||
uart_input::recall_bindings() {
|
||||
|
||||
m_inputbindings.clear();
|
||||
std::string filePath = "eu07_input-uart.ini";
|
||||
|
||||
#ifdef _WIN32
|
||||
if (const char *appdata = std::getenv("APPDATA"))
|
||||
{
|
||||
fs::path appPath = fs::path(appdata) / "MaSzyna" / "eu07_input-uart.ini";
|
||||
if (fs::exists(appPath))
|
||||
filePath = appPath.string();
|
||||
}
|
||||
#else
|
||||
if (const char *home = std::getenv("HOME"))
|
||||
{
|
||||
fs::path appPath = fs::path(home) / ".config" / "MaSzyna" / "eu07_input-uart.ini";
|
||||
if (fs::exists(appPath))
|
||||
filePath = appPath.string();
|
||||
}
|
||||
#endif
|
||||
cParser bindingparser(filePath.c_str(), cParser::buffer_FILE);
|
||||
if (false == bindingparser.ok())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// build helper translation tables
|
||||
std::unordered_map<std::string, user_command> nametocommandmap;
|
||||
std::size_t commandid = 0;
|
||||
for( auto const &description : simulation::Commands_descriptions ) {
|
||||
nametocommandmap.emplace(
|
||||
description.name,
|
||||
static_cast<user_command>( commandid ) );
|
||||
++commandid;
|
||||
}
|
||||
std::unordered_map<std::string, input_type_t> nametotypemap {
|
||||
{ "impulse", input_type_t::impulse },
|
||||
{ "toggle", input_type_t::toggle },
|
||||
{ "value", input_type_t::value } };
|
||||
|
||||
// NOTE: to simplify things we expect one entry per line, and whole entry in one line
|
||||
while( true == bindingparser.getTokens( 1, true, "\n\r" ) ) {
|
||||
|
||||
std::string bindingentry;
|
||||
bindingparser >> bindingentry;
|
||||
cParser entryparser( bindingentry );
|
||||
|
||||
if( true == entryparser.getTokens( 2, true, "\n\r\t " ) ) {
|
||||
|
||||
std::size_t bindingpin {};
|
||||
std::string bindingtypename {};
|
||||
entryparser
|
||||
>> bindingpin
|
||||
>> bindingtypename;
|
||||
|
||||
auto const typelookup = nametotypemap.find( bindingtypename );
|
||||
if( typelookup == nametotypemap.end() ) {
|
||||
|
||||
WriteLog( "Uart binding for input pin " + std::to_string( bindingpin ) + " specified unknown control type, \"" + bindingtypename + "\"" );
|
||||
}
|
||||
else {
|
||||
|
||||
auto const bindingtype { typelookup->second };
|
||||
std::array<user_command, 2> bindingcommands { user_command::none, user_command::none };
|
||||
auto const commandcount { ( bindingtype == input_type_t::toggle ? 2 : 1 ) };
|
||||
for( int commandidx = 0; commandidx < commandcount; ++commandidx ) {
|
||||
// grab command(s) associated with the input pin
|
||||
auto const bindingcommandname { entryparser.getToken<std::string>() };
|
||||
if( true == bindingcommandname.empty() ) {
|
||||
// no tokens left, may as well complain then call it a day
|
||||
WriteLog( "Uart binding for input pin " + std::to_string( bindingpin ) + " didn't specify associated command(s)" );
|
||||
break;
|
||||
}
|
||||
auto const commandlookup = nametocommandmap.find( bindingcommandname );
|
||||
if( commandlookup == nametocommandmap.end() ) {
|
||||
WriteLog( "Uart binding for input pin " + std::to_string( bindingpin ) + " specified unknown command, \"" + bindingcommandname + "\"" );
|
||||
}
|
||||
else {
|
||||
bindingcommands[ commandidx ] = commandlookup->second;
|
||||
}
|
||||
}
|
||||
// push the binding on the list
|
||||
m_inputbindings.emplace_back( bindingpin, bindingtype, bindingcommands[ 0 ], bindingcommands[ 1 ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define SPLIT_INT16(x) (uint8_t)(x), (uint8_t)((x) >> 8)
|
||||
|
||||
void uart_input::poll()
|
||||
{
|
||||
uart_status *status = &UartStatus;
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
|
||||
/* handle baud change */
|
||||
if(status->active_baud_index != status->selected_baud_index) {
|
||||
status->baud = std::stoul(uart_baudrates_list[status->selected_baud_index]);
|
||||
status->active_baud_index = status->selected_baud_index;
|
||||
status->reset_stats();
|
||||
status->is_connected = false;
|
||||
setup_port();
|
||||
}
|
||||
|
||||
/* handle port change */
|
||||
if(status->available_ports.size() > 0 && status->selected_port_index >= 0 && status->active_port_index != status->selected_port_index) {
|
||||
status->port_name = status->available_ports[status->selected_port_index];
|
||||
status->active_port_index = status->selected_port_index;
|
||||
status->reset_stats();
|
||||
status->is_connected = false;
|
||||
setup_port();
|
||||
}
|
||||
|
||||
if (
|
||||
(!port && std::chrono::duration<float>(now - last_port_find).count() > 1.0)
|
||||
|| (port && std::chrono::duration<float>(now - last_port_find).count() > 5.0)
|
||||
) {
|
||||
find_ports();
|
||||
}
|
||||
|
||||
if(!status->enabled) {
|
||||
if(port) {
|
||||
sp_close(port);
|
||||
sp_free_port(port);
|
||||
port = nullptr;
|
||||
}
|
||||
status->is_connected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::chrono::duration<float>(now - last_update).count() < conf.updatetime)
|
||||
return;
|
||||
last_update = now;
|
||||
|
||||
|
||||
/* if connection error occured, slow down reconnection tries */
|
||||
if (!port && error_notified && std::chrono::duration<float>(now - last_setup).count() < 1.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!port) {
|
||||
setup_port();
|
||||
return;
|
||||
}
|
||||
|
||||
auto const *t =simulation::Train;
|
||||
if (!t)
|
||||
return;
|
||||
|
||||
sp_return ret;
|
||||
|
||||
if ((ret = sp_input_waiting(port)) >= 20)
|
||||
{
|
||||
std::array<uint8_t, 20> tmp_buffer; // TBD, TODO: replace with vector of configurable size?
|
||||
ret = sp_blocking_read(port, (void*)tmp_buffer.data(), tmp_buffer.size(), 0);
|
||||
|
||||
if (ret < 0) {
|
||||
setup_port();
|
||||
return;
|
||||
}
|
||||
|
||||
bool sync;
|
||||
if (tmp_buffer[0] != 0xEF || tmp_buffer[1] != 0xEF || tmp_buffer[2] != 0xEF || tmp_buffer[3] != 0xEF) {
|
||||
UartStatus.is_synced = false;
|
||||
if (conf.debug)
|
||||
WriteLog("uart: bad sync");
|
||||
sync = false;
|
||||
}
|
||||
else {
|
||||
UartStatus.is_synced = true;
|
||||
if (conf.debug)
|
||||
WriteLog("uart: sync ok");
|
||||
sync = true;
|
||||
}
|
||||
|
||||
if (!sync) {
|
||||
int sync_cnt = 0;
|
||||
int sync_fail = 0;
|
||||
unsigned char sc = 0;
|
||||
while ((ret = sp_blocking_read(port, &sc, 1, 10)) >= 0) {
|
||||
if (conf.debug)
|
||||
WriteLog("uart: read byte: " + std::to_string((int)sc));
|
||||
if (sc == 0xEF)
|
||||
sync_cnt++;
|
||||
else {
|
||||
sync_cnt = 0;
|
||||
sync_fail++;
|
||||
}
|
||||
if (sync_cnt == 4) {
|
||||
if (conf.debug)
|
||||
WriteLog("uart: synced, skipping one packet..");
|
||||
ret = sp_blocking_read(port, (void*)tmp_buffer.data(), 16, 500);
|
||||
if (ret < 0) {
|
||||
setup_port();
|
||||
return;
|
||||
}
|
||||
data_pending = false;
|
||||
break;
|
||||
}
|
||||
if (sync_fail >= 60) {
|
||||
if (conf.debug)
|
||||
WriteLog("uart: tired of trying");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
setup_port();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<uint8_t, 16> buffer;
|
||||
memmove(&buffer[0], &tmp_buffer[4], 16);
|
||||
UartStatus.packets_received++;
|
||||
|
||||
if (conf.debug)
|
||||
{
|
||||
char buf[buffer.size() * 3 + 1];
|
||||
size_t pos = 0;
|
||||
for (uint8_t b : buffer)
|
||||
pos += sprintf(&buf[pos], "%02X ", b);
|
||||
WriteLog("uart: rx: " + std::string(buf));
|
||||
}
|
||||
|
||||
data_pending = false;
|
||||
|
||||
for (auto const &entry : m_inputbindings) {
|
||||
|
||||
auto const byte { std::get<std::size_t>( entry ) / 8 };
|
||||
auto const bit { std::get<std::size_t>( entry ) % 8 };
|
||||
|
||||
bool const state { ( ( buffer[ byte ] & ( 1 << bit ) ) != 0 ) };
|
||||
bool const changed { ( ( ( old_packet[ byte ] & ( 1 << bit ) ) != 0 ) != state ) };
|
||||
|
||||
if( false == changed ) { continue; }
|
||||
|
||||
auto const type { std::get<input_type_t>( entry ) };
|
||||
auto const action { (
|
||||
type != input_type_t::impulse ?
|
||||
GLFW_PRESS :
|
||||
( state ?
|
||||
GLFW_PRESS :
|
||||
GLFW_RELEASE ) ) };
|
||||
|
||||
auto const command { (
|
||||
type != input_type_t::toggle ?
|
||||
std::get<2>( entry ) :
|
||||
( state ?
|
||||
std::get<2>( entry ) :
|
||||
std::get<3>( entry ) ) ) };
|
||||
|
||||
// TODO: pass correct entity id once the missing systems are in place
|
||||
relay.post( command, 0, 0, action, 0 );
|
||||
}
|
||||
|
||||
if( true == conf.mainenable ) {
|
||||
// master controller
|
||||
if (false == conf.mainpercentage) {
|
||||
//old method for direct positions
|
||||
relay.post(
|
||||
user_command::mastercontrollerset,
|
||||
buffer[6],
|
||||
0,
|
||||
GLFW_PRESS,
|
||||
// TODO: pass correct entity id once the missing systems are in place
|
||||
0);
|
||||
}
|
||||
else {
|
||||
auto desiredpercent{ buffer[6] * 0.01 };
|
||||
auto desiredposition{ desiredpercent > 0.01 ? 1 + ((simulation::Train->Occupied()->MainCtrlPosNo - 1) * desiredpercent) : buffer[6] };
|
||||
relay.post(
|
||||
user_command::mastercontrollerset,
|
||||
desiredposition,
|
||||
0,
|
||||
GLFW_PRESS,
|
||||
// TODO: pass correct entity id once the missing systems are in place
|
||||
0);
|
||||
simulation::Train->Occupied()->eimic_analog = desiredpercent;
|
||||
}
|
||||
}
|
||||
if( true == conf.scndenable ) {
|
||||
// second controller
|
||||
relay.post(
|
||||
user_command::secondcontrollerset,
|
||||
static_cast<int8_t>(buffer[ 7 ]),
|
||||
0,
|
||||
GLFW_PRESS,
|
||||
// TODO: pass correct entity id once the missing systems are in place
|
||||
0 );
|
||||
}
|
||||
if( true == conf.trainenable ) {
|
||||
// train brake
|
||||
double const position { (float)( ( (uint16_t)buffer[ 8 ] | ( (uint16_t)buffer[ 9 ] << 8 ) ) - conf.mainbrakemin ) / ( conf.mainbrakemax - conf.mainbrakemin ) };
|
||||
relay.post(
|
||||
user_command::trainbrakeset,
|
||||
position,
|
||||
0,
|
||||
GLFW_PRESS,
|
||||
// TODO: pass correct entity id once the missing systems are in place
|
||||
0 );
|
||||
}
|
||||
if( true == conf.localenable ) {
|
||||
// independent brake
|
||||
double const position { (float)( ( (uint16_t)buffer[ 10 ] | ( (uint16_t)buffer[ 11 ] << 8 ) ) - conf.localbrakemin ) / ( conf.localbrakemax - conf.localbrakemin ) };
|
||||
relay.post(
|
||||
user_command::independentbrakeset,
|
||||
position,
|
||||
0,
|
||||
GLFW_PRESS,
|
||||
// TODO: pass correct entity id once the missing systems are in place
|
||||
0 );
|
||||
}
|
||||
if( true == conf.radiochannelenable ) {
|
||||
relay.post(
|
||||
user_command::radiochannelset,
|
||||
static_cast<int8_t>(buffer[12] & 0xF),
|
||||
0,
|
||||
GLFW_PRESS,
|
||||
0
|
||||
);
|
||||
}
|
||||
if( true == conf.radiovolumeenable ) {
|
||||
int8_t requested_volume = static_cast<int8_t>((buffer[12] & 0xF0) >> 4);
|
||||
relay.post(
|
||||
user_command::radiovolumeset,
|
||||
requested_volume == 0xF ? 1.0 : requested_volume * (1.0 / 15.0),
|
||||
0,
|
||||
GLFW_PRESS,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
old_packet = buffer;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
setup_port();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data_pending && sp_output_waiting(port) == 0)
|
||||
{
|
||||
// TODO: ugly! move it into structure like input_bits
|
||||
auto const trainstate = t->get_state();
|
||||
|
||||
SYSTEMTIME time = simulation::Time.data();
|
||||
uint16_t tacho = Global.iPause ? 0 : (trainstate.velocity * conf.tachoscale);
|
||||
uint16_t tank_press = (uint16_t)std::min(conf.tankuart, trainstate.reservoir_pressure * 0.1f / conf.tankmax * conf.tankuart);
|
||||
uint16_t pipe_press = (uint16_t)std::min(conf.pipeuart, trainstate.pipe_pressure * 0.1f / conf.pipemax * conf.pipeuart);
|
||||
uint16_t brake_press = (uint16_t)std::min(conf.brakeuart, trainstate.brake_pressure * 0.1f / conf.brakemax * conf.brakeuart);
|
||||
uint16_t pantograph_press = (uint16_t)std::min(conf.pantographuart, trainstate.pantograph_pressure * 0.1f / conf.pantographmax * conf.pantographuart );
|
||||
uint16_t hv_voltage = (uint16_t)std::min(conf.hvuart, trainstate.hv_voltage / conf.hvmax * conf.hvuart);
|
||||
uint16_t current1 = (uint16_t)std::min(conf.currentuart, trainstate.hv_current[0] / conf.currentmax * conf.currentuart);
|
||||
uint16_t current2 = (uint16_t)std::min(conf.currentuart, trainstate.hv_current[1] / conf.currentmax * conf.currentuart);
|
||||
uint16_t current3 = (uint16_t)std::min(conf.currentuart, trainstate.hv_current[2] / conf.currentmax * conf.currentuart);
|
||||
uint32_t odometer = trainstate.distance * 10000.0;
|
||||
uint16_t lv_voltage = (uint16_t)std::min( conf.lvuart, trainstate.lv_voltage / conf.lvmax * conf.lvuart );
|
||||
if( trainstate.cab > 0 ) {
|
||||
// NOTE: moving from a cab to engine room doesn't change cab indicator
|
||||
m_trainstatecab = trainstate.cab - 1;
|
||||
}
|
||||
|
||||
std::array<uint8_t, 52> buffer {
|
||||
//preamble
|
||||
0xEF, 0xEF, 0xEF, 0xEF,
|
||||
//byte 0-1 (counting without preamble)
|
||||
SPLIT_INT16(tacho),
|
||||
//byte 2
|
||||
(uint8_t)(
|
||||
trainstate.epbrake_enabled << 0
|
||||
| trainstate.ventilator_overload << 1
|
||||
| trainstate.motor_overload_threshold << 2
|
||||
| trainstate.emergencybrake << 3
|
||||
| trainstate.lockpipe << 4
|
||||
| trainstate.dir_forward << 5
|
||||
| trainstate.dir_backward << 6),
|
||||
//byte 3
|
||||
(uint8_t)(
|
||||
trainstate.coupled_hv_voltage_relays << 0
|
||||
| trainstate.doorleftallowed << 1
|
||||
| trainstate.doorleftopened << 2
|
||||
| trainstate.doorrightallowed << 3
|
||||
| trainstate.doorrightopened << 4
|
||||
| trainstate.doorstepallowed << 5
|
||||
| trainstate.battery << 6
|
||||
| trainstate.radiomessageindicator << 7),
|
||||
//byte 4
|
||||
(uint8_t)(
|
||||
trainstate.train_heating << 0
|
||||
| trainstate.motor_resistors << 1
|
||||
| trainstate.wheelslip << 2
|
||||
| trainstate.alerter << 6
|
||||
| trainstate.shp << 7),
|
||||
//byte 5
|
||||
(uint8_t)(
|
||||
trainstate.motor_connectors << 0
|
||||
| trainstate.converter_overload << 2
|
||||
| trainstate.ground_relay << 3
|
||||
| trainstate.motor_overload << 4
|
||||
| trainstate.line_breaker << 5
|
||||
| trainstate.compressor_overload << 6),
|
||||
//byte 6
|
||||
(uint8_t)(
|
||||
m_trainstatecab << 2
|
||||
| trainstate.recorder_braking << 3
|
||||
| trainstate.recorder_power << 4
|
||||
| trainstate.radio_stop << 5
|
||||
| trainstate.springbrake_active << 6
|
||||
| trainstate.alerter_sound << 7),
|
||||
//byte 7-8
|
||||
SPLIT_INT16(brake_press),
|
||||
//byte 9-10
|
||||
SPLIT_INT16(pipe_press),
|
||||
//byte 11-12
|
||||
SPLIT_INT16(tank_press),
|
||||
//byte 13-14
|
||||
SPLIT_INT16(hv_voltage),
|
||||
//byte 15-16
|
||||
SPLIT_INT16(current1),
|
||||
//byte 17-18
|
||||
SPLIT_INT16(current2),
|
||||
//byte 19-20
|
||||
SPLIT_INT16(current3),
|
||||
//byte 21-22
|
||||
SPLIT_INT16((time.wYear - 1) * 12 + time.wMonth - 1),
|
||||
//byte 23-24
|
||||
SPLIT_INT16((time.wDay - 1) * 1440 + time.wHour * 60 + time.wMinute),
|
||||
//byte 25-26
|
||||
SPLIT_INT16(time.wSecond * 1000 + time.wMilliseconds),
|
||||
//byte 27-30
|
||||
SPLIT_INT16((uint16_t)odometer), SPLIT_INT16((uint16_t)(odometer >> 16)),
|
||||
//byte 31-32
|
||||
SPLIT_INT16(lv_voltage),
|
||||
//byte 33
|
||||
(uint8_t)trainstate.radio_channel,
|
||||
//byte 34-35
|
||||
SPLIT_INT16(pantograph_press),
|
||||
//byte 36-47
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
if (conf.debug)
|
||||
{
|
||||
char buf[buffer.size() * 3 + 1];
|
||||
buf[ buffer.size() * 3 ] = 0;
|
||||
size_t pos = 0;
|
||||
for (uint8_t b : buffer)
|
||||
pos += sprintf(&buf[pos], "%02X ", b);
|
||||
WriteLog("uart: tx: " + std::string(buf));
|
||||
}
|
||||
|
||||
ret = sp_blocking_write(port, (void*)buffer.data(), buffer.size(), 0);
|
||||
if (ret < 0) {
|
||||
setup_port();
|
||||
return;
|
||||
}
|
||||
UartStatus.packets_sent++;
|
||||
|
||||
data_pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool uart_input::is_connected() {
|
||||
return (port != nullptr);
|
||||
}
|
||||
110
utilities/uart.h
Normal file
110
utilities/uart.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include <libserialport.h>
|
||||
#include "input/command.h"
|
||||
|
||||
extern const char* uart_baudrates_list[];
|
||||
extern const size_t uart_baudrates_list_num;
|
||||
|
||||
class uart_status {
|
||||
public:
|
||||
std::string port_name = "";
|
||||
std::vector<std::string> available_ports = {};
|
||||
int selected_port_index = -1;
|
||||
int selected_baud_index = -1;
|
||||
int active_port_index = -1;
|
||||
int active_baud_index = -1;
|
||||
int baud = 0;
|
||||
bool enabled = false;
|
||||
bool is_connected = false;
|
||||
bool is_synced = false;
|
||||
unsigned long packets_sent = 0;
|
||||
unsigned long packets_received = 0;
|
||||
|
||||
void reset_stats();
|
||||
};
|
||||
|
||||
class uart_input
|
||||
{
|
||||
public:
|
||||
// types
|
||||
struct conf_t {
|
||||
bool enable = false;
|
||||
std::string port;
|
||||
int baud;
|
||||
float updatetime;
|
||||
|
||||
float mainbrakemin = 0.0f;
|
||||
float mainbrakemax = 65535.0f;
|
||||
float localbrakemin = 0.0f;
|
||||
float localbrakemax = 65535.0f;
|
||||
float tankmax = 10.0f;
|
||||
float tankuart = 65535.0f;
|
||||
float pipemax = 10.0f;
|
||||
float pipeuart = 65535.0f;
|
||||
float brakemax = 10.0f;
|
||||
float brakeuart = 65535.0f;
|
||||
float pantographmax = 10.0f;
|
||||
float pantographuart = 65535.0f;
|
||||
float hvmax = 100000.0f;
|
||||
float hvuart = 65535.0f;
|
||||
float currentmax = 10000.0f;
|
||||
float currentuart = 65535.0f;
|
||||
float lvmax = 150.0f;
|
||||
float lvuart = 65535.0f;
|
||||
float tachoscale = 1.0f;
|
||||
|
||||
bool mainenable = true;
|
||||
bool scndenable = true;
|
||||
bool trainenable = true;
|
||||
bool localenable = true;
|
||||
bool radiovolumeenable = false;
|
||||
bool radiochannelenable = false;
|
||||
|
||||
bool debug = false;
|
||||
|
||||
bool mainpercentage = false;
|
||||
};
|
||||
|
||||
// methods
|
||||
uart_input();
|
||||
~uart_input();
|
||||
bool
|
||||
init() { return recall_bindings(); }
|
||||
bool
|
||||
recall_bindings();
|
||||
void
|
||||
poll();
|
||||
bool
|
||||
is_connected();
|
||||
|
||||
private:
|
||||
// types
|
||||
enum class input_type_t
|
||||
{
|
||||
toggle, // two commands, each mapped to one state; press event on state change
|
||||
impulse, // one command; press event when set, release when cleared
|
||||
value // one command; press event, value of specified byte passed as param1
|
||||
};
|
||||
|
||||
using input_pin_t = std::tuple<std::size_t, input_type_t, user_command, user_command>;
|
||||
using inputpin_sequence = std::vector<input_pin_t>;
|
||||
|
||||
bool setup_port();
|
||||
void find_ports();
|
||||
|
||||
// members
|
||||
sp_port *port = nullptr;
|
||||
inputpin_sequence m_inputbindings;
|
||||
command_relay relay;
|
||||
std::array<std::uint8_t, 16> old_packet; // TBD, TODO: replace with vector of configurable size?
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> last_update;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> last_setup;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> last_port_find;
|
||||
conf_t conf;
|
||||
bool data_pending = false;
|
||||
bool error_notified = false;
|
||||
std::uint8_t m_trainstatecab { 0 }; // helper, keeps track of last active cab. 0: front cab, 1: rear cab
|
||||
};
|
||||
|
||||
extern uart_status UartStatus;
|
||||
610
utilities/utilities.cpp
Normal file
610
utilities/utilities.cpp
Normal file
@@ -0,0 +1,610 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
/*
|
||||
MaSzyna EU07 - SPKS
|
||||
Brakes.
|
||||
Copyright (C) 2007-2014 Maciej Cierniak
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define stat _stat
|
||||
#endif
|
||||
|
||||
#include "utilities/utilities.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/parser.h"
|
||||
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
bool DebugModeFlag = false;
|
||||
bool FreeFlyModeFlag = false;
|
||||
bool EditorModeFlag = false;
|
||||
bool DebugCameraFlag = false;
|
||||
bool DebugTractionFlag = false;
|
||||
|
||||
double Max0R(double x1, double x2)
|
||||
{
|
||||
if (x1 > x2)
|
||||
return x1;
|
||||
else
|
||||
return x2;
|
||||
}
|
||||
|
||||
double Min0R(double x1, double x2)
|
||||
{
|
||||
if (x1 < x2)
|
||||
return x1;
|
||||
else
|
||||
return x2;
|
||||
}
|
||||
|
||||
// shitty replacement for Borland timestamp function
|
||||
// TODO: replace with something sensible
|
||||
std::string Now()
|
||||
{
|
||||
|
||||
std::time_t timenow = std::time(nullptr);
|
||||
std::tm tm = *std::localtime(&timenow);
|
||||
std::stringstream converter;
|
||||
converter << std::put_time(&tm, "%c");
|
||||
return converter.str();
|
||||
}
|
||||
|
||||
// zwraca różnicę czasu
|
||||
// jeśli pierwsza jest aktualna, a druga rozkładowa, to ujemna oznacza opóżnienie
|
||||
// na dłuższą metę trzeba uwzględnić datę, jakby opóżnienia miały przekraczać 12h (towarowych)
|
||||
double CompareTime(double t1h, double t1m, double t2h, double t2m)
|
||||
{
|
||||
|
||||
if ((t2h < 0))
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
auto t = (t2h - t1h) * 60 + t2m - t1m; // jeśli t2=00:05, a t1=23:50, to różnica wyjdzie ujemna
|
||||
if ((t < -720)) // jeśli różnica przekracza 12h na minus
|
||||
t = t + 1440; // to dodanie doby minut;else
|
||||
if ((t > 720)) // jeśli przekracza 12h na plus
|
||||
t = t - 1440; // to odjęcie doby minut
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
bool SetFlag(int &Flag, int const Value)
|
||||
{
|
||||
|
||||
if (Value > 0)
|
||||
{
|
||||
if (false == TestFlag(Flag, Value))
|
||||
{
|
||||
Flag |= Value;
|
||||
return true; // true, gdy było wcześniej 0 i zostało ustawione
|
||||
}
|
||||
}
|
||||
else if (Value < 0)
|
||||
{
|
||||
// Value jest ujemne, czyli zerowanie flagi
|
||||
return ClearFlag(Flag, -Value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClearFlag(int &Flag, int const Value)
|
||||
{
|
||||
|
||||
if (true == TestFlag(Flag, Value))
|
||||
{
|
||||
Flag &= ~Value;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
double Random(double a, double b)
|
||||
{
|
||||
uint32_t val = Global.random_engine();
|
||||
return interpolate(a, b, (double)val / Global.random_engine.max());
|
||||
}
|
||||
|
||||
int RandomInt(int min, int max)
|
||||
{
|
||||
static std::mt19937 engine(std::random_device{}());
|
||||
std::uniform_int_distribution<int> dist(min, max);
|
||||
return dist(engine);
|
||||
}
|
||||
|
||||
std::string generate_uuid_v4()
|
||||
{
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<int> dist(0, 255);
|
||||
|
||||
std::array<uint8_t, 16> bytes;
|
||||
for (auto &b : bytes)
|
||||
b = static_cast<uint8_t>(dist(gen));
|
||||
|
||||
// UUID v4 (RFC 4122)
|
||||
bytes[6] = (bytes[6] & 0x0F) | 0x40;
|
||||
bytes[8] = (bytes[8] & 0x3F) | 0x80;
|
||||
|
||||
char buf[37]; // 36 znaków + \0
|
||||
std::snprintf(buf, sizeof(buf),
|
||||
"%02x%02x%02x%02x-"
|
||||
"%02x%02x-"
|
||||
"%02x%02x-"
|
||||
"%02x%02x-"
|
||||
"%02x%02x%02x%02x%02x%02x",
|
||||
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]);
|
||||
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
double LocalRandom(double a, double b)
|
||||
{
|
||||
uint32_t val = Global.local_random_engine();
|
||||
return interpolate(a, b, (double)val / Global.random_engine.max());
|
||||
}
|
||||
|
||||
bool FuzzyLogic(double Test, double Threshold, double Probability)
|
||||
{
|
||||
if ((Test > Threshold) && (!DebugModeFlag))
|
||||
return (Random() < Probability * Threshold * 1.0 / Test) /*im wiekszy Test tym wieksza szansa*/;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FuzzyLogicAI(double Test, double Threshold, double Probability)
|
||||
{
|
||||
if ((Test > Threshold))
|
||||
return (Random() < Probability * Threshold * 1.0 / Test) /*im wiekszy Test tym wieksza szansa*/;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string DUE(std::string s) /*Delete Before Equal sign*/
|
||||
{
|
||||
// DUE = Copy(s, Pos("=", s) + 1, length(s));
|
||||
return s.substr(s.find("=") + 1, s.length());
|
||||
}
|
||||
|
||||
std::string DWE(std::string s) /*Delete After Equal sign*/
|
||||
{
|
||||
size_t ep = s.find("=");
|
||||
if (ep != std::string::npos)
|
||||
// DWE = Copy(s, 1, ep - 1);
|
||||
return s.substr(0, ep);
|
||||
else
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string ExchangeCharInString(std::string const &Source, char const From, char const To)
|
||||
{
|
||||
std::string replacement;
|
||||
replacement.reserve(Source.size());
|
||||
std::for_each(std::begin(Source), std::end(Source),
|
||||
[&](char const idx)
|
||||
{
|
||||
if (idx != From)
|
||||
{
|
||||
replacement += idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
replacement += To;
|
||||
}
|
||||
});
|
||||
|
||||
return replacement;
|
||||
}
|
||||
|
||||
std::vector<std::string> &Split(const std::string &s, char delim, std::vector<std::string> &elems)
|
||||
{ // dzieli tekst na wektor tekstow
|
||||
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim))
|
||||
{
|
||||
elems.push_back(item);
|
||||
}
|
||||
return elems;
|
||||
}
|
||||
|
||||
std::vector<std::string> Split(const std::string &s, char delim)
|
||||
{ // dzieli tekst na wektor tekstow
|
||||
std::vector<std::string> elems;
|
||||
Split(s, delim, elems);
|
||||
return elems;
|
||||
}
|
||||
|
||||
std::vector<std::string> Split(const std::string &s)
|
||||
{ // dzieli tekst na wektor tekstow po białych znakach
|
||||
std::vector<std::string> elems;
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
while (ss >> item)
|
||||
{
|
||||
elems.push_back(item);
|
||||
}
|
||||
return elems;
|
||||
}
|
||||
|
||||
std::pair<std::string, int> split_string_and_number(std::string const &Key)
|
||||
{
|
||||
|
||||
auto const indexstart{Key.find_first_of("-1234567890")};
|
||||
auto const indexend{Key.find_first_not_of("-1234567890", indexstart)};
|
||||
if (indexstart != std::string::npos)
|
||||
{
|
||||
return {Key.substr(0, indexstart), std::stoi(Key.substr(indexstart, indexend - indexstart))};
|
||||
}
|
||||
return {Key, 0};
|
||||
}
|
||||
|
||||
std::string to_string(int Value)
|
||||
{
|
||||
std::ostringstream o;
|
||||
o << Value;
|
||||
return o.str();
|
||||
};
|
||||
|
||||
std::string to_string(unsigned int Value)
|
||||
{
|
||||
std::ostringstream o;
|
||||
o << Value;
|
||||
return o.str();
|
||||
};
|
||||
|
||||
std::string to_string(double Value)
|
||||
{
|
||||
std::ostringstream o;
|
||||
o << Value;
|
||||
return o.str();
|
||||
};
|
||||
|
||||
std::string to_string(int Value, int width)
|
||||
{
|
||||
std::ostringstream o;
|
||||
o.width(width);
|
||||
o << Value;
|
||||
return o.str();
|
||||
};
|
||||
|
||||
std::string to_string(double Value, int precision)
|
||||
{
|
||||
std::ostringstream o;
|
||||
o << std::fixed << std::setprecision(precision);
|
||||
o << Value;
|
||||
return o.str();
|
||||
};
|
||||
|
||||
std::string to_string(double const Value, int const Precision, int const Width)
|
||||
{
|
||||
std::ostringstream converter;
|
||||
converter << std::setw(Width) << std::fixed << std::setprecision(Precision) << Value;
|
||||
return converter.str();
|
||||
};
|
||||
|
||||
std::string to_hex_str(int const Value, int const Width)
|
||||
{
|
||||
std::ostringstream converter;
|
||||
converter << "0x" << std::uppercase << std::setfill('0') << std::setw(Width) << std::hex << Value;
|
||||
return converter.str();
|
||||
};
|
||||
|
||||
bool string_ends_with(const std::string &string, const std::string &ending)
|
||||
{
|
||||
if (string.length() < ending.length())
|
||||
return false;
|
||||
|
||||
return string.compare(string.length() - ending.length(), ending.length(), ending) == 0;
|
||||
}
|
||||
|
||||
bool string_starts_with(const std::string &string, const std::string &begin)
|
||||
{
|
||||
if (string.length() < begin.length())
|
||||
return false;
|
||||
|
||||
return string.compare(0, begin.length(), begin) == 0;
|
||||
}
|
||||
|
||||
std::string const fractionlabels[] = {" ", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"};
|
||||
|
||||
std::string to_minutes_str(float const Minutes, bool const Leadingzero, int const Width)
|
||||
{
|
||||
|
||||
float minutesintegral;
|
||||
auto const minutesfractional{std::modf(Minutes, &minutesintegral)};
|
||||
auto const width{Width - 1};
|
||||
auto minutes = (std::string(width - 1, ' ') + (Leadingzero ? to_string(100 + minutesintegral).substr(1, 2) : to_string(minutesintegral, 0)));
|
||||
return (minutes.substr(minutes.size() - width, width) + fractionlabels[static_cast<int>(std::floor(minutesfractional * 10 + 0.1))]);
|
||||
}
|
||||
|
||||
int stol_def(const std::string &str, const int &DefaultValue)
|
||||
{
|
||||
|
||||
int result{DefaultValue};
|
||||
std::stringstream converter;
|
||||
converter << str;
|
||||
converter >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ToLower(std::string const &text)
|
||||
{
|
||||
|
||||
auto lowercase{text};
|
||||
std::transform(std::begin(text), std::end(text), std::begin(lowercase), [](unsigned char c) { return std::tolower(c); });
|
||||
return lowercase;
|
||||
}
|
||||
|
||||
std::string ToUpper(std::string const &text)
|
||||
{
|
||||
|
||||
auto uppercase{text};
|
||||
std::transform(std::begin(text), std::end(text), std::begin(uppercase), [](unsigned char c) { return std::toupper(c); });
|
||||
return uppercase;
|
||||
}
|
||||
|
||||
// replaces polish letters with basic ascii
|
||||
void win1250_to_ascii(std::string &Input)
|
||||
{
|
||||
|
||||
std::unordered_map<char, char> const charmap{{165, 'A'}, {198, 'C'}, {202, 'E'}, {163, 'L'}, {209, 'N'}, {211, 'O'}, {140, 'S'}, {143, 'Z'}, {175, 'Z'},
|
||||
{185, 'a'}, {230, 'c'}, {234, 'e'}, {179, 'l'}, {241, 'n'}, {243, 'o'}, {156, 's'}, {159, 'z'}, {191, 'z'}};
|
||||
std::unordered_map<char, char>::const_iterator lookup;
|
||||
for (auto &input : Input)
|
||||
{
|
||||
if ((lookup = charmap.find(input)) != charmap.end())
|
||||
input = lookup->second;
|
||||
}
|
||||
}
|
||||
|
||||
std::string win1250_to_utf8(const std::string &Input)
|
||||
{
|
||||
std::unordered_map<char, std::string> const charmap{{165, "Ą"}, {198, "Ć"}, {202, "Ę"}, {163, "Ł"}, {209, "Ń"}, {211, "Ó"}, {140, "Ś"}, {143, "Ź"}, {175, "Ż"},
|
||||
{185, "ą"}, {230, "ć"}, {234, "ę"}, {179, "ł"}, {241, "ń"}, {243, "ó"}, {156, "ś"}, {159, "ź"}, {191, "ż"}};
|
||||
std::string output;
|
||||
std::unordered_map<char, std::string>::const_iterator lookup;
|
||||
for (auto &input : Input)
|
||||
{
|
||||
if ((lookup = charmap.find(input)) != charmap.end())
|
||||
output += lookup->second;
|
||||
else
|
||||
output += input;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
// Ra: tymczasowe rozwiązanie kwestii zagranicznych (czeskich) napisów
|
||||
char charsetconversiontable[] = "E?,?\"_++?%S<STZZ?`'\"\".--??s>stzz"
|
||||
" ^^L$A|S^CS<--RZo±,l'uP.,as>L\"lz"
|
||||
"RAAAALCCCEEEEIIDDNNOOOOxRUUUUYTB"
|
||||
"raaaalccceeeeiiddnnoooo-ruuuuyt?";
|
||||
|
||||
// wycięcie liter z ogonkami
|
||||
std::string Bezogonkow(std::string Input, bool const Underscorestospaces)
|
||||
{
|
||||
|
||||
char const extendedcharsetbit{static_cast<char>(0x80)};
|
||||
char const space{' '};
|
||||
char const underscore{'_'};
|
||||
|
||||
for (auto &input : Input)
|
||||
{
|
||||
if (input & extendedcharsetbit)
|
||||
{
|
||||
input = charsetconversiontable[input ^ extendedcharsetbit];
|
||||
}
|
||||
else if (input < space)
|
||||
{
|
||||
input = space;
|
||||
}
|
||||
else if (Underscorestospaces && (input == underscore))
|
||||
{
|
||||
input = space;
|
||||
}
|
||||
}
|
||||
|
||||
return Input;
|
||||
}
|
||||
|
||||
template <> bool extract_value(bool &Variable, std::string const &Key, std::string const &Input, std::string const &Default)
|
||||
{
|
||||
|
||||
auto value = extract_value(Key, Input);
|
||||
if (false == value.empty())
|
||||
{
|
||||
// set the specified variable to retrieved value
|
||||
Variable = (ToLower(value) == "yes");
|
||||
return true; // located the variable
|
||||
}
|
||||
else
|
||||
{
|
||||
// set the variable to provided default value
|
||||
if (false == Default.empty())
|
||||
{
|
||||
Variable = (ToLower(Default) == "yes");
|
||||
}
|
||||
return false; // couldn't locate the variable in provided input
|
||||
}
|
||||
}
|
||||
|
||||
bool FileExists(std::string const &Filename)
|
||||
{
|
||||
return std::filesystem::exists(Filename);
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> FileExists(std::vector<std::string> const &Names, std::vector<std::string> const &Extensions)
|
||||
{
|
||||
|
||||
for (auto const &name : Names)
|
||||
{
|
||||
for (auto const &extension : Extensions)
|
||||
{
|
||||
if (FileExists(name + extension))
|
||||
{
|
||||
return {name, extension};
|
||||
}
|
||||
}
|
||||
}
|
||||
// nothing found
|
||||
return {{}, {}};
|
||||
}
|
||||
|
||||
// returns time of last modification for specified file
|
||||
std::time_t last_modified(std::string const &Filename)
|
||||
{
|
||||
std::string fn = Filename;
|
||||
struct stat filestat;
|
||||
if (::stat(fn.c_str(), &filestat) == 0)
|
||||
return filestat.st_mtime;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// potentially erases file extension from provided file name. returns: true if extension was removed, false otherwise
|
||||
bool erase_extension(std::string &Filename)
|
||||
{
|
||||
|
||||
auto const extensionpos{Filename.rfind('.')};
|
||||
|
||||
if (extensionpos == std::string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (extensionpos != Filename.rfind("..") + 1)
|
||||
{
|
||||
// we can get extension for .mat or, in legacy files, some image format. just trim it and set it to material file extension
|
||||
Filename.erase(extensionpos);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void erase_leading_slashes(std::string &Filename)
|
||||
{
|
||||
|
||||
while (Filename[0] == '/')
|
||||
{
|
||||
Filename.erase(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// potentially replaces backward slashes in provided file path with unix-compatible forward slashes
|
||||
void replace_slashes(std::string &Filename)
|
||||
{
|
||||
|
||||
std::replace(std::begin(Filename), std::end(Filename), '\\', '/');
|
||||
}
|
||||
|
||||
// returns potential path part from provided file name
|
||||
std::string substr_path(std::string const &Filename)
|
||||
{
|
||||
|
||||
return (Filename.rfind('/') != std::string::npos ? Filename.substr(0, Filename.rfind('/') + 1) : "");
|
||||
}
|
||||
|
||||
// returns length of common prefix between two provided strings
|
||||
std::ptrdiff_t len_common_prefix(std::string const &Left, std::string const &Right)
|
||||
{
|
||||
|
||||
auto const *left{Left.data()};
|
||||
auto const *right{Right.data()};
|
||||
// compare up to the length of the shorter string
|
||||
return (Right.size() <= Left.size() ? std::distance(right, std::mismatch(right, right + Right.size(), left).first) : std::distance(left, std::mismatch(left, left + Left.size(), right).first));
|
||||
}
|
||||
|
||||
// returns true if provided string ends with another provided string
|
||||
bool ends_with(std::string_view String, std::string_view Suffix)
|
||||
{
|
||||
|
||||
return (String.size() >= Suffix.size()) && (0 == String.compare(String.size() - Suffix.size(), Suffix.size(), Suffix));
|
||||
}
|
||||
|
||||
// returns true if provided string begins with another provided string
|
||||
bool starts_with(std::string_view const String, std::string_view Prefix)
|
||||
{
|
||||
|
||||
return (String.size() >= Prefix.size()) && (0 == String.compare(0, Prefix.size(), Prefix));
|
||||
}
|
||||
|
||||
// returns true if provided string contains another provided string
|
||||
bool contains(std::string_view const String, std::string_view Substring)
|
||||
{
|
||||
|
||||
return (String.find(Substring) != std::string::npos);
|
||||
}
|
||||
|
||||
bool contains(std::string_view const String, char Character)
|
||||
{
|
||||
|
||||
return (String.find(Character) != std::string::npos);
|
||||
}
|
||||
|
||||
// helper, restores content of a 3d vector from provided input stream
|
||||
// TODO: review and clean up the helper routines, there's likely some redundant ones
|
||||
|
||||
glm::dvec3 LoadPoint(cParser &Input)
|
||||
{
|
||||
// pobranie współrzędnych punktu
|
||||
glm::dvec3 point;
|
||||
Input.getTokens(3);
|
||||
Input >> point.x >> point.y >> point.z;
|
||||
return point;
|
||||
}
|
||||
|
||||
// extracts a group of tokens from provided data stream, returns one of them picked randomly
|
||||
std::string deserialize_random_set(cParser &Input, char const *Break)
|
||||
{
|
||||
|
||||
auto token{Input.getToken<std::string>(true, Break)};
|
||||
std::replace(token.begin(), token.end(), '\\', '/');
|
||||
if (token != "[")
|
||||
{
|
||||
// simple case, single token
|
||||
return token;
|
||||
}
|
||||
// if instead of a single token we've encountered '[' this marks a beginning of a random set
|
||||
// we retrieve all entries, then return a random one
|
||||
std::vector<std::string> tokens;
|
||||
while (((token = deserialize_random_set(Input, Break)) != "") && (token != "]"))
|
||||
{
|
||||
tokens.emplace_back(token);
|
||||
}
|
||||
if (false == tokens.empty())
|
||||
{
|
||||
std::shuffle(std::begin(tokens), std::end(tokens), Global.random_engine);
|
||||
return tokens.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
// shouldn't ever get here but, eh
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
int count_trailing_zeros(uint32_t val)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
for (uint32_t shift = 1; !(val & shift); shift <<= 1)
|
||||
r++;
|
||||
|
||||
return r;
|
||||
}
|
||||
462
utilities/utilities.h
Normal file
462
utilities/utilities.h
Normal file
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <ctime>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include "utilities/parser.h"
|
||||
|
||||
/*rozne takie duperele do operacji na stringach w paszczalu, pewnie w delfi sa lepsze*/
|
||||
/*konwersja zmiennych na stringi, funkcje matematyczne, logiczne, lancuchowe, I/O etc*/
|
||||
|
||||
template <typename T> T sign(T x)
|
||||
{
|
||||
return x < static_cast<T>(0) ? static_cast<T>(-1) : (x > static_cast<T>(0) ? static_cast<T>(1) : static_cast<T>(0));
|
||||
}
|
||||
|
||||
#define DegToRad(a) ((M_PI / 180.0) * (a)) //(a) w nawiasie, bo może być dodawaniem
|
||||
#define RadToDeg(r) ((180.0 / M_PI) * (r))
|
||||
|
||||
#define szSceneryPath "scenery/"
|
||||
#define szTexturePath "textures/"
|
||||
#define szModelPath "models/"
|
||||
#define szDynamicPath "dynamic/"
|
||||
#define szSoundPath "sounds/"
|
||||
#define szDataPath "data/"
|
||||
|
||||
#define MAKE_ID4(a, b, c, d) (((std::uint32_t)(d) << 24) | ((std::uint32_t)(c) << 16) | ((std::uint32_t)(b) << 8) | (std::uint32_t)(a))
|
||||
|
||||
extern bool DebugModeFlag;
|
||||
extern bool FreeFlyModeFlag;
|
||||
extern bool EditorModeFlag;
|
||||
extern bool DebugCameraFlag;
|
||||
extern bool DebugTractionFlag;
|
||||
|
||||
/*funkcje matematyczne*/
|
||||
double Max0R(double x1, double x2);
|
||||
double Min0R(double x1, double x2);
|
||||
|
||||
inline double Sign(double x)
|
||||
{
|
||||
return x >= 0 ? 1.0 : -1.0;
|
||||
}
|
||||
|
||||
inline long Round(double const f)
|
||||
{
|
||||
return (long)(f + 0.5);
|
||||
// return lround(f);
|
||||
}
|
||||
|
||||
double Random(double a, double b);
|
||||
int RandomInt(int min, int max);
|
||||
std::string generate_uuid_v4();
|
||||
double LocalRandom(double a, double b);
|
||||
|
||||
inline double Random()
|
||||
{
|
||||
return Random(0.0, 1.0);
|
||||
}
|
||||
|
||||
inline double Random(double b)
|
||||
{
|
||||
return Random(0.0, b);
|
||||
}
|
||||
|
||||
inline double LocalRandom()
|
||||
{
|
||||
return LocalRandom(0.0, 1.0);
|
||||
}
|
||||
|
||||
inline double LocalRandom(double b)
|
||||
{
|
||||
return LocalRandom(0.0, b);
|
||||
}
|
||||
|
||||
inline double BorlandTime()
|
||||
{
|
||||
auto timesinceepoch = std::time(nullptr);
|
||||
return timesinceepoch / (24.0 * 60 * 60);
|
||||
/*
|
||||
// std alternative
|
||||
auto timesinceepoch = std::chrono::system_clock::now().time_since_epoch();
|
||||
return std::chrono::duration_cast<std::chrono::seconds>( timesinceepoch ).count() / (24.0 * 60 * 60);
|
||||
*/
|
||||
}
|
||||
|
||||
std::string Now();
|
||||
|
||||
double CompareTime(double t1h, double t1m, double t2h, double t2m);
|
||||
|
||||
/*funkcje logiczne*/
|
||||
inline bool TestFlag(int const Flag, int const Value)
|
||||
{
|
||||
return ((Flag & Value) == Value);
|
||||
}
|
||||
inline bool TestFlagAny(int const Flag, int const Value)
|
||||
{
|
||||
return ((Flag & Value) != 0);
|
||||
}
|
||||
bool SetFlag(int &Flag, int const Value);
|
||||
bool ClearFlag(int &Flag, int const Value);
|
||||
|
||||
bool FuzzyLogic(double Test, double Threshold, double Probability);
|
||||
/*jesli Test>Threshold to losowanie*/
|
||||
bool FuzzyLogicAI(double Test, double Threshold, double Probability);
|
||||
/*to samo ale zawsze niezaleznie od DebugFlag*/
|
||||
|
||||
/*operacje na stringach*/
|
||||
std::string DUE(std::string s); /*Delete Until Equal sign*/
|
||||
std::string DWE(std::string s); /*Delete While Equal sign*/
|
||||
std::string ExchangeCharInString(std::string const &Source, char const From, char const To); // zamienia jeden znak na drugi
|
||||
std::vector<std::string> &Split(const std::string &s, char delim, std::vector<std::string> &elems);
|
||||
std::vector<std::string> Split(const std::string &s, char delim);
|
||||
// std::vector<std::string> Split(const std::string &s);
|
||||
std::pair<std::string, int> split_string_and_number(std::string const &Key);
|
||||
|
||||
std::string to_string(int Value);
|
||||
std::string to_string(unsigned int Value);
|
||||
std::string to_string(int Value, int width);
|
||||
std::string to_string(double Value);
|
||||
std::string to_string(double Value, int precision);
|
||||
std::string to_string(double Value, int precision, int width);
|
||||
std::string to_hex_str(int const Value, int const width = 4);
|
||||
std::string to_minutes_str(float const Minutes, bool const Leadingzero, int const Width);
|
||||
|
||||
inline std::string to_string(bool Value)
|
||||
{
|
||||
return (Value == true ? "true" : "false");
|
||||
}
|
||||
|
||||
template <typename Type_, glm::precision Precision_ = glm::defaultp> std::string to_string(glm::tvec3<Type_, Precision_> const &Value)
|
||||
{
|
||||
return to_string(Value.x, 2) + ", " + to_string(Value.y, 2) + ", " + to_string(Value.z, 2);
|
||||
}
|
||||
|
||||
template <typename Type_, glm::precision Precision_ = glm::defaultp> std::string to_string(glm::tvec4<Type_, Precision_> const &Value, int const Width = 2)
|
||||
{
|
||||
return to_string(Value.x, Width) + ", " + to_string(Value.y, Width) + ", " + to_string(Value.z, Width) + ", " + to_string(Value.w, Width);
|
||||
}
|
||||
|
||||
bool string_ends_with(std::string const &string, std::string const &ending);
|
||||
bool string_starts_with(std::string const &string, std::string const &begin);
|
||||
|
||||
int stol_def(const std::string &str, const int &DefaultValue);
|
||||
|
||||
std::string ToLower(std::string const &text);
|
||||
std::string ToUpper(std::string const &text);
|
||||
|
||||
// replaces polish letters with basic ascii
|
||||
void win1250_to_ascii(std::string &Input);
|
||||
// TODO: unify with win1250_to_ascii()
|
||||
std::string Bezogonkow(std::string Input, bool const Underscorestospaces = false);
|
||||
|
||||
std::string win1250_to_utf8(const std::string &input);
|
||||
|
||||
inline std::string extract_value(std::string const &Key, std::string const &Input)
|
||||
{
|
||||
// NOTE, HACK: the leading space allows to uniformly look for " variable=" substring
|
||||
std::string const input{" " + Input};
|
||||
std::string value;
|
||||
auto lookup = input.find(" " + Key + "=");
|
||||
if (lookup != std::string::npos)
|
||||
{
|
||||
value = input.substr(input.find_first_not_of(' ', lookup + Key.size() + 2));
|
||||
lookup = value.find(' ');
|
||||
if (lookup != std::string::npos)
|
||||
{
|
||||
// trim everything past the value
|
||||
value.erase(lookup);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename Type_> bool extract_value(Type_ &Variable, std::string const &Key, std::string const &Input, std::string const &Default)
|
||||
{
|
||||
|
||||
auto value = extract_value(Key, Input);
|
||||
if (false == value.empty())
|
||||
{
|
||||
// set the specified variable to retrieved value
|
||||
std::stringstream converter;
|
||||
converter << value;
|
||||
converter >> Variable;
|
||||
return true; // located the variable
|
||||
}
|
||||
else
|
||||
{
|
||||
// set the variable to provided default value
|
||||
if (false == Default.empty())
|
||||
{
|
||||
std::stringstream converter;
|
||||
converter << Default;
|
||||
converter >> Variable;
|
||||
}
|
||||
return false; // couldn't locate the variable in provided input
|
||||
}
|
||||
}
|
||||
|
||||
template <> bool extract_value(bool &Variable, std::string const &Key, std::string const &Input, std::string const &Default);
|
||||
|
||||
bool FileExists(std::string const &Filename);
|
||||
|
||||
std::pair<std::string, std::string> FileExists(std::vector<std::string> const &Names, std::vector<std::string> const &Extensions);
|
||||
|
||||
// returns time of last modification for specified file
|
||||
std::time_t last_modified(std::string const &Filename);
|
||||
|
||||
// potentially erases file extension from provided file name. returns: true if extension was removed, false otherwise
|
||||
bool erase_extension(std::string &Filename);
|
||||
|
||||
// potentially erase leading slashes from provided file path
|
||||
void erase_leading_slashes(std::string &Filename);
|
||||
|
||||
// potentially replaces backward slashes in provided file path with unix-compatible forward slashes
|
||||
void replace_slashes(std::string &Filename);
|
||||
|
||||
// returns potential path part from provided file name
|
||||
std::string substr_path(std::string const &Filename);
|
||||
|
||||
// returns common prefix of two provided strings
|
||||
std::ptrdiff_t len_common_prefix(std::string const &Left, std::string const &Right);
|
||||
|
||||
// returns true if provided string ends with another provided string
|
||||
bool ends_with(std::string_view String, std::string_view Suffix);
|
||||
// returns true if provided string begins with another provided string
|
||||
bool starts_with(std::string_view String, std::string_view Prefix);
|
||||
// returns true if provided string contains another provided string
|
||||
bool contains(std::string_view const String, std::string_view Substring);
|
||||
bool contains(std::string_view const String, char Character);
|
||||
|
||||
template <typename Type_> void SafeDelete(Type_ &Pointer)
|
||||
{
|
||||
delete Pointer;
|
||||
Pointer = nullptr;
|
||||
}
|
||||
|
||||
template <typename Type_> void SafeDeleteArray(Type_ &Pointer)
|
||||
{
|
||||
delete[] Pointer;
|
||||
Pointer = nullptr;
|
||||
}
|
||||
|
||||
template <typename Type_> Type_ is_equal(Type_ const &Left, Type_ const &Right, Type_ const Epsilon = 1e-5)
|
||||
{
|
||||
|
||||
if (Epsilon != 0)
|
||||
{
|
||||
return glm::epsilonEqual(Left, Right, Epsilon);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (Left == Right);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type_> Type_ clamp(Type_ const Value, Type_ const Min, Type_ const Max)
|
||||
{
|
||||
|
||||
Type_ value = Value;
|
||||
if (value < Min)
|
||||
{
|
||||
value = Min;
|
||||
}
|
||||
if (value > Max)
|
||||
{
|
||||
value = Max;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// keeps the provided value in specified range 0-Range, as if the range was circular buffer
|
||||
template <typename Type_> Type_ clamp_circular(Type_ Value, Type_ const Range = static_cast<Type_>(360))
|
||||
{
|
||||
|
||||
Value -= Range * (int)(Value / Range); // clamp the range to 0-360
|
||||
if (Value < Type_(0))
|
||||
Value += Range;
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
// rounds down provided value to nearest power of two
|
||||
template <typename Type_> Type_ clamp_power_of_two(Type_ Value, Type_ const Min = static_cast<Type_>(1), Type_ const Max = static_cast<Type_>(16384))
|
||||
{
|
||||
|
||||
Type_ p2size{Min};
|
||||
Type_ size;
|
||||
while ((p2size <= Max) && (p2size <= Value))
|
||||
{
|
||||
size = p2size;
|
||||
p2size = p2size << 1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
template <typename Type_> Type_ quantize(Type_ const Value, Type_ const Step)
|
||||
{
|
||||
|
||||
return (Step * std::round(Value / Step));
|
||||
}
|
||||
|
||||
template <typename Type_> Type_ min_speed(Type_ const Left, Type_ const Right)
|
||||
{
|
||||
|
||||
if (Left == Right)
|
||||
{
|
||||
return Left;
|
||||
}
|
||||
|
||||
return std::min((Left != -1 ? Left : std::numeric_limits<Type_>::max()), (Right != -1 ? Right : std::numeric_limits<Type_>::max()));
|
||||
}
|
||||
|
||||
template <typename Type_> Type_ interpolate(Type_ const &First, Type_ const &Second, float const Factor)
|
||||
{
|
||||
|
||||
return static_cast<Type_>((First * (1.0f - Factor)) + (Second * Factor));
|
||||
}
|
||||
|
||||
template <typename Type_> Type_ interpolate(Type_ const &First, Type_ const &Second, double const Factor)
|
||||
{
|
||||
|
||||
return static_cast<Type_>((First * (1.0 - Factor)) + (Second * Factor));
|
||||
}
|
||||
|
||||
template <typename Type_> Type_ smoothInterpolate(Type_ const &First, Type_ const &Second, double Factor)
|
||||
{
|
||||
// Apply smoothing (ease-in-out quadratic)
|
||||
Factor = Factor * Factor * (3 - 2 * Factor);
|
||||
|
||||
return static_cast<Type_>((First * (1.0 - Factor)) + (Second * Factor));
|
||||
}
|
||||
|
||||
// tests whether provided points form a degenerate triangle
|
||||
template <typename VecType_> bool degenerate(VecType_ const &Vertex1, VecType_ const &Vertex2, VecType_ const &Vertex3)
|
||||
{
|
||||
|
||||
// degenerate( A, B, C, minarea ) = ( ( B - A ).cross( C - A ) ).lengthSquared() < ( 4.0f * minarea * minarea );
|
||||
return (glm::length2(glm::cross(Vertex2 - Vertex1, Vertex3 - Vertex1)) == 0.0);
|
||||
}
|
||||
|
||||
// calculates bounding box for provided set of points
|
||||
template <class Iterator_, class VecType_> void bounding_box(VecType_ &Mincorner, VecType_ &Maxcorner, Iterator_ First, Iterator_ Last)
|
||||
{
|
||||
|
||||
Mincorner = VecType_(std::numeric_limits<typename VecType_::value_type>::max());
|
||||
Maxcorner = VecType_(std::numeric_limits<typename VecType_::value_type>::lowest());
|
||||
|
||||
std::for_each(First, Last,
|
||||
[&](typename Iterator_::value_type &point)
|
||||
{
|
||||
Mincorner = glm::min(Mincorner, VecType_{point});
|
||||
Maxcorner = glm::max(Maxcorner, VecType_{point});
|
||||
});
|
||||
}
|
||||
|
||||
// finds point on specified segment closest to specified point in 3d space. returns: point on segment as value in range 0-1 where 0 = start and 1 = end of the segment
|
||||
template <typename VecType_> typename VecType_::value_type nearest_segment_point(VecType_ const &Segmentstart, VecType_ const &Segmentend, VecType_ const &Point)
|
||||
{
|
||||
|
||||
auto const v = Segmentend - Segmentstart;
|
||||
auto const w = Point - Segmentstart;
|
||||
|
||||
auto const c1 = glm::dot(w, v);
|
||||
if (c1 <= 0.0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
auto const c2 = glm::dot(v, v);
|
||||
if (c2 <= c1)
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
return c1 / c2;
|
||||
}
|
||||
|
||||
glm::dvec3 LoadPoint(class cParser &Input);
|
||||
|
||||
// extracts a group of tokens from provided data stream
|
||||
std::string deserialize_random_set(cParser &Input, char const *Break = "\n\r\t ;");
|
||||
|
||||
int count_trailing_zeros(uint32_t val);
|
||||
|
||||
// extracts a group of <key, value> pairs from provided data stream
|
||||
// NOTE: expects no more than single pair per line
|
||||
template <typename MapType_> void deserialize_map(MapType_ &Map, cParser &Input)
|
||||
{
|
||||
|
||||
while (Input.ok() && !Input.eof())
|
||||
{
|
||||
auto const key{Input.getToken<typename MapType_::key_type>(false)};
|
||||
auto const value{Input.getToken<typename MapType_::mapped_type>(false, "\n")};
|
||||
Map.emplace(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
namespace threading
|
||||
{
|
||||
|
||||
// simple POD pairing of a data item and a mutex
|
||||
// NOTE: doesn't do any locking itself, it's merely for cleaner argument arrangement and passing
|
||||
template <typename Type_> struct lockable
|
||||
{
|
||||
|
||||
Type_ data;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
// basic wrapper simplifying use of std::condition_variable for most typical cases.
|
||||
// has its own mutex and secondary variable to ignore spurious wakeups
|
||||
class condition_variable
|
||||
{
|
||||
|
||||
public:
|
||||
// methods
|
||||
void wait()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_condition.wait(lock, [this]() { return m_spurious == false; });
|
||||
}
|
||||
template <class Rep_, class Period_> void wait_for(const std::chrono::duration<Rep_, Period_> &Time)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_condition.wait_for(lock, Time, [this]() { return m_spurious == false; });
|
||||
}
|
||||
void notify_one()
|
||||
{
|
||||
spurious(false);
|
||||
m_condition.notify_one();
|
||||
}
|
||||
void notify_all()
|
||||
{
|
||||
spurious(false);
|
||||
m_condition.notify_all();
|
||||
}
|
||||
void spurious(bool const Spurious)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_spurious = Spurious;
|
||||
}
|
||||
|
||||
private:
|
||||
// members
|
||||
mutable std::mutex m_mutex;
|
||||
std::condition_variable m_condition;
|
||||
bool m_spurious{true};
|
||||
};
|
||||
|
||||
} // namespace threading
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user