16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-22 04:39:18 +02:00

Reorganize source files into logical subdirectories

Co-authored-by: Hirek193 <23196899+Hirek193@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-14 19:01:57 +00:00
parent f981f81d55
commit 0531086bb9
221 changed files with 131 additions and 108 deletions

94
utilities/Classes.h Normal file
View 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
View 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 "Float3d.h"
#include "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
View 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);
};

1514
utilities/Globals.cpp Normal file

File diff suppressed because it is too large Load Diff

375
utilities/Globals.h Normal file
View File

@@ -0,0 +1,375 @@
/*
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 "Classes.h"
#include "Camera.h"
#include "dumb3d.h"
#include "Float3d.h"
#include "light.h"
#include "utilities.h"
#include "motiontelemetry.h"
#include <map>
#ifdef WITH_UART
#include "uart.h"
#endif
#ifdef WITH_ZMQ
#include "zmq_input.h"
#endif
struct global_settings {
// members
// data items
// TODO: take these out of the settings
std::chrono::steady_clock::time_point startTimestamp;
/// <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
View 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 "Logs.h"
#include "Globals.h"
#include "winheaders.h"
#include "utilities.h"
#include "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
View 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
View 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
View 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 "Timer.h"
#include "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
View 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
View 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
View 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
View 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
View 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
View 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 "dictionary.h"
#include "simulation.h"
#include "utilities.h"
#include "DynObj.h"
#include "Driver.h"
#include "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
View 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
View 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 "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
View 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
View File

@@ -0,0 +1,63 @@
#include "stdafx.h"
#include "headtrack.h"
#include "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
View 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();
};

View File

@@ -0,0 +1,161 @@
#include "stdafx.h"
#include "motiontelemetry.h"
#include "Globals.h"
#include "Logs.h"
#include "Train.h"
#include "Timer.h"
#include "Driver.h"
#include "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");
}

View 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
View 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 "parser.h"
#include "utilities.h"
#include "Logs.h"
#include "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
View 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
View 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 "translation.h"
#include "Logs.h"
#include "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
View 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
View File

@@ -0,0 +1,659 @@
#include "stdafx.h"
#include "uart.h"
#include "Globals.h"
#include "simulation.h"
#include "Train.h"
#include "parser.h"
#include "Logs.h"
#include "simulationtime.h"
#include "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
View File

@@ -0,0 +1,110 @@
#pragma once
#include <libserialport.h>
#include "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
View 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.h"
#include "Globals.h"
#include "parser.h"
#include "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
View 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 "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
//---------------------------------------------------------------------------