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

Merge pull request #37 from MaSzyna-EU07/wipers

Wipers
This commit is contained in:
2026-01-03 18:17:03 +01:00
committed by GitHub
15 changed files with 2360 additions and 2127 deletions

View File

@@ -70,20 +70,24 @@ extern WNDPROC BaseWindowProc;
// user input callbacks
void focus_callback( GLFWwindow *window, int focus ) {
void focus_callback(GLFWwindow *window, int focus)
{
Application.on_focus_change(focus != 0);
}
void framebuffer_resize_callback( GLFWwindow *, int w, int h ) {
void framebuffer_resize_callback(GLFWwindow *, int w, int h)
{
Global.fb_size = glm::ivec2(w, h);
}
void window_resize_callback( GLFWwindow *, int w, int h ) {
void window_resize_callback(GLFWwindow *, int w, int h)
{
Global.window_size = glm::ivec2(w, h);
Application.on_window_resize(w, h);
}
void cursor_pos_callback( GLFWwindow *window, double x, double y ) {
void cursor_pos_callback(GLFWwindow *window, double x, double y)
{
Global.cursor_pos = glm::ivec2(x, y);
Application.on_cursor_pos(x, y);
}
@@ -93,11 +97,13 @@ void mouse_button_callback( GLFWwindow* window, int button, int action, int mods
Application.on_mouse_button(button, action, mods);
}
void scroll_callback( GLFWwindow* window, double xoffset, double yoffset ) {
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
Application.on_scroll(xoffset, yoffset);
}
void key_callback( GLFWwindow *window, int key, int scancode, int action, int mods ) {
void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods)
{
Application.on_key(key, scancode, action, mods);
}
@@ -127,11 +133,13 @@ int eu07_application::run_crashgui()
bool y, n;
if (Global.asLang == "pl") {
if (Global.asLang == "pl")
{
ImGui::Begin(u8"Raportowanie błędów", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize);
ImGui::TextUnformatted(u8"Podczas ostatniego uruchomienia symulatora wystąpił błąd.\nWysłać raport o błędzie do deweloperów?\n");
ImGui::TextUnformatted((u8"Usługa udostępniana przez " + crashreport_get_provider() + "\n").c_str());
y = ImGui::Button(u8"Tak", ImVec2S(60, 0)); ImGui::SameLine();
y = ImGui::Button(u8"Tak", ImVec2S(60, 0));
ImGui::SameLine();
ImGui::Checkbox(u8"W przyszłości przesyłaj raporty o błędach automatycznie", &autoup);
ImGui::SameLine();
@@ -146,11 +154,14 @@ int eu07_application::run_crashgui()
ImGui::NewLine();
n = ImGui::Button(u8"Nie", ImVec2S(60, 0));
ImGui::End();
} else {
}
else
{
ImGui::Begin("Crash reporting", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize);
ImGui::TextUnformatted("Crash occurred during last launch of the simulator.\nSend crash report to developers?\n");
ImGui::TextUnformatted(("Service provided by " + crashreport_get_provider() + "\n").c_str());
y = ImGui::Button("Yes", ImVec2S(60, 0)); ImGui::SameLine();
y = ImGui::Button("Yes", ImVec2S(60, 0));
ImGui::SameLine();
ImGui::Checkbox("In future send crash reports automatically", &autoup);
ImGui::SameLine();
@@ -170,14 +181,16 @@ int eu07_application::run_crashgui()
ui_layer::render_internal();
glfwSwapBuffers(m_windows.front());
if (y) {
if (y)
{
crashreport_upload_accept();
if (autoup)
crashreport_set_autoupload();
return 0;
}
if (n) {
if (n)
{
crashreport_upload_reject();
return 0;
}
@@ -282,9 +295,6 @@ void eu07_application::DiscordRPCService()
}
}
Discord_UpdatePresence(&discord_rpc);
}
std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // update RPC every 5 secs
@@ -292,8 +302,8 @@ void eu07_application::DiscordRPCService()
#endif
}
int
eu07_application::init( int Argc, char *Argv[] ) {
int eu07_application::init(int Argc, char *Argv[])
{
int result{0};
// start logging service
@@ -302,12 +312,12 @@ eu07_application::init( int Argc, char *Argv[] ) {
init_debug();
init_files();
if( ( result = init_settings( Argc, Argv ) ) != 0 ) {
if ((result = init_settings(Argc, Argv)) != 0)
{
ErrorLog("Failed to initialize settings! Maybe you're missing eu07.ini file?");
return result;
}
WriteLog("Starting MaSzyna rail vehicle simulator (release: " + Global.asVersion + ")");
WriteLog("For online documentation and additional files refer to: http://eu07.pl");
WriteLog("Authors: Marcin_EU, McZapkie, ABu, Winger, Tolaris, nbmx, OLO_EU, Bart, Quark-t, "
@@ -329,16 +339,19 @@ eu07_application::init( int Argc, char *Argv[] ) {
WriteLog("// startup");
if( ( result = init_glfw() ) != 0 ) {
if ((result = init_glfw()) != 0)
{
ErrorLog("Failed to initialize glfw!");
return result;
}
if( needs_ogl() && ( result = init_ogl() ) != 0 ) {
if (needs_ogl() && (result = init_ogl()) != 0)
{
ErrorLog("Failed to initialize ogl!");
return result;
}
if (crashreport_is_pending()) { // run crashgui as early as possible
if (crashreport_is_pending())
{ // run crashgui as early as possible
if ((result = run_crashgui()) != 0)
{
ErrorLog("Failed to run crash gui!");
@@ -346,33 +359,40 @@ eu07_application::init( int Argc, char *Argv[] ) {
}
}
if( ( result = init_locale() ) != 0 ) {
if ((result = init_locale()) != 0)
{
ErrorLog("Failed to initialize locales! Maybe you're missing lang directory?");
return result;
}
if( ( result = init_gfx() ) != 0 ) {
if ((result = init_gfx()) != 0)
{
ErrorLog("Failed to initialize GFX!");
return result;
}
if( ( result = init_ui() ) != 0 ) { // ui now depends on activated renderer
if ((result = init_ui()) != 0)
{ // ui now depends on activated renderer
ErrorLog("Failed to init UI!");
return result;
}
if( ( result = init_audio() ) != 0 ) {
if ((result = init_audio()) != 0)
{
ErrorLog("Failed to initialize OpenAL");
return result;
}
if( ( result = init_data() ) != 0 ) {
if ((result = init_data()) != 0)
{
ErrorLog("Failed to load data/ contents! Maybe your installation is broken?");
return result;
}
crashreport_add_info("python_enabled", Global.python_enabled ? "yes" : "no");
if( Global.python_enabled ) {
if (Global.python_enabled)
{
m_taskqueue.init();
}
if( ( result = init_modes() ) != 0 ) {
if ((result = init_modes()) != 0)
{
ErrorLog("Failed to initialize game modes");
return result;
}
@@ -387,13 +407,13 @@ eu07_application::init( int Argc, char *Argv[] ) {
return result;
}
double eu07_application::generate_sync() {
double eu07_application::generate_sync()
{
if (Timer::GetDeltaTime() == 0.0)
return 0.0;
double sync = 0.0;
for (const TDynamicObject* vehicle : simulation::Vehicles.sequence()) {
for (const TDynamicObject *vehicle : simulation::Vehicles.sequence())
{
auto const pos{vehicle->GetPosition()};
sync += pos.x + pos.y + pos.z;
}
@@ -401,8 +421,10 @@ double eu07_application::generate_sync() {
return sync;
}
void eu07_application::queue_quit(bool direct) {
if (direct || !m_modes[m_modestack.top()]->is_command_processor()) {
void eu07_application::queue_quit(bool direct)
{
if (direct || !m_modes[m_modestack.top()]->is_command_processor())
{
glfwSetWindowShouldClose(m_windows[0], GLFW_TRUE);
return;
}
@@ -411,20 +433,20 @@ void eu07_application::queue_quit(bool direct) {
relay.post(user_command::quitsimulation, 0.0, 0.0, GLFW_PRESS, 0);
}
bool
eu07_application::is_server() const {
bool eu07_application::is_server() const
{
return (m_network && m_network->servers);
}
bool
eu07_application::is_client() const {
bool eu07_application::is_client() const
{
return (m_network && m_network->client);
}
int
eu07_application::run() {
int eu07_application::run()
{
auto frame{0};
// main application loop
while (!glfwWindowShouldClose(m_windows.front()) && !m_modestack.empty())
@@ -448,7 +470,8 @@ eu07_application::run() {
double frameStartTime = Timer::GetTime();
if (m_modes[m_modestack.top()]->is_command_processor()) {
if (m_modes[m_modestack.top()]->is_command_processor())
{
// active mode is doing real calculations (e.g. drivermode)
int loop_remaining = MAX_NETWORK_PER_FRAME;
while (--loop_remaining > 0)
@@ -486,7 +509,8 @@ eu07_application::run() {
loop_remaining = -1;
}
// if we're master
else {
else
{
// just push local commands to execution
add_to_dequemap(commands_to_exec, local_commands);
@@ -518,9 +542,9 @@ eu07_application::run() {
if (m_network && m_network->client)
{
// verify sync
if (sync != slave_sync) {
WriteLog("net: desync! calculated: " + std::to_string(sync)
+ ", received: " + std::to_string(slave_sync), logtype::net);
if (sync != slave_sync)
{
WriteLog("net: desync! calculated: " + std::to_string(sync) + ", received: " + std::to_string(slave_sync), logtype::net);
Global.desync = slave_sync - sync;
}
@@ -531,17 +555,22 @@ eu07_application::run() {
}
}
if (!loop_remaining) {
if (!loop_remaining)
{
// loop break forced by counter
float received = m_network->client->get_frame_counter();
float awaiting = m_network->client->get_awaiting_frames();
// TODO: don't meddle with mode progresbar
m_modes[m_modestack.top()]->set_progress(100.0f * (received - awaiting) / received);
} else {
}
else
{
m_modes[m_modestack.top()]->set_progress(0.0f, 0.0f);
}
} else {
}
else
{
// active mode is loader
// clear local command queue
@@ -567,7 +596,8 @@ eu07_application::run() {
m_modes[m_modestack.top()]->on_event_poll();
if (m_screenshot_queued) {
if (m_screenshot_queued)
{
m_screenshot_queued = false;
GfxRenderer->MakeScreenshot();
}
@@ -576,7 +606,8 @@ eu07_application::run() {
m_network->update();
auto const frametime{Timer::subsystem.mainloop_total.stop()};
if( Global.minframetime.count() != 0.0f && ( Global.minframetime - frametime ).count() > 0.0f ) {
if (Global.minframetime.count() != 0.0f && (Global.minframetime - frametime).count() > 0.0f)
{
std::this_thread::sleep_for(Global.minframetime - frametime);
}
}
@@ -598,33 +629,33 @@ eu07_application::run() {
}
// issues request for a worker thread to perform specified task. returns: true if task was scheduled
bool
eu07_application::request( python_taskqueue::task_request const &Task ) {
bool eu07_application::request(python_taskqueue::task_request const &Task)
{
auto const result{m_taskqueue.insert(Task)};
if( ( false == result )
&& ( Task.input != nullptr ) ) {
if ((false == result) && (Task.input != nullptr))
{
// clean up allocated resources since the worker won't
}
return result;
}
// ensures the main thread holds the python gil and can safely execute python calls
void
eu07_application::acquire_python_lock() {
void eu07_application::acquire_python_lock()
{
m_taskqueue.acquire_lock();
}
// frees the python gil and swaps out the main thread
void
eu07_application::release_python_lock() {
void eu07_application::release_python_lock()
{
m_taskqueue.release_lock();
}
void
eu07_application::exit() {
void eu07_application::exit()
{
Global.applicationQuitOrder = true;
auto it = Global.threads.find("LogService");
if (it != Global.threads.end())
@@ -640,7 +671,6 @@ eu07_application::exit() {
it->second.join();
}
for (auto &mode : m_modes)
mode.reset();
@@ -652,7 +682,8 @@ eu07_application::exit() {
ui_layer::shutdown();
for( auto *window : m_windows ) {
for (auto *window : m_windows)
{
glfwDestroyWindow(window);
}
m_taskqueue.exit();
@@ -662,37 +693,48 @@ eu07_application::exit() {
system(Global.exec_on_exit.c_str());
}
void
eu07_application::render_ui() {
void eu07_application::render_ui()
{
if( m_modestack.empty() ) { return; }
if (m_modestack.empty())
{
return;
}
m_modes[m_modestack.top()]->render_ui();
}
void eu07_application::begin_ui_frame() {
void eu07_application::begin_ui_frame()
{
if( m_modestack.empty() ) { return; }
if (m_modestack.empty())
{
return;
}
m_modes[m_modestack.top()]->begin_ui_frame();
}
bool
eu07_application::pop_mode() {
if( m_modestack.empty() ) { return false; }
bool eu07_application::pop_mode()
{
if (m_modestack.empty())
{
return false;
}
m_modes[m_modestack.top()]->exit();
m_modestack.pop();
return true;
}
bool
eu07_application::push_mode( mode const Mode ) {
bool eu07_application::push_mode(mode const Mode)
{
if (Mode >= count_)
return false;
if (!m_modes[Mode]) {
if (!m_modes[Mode])
{
if (Mode == launcher)
m_modes[Mode] = std::make_shared<launcher_mode>();
if (Mode == scenarioloader)
@@ -712,36 +754,42 @@ eu07_application::push_mode( mode const Mode ) {
return true;
}
void
eu07_application::set_title( std::string const &Title ) {
void eu07_application::set_title(std::string const &Title)
{
glfwSetWindowTitle(m_windows.front(), Title.c_str());
}
void
eu07_application::set_progress( float const Progress, float const Subtaskprogress ) {
void eu07_application::set_progress(float const Progress, float const Subtaskprogress)
{
if( m_modestack.empty() ) { return; }
if (m_modestack.empty())
{
return;
}
m_modes[m_modestack.top()]->set_progress(Progress, Subtaskprogress);
}
void
eu07_application::set_tooltip( std::string const &Tooltip ) {
void eu07_application::set_tooltip(std::string const &Tooltip)
{
if( m_modestack.empty() ) { return; }
if (m_modestack.empty())
{
return;
}
m_modes[m_modestack.top()]->set_tooltip(Tooltip);
}
void
eu07_application::set_cursor( int const Mode ) {
void eu07_application::set_cursor(int const Mode)
{
ui_layer::set_cursor(Mode);
}
void
eu07_application::set_cursor_pos( double const Horizontal, double const Vertical ) {
void eu07_application::set_cursor_pos(double const Horizontal, double const Vertical)
{
glfwSetCursorPos(m_windows.front(), Horizontal, Vertical);
}
@@ -757,13 +805,16 @@ eu07_application::get_input_hint( user_command const Command ) const {
}
*/
void
eu07_application::on_key( int const Key, int const Scancode, int const Action, int const Mods ) {
void eu07_application::on_key(int const Key, int const Scancode, int const Action, int const Mods)
{
if (ui_layer::key_callback(Key, Scancode, Action, Mods))
return;
if( m_modestack.empty() ) { return; }
if (m_modestack.empty())
{
return;
}
#ifdef __unix__
if (Key == GLFW_KEY_LEFT_SHIFT || Key == GLFW_KEY_RIGHT_SHIFT)
@@ -777,43 +828,55 @@ eu07_application::on_key( int const Key, int const Scancode, int const Action, i
m_modes[m_modestack.top()]->on_key(Key, Scancode, Action, Mods);
}
void
eu07_application::on_cursor_pos( double const Horizontal, double const Vertical ) {
void eu07_application::on_cursor_pos(double const Horizontal, double const Vertical)
{
if( m_modestack.empty() ) { return; }
if (m_modestack.empty())
{
return;
}
m_modes[m_modestack.top()]->on_cursor_pos(Horizontal, Vertical);
}
void
eu07_application::on_mouse_button( int const Button, int const Action, int const Mods ) {
void eu07_application::on_mouse_button(int const Button, int const Action, int const Mods)
{
if (ui_layer::mouse_button_callback(Button, Action, Mods))
return;
if( m_modestack.empty() ) { return; }
if (m_modestack.empty())
{
return;
}
m_modes[m_modestack.top()]->on_mouse_button(Button, Action, Mods);
}
void
eu07_application::on_scroll( double const Xoffset, double const Yoffset ) {
void eu07_application::on_scroll(double const Xoffset, double const Yoffset)
{
if (ui_layer::scroll_callback(Xoffset, Yoffset))
return;
if( m_modestack.empty() ) { return; }
if (m_modestack.empty())
{
return;
}
m_modes[m_modestack.top()]->on_scroll(Xoffset, Yoffset);
}
void eu07_application::on_char(unsigned int c) {
void eu07_application::on_char(unsigned int c)
{
if (ui_layer::char_callback(c))
return;
}
void eu07_application::on_focus_change(bool focus) {
if( Global.bInactivePause && m_network.has_value() && !m_network->client) {// jeśli ma być pauzowanie okna w tle
void eu07_application::on_focus_change(bool focus)
{
if (Global.bInactivePause && m_network.has_value() && !m_network->client)
{ // jeśli ma być pauzowanie okna w tle
command_relay relay;
relay.post(user_command::focuspauseset, focus ? 1.0 : 0.0, 0.0, GLFW_PRESS, 0);
}
@@ -826,15 +889,12 @@ void eu07_application::on_window_resize(int w, int h)
m_modes[m_modestack.top()]->on_window_resize(w, h);
}
GLFWwindow *eu07_application::window(int const Windowindex, bool visible, int width, int height, GLFWmonitor *monitor, bool keep_ownership, bool share_ctx)
{
GLFWwindow *
eu07_application::window(int const Windowindex, bool visible, int width, int height, GLFWmonitor *monitor, bool keep_ownership , bool share_ctx) {
if( Windowindex >= 0 ) {
return (
Windowindex < m_windows.size() ?
m_windows[ Windowindex ] :
nullptr );
if (Windowindex >= 0)
{
return (Windowindex < m_windows.size() ? m_windows[Windowindex] : nullptr);
}
// for index -1 create a new child window
@@ -849,8 +909,7 @@ eu07_application::window(int const Windowindex, bool visible, int width, int hei
glfwWindowHint(GLFW_VISIBLE, visible);
auto *childwindow = glfwCreateWindow( width, height, "eu07window", monitor,
share_ctx ? m_windows.front() : nullptr);
auto *childwindow = glfwCreateWindow(width, height, "eu07window", monitor, share_ctx ? m_windows.front() : nullptr);
if (!childwindow)
return nullptr;
@@ -863,11 +922,13 @@ eu07_application::window(int const Windowindex, bool visible, int width, int hei
}
// private:
GLFWmonitor* eu07_application::find_monitor(const std::string &str) const {
GLFWmonitor *eu07_application::find_monitor(const std::string &str) const
{
int monitor_count;
GLFWmonitor **monitors = glfwGetMonitors(&monitor_count);
for (size_t i = 0; i < monitor_count; i++) {
for (size_t i = 0; i < monitor_count; i++)
{
if (describe_monitor(monitors[i]) == str)
return monitors[i];
}
@@ -875,7 +936,8 @@ GLFWmonitor* eu07_application::find_monitor(const std::string &str) const {
return nullptr;
}
std::string eu07_application::describe_monitor(GLFWmonitor *monitor) const {
std::string eu07_application::describe_monitor(GLFWmonitor *monitor) const
{
std::string name(glfwGetMonitorName(monitor));
std::replace(std::begin(name), std::end(name), ' ', '_');
@@ -892,7 +954,8 @@ bool eu07_application::needs_ogl() const
return !Global.NvRenderer;
}
void eu07_application::init_debug() {
void eu07_application::init_debug()
{
#if defined(_MSC_VER) && defined(_DEBUG)
// memory leaks
@@ -907,8 +970,8 @@ void eu07_application::init_debug() {
#endif
}
void
eu07_application::init_files() {
void eu07_application::init_files()
{
#ifdef _WIN32
DeleteFile("log.txt");
@@ -922,8 +985,8 @@ eu07_application::init_files() {
}
namespace fs = std::filesystem;
int
eu07_application::init_settings( int Argc, char *Argv[] ) {
int eu07_application::init_settings(int Argc, char *Argv[])
{
Global.asVersion = VERSION_INFO;
fs::path iniPath;
@@ -950,26 +1013,29 @@ eu07_application::init_settings( int Argc, char *Argv[] ) {
}
// process command line arguments
for( int i = 1; i < Argc; ++i ) {
for (int i = 1; i < Argc; ++i)
{
std::string token{Argv[i]};
if( token == "-s" ) {
if( i + 1 < Argc ) {
if (token == "-s")
{
if (i + 1 < Argc)
{
Global.SceneryFile = ToLower(Argv[++i]);
}
}
else if( token == "-v" ) {
if( i + 1 < Argc ) {
else if (token == "-v")
{
if (i + 1 < Argc)
{
Global.local_start_vehicle = ToLower(Argv[++i]);
}
}
else {
std::cout
<< "usage: " << std::string( Argv[ 0 ] )
<< " [-s sceneryfilepath]"
<< " [-v vehiclename]"
<< std::endl;
else
{
std::cout << "usage: " << std::string(Argv[0]) << " [-s sceneryfilepath]"
<< " [-v vehiclename]" << std::endl;
return -1;
}
}
@@ -977,16 +1043,16 @@ eu07_application::init_settings( int Argc, char *Argv[] ) {
return 0;
}
int
eu07_application::init_locale() {
int eu07_application::init_locale()
{
Translations.init();
return 0;
}
int
eu07_application::init_glfw() {
int eu07_application::init_glfw()
{
{
int glfw_major, glfw_minor, glfw_rev;
glfwGetVersion(&glfw_major, &glfw_minor, &glfw_rev);
@@ -994,7 +1060,8 @@ eu07_application::init_glfw() {
}
#ifdef GLFW_ANGLE_PLATFORM_TYPE
if (m_glfwversion >= 30400) {
if (m_glfwversion >= 30400)
{
int platform = GLFW_ANGLE_PLATFORM_TYPE_NONE;
if (Global.gfx_angleplatform == "opengl")
platform = GLFW_ANGLE_PLATFORM_TYPE_OPENGL;
@@ -1013,7 +1080,8 @@ eu07_application::init_glfw() {
}
#endif
if( glfwInit() == GLFW_FALSE ) {
if (glfwInit() == GLFW_FALSE)
{
ErrorLog("Bad init: failed to initialize glfw");
return -1;
}
@@ -1023,7 +1091,8 @@ eu07_application::init_glfw() {
GLFWmonitor **monitors = glfwGetMonitors(&monitor_count);
WriteLog("available monitors:");
for (size_t i = 0; i < monitor_count; i++) {
for (size_t i = 0; i < monitor_count; i++)
{
WriteLog(describe_monitor(monitors[i]));
}
}
@@ -1033,7 +1102,8 @@ eu07_application::init_glfw() {
monitor = glfwGetPrimaryMonitor();
glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE);
if ((Global.gfx_skippipeline || Global.LegacyRenderer) && Global.iMultisampling > 0) {
if ((Global.gfx_skippipeline || Global.LegacyRenderer) && Global.iMultisampling > 0)
{
glfwWindowHint(GLFW_SAMPLES, 1 << Global.iMultisampling);
}
@@ -1043,17 +1113,21 @@ eu07_application::init_glfw() {
{
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
}
else {
if( !Global.LegacyRenderer ) {
else
{
if (!Global.LegacyRenderer)
{
Global.bUseVBO = true;
// activate core profile for opengl 3.3 renderer
if( !Global.gfx_usegles ) {
if (!Global.gfx_usegles)
{
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
}
else {
else
{
#ifdef GLFW_CONTEXT_CREATION_API
if (m_glfwversion >= 30200)
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
@@ -1062,8 +1136,11 @@ eu07_application::init_glfw() {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
}
} else {
if (Global.gfx_usegles) {
}
else
{
if (Global.gfx_usegles)
{
ErrorLog("legacy renderer not supported in gles mode");
return -1;
}
@@ -1079,17 +1156,18 @@ eu07_application::init_glfw() {
glfwWindowHint(GLFW_SRGB_CAPABLE, !Global.gfx_shadergamma);
if( Global.fullscreen_windowed ) {
if (Global.fullscreen_windowed)
{
auto const mode = glfwGetVideoMode(monitor);
Global.window_size.x = mode->width;
Global.window_size.y = mode->height;
Global.bFullScreen = true;
}
auto *mainwindow = window(
-1, true, Global.window_size.x, Global.window_size.y, ( Global.bFullScreen ? monitor : nullptr ), true, false );
auto *mainwindow = window(-1, true, Global.window_size.x, Global.window_size.y, (Global.bFullScreen ? monitor : nullptr), true, false);
if( mainwindow == nullptr ) {
if (mainwindow == nullptr)
{
ErrorLog("Bad init: failed to create glfw window");
return -1;
}
@@ -1118,8 +1196,8 @@ eu07_application::init_glfw() {
return 0;
}
void
eu07_application::init_callbacks() {
void eu07_application::init_callbacks()
{
auto *window{m_windows.front()};
glfwSetWindowSizeCallback(window, window_resize_callback);
@@ -1132,8 +1210,8 @@ eu07_application::init_callbacks() {
glfwSetWindowFocusCallback(window, focus_callback);
}
int
eu07_application::init_ogl() {
int eu07_application::init_ogl()
{
if (!Global.gfx_usegles)
{
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
@@ -1154,19 +1232,21 @@ eu07_application::init_ogl() {
return 0;
}
int
eu07_application::init_ui() {
if( false == ui_layer::init( m_windows.front() ) ) {
int eu07_application::init_ui()
{
if (false == ui_layer::init(m_windows.front()))
{
return -1;
}
init_callbacks();
return 0;
}
int
eu07_application::init_gfx() {
int eu07_application::init_gfx()
{
if( Global.GfxRenderer == "default" ) {
if (Global.GfxRenderer == "default")
{
// default render path
GfxRenderer = gfx_renderer_factory::get_instance()->create("modern");
}
@@ -1174,18 +1254,21 @@ eu07_application::init_gfx() {
{
GfxRenderer = gfx_renderer_factory::get_instance()->create(Global.GfxRenderer);
}
else {
else
{
// legacy render path
GfxRenderer = gfx_renderer_factory::get_instance()->create("legacy");
Global.DisabledLogTypes |= static_cast<unsigned int>(logtype::material);
}
if (!GfxRenderer) {
if (!GfxRenderer)
{
ErrorLog("no renderer found!");
return -1;
}
if( false == GfxRenderer->Init( m_windows.front() ) ) {
if (false == GfxRenderer->Init(m_windows.front()))
{
return -1;
}
@@ -1199,26 +1282,26 @@ eu07_application::init_gfx() {
return 0;
}
int
eu07_application::init_audio() {
int eu07_application::init_audio()
{
if( Global.bSoundEnabled ) {
if (Global.bSoundEnabled)
{
Global.bSoundEnabled &= audio::renderer.init();
}
// NOTE: lack of audio isn't deemed a failure serious enough to throw in the towel
return 0;
}
int
eu07_application::init_data() {
int eu07_application::init_data()
{
// HACK: grab content of the first {} block in load_unit_weights using temporary parser, then parse it normally. on any error our weight list will be empty string
auto loadweights{cParser(cParser("data/load_weights.txt", cParser::buffer_FILE).getToken<std::string>(true, "{}"), cParser::buffer_TEXT)};
while( true == loadweights.getTokens( 2 ) ) {
while (true == loadweights.getTokens(2))
{
std::pair<std::string, float> weightpair;
loadweights
>> weightpair.first
>> weightpair.second;
loadweights >> weightpair.first >> weightpair.second;
weightpair.first.erase(weightpair.first.end() - 1); // trim trailing ':' from the key
simulation::Weights.emplace(weightpair.first, weightpair.second);
}
@@ -1228,11 +1311,12 @@ eu07_application::init_data() {
return 0;
}
int
eu07_application::init_modes() {
int eu07_application::init_modes()
{
Global.local_random_engine.seed(std::random_device{}());
if ((!Global.network_servers.empty() || Global.network_client) && Global.SceneryFile.empty()) {
if ((!Global.network_servers.empty() || Global.network_client) && Global.SceneryFile.empty())
{
ErrorLog("launcher mode is currently not supported in network mode");
return -1;
}
@@ -1246,21 +1330,27 @@ eu07_application::init_modes() {
return 0;
}
bool eu07_application::init_network() {
if (!Global.network_servers.empty() || Global.network_client) {
bool eu07_application::init_network()
{
if (!Global.network_servers.empty() || Global.network_client)
{
// create network manager
m_network.emplace();
}
for (auto const &pair : Global.network_servers) {
for (auto const &pair : Global.network_servers)
{
// create all servers
m_network->create_server(pair.first, pair.second);
}
if (Global.network_client) {
if (Global.network_client)
{
// create client
m_network->connect(Global.network_client->first, Global.network_client->second);
} else {
}
else
{
// we're simulation master
if (!Global.random_seed)
Global.random_seed = std::random_device{}();
@@ -1275,8 +1365,7 @@ bool eu07_application::init_network() {
tmp = std::gmtime(&utc_now);
memcpy(&tm_utc, tmp, sizeof(tm));
int64_t offset = (tm_local.tm_hour * 3600 + tm_local.tm_min * 60 + tm_local.tm_sec)
- (tm_utc.tm_hour * 3600 + tm_utc.tm_min * 60 + tm_utc.tm_sec);
int64_t offset = (tm_local.tm_hour * 3600 + tm_local.tm_min * 60 + tm_local.tm_sec) - (tm_utc.tm_hour * 3600 + tm_utc.tm_min * 60 + tm_utc.tm_sec);
Global.starting_timestamp = utc_now + offset;
Global.ready_to_load = true;

View File

@@ -444,6 +444,11 @@ driver_mode::on_key( int const Key, int const Scancode, int const Action, int co
// give the ui first shot at the input processing...
if( !anyModifier && true == m_userinterface->on_key( Key, Action ) ) { return; }
if (Key == (GLFW_MOD_SHIFT | GLFW_KEY_F12) && Action == GLFW_PRESS)
{
m_userinterface->showDebugUI();
return;
}
// ...if the input is left untouched, pass it on
if( true == m_input.keyboard.key( Key, Action ) ) { return; }

View File

@@ -87,6 +87,11 @@ void driver_ui::render_menu_contents() {
}
}
void driver_ui::showDebugUI()
{
m_debugpanel.is_open = !m_debugpanel.is_open;
}
// potentially processes provided input key. returns: true if key was processed, false otherwise
bool
driver_ui::on_key( int const Key, int const Action ) {
@@ -101,8 +106,7 @@ driver_ui::on_key( int const Key, int const Action ) {
case GLFW_KEY_F10:
case GLFW_KEY_F12: { // ui mode selectors
if( ( true == Global.ctrlState )
|| ( true == Global.shiftState ) ) {
if( ( true == Global.ctrlState )) {
// only react to keys without modifiers
return false;
}
@@ -159,9 +163,12 @@ driver_ui::on_key( int const Key, int const Action ) {
case GLFW_KEY_F12 : {
// debug panel
if (Global.shiftState)
{
m_debugpanel.is_open = !m_debugpanel.is_open;
return true;
}
}
default: {
break;

View File

@@ -27,6 +27,7 @@ public:
// constructors
driver_ui();
// methods
void showDebugUI() override;
// potentially processes provided input key. returns: true if the input was processed, false otherwise
bool
on_key( int const Key, int const Action ) override;
@@ -43,6 +44,7 @@ public:
protected:
void render_menu_contents() override;
private:
// methods
// sets visibility of the cursor

View File

@@ -21,28 +21,27 @@ http://mozilla.org/MPL/2.0/.
#include "renderer.h"
#include "AnimModel.h"
bool
editor_mode::editormode_input::init() {
bool editor_mode::editormode_input::init()
{
return (
mouse.init()
&& keyboard.init() );
return (mouse.init() && keyboard.init());
}
void
editor_mode::editormode_input::poll() {
void editor_mode::editormode_input::poll()
{
keyboard.poll();
}
editor_mode::editor_mode() {
editor_mode::editor_mode()
{
m_userinterface = std::make_shared<editor_ui>();
}
// initializes internal data structures of the mode. returns: true on success, false otherwise
bool
editor_mode::init() {
bool editor_mode::init()
{
Camera.Init({0, 15, 0}, {glm::radians(-30.0), glm::radians(180.0), 0}, nullptr);
@@ -50,8 +49,8 @@ editor_mode::init() {
}
// mode-specific update of simulation data. returns: false on error, true otherwise
bool
editor_mode::update() {
bool editor_mode::update()
{
Timer::UpdateTimers(true);
@@ -63,16 +62,81 @@ editor_mode::update() {
// fixed step render time routines:
fTime50Hz += deltarealtime; // w pauzie też trzeba zliczać czas, bo przy dużym FPS będzie problem z odczytem ramek
while( fTime50Hz >= 1.0 / 50.0 ) {
while (fTime50Hz >= 1.0 / 50.0)
{
#ifdef _WIN32
Console::Update(); // to i tak trzeba wywoływać
#endif
m_userinterface->update();
if (mouseHold)
{
auto const mode = static_cast<editor_ui *>(m_userinterface.get())->mode();
auto const rotation_mode = static_cast<editor_ui *>(m_userinterface.get())->rot_mode();
auto const fixed_rotation_value = static_cast<editor_ui *>(m_userinterface.get())->rot_val();
int Action = GLFW_REPEAT;
int Button = GLFW_MOUSE_BUTTON_LEFT;
{
GfxRenderer->Pick_Node_Callback(
[this, mode, rotation_mode, fixed_rotation_value, Action, Button](scene::basic_node *node)
{
editor_ui *ui = static_cast<editor_ui *>(m_userinterface.get());
if (mode == nodebank_panel::BRUSH)
{
const std::string *src = ui->get_active_node_template();
// std::string name = "editor_" + std::to_string(LocalRandom(0.0, 100000.0));
std::string name = "editor_" + generate_uuid_v4();
if (!src)
return;
glm::vec3 newPos = GfxRenderer->Mouse_Position();
float distance = glm::distance(newPos, oldPos);
if (distance < ui->getSpacing())
return;
TAnimModel *cloned = simulation::State.create_model(*src, name, Camera.Pos + newPos);
oldPos = newPos;
if (!cloned)
return;
// if (!m_dragging)
// return;
m_node = cloned;
if (rotation_mode == functions_panel::RANDOM)
{
auto const rotation{glm::vec3{0, LocalRandom(0.0, 360.0), 0}};
m_editor.rotate(m_node, rotation, 1);
}
else if (rotation_mode == functions_panel::FIXED)
{
auto const rotation{glm::vec3{0, fixed_rotation_value, 0}};
m_editor.rotate(m_node, rotation, 0);
}
// Application.set_cursor( GLFW_CURSOR_DISABLED );
ui->set_node(m_node);
}
});
}
}
// decelerate camera
Camera.Velocity *= 0.65;
if( std::abs( Camera.Velocity.x ) < 0.01 ) { Camera.Velocity.x = 0.0; }
if( std::abs( Camera.Velocity.y ) < 0.01 ) { Camera.Velocity.y = 0.0; }
if( std::abs( Camera.Velocity.z ) < 0.01 ) { Camera.Velocity.z = 0.0; }
if (std::abs(Camera.Velocity.x) < 0.01)
{
Camera.Velocity.x = 0.0;
}
if (std::abs(Camera.Velocity.y) < 0.01)
{
Camera.Velocity.y = 0.0;
}
if (std::abs(Camera.Velocity.z) < 0.01)
{
Camera.Velocity.z = 0.0;
}
fTime50Hz -= 1.0 / 50.0;
}
@@ -90,8 +154,8 @@ editor_mode::update() {
return true;
}
void
editor_mode::update_camera( double const Deltatime ) {
void editor_mode::update_camera(double const Deltatime)
{
// uwzględnienie ruchu wywołanego klawiszami
Camera.Update();
@@ -102,26 +166,21 @@ editor_mode::update_camera( double const Deltatime ) {
}
// maintenance method, called when the mode is activated
void
editor_mode::enter() {
void editor_mode::enter()
{
m_statebackup = {Global.pCamera, FreeFlyModeFlag, Global.ControlPicking};
Camera = Global.pCamera;
// NOTE: camera placement is effectively a copy of drivermode DistantView( true )
// TBD, TODO: refactor into a common vehicle method?
if( false == FreeFlyModeFlag ) {
if (false == FreeFlyModeFlag)
{
auto const *vehicle{Camera.m_owner};
auto const cab {
( vehicle->MoverParameters->CabOccupied == 0 ?
1 :
vehicle->MoverParameters->CabOccupied ) };
auto const cab{(vehicle->MoverParameters->CabOccupied == 0 ? 1 : vehicle->MoverParameters->CabOccupied)};
auto const left{vehicle->VectorLeft() * cab};
Camera.Pos =
Math3D::vector3( Camera.Pos.x, vehicle->GetPosition().y, Camera.Pos.z )
+ left * vehicle->GetWidth()
+ Math3D::vector3( 1.25 * left.x, 1.6, 1.25 * left.z );
Camera.Pos = Math3D::vector3(Camera.Pos.x, vehicle->GetPosition().y, Camera.Pos.z) + left * vehicle->GetWidth() + Math3D::vector3(1.25 * left.x, 1.6, 1.25 * left.z);
Camera.m_owner = nullptr;
Camera.LookAt = vehicle->GetPosition();
Camera.RaLook(); // jednorazowe przestawienie kamery
@@ -134,26 +193,24 @@ editor_mode::enter() {
}
// maintenance method, called when the mode is deactivated
void
editor_mode::exit() {
void editor_mode::exit()
{
EditorModeFlag = false;
Global.ControlPicking = m_statebackup.picking;
FreeFlyModeFlag = m_statebackup.freefly;
Global.pCamera = m_statebackup.camera;
Application.set_cursor(
( Global.ControlPicking ?
GLFW_CURSOR_NORMAL :
GLFW_CURSOR_DISABLED ) );
Application.set_cursor((Global.ControlPicking ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED));
if( false == Global.ControlPicking ) {
if (false == Global.ControlPicking)
{
Application.set_cursor_pos(0, 0);
}
}
void
editor_mode::on_key( int const Key, int const Scancode, int const Action, int const Mods ) {
void editor_mode::on_key(int const Key, int const Scancode, int const Action, int const Mods)
{
#ifndef __unix__
Global.shiftState = (Mods & GLFW_MOD_SHIFT) ? true : false;
Global.ctrlState = (Mods & GLFW_MOD_CONTROL) ? true : false;
@@ -162,44 +219,60 @@ editor_mode::on_key( int const Key, int const Scancode, int const Action, int co
bool anyModifier = Mods & (GLFW_MOD_SHIFT | GLFW_MOD_CONTROL | GLFW_MOD_ALT);
// give the ui first shot at the input processing...
if( !anyModifier && true == m_userinterface->on_key( Key, Action ) ) { return; }
if (!anyModifier && true == m_userinterface->on_key(Key, Action))
{
return;
}
// ...if the input is left untouched, pass it on
if( true == m_input.keyboard.key( Key, Action ) ) { return; }
if (true == m_input.keyboard.key(Key, Action))
{
return;
}
if( Action == GLFW_RELEASE ) { return; }
if (Action == GLFW_RELEASE)
{
return;
}
// legacy hardcoded keyboard commands
// TODO: replace with current command system, move to input object(s)
switch( Key ) {
switch (Key)
{
case GLFW_KEY_F11: {
case GLFW_KEY_F11:
{
if( Action != GLFW_PRESS ) { break; }
if (Action != GLFW_PRESS)
{
break;
}
// mode switch
// TODO: unsaved changes warning
if( ( false == Global.ctrlState )
&& ( false == Global.shiftState ) ) {
if ((false == Global.ctrlState) && (false == Global.shiftState))
{
Application.pop_mode();
}
// scenery export
if( Global.ctrlState
&& Global.shiftState ) {
if (Global.ctrlState && Global.shiftState)
{
simulation::State.export_as_text(Global.SceneryFile);
}
break;
}
case GLFW_KEY_F12: {
case GLFW_KEY_F12:
{
// quick debug mode toggle
if( Global.ctrlState
&& Global.shiftState ) {
if (Global.ctrlState && Global.shiftState)
{
DebugModeFlag = !DebugModeFlag;
}
break;
}
// TODO: ensure delete method can play nice with history stack
case GLFW_KEY_DELETE: {
case GLFW_KEY_DELETE:
{
TAnimModel *model = dynamic_cast<TAnimModel *>(m_node);
if (!model)
break;
@@ -214,34 +287,45 @@ editor_mode::on_key( int const Key, int const Scancode, int const Action, int co
break;
}
default: {
default:
{
break;
}
}
}
void
editor_mode::on_cursor_pos( double const Horizontal, double const Vertical ) {
void editor_mode::on_cursor_pos(double const Horizontal, double const Vertical)
{
auto const mousemove{glm::dvec2{Horizontal, Vertical} - m_input.mouse.position()};
m_input.mouse.position(Horizontal, Vertical);
if( m_input.mouse.button( GLFW_MOUSE_BUTTON_LEFT ) == GLFW_RELEASE ) { return; }
if( m_node == nullptr ) { return; }
if (m_input.mouse.button(GLFW_MOUSE_BUTTON_LEFT) == GLFW_RELEASE)
{
return;
}
if (m_node == nullptr)
{
return;
}
if( m_takesnapshot ) {
if (m_takesnapshot)
{
// take a snapshot of selected node(s)
// TODO: implement this
m_takesnapshot = false;
}
if( mode_translation() ) {
if (mode_translation())
{
// move selected node
if( mode_translation_vertical() ) {
if (mode_translation_vertical())
{
auto const translation{mousemove.y * -0.01f};
m_editor.translate(m_node, translation);
}
else {
else
{
auto const mouseworldposition{Camera.Pos + GfxRenderer->Mouse_Position()};
m_editor.translate(m_node, mouseworldposition, mode_snap());
}
@@ -307,28 +391,33 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods
m_input.mouse.button( Button, Action );
}
*/
void
editor_mode::on_mouse_button( int const Button, int const Action, int const Mods ) {
void editor_mode::on_mouse_button(int const Button, int const Action, int const Mods)
{
// give the ui first shot at the input processing...
if( true == m_userinterface->on_mouse_button( Button, Action ) ) { return; }
if( Button == GLFW_MOUSE_BUTTON_LEFT ) {
if( Action == GLFW_PRESS ) {
// left button press
if (Button == GLFW_MOUSE_BUTTON_LEFT)
{
auto const mode = static_cast<editor_ui *>(m_userinterface.get())->mode();
auto const rotation_mode = static_cast<editor_ui *>(m_userinterface.get())->rot_mode();
auto const fixed_rotation_value =
static_cast<editor_ui *>(m_userinterface.get())->rot_val();
auto const fixed_rotation_value = static_cast<editor_ui *>(m_userinterface.get())->rot_val();
if (true == m_userinterface->on_mouse_button(Button, Action))
{
return;
}
if (Action == GLFW_PRESS)
{
// left button press
mouseHold = true;
m_node = nullptr;
GfxRenderer->Pick_Node_Callback([this, mode, rotation_mode, fixed_rotation_value,
Action, Button](scene::basic_node *node) {
GfxRenderer->Pick_Node_Callback(
[this, mode, rotation_mode, fixed_rotation_value, Action, Button](scene::basic_node *node)
{
editor_ui *ui = static_cast<editor_ui *>(m_userinterface.get());
if (mode == nodebank_panel::MODIFY) {
if (mode == nodebank_panel::MODIFY)
{
if (!m_dragging)
return;
@@ -339,8 +428,10 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods
// m_dragging = false;
ui->set_node(m_node);
}
else if (mode == nodebank_panel::COPY) {
if (node && typeid(*node) == typeid(TAnimModel)) {
else if (mode == nodebank_panel::COPY)
{
if (node && typeid(*node) == typeid(TAnimModel))
{
std::string as_text;
node->export_as_text(as_text);
@@ -349,9 +440,10 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods
m_dragging = false;
}
else if (mode == nodebank_panel::ADD) {
else if (mode == nodebank_panel::ADD)
{
const std::string *src = ui->get_active_node_template();
std::string name = "editor_" + std::to_string(LocalRandom(0.0, 100000.0));
std::string name = "editor_" + generate_uuid_v4();
if (!src)
return;
@@ -364,7 +456,6 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods
if (!m_dragging)
return;
m_node = cloned;
if (rotation_mode == functions_panel::RANDOM)
@@ -379,7 +470,6 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods
auto const rotation{glm::vec3{0, fixed_rotation_value, 0}};
m_editor.rotate(m_node, rotation, 0);
}
// Application.set_cursor( GLFW_CURSOR_DISABLED );
ui->set_node(m_node);
@@ -388,7 +478,11 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods
m_dragging = true;
}
else {
else
{
if (Action == GLFW_RELEASE)
mouseHold = false;
// left button release
// if( m_node )
// Application.set_cursor( GLFW_CURSOR_NORMAL );
@@ -440,26 +534,26 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods
m_input.mouse.button(Button, Action);
}
void
editor_mode::on_event_poll() {
void editor_mode::on_event_poll()
{
m_input.poll();
}
bool
editor_mode::is_command_processor() const {
bool editor_mode::is_command_processor() const
{
return false;
}
bool
editor_mode::mode_translation() const {
bool editor_mode::mode_translation() const
{
return (false == Global.altState);
}
bool
editor_mode::mode_translation_vertical() const {
bool editor_mode::mode_translation_vertical() const
{
return (true == Global.shiftState);
}
@@ -480,8 +574,8 @@ bool editor_mode::mode_rotationZ() const
return ((true == Global.altState) && (true == Global.ctrlState) && (true == Global.shiftState));
}
bool
editor_mode::mode_snap() const {
bool editor_mode::mode_snap() const
{
return ((false == Global.altState) && (true == Global.ctrlState) && (false == Global.shiftState));
}

View File

@@ -16,7 +16,8 @@ http://mozilla.org/MPL/2.0/.
#include "sceneeditor.h"
#include "scenenode.h"
class editor_mode : public application_mode {
class editor_mode : public application_mode
{
public:
// constructors
@@ -34,14 +35,21 @@ public:
void on_key(int Key, int Scancode, int Action, int Mods) override;
void on_cursor_pos(double Horizontal, double Vertical) override;
void on_mouse_button(int Button, int Action, int Mods) override;
void on_scroll( double const Xoffset, double const Yoffset ) override { ; }
void on_window_resize( int w, int h ) override { ; }
void on_scroll(double const Xoffset, double const Yoffset) override
{
;
}
void on_window_resize(int w, int h) override
{
;
}
void on_event_poll() override;
bool is_command_processor() const override;
private:
// types
struct editormode_input {
struct editormode_input
{
editormouse_input mouse;
editorkeyboard_input keyboard;
@@ -50,27 +58,21 @@ private:
void poll();
};
struct state_backup {
struct state_backup
{
TCamera camera;
bool freefly;
bool picking;
};
// methods
void
update_camera( double const Deltatime );
bool
mode_translation() const;
bool
mode_translation_vertical() const;
bool
mode_rotationY() const;
bool
mode_rotationX() const;
bool
mode_rotationZ() const;
bool
mode_snap() const;
void update_camera(double const Deltatime);
bool mode_translation() const;
bool mode_translation_vertical() const;
bool mode_rotationY() const;
bool mode_rotationX() const;
bool mode_rotationZ() const;
bool mode_snap() const;
// members
state_backup m_statebackup; // helper, cached variables to be restored on mode exit
editormode_input m_input;
@@ -80,4 +82,6 @@ private:
scene::basic_node *m_node; // currently selected scene node
bool m_takesnapshot{true}; // helper, hints whether snapshot of selected node(s) should be taken before modification
bool m_dragging = false;
glm::vec3 oldPos;
bool mouseHold{false};
};

View File

@@ -14,7 +14,8 @@ http://mozilla.org/MPL/2.0/.
#include "scenenode.h"
#include "renderer.h"
editor_ui::editor_ui() {
editor_ui::editor_ui()
{
clear_panels();
// bind the panels with ui object. maybe not the best place for this but, eh
@@ -25,12 +26,13 @@ editor_ui::editor_ui() {
}
// updates state of UI elements
void
editor_ui::update() {
void editor_ui::update()
{
set_tooltip("");
if( Global.ControlPicking && DebugModeFlag ) {
if (Global.ControlPicking && DebugModeFlag)
{
const auto sceneryNode = GfxRenderer->Pick_Node();
const std::string content = sceneryNode ? sceneryNode->tooltip() : "";
set_tooltip(content);
@@ -41,37 +43,39 @@ editor_ui::update() {
m_functionspanel.update(m_node);
}
void
editor_ui::set_node( scene::basic_node * Node ) {
void editor_ui::set_node(scene::basic_node *Node)
{
m_node = Node;
}
void
editor_ui::add_node_template(const std::string &desc) {
void editor_ui::add_node_template(const std::string &desc)
{
m_nodebankpanel.add_template(desc);
}
std::string const *
editor_ui::get_active_node_template() {
std::string const *editor_ui::get_active_node_template()
{
return m_nodebankpanel.get_active_template();
}
nodebank_panel::edit_mode
editor_ui::mode() {
nodebank_panel::edit_mode editor_ui::mode()
{
return m_nodebankpanel.mode;
}
float editor_ui::getSpacing()
{
return m_nodebankpanel.spacing;
}
functions_panel::rotation_mode
editor_ui::rot_mode() {
functions_panel::rotation_mode editor_ui::rot_mode()
{
return m_functionspanel.rot_mode;
}
float
editor_ui::rot_val() {
float editor_ui::rot_val()
{
return m_functionspanel.rot_value;
}
bool
editor_ui::rot_from_last()
bool editor_ui::rot_from_last()
{
return m_functionspanel.rot_from_last;
}

View File

@@ -12,35 +12,30 @@ http://mozilla.org/MPL/2.0/.
#include "uilayer.h"
#include "editoruipanels.h"
namespace scene {
namespace scene
{
class basic_node;
}
class editor_ui : public ui_layer {
class editor_ui : public ui_layer
{
public:
// constructors
editor_ui();
// methods
// updates state of UI elements
void
update() override;
void
set_node( scene::basic_node * Node );
void
add_node_template(const std::string &desc);
float
rot_val();
bool
rot_from_last();
functions_panel::rotation_mode
rot_mode();
const std::string *
get_active_node_template();
nodebank_panel::edit_mode
mode();
void update() override;
void set_node(scene::basic_node *Node);
void add_node_template(const std::string &desc);
float rot_val();
bool rot_from_last();
functions_panel::rotation_mode rot_mode();
const std::string *get_active_node_template();
nodebank_panel::edit_mode mode();
float getSpacing();
private:
// members

View File

@@ -17,14 +17,18 @@ http://mozilla.org/MPL/2.0/.
#include "Track.h"
#include "Event.h"
#include "MemCell.h"
#include "editoruilayer.h"
#include "renderer.h"
#include "utilities.h"
void
itemproperties_panel::update( scene::basic_node const *Node ) {
void itemproperties_panel::update(scene::basic_node const *Node)
{
m_node = Node;
if( false == is_open ) { return; }
if (false == is_open)
{
return;
}
text_lines.clear();
m_grouplines.clear();
@@ -35,7 +39,8 @@ itemproperties_panel::update( scene::basic_node const *Node ) {
auto const *node{Node};
auto const &camera{Global.pCamera};
if( node == nullptr ) {
if (node == nullptr)
{
auto const mouseposition{camera.Pos + GfxRenderer->Mouse_Position()};
textline = "mouse location: [" + to_string(mouseposition.x, 2) + ", " + to_string(mouseposition.y, 2) + ", " + to_string(mouseposition.z, 2) + "]";
text_lines.emplace_back(textline, Global.UITextColor);
@@ -56,142 +61,130 @@ itemproperties_panel::update( scene::basic_node const *Node ) {
}
}
*/
textline =
"name: " + ( node->name().empty() ? "(none)" : Bezogonkow( node->name() ) )
+ "\nlocation: [" + to_string( node->location().x, 2 ) + ", " + to_string( node->location().y, 2 ) + ", " + to_string( node->location().z, 2 ) + "]"
+ " (distance: " + to_string( glm::length( glm::dvec3{ node->location().x, 0.0, node->location().z } -glm::dvec3{ camera.Pos.x, 0.0, camera.Pos.z } ), 1 ) + " m)";
textline = "name: " + (node->name().empty() ? "(none)" : Bezogonkow(node->name())) + "\nlocation: [" + to_string(node->location().x, 2) + ", " + to_string(node->location().y, 2) + ", " +
to_string(node->location().z, 2) + "]" +
" (distance: " + to_string(glm::length(glm::dvec3{node->location().x, 0.0, node->location().z} - glm::dvec3{camera.Pos.x, 0.0, camera.Pos.z}), 1) + " m)";
text_lines.emplace_back(textline, Global.UITextColor);
// subclass-specific data
// TBD, TODO: specialized data dump method in each node subclass, or data imports in the panel for provided subclass pointer?
if( typeid( *node ) == typeid( TAnimModel ) ) {
if (typeid(*node) == typeid(TAnimModel))
{
auto const *subnode = static_cast<TAnimModel const *>(node);
textline = "angle_x: " + to_string(clamp_circular(subnode->vAngle.x, 360.f), 2) + " deg, " +
"angle_y: " + to_string(clamp_circular(subnode->vAngle.y, 360.f), 2) + " deg, " +
textline = "angle_x: " + to_string(clamp_circular(subnode->vAngle.x, 360.f), 2) + " deg, " + "angle_y: " + to_string(clamp_circular(subnode->vAngle.y, 360.f), 2) + " deg, " +
"angle_z: " + to_string(clamp_circular(subnode->vAngle.z, 360.f), 2) + " deg";
textline += ";\nlights: ";
if( subnode->iNumLights > 0 ) {
if (subnode->iNumLights > 0)
{
textline += '[';
for( int lightidx = 0; lightidx < subnode->iNumLights; ++lightidx ) {
for (int lightidx = 0; lightidx < subnode->iNumLights; ++lightidx)
{
textline += to_string(subnode->lsLights[lightidx]);
if( lightidx < subnode->iNumLights - 1 ) {
if (lightidx < subnode->iNumLights - 1)
{
textline += ", ";
}
}
textline += ']';
}
else {
else
{
textline += "(none)";
}
text_lines.emplace_back(textline, Global.UITextColor);
// 3d shape
auto modelfile { (
( subnode->pModel != nullptr ) ?
subnode->pModel->NameGet() :
"(none)" ) };
if( modelfile.find( szModelPath ) == 0 ) {
auto modelfile{((subnode->pModel != nullptr) ? subnode->pModel->NameGet() : "(none)")};
if (modelfile.find(szModelPath) == 0)
{
// don't include 'models/' in the path
modelfile.erase(0, std::string{szModelPath}.size());
}
// texture
auto texturefile { (
( subnode->Material()->replacable_skins[ 1 ] != null_handle ) ?
GfxRenderer->Material( subnode->Material()->replacable_skins[ 1 ] )->GetName() :
"(none)" ) };
if( texturefile.find( szTexturePath ) == 0 ) {
auto texturefile{((subnode->Material()->replacable_skins[1] != null_handle) ? GfxRenderer->Material(subnode->Material()->replacable_skins[1])->GetName() : "(none)")};
if (texturefile.find(szTexturePath) == 0)
{
// don't include 'textures/' in the path
texturefile.erase(0, std::string{szTexturePath}.size());
}
text_lines.emplace_back("mesh: " + modelfile, Global.UITextColor);
text_lines.emplace_back("skin: " + texturefile, Global.UITextColor);
}
else if( typeid( *node ) == typeid( TTrack ) ) {
else if (typeid(*node) == typeid(TTrack))
{
auto const *subnode = static_cast<TTrack const *>(node);
std::string isolatedlist;
for (const TIsolated *iso : subnode->Isolated) {
for (const TIsolated *iso : subnode->Isolated)
{
if (!isolatedlist.empty())
isolatedlist += ", ";
isolatedlist += iso->asName;
}
// basic attributes
textline =
"isolated: " + ( !isolatedlist.empty() ? isolatedlist : "(none)" )
+ "\nvelocity: " + to_string( subnode->SwitchExtension ? subnode->SwitchExtension->fVelocity : subnode->fVelocity )
+ "\nwidth: " + to_string( subnode->fTrackWidth ) + " m"
+ "\nfriction: " + to_string( subnode->fFriction, 2 )
+ "\nquality: " + to_string( subnode->iQualityFlag );
textline = "isolated: " + (!isolatedlist.empty() ? isolatedlist : "(none)") + "\nvelocity: " + to_string(subnode->SwitchExtension ? subnode->SwitchExtension->fVelocity : subnode->fVelocity) +
"\nwidth: " + to_string(subnode->fTrackWidth) + " m" + "\nfriction: " + to_string(subnode->fFriction, 2) + "\nquality: " + to_string(subnode->iQualityFlag);
text_lines.emplace_back(textline, Global.UITextColor);
// textures
auto texturefile { (
( subnode->m_material1 != null_handle ) ?
GfxRenderer->Material( subnode->m_material1 )->GetName() :
"(none)" ) };
if( texturefile.find( szTexturePath ) == 0 ) {
auto texturefile{((subnode->m_material1 != null_handle) ? GfxRenderer->Material(subnode->m_material1)->GetName() : "(none)")};
if (texturefile.find(szTexturePath) == 0)
{
texturefile.erase(0, std::string{szTexturePath}.size());
}
auto texturefile2{ (
( subnode->m_material2 != null_handle ) ?
GfxRenderer->Material( subnode->m_material2 )->GetName() :
"(none)" ) };
if( texturefile2.find( szTexturePath ) == 0 ) {
auto texturefile2{((subnode->m_material2 != null_handle) ? GfxRenderer->Material(subnode->m_material2)->GetName() : "(none)")};
if (texturefile2.find(szTexturePath) == 0)
{
texturefile2.erase(0, std::string{szTexturePath}.size());
}
textline = "skins:\n " + texturefile + "\n " + texturefile2;
text_lines.emplace_back(textline, Global.UITextColor);
// paths
textline = "paths: ";
for( auto const &path : subnode->m_paths ) {
textline +=
"\n ["
+ to_string( path.points[ segment_data::point::start ].x, 3 ) + ", "
+ to_string( path.points[ segment_data::point::start ].y, 3 ) + ", "
+ to_string( path.points[ segment_data::point::start ].z, 3 ) + "]->"
+ " ["
+ to_string( path.points[ segment_data::point::end ].x, 3 ) + ", "
+ to_string( path.points[ segment_data::point::end ].y, 3 ) + ", "
+ to_string( path.points[ segment_data::point::end ].z, 3 ) + "] ";
for (auto const &path : subnode->m_paths)
{
textline += "\n [" + to_string(path.points[segment_data::point::start].x, 3) + ", " + to_string(path.points[segment_data::point::start].y, 3) + ", " +
to_string(path.points[segment_data::point::start].z, 3) + "]->" + " [" + to_string(path.points[segment_data::point::end].x, 3) + ", " +
to_string(path.points[segment_data::point::end].y, 3) + ", " + to_string(path.points[segment_data::point::end].z, 3) + "] ";
}
text_lines.emplace_back(textline, Global.UITextColor);
// events
textline.clear();
std::vector< std::pair< std::string, TTrack::event_sequence const * > > const eventsequences {
{ "ev0", &subnode->m_events0 }, { "ev0all", &subnode->m_events0all },
std::vector<std::pair<std::string, TTrack::event_sequence const *>> const eventsequences{{"ev0", &subnode->m_events0}, {"ev0all", &subnode->m_events0all},
{"ev1", &subnode->m_events1}, {"ev1all", &subnode->m_events1all},
{"ev2", &subnode->m_events2}, {"ev2all", &subnode->m_events2all}};
for( auto const &eventsequence : eventsequences ) {
for (auto const &eventsequence : eventsequences)
{
if( eventsequence.second->empty() ) { continue; }
if (eventsequence.second->empty())
{
continue;
}
textline += (textline.empty() ? "" : "\n") + eventsequence.first + ": [";
for( auto const &event : *( eventsequence.second ) ) {
if( textline.back() != '[' ) {
for (auto const &event : *(eventsequence.second))
{
if (textline.back() != '[')
{
textline += ", ";
}
textline += (
event.second != nullptr ?
Bezogonkow( event.second->m_name ) :
event.first + " (missing)" );
textline += (event.second != nullptr ? Bezogonkow(event.second->m_name) : event.first + " (missing)");
}
textline += "] ";
}
text_lines.emplace_back(textline, Global.UITextColor);
}
else if( typeid( *node ) == typeid( TMemCell ) ) {
else if (typeid(*node) == typeid(TMemCell))
{
auto const *subnode = static_cast<TMemCell const *>(node);
textline =
"data: [" + subnode->Text() + "]"
+ " [" + to_string( subnode->Value1(), 2 ) + "]"
+ " [" + to_string( subnode->Value2(), 2 ) + "]";
textline = "data: [" + subnode->Text() + "]" + " [" + to_string(subnode->Value1(), 2) + "]" + " [" + to_string(subnode->Value2(), 2) + "]";
text_lines.emplace_back(textline, Global.UITextColor);
textline = "track: " + (subnode->asTrackName.empty() ? "(none)" : Bezogonkow(subnode->asTrackName));
text_lines.emplace_back(textline, Global.UITextColor);
@@ -200,12 +193,13 @@ itemproperties_panel::update( scene::basic_node const *Node ) {
update_group();
}
void
itemproperties_panel::update_group() {
void itemproperties_panel::update_group()
{
auto const grouphandle{m_node->group()};
if( grouphandle == null_handle ) {
if (grouphandle == null_handle)
{
m_grouphandle = null_handle;
m_groupprefix.clear();
return;
@@ -213,77 +207,88 @@ itemproperties_panel::update_group() {
auto const &nodegroup{scene::Groups.group(grouphandle)};
if( m_grouphandle != grouphandle ) {
if (m_grouphandle != grouphandle)
{
// calculate group name from shared prefix of item names
std::vector<std::reference_wrapper<std::string const>> names;
// build list of custom item and event names
for( auto const *node : nodegroup.nodes ) {
for (auto const *node : nodegroup.nodes)
{
auto const &name{node->name()};
if( name.empty() || name == "none" ) { continue; }
if (name.empty() || name == "none")
{
continue;
}
names.emplace_back(name);
}
for( auto const *event : nodegroup.events ) {
for (auto const *event : nodegroup.events)
{
auto const &name{event->m_name};
if( name.empty() || name == "none" ) { continue; }
if (name.empty() || name == "none")
{
continue;
}
names.emplace_back(name);
}
// find the common prefix
if( names.size() > 1 ) {
if (names.size() > 1)
{
m_groupprefix = names.front();
for( auto const &name : names ) {
for (auto const &name : names)
{
// NOTE: first calculation runs over two instances of the same name, but, eh
auto const prefixlength{len_common_prefix(m_groupprefix, name)};
if( prefixlength > 0 ) {
if (prefixlength > 0)
{
m_groupprefix = m_groupprefix.substr(0, prefixlength);
}
else {
else
{
m_groupprefix.clear();
break;
}
}
}
else {
else
{
// less than two names to compare means no prefix
m_groupprefix.clear();
}
m_grouphandle = grouphandle;
}
m_grouplines.emplace_back(
"nodes: " + to_string( static_cast<int>( nodegroup.nodes.size() ) )
+ "\nevents: " + to_string( static_cast<int>( nodegroup.events.size() ) ),
Global.UITextColor );
m_grouplines.emplace_back(
"names prefix: " + ( m_groupprefix.empty() ? "(none)" : m_groupprefix ),
Global.UITextColor );
m_grouplines.emplace_back("nodes: " + to_string(static_cast<int>(nodegroup.nodes.size())) + "\nevents: " + to_string(static_cast<int>(nodegroup.events.size())), Global.UITextColor);
m_grouplines.emplace_back("names prefix: " + (m_groupprefix.empty() ? "(none)" : m_groupprefix), Global.UITextColor);
}
void itemproperties_panel::render()
{
void
itemproperties_panel::render() {
if (false == is_open)
{
return;
}
if (true == text_lines.empty())
{
return;
}
if( false == is_open ) { return; }
if( true == text_lines.empty() ) { return; }
auto flags = ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoCollapse | (size.x > 0 ? ImGuiWindowFlags_NoResize : 0);
auto flags =
ImGuiWindowFlags_NoFocusOnAppearing
| ImGuiWindowFlags_NoCollapse
| ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 );
if( size.x > 0 ) {
if (size.x > 0)
{
ImGui::SetNextWindowSize(ImVec2S(size.x, size.y));
}
if( size_min.x > 0 ) {
if (size_min.x > 0)
{
ImGui::SetNextWindowSizeConstraints(ImVec2S(size_min.x, size_min.y), ImVec2(size_max.x, size_max.y));
}
auto const panelname { (
title.empty() ?
m_name :
title )
+ "###" + m_name };
if( true == ImGui::Begin( panelname.c_str(), nullptr, flags ) ) {
auto const panelname{(title.empty() ? m_name : title) + "###" + m_name};
if (true == ImGui::Begin(panelname.c_str(), nullptr, flags))
{
// header section
for( auto const &line : text_lines ) {
for (auto const &line : text_lines)
{
ImGui::TextColored(ImVec4(line.color.r, line.color.g, line.color.b, line.color.a), line.data.c_str());
}
// group section
@@ -292,24 +297,33 @@ itemproperties_panel::render() {
ImGui::End();
}
bool
itemproperties_panel::render_group() {
bool itemproperties_panel::render_group()
{
if( m_node == nullptr ) { return false; }
if( m_grouplines.empty() ) { return false; }
if (m_node == nullptr)
{
return false;
}
if (m_grouplines.empty())
{
return false;
}
if( false == ImGui::CollapsingHeader( "Parent Group" ) ) { return false; }
if (false == ImGui::CollapsingHeader("Parent Group"))
{
return false;
}
for( auto const &line : m_grouplines ) {
for (auto const &line : m_grouplines)
{
ImGui::TextColored(ImVec4(line.color.r, line.color.g, line.color.b, line.color.a), line.data.c_str());
}
return true;
}
nodebank_panel::nodebank_panel( std::string const &Name, bool const Isopen ) : ui_panel( Name, Isopen ) {
nodebank_panel::nodebank_panel(std::string const &Name, bool const Isopen) : ui_panel(Name, Isopen)
{
size_min = {100, 50};
size_max = {1000, 1000};
@@ -319,44 +333,30 @@ nodebank_panel::nodebank_panel( std::string const &Name, bool const Isopen ) : u
file.open("nodebank.txt", std::ios_base::in | std::ios_base::binary);
std::string line;
while( std::getline( file, line ) ) {
if( line.size() < 4 ) {
while (std::getline(file, line))
{
if (line.size() < 4)
{
continue;
}
auto const labelend{line.find("node")};
auto const nodedata { (
labelend == std::string::npos ? "" :
labelend == 0 ? line :
line.substr( labelend ) ) };
auto const label { (
labelend == std::string::npos ? line :
labelend == 0 ? generate_node_label( nodedata ) :
line.substr( 0, labelend ) ) };
auto const nodedata{(labelend == std::string::npos ? "" : labelend == 0 ? line : line.substr(labelend))};
auto const label{(labelend == std::string::npos ? line : labelend == 0 ? generate_node_label(nodedata) : line.substr(0, labelend))};
m_nodebank.push_back({label, std::make_shared<std::string>(nodedata)});
}
// sort alphabetically content of each group
auto groupbegin{m_nodebank.begin()};
auto groupend{groupbegin};
while( groupbegin != m_nodebank.end() ) {
groupbegin =
std::find_if(
groupend, m_nodebank.end(),
[]( auto const &Entry ) {
return ( false == Entry.second->empty() ); } );
groupend =
std::find_if(
groupbegin, m_nodebank.end(),
[]( auto const &Entry ) {
return ( Entry.second->empty() ); } );
std::sort(
groupbegin, groupend,
[]( auto const &Left, auto const &Right ) {
return ( Left.first < Right.first ); } );
while (groupbegin != m_nodebank.end())
{
groupbegin = std::find_if(groupend, m_nodebank.end(), [](auto const &Entry) { return (false == Entry.second->empty()); });
groupend = std::find_if(groupbegin, m_nodebank.end(), [](auto const &Entry) { return (Entry.second->empty()); });
std::sort(groupbegin, groupend, [](auto const &Left, auto const &Right) { return (Left.first < Right.first); });
}
}
void
nodebank_panel::nodebank_reload(){
void nodebank_panel::nodebank_reload()
{
m_nodebank.clear();
std::ifstream file;
file.open("nodebank.txt", std::ios_base::in | std::ios_base::binary);
@@ -368,12 +368,8 @@ nodebank_panel::nodebank_reload(){
continue;
}
auto const labelend{line.find("node")};
auto const nodedata{
(labelend == std::string::npos ? "" : labelend == 0 ? line : line.substr(labelend))};
auto const label{
(labelend == std::string::npos ?
line :
labelend == 0 ? generate_node_label(nodedata) : line.substr(0, labelend))};
auto const nodedata{(labelend == std::string::npos ? "" : labelend == 0 ? line : line.substr(labelend))};
auto const label{(labelend == std::string::npos ? line : labelend == 0 ? generate_node_label(nodedata) : line.substr(0, labelend))};
m_nodebank.push_back({label, std::make_shared<std::string>(nodedata)});
}
@@ -382,44 +378,41 @@ nodebank_panel::nodebank_reload(){
auto groupend{groupbegin};
while (groupbegin != m_nodebank.end())
{
groupbegin = std::find_if(groupend, m_nodebank.end(), [](auto const &Entry) {
return (false == Entry.second->empty());
});
groupend = std::find_if(groupbegin, m_nodebank.end(),
[](auto const &Entry) { return (Entry.second->empty()); });
std::sort(groupbegin, groupend,
[](auto const &Left, auto const &Right) { return (Left.first < Right.first); });
groupbegin = std::find_if(groupend, m_nodebank.end(), [](auto const &Entry) { return (false == Entry.second->empty()); });
groupend = std::find_if(groupbegin, m_nodebank.end(), [](auto const &Entry) { return (Entry.second->empty()); });
std::sort(groupbegin, groupend, [](auto const &Left, auto const &Right) { return (Left.first < Right.first); });
}
}
void
nodebank_panel::render() {
void nodebank_panel::render()
{
if( false == is_open ) { return; }
if (false == is_open)
{
return;
}
auto flags =
ImGuiWindowFlags_NoFocusOnAppearing
| ImGuiWindowFlags_NoCollapse
| ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 );
auto flags = ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoCollapse | (size.x > 0 ? ImGuiWindowFlags_NoResize : 0);
if( size.x > 0 ) {
if (size.x > 0)
{
ImGui::SetNextWindowSize(ImVec2S(size.x, size.y));
}
if( size_min.x > 0 ) {
if (size_min.x > 0)
{
ImGui::SetNextWindowSizeConstraints(ImVec2S(size_min.x, size_min.y), ImVec2S(size_max.x, size_max.y));
}
auto const panelname { (
title.empty() ?
name() :
title )
+ "###" + name() };
auto const panelname{(title.empty() ? name() : title) + "###" + name()};
if( true == ImGui::Begin( panelname.c_str(), nullptr, flags ) ) {
if (true == ImGui::Begin(panelname.c_str(), nullptr, flags))
{
ImGui::RadioButton("Modify node", (int *)&mode, MODIFY);
ImGui::SameLine();
ImGui::RadioButton("Insert from bank", (int *)&mode, ADD);
ImGui::SameLine();
ImGui::RadioButton("Brush mode", (int *)&mode, BRUSH);
ImGui::SameLine();
ImGui::RadioButton("Copy to bank", (int *)&mode, COPY);
ImGui::SameLine();
if (ImGui::Button("Reload Nodebank"))
@@ -427,6 +420,10 @@ nodebank_panel::render() {
nodebank_reload();
}
if (mode == BRUSH)
{
ImGui::SliderFloat("Spacing", &spacing, 0.1f, 20.0f, "%.1f m");
}
ImGui::PushItemWidth(-1);
ImGui::InputTextWithHint("Search", "Search node bank", m_nodesearch, IM_ARRAYSIZE(m_nodesearch));
@@ -435,17 +432,21 @@ nodebank_panel::render() {
auto idx{0};
auto isvisible{false};
auto const searchfilter{std::string(m_nodesearch)};
for (auto const &entry : m_nodebank) {
if( entry.second->empty() ) {
for (auto const &entry : m_nodebank)
{
if (entry.second->empty())
{
// special case, header indicator
isvisible = ImGui::CollapsingHeader(entry.first.c_str());
}
else {
if( false == isvisible ) {
else
{
if (false == isvisible)
{
continue;
}
if( ( false == searchfilter.empty() )
&& ( false == contains( entry.first, searchfilter ) ) ) {
if ((false == searchfilter.empty()) && (false == contains(entry.first, searchfilter)))
{
continue;
}
auto const label{" " + entry.first + "##" + std::to_string(idx)};
@@ -461,8 +462,8 @@ nodebank_panel::render() {
ImGui::End();
}
void
nodebank_panel::add_template(const std::string &desc) {
void nodebank_panel::add_template(const std::string &desc)
{
auto const label{generate_node_label(desc)};
m_nodebank.push_back({label, std::make_shared<std::string>(desc)});
@@ -472,12 +473,13 @@ nodebank_panel::add_template(const std::string &desc) {
file << label << " " << desc;
}
const std::string *nodebank_panel::get_active_template() {
const std::string *nodebank_panel::get_active_template()
{
return m_selectedtemplate.get();
}
std::string
nodebank_panel::generate_node_label( std::string Input ) const {
std::string nodebank_panel::generate_node_label(std::string Input) const
{
auto tokenizer{cParser(Input)};
tokenizer.getTokens(9, false); // skip leading tokens
@@ -486,10 +488,7 @@ nodebank_panel::generate_node_label( std::string Input ) const {
replace_slashes(model);
erase_extension(model);
replace_slashes(texture);
return (
texture == "none" ?
model :
model + " (" + texture + ")" );
return (texture == "none" ? model : model + " (" + texture + ")");
}
void functions_panel::update(scene::basic_node const *Node)
@@ -509,44 +508,41 @@ void functions_panel::update(scene::basic_node const *Node)
// scenario inspector
auto const *node{Node};
auto const &camera{Global.pCamera};
}
void
functions_panel::render() {
void functions_panel::render()
{
if( false == is_open ) { return; }
if (false == is_open)
{
return;
}
auto flags =
ImGuiWindowFlags_NoFocusOnAppearing
| ImGuiWindowFlags_NoCollapse
| ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 );
auto flags = ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoCollapse | (size.x > 0 ? ImGuiWindowFlags_NoResize : 0);
if( size.x > 0 ) {
if (size.x > 0)
{
ImGui::SetNextWindowSize(ImVec2S(size.x, size.y));
}
if( size_min.x > 0 ) {
if (size_min.x > 0)
{
ImGui::SetNextWindowSizeConstraints(ImVec2S(size_min.x, size_min.y), ImVec2(size_max.x, size_max.y));
}
auto const panelname { (
title.empty() ?
m_name :
title )
+ "###" + m_name };
if( true == ImGui::Begin( panelname.c_str(), nullptr, flags ) ) {
auto const panelname{(title.empty() ? m_name : title) + "###" + m_name};
if (true == ImGui::Begin(panelname.c_str(), nullptr, flags))
{
// header section
ImGui::RadioButton("Random rotation", (int *)&rot_mode, RANDOM);
ImGui::RadioButton("Fixed rotation", (int *)&rot_mode, FIXED);
if(rot_mode == FIXED){
if (rot_mode == FIXED)
{
// ImGui::Checkbox("Get rotation from last object", &rot_from_last);
ImGui::SliderFloat("Rotation Value", &rot_value, 0.0f, 360.0f, "%.1f");
};
ImGui::RadioButton("Default rotation", (int *)&rot_mode, DEFAULT);
for( auto const &line : text_lines ) {
for (auto const &line : text_lines)
{
ImGui::TextColored(ImVec4(line.color.r, line.color.g, line.color.b, line.color.a), line.data.c_str());
}
}

View File

@@ -26,12 +26,11 @@ struct item_properties {
changeable<glm::vec3> rotation {};
};
*/
class itemproperties_panel : public ui_panel {
class itemproperties_panel : public ui_panel
{
public:
itemproperties_panel( std::string const &Name, bool const Isopen )
: ui_panel( Name, Isopen )
{}
itemproperties_panel(std::string const &Name, bool const Isopen) : ui_panel(Name, Isopen) {}
void update(scene::basic_node const *Node);
void render() override;
@@ -48,10 +47,12 @@ private:
std::vector<text_line> m_grouplines;
};
class nodebank_panel : public ui_panel {
class nodebank_panel : public ui_panel
{
public:
enum edit_mode {
enum edit_mode
{
MODIFY,
COPY,
ADD,
@@ -59,6 +60,8 @@ public:
};
edit_mode mode = MODIFY;
float spacing{1.0f};
nodebank_panel(std::string const &Name, bool const Isopen);
void nodebank_reload();
void render() override;
@@ -94,11 +97,9 @@ class functions_panel : public ui_panel
void update(scene::basic_node const *Node);
void render() override;
private:
// methods
// members
scene::basic_node const *m_node{nullptr}; // scene node bound to the panel
scene::group_handle m_grouphandle{null_handle}; // scene group bound to the panel

View File

@@ -25,6 +25,7 @@ layout(location = 1) out vec4 out_motion;
#include <light_common.glsl>
#include <apply_fog.glsl>
#include <tonemapping.glsl>
#include <random.glsl>
uniform sampler2D diffuse;
uniform sampler2D raindropsatlas;
@@ -34,10 +35,6 @@ uniform float specular_intensity = 1.0;
uniform float wobble_strength = 0.002;
uniform float wobble_speed = 30.0;
float hash(vec2 p) {
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}
vec4 getDropTex(float choice, vec2 uv) {
vec2 offset;
if (choice < 0.25) offset = vec2(0.0, 0.0);
@@ -54,13 +51,14 @@ void main() {
if (tex_color.a < 0.01) discard;
vec2 rainCoord = f_coord;
float gridSize = ceil(param[2].x);
const float numDrops = 20000.0;
const float cycleDuration = 4.0;
const float squareMin = 0.0035;
const float squareMax = 0.008;
float gridSize = ceil(param[2].x);
float squareMin = 0.5 / gridSize;
float squareMax = 1.2 / gridSize;
vec2 cell = floor(rainCoord * gridSize);
vec3 dropLayer = vec3(0.0);
@@ -76,37 +74,37 @@ void main() {
float side;
float mixFactor = GetMixFactor(neighborCenter, side);
if(mixFactor < hash(neighborCell + vec2(mix(0., .2137, side), 0.))) {
uint seed = Hash(uvec3(neighborCell, side));
if(mixFactor < RandF(seed)) {
continue;
}
float i = neighborCell.x + neighborCell.y * gridSize;
// Show a percentage of droplets given by rain intensity param
float activationSeed = hash(vec2(i, 0.333 + side));
float activationSeed = RandF(seed);
if (activationSeed > rain_params.x)
continue; // kropla nieaktywna
// Randomly modulate droplet center & size
vec2 dropCenter = (neighborCell + vec2(hash(vec2(i, 0.12 + side)), hash(vec2(i, 0.34 + side)))) / gridSize;
float squareSize = mix(squareMin, squareMax, hash(vec2(i, 0.56 + side)));
vec2 dropCenter = (neighborCell + vec2(RandF(seed), RandF(seed))) / gridSize;
float squareSize = mix(squareMin, squareMax, RandF(seed));
float lifeTime = time + hash(vec2(i, 0.78 + side)) * cycleDuration;
float lifeTime = time + RandF(seed) * cycleDuration;
float phase = fract(lifeTime / cycleDuration);
float active = clamp(1.0 - phase, 0.0, 1.0);
float actiwe = clamp(1.0 - phase, 0.0, 1.0);
// Gravity influence (TODO add vehicle speed & wind here!)
float gravityStart = 0.5;
float gravityPhase = smoothstep(gravityStart, 1.0, phase);
float dropMass = mix(0.3, 1.2, hash(vec2(i, 0.21 + side)));
float dropMass = mix(0.3, 1.2, RandF(seed));
float gravitySpeed = 0.15 * dropMass;
vec2 gravityOffset = vec2(0.0, gravityPhase * gravitySpeed * phase);
// Random wobble
bool hasWobble = (hash(vec2(i, 0.91 + side)) < 0.10);
bool hasWobble = (RandF(seed) < 0.10);
vec2 wobbleOffset = vec2(0.0);
if (hasWobble && gravityPhase > 0.0) {
float intensity = sin(time * wobble_speed + i) * wobble_strength * gravityPhase;
float intensity = sin(time * wobble_speed + RandF(seed) * 100.) * wobble_strength * gravityPhase;
wobbleOffset = vec2(intensity, 0.0);
}
@@ -125,7 +123,7 @@ void main() {
if (mask > 0.001) {
vec2 localUV = (diff + squareSize * 0.5) / squareSize;
float choice = hash(vec2(i, 0.99 + side));
float choice = RandF(seed);
vec4 dropTex = getDropTex(choice, localUV);
float sharpAlpha = smoothstep(0.3, 0.9, dropTex.a);
@@ -139,15 +137,15 @@ void main() {
float sparkle = 0.0;
if (hasWobble) {
sparkle = pow(abs(sin(f_coord.x * 8.0 + time * 2.0 + hash(vec2(i, 0.9 + side)) * 6.2831)), 40.0)
sparkle = pow(abs(sin(f_coord.x * 8.0 + time * 2.0 + RandF(seed) * 6.2831)), 40.0)
* mix(0.2, 1.0, sunFactor);
}
vec3 specularColor = vec3(1.0) * specular_intensity * sparkle;
vec3 dropLit = dropTex.rgb * dynBright + specularColor * sharpAlpha;
dropLayer += dropLit * sharpAlpha * active * blackAlpha * mask;
dropMaskSum += sharpAlpha * active * blackAlpha * mask;
dropLayer += dropLit * sharpAlpha * actiwe * blackAlpha * mask;
dropMaskSum += sharpAlpha * actiwe * blackAlpha * mask;
}
}
}

View File

@@ -55,13 +55,11 @@ void ui_panel::render()
ImGui::SetNextWindowSizeConstraints(ImVec2S(size_min.x, size_min.y), ImVec2S(size_max.x, size_max.y));
auto const panelname{(title.empty() ? m_name : title) + "###" + m_name};
if (ImGui::Begin(panelname.c_str(), &is_open, flags)) {
if (ImGui::Begin(panelname.c_str(), &is_open, flags))
{
render_contents();
popups.remove_if([](const std::unique_ptr<ui::popup> &popup)
{
return popup->render();
});
popups.remove_if([](const std::unique_ptr<ui::popup> &popup) { return popup->render(); });
}
ImGui::End();
@@ -138,7 +136,8 @@ void ui_layer::load_random_background()
if (f.is_regular_file())
images.emplace_back(std::filesystem::relative(f.path(), "textures/").string());
if (!images.empty()) {
if (!images.empty())
{
std::string &selected = images[std::lround(LocalRandom(images.size() - 1))];
set_background(selected);
}
@@ -219,8 +218,7 @@ bool ui_layer::init(GLFWwindow *Window)
// m_imguiio->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
// m_imguiio->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
static const ImWchar ranges[] =
{
static const ImWchar ranges[] = {
0x0020, 0x00FF, // Basic Latin + Latin Supplement
0x0100, 0x017F, // Latin Extended-A
0x2070, 0x2079, // superscript
@@ -269,27 +267,33 @@ bool ui_layer::on_key(int const Key, int const Action)
{
if (Action == GLFW_PRESS)
{
if (Key == GLFW_KEY_PRINT_SCREEN) {
if (Key == GLFW_KEY_PRINT_SCREEN)
{
Application.queue_screenshot();
return true;
}
if (Key == GLFW_KEY_F9) {
if (Key == GLFW_KEY_F9)
{
m_logpanel.is_open = !m_logpanel.is_open;
return true;
}
if (Key == GLFW_KEY_F10) {
if (Key == GLFW_KEY_F10)
{
m_quit_active = !m_quit_active;
return true;
}
if (m_quit_active)
{
if (Key == GLFW_KEY_Y) {
if (Key == GLFW_KEY_Y)
{
Application.queue_quit(false);
return true;
} else if (Key == GLFW_KEY_N) {
}
else if (Key == GLFW_KEY_N)
{
m_quit_active = false;
return true;
}
@@ -315,13 +319,13 @@ void ui_layer::on_window_resize(int w, int h)
panel->on_window_resize(w, h);
}
void ui_layer::update()
{
for (auto *panel : m_panels)
panel->update();
for (auto it = m_ownedpanels.rbegin(); it != m_ownedpanels.rend(); it++) {
for (auto it = m_ownedpanels.rbegin(); it != m_ownedpanels.rend(); it++)
{
(*it)->update();
if (!(*it)->is_open)
m_ownedpanels.erase(std::next(it).base());
@@ -408,7 +412,8 @@ void ui_layer::clear_panels()
void ui_layer::add_owned_panel(ui_panel *Panel)
{
for (auto &panel : m_ownedpanels)
if (panel->name() == Panel->name()) {
if (panel->name() == Panel->name())
{
delete Panel;
return;
}
@@ -443,7 +448,8 @@ void ui_layer::render_menu_contents()
if (ImGui::BeginMenu(STR_C("General")))
{
bool flag = DebugModeFlag;
if (ImGui::MenuItem(STR_C("Debug mode"), nullptr, &flag)) {
if (ImGui::MenuItem(STR_C("Debug mode"), nullptr, &flag))
{
command_relay relay;
relay.post(user_command::debugtoggle, 0.0, 0.0, GLFW_RELEASE, 0);
}
@@ -467,7 +473,8 @@ void ui_layer::render_menu_contents()
if (ImGui::BeginMenu(STR_C("Windows")))
{
ImGui::MenuItem(STR_C("Log"), "F9", &m_logpanel.is_open);
if (DebugModeFlag) {
if (DebugModeFlag)
{
ImGui::MenuItem(STR_C("ImGui Demo"), nullptr, &m_imgui_demo);
bool ret = ImGui::MenuItem(STR_C("Headlight config"), nullptr, GfxRenderer->Debug_Ui_State(std::nullopt));

View File

@@ -82,6 +82,7 @@ public:
// assign texturing hardware unit
static void set_unit( GLint const Textureunit ) { m_textureunit = GL_TEXTURE0 + Textureunit; }
static void shutdown();
virtual void showDebugUI() {};
// potentially processes provided input key. returns: true if the input was processed, false otherwise
virtual bool on_key( int Key, int Action );
// potentially processes provided mouse movement. returns: true if the input was processed, false otherwise

View File

@@ -53,7 +53,8 @@ double Min0R(double x1, double x2)
// shitty replacement for Borland timestamp function
// TODO: replace with something sensible
std::string Now() {
std::string Now()
{
std::time_t timenow = std::time(nullptr);
std::tm tm = *std::localtime(&timenow);
@@ -65,7 +66,8 @@ std::string Now() {
// 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) {
double CompareTime(double t1h, double t1m, double t2h, double t2m)
{
if ((t2h < 0))
return 0;
@@ -80,28 +82,35 @@ double CompareTime(double t1h, double t1m, double t2h, double t2m) {
}
}
bool SetFlag( int &Flag, int const Value ) {
bool SetFlag(int &Flag, int const Value)
{
if( Value > 0 ) {
if( false == TestFlag( Flag, 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 ) {
else if (Value < 0)
{
// Value jest ujemne, czyli zerowanie flagi
return ClearFlag(Flag, -Value);
}
return false;
}
bool ClearFlag( int &Flag, int const Value ) {
bool ClearFlag(int &Flag, int const Value)
{
if( true == TestFlag( Flag, Value ) ) {
if (true == TestFlag(Flag, Value))
{
Flag &= ~Value;
return true;
}
else {
else
{
return false;
}
}
@@ -119,6 +128,31 @@ int RandomInt(int min, int 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)
{
@@ -129,8 +163,7 @@ double LocalRandom(double a, double b)
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*/;
return (Random() < Probability * Threshold * 1.0 / Test) /*im wiekszy Test tym wieksza szansa*/;
else
return false;
}
@@ -138,8 +171,7 @@ bool FuzzyLogic(double Test, double Threshold, double Probability)
bool FuzzyLogicAI(double Test, double Threshold, double Probability)
{
if ((Test > Threshold))
return
(Random() < Probability * Threshold * 1.0 / Test) /*im wiekszy Test tym wieksza szansa*/;
return (Random() < Probability * Threshold * 1.0 / Test) /*im wiekszy Test tym wieksza szansa*/;
else
return false;
}
@@ -162,12 +194,20 @@ std::string DWE(std::string s) /*Delete After Equal sign*/
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; } } );
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;
}
@@ -203,15 +243,14 @@ std::vector<std::string> Split(const std::string &s)
return elems;
}
std::pair<std::string, int>
split_string_and_number( std::string const &Key ) {
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 ) ) };
if (indexstart != std::string::npos)
{
return {Key.substr(0, indexstart), std::stoi(Key.substr(indexstart, indexend - indexstart))};
}
return {Key, 0};
}
@@ -285,23 +324,18 @@ bool string_starts_with(const std::string &string, const std::string &begin)
std::string const fractionlabels[] = {" ", u8"¹", u8"²", u8"³", u8"", u8"", u8"", u8"", u8"", u8""};
std::string to_minutes_str( float const Minutes, bool const Leadingzero, int const Width ) {
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 ) ) ] );
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 stol_def(const std::string &str, const int &DefaultValue)
{
int result{DefaultValue};
std::stringstream converter;
@@ -310,49 +344,44 @@ int stol_def(const std::string &str, const int &DefaultValue) {
return result;
}
std::string ToLower(std::string const &text) {
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 ); } );
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) {
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 ); } );
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 ) {
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 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 ) {
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, u8"Ą" }, { 198, u8"Ć" }, { 202, u8"Ę" }, { 163, u8"Ł" }, { 209, u8"Ń" }, { 211, u8"Ó" }, { 140, u8"Ś" }, { 143, u8"Ź" }, { 175, u8"Ż" },
{ 185, u8"ą" }, { 230, u8"ć" }, { 234, u8"ę" }, { 179, u8"ł" }, { 241, u8"ń" }, { 243, u8"ó" }, { 156, u8"ś" }, { 159, u8"ź" }, { 191, u8"ż" }
};
std::string win1250_to_utf8(const std::string &Input)
{
std::unordered_map<char, std::string> const charmap{{165, u8"Ą"}, {198, u8"Ć"}, {202, u8"Ę"}, {163, u8"Ł"}, {209, u8"Ń"}, {211, u8"Ó"}, {140, u8"Ś"}, {143, u8"Ź"}, {175, u8"Ż"},
{185, u8"ą"}, {230, u8"ć"}, {234, u8"ę"}, {179, u8"ł"}, {241, u8"ń"}, {243, u8"ó"}, {156, u8"ś"}, {159, u8"ź"}, {191, u8"ż"}};
std::string output;
std::unordered_map<char, std::string>::const_iterator lookup;
for( auto &input : Input ) {
for (auto &input : Input)
{
if ((lookup = charmap.find(input)) != charmap.end())
output += lookup->second;
else
@@ -362,27 +391,31 @@ std::string win1250_to_utf8(const std::string &Input) {
}
// Ra: tymczasowe rozwiązanie kwestii zagranicznych (czeskich) napisów
char charsetconversiontable[] =
"E?,?\"_++?%S<STZZ?`'\"\".--??s>stzz"
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) {
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 ) {
for (auto &input : Input)
{
if (input & extendedcharsetbit)
{
input = charsetconversiontable[input ^ extendedcharsetbit];
}
else if( input < space ) {
else if (input < space)
{
input = space;
}
else if( Underscorestospaces && ( input == underscore ) ) {
else if (Underscorestospaces && (input == underscore))
{
input = space;
}
}
@@ -390,35 +423,41 @@ std::string Bezogonkow(std::string Input, bool const Underscorestospaces) {
return Input;
}
template <>
bool
extract_value( bool &Variable, std::string const &Key, std::string const &Input, std::string const &Default ) {
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() ) {
if (false == value.empty())
{
// set the specified variable to retrieved value
Variable = (ToLower(value) == "yes");
return true; // located the variable
}
else {
else
{
// set the variable to provided default value
if( false == Default.empty() ) {
if (false == Default.empty())
{
Variable = (ToLower(Default) == "yes");
}
return false; // couldn't locate the variable in provided input
}
}
bool FileExists( std::string const &Filename ) {
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 ) {
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 ) ) {
for (auto const &name : Names)
{
for (auto const &extension : Extensions)
{
if (FileExists(name + extension))
{
return {name, extension};
}
}
@@ -428,8 +467,8 @@ FileExists( std::vector<std::string> const &Names, std::vector<std::string> cons
}
// returns time of last modification for specified file
std::time_t
last_modified( std::string const &Filename ) {
std::time_t last_modified(std::string const &Filename)
{
std::string fn = Filename;
struct stat filestat;
if (::stat(fn.c_str(), &filestat) == 0)
@@ -439,14 +478,18 @@ 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 ) {
bool erase_extension(std::string &Filename)
{
auto const extensionpos{Filename.rfind('.')};
if( extensionpos == std::string::npos ) { return false; }
if (extensionpos == std::string::npos)
{
return false;
}
if( extensionpos != Filename.rfind( ".." ) + 1 ) {
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;
@@ -454,70 +497,62 @@ erase_extension( std::string &Filename ) {
return false;
}
void
erase_leading_slashes( std::string &Filename ) {
void erase_leading_slashes(std::string &Filename)
{
while( Filename[ 0 ] == '/' ) {
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 ) {
void replace_slashes(std::string &Filename)
{
std::replace(
std::begin( Filename ), std::end( 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 ) {
std::string substr_path(std::string const &Filename)
{
return (
Filename.rfind( '/' ) != std::string::npos ?
Filename.substr( 0, Filename.rfind( '/' ) + 1 ) :
"" );
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 ) {
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 ) );
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 ) {
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 ) );
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 ) {
bool starts_with(std::string_view const String, std::string_view Prefix)
{
return ( String.size() >= Prefix.size() )
&& ( 0 == String.compare( 0, Prefix.size(), 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 ) {
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 ) {
bool contains(std::string_view const String, char Character)
{
return (String.find(Character) != std::string::npos);
}
@@ -525,39 +560,40 @@ contains( std::string_view const String, char Character ) {
// 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 ) {
glm::dvec3 LoadPoint(cParser &Input)
{
// pobranie współrzędnych punktu
glm::dvec3 point;
Input.getTokens(3);
Input
>> point.x
>> point.y
>> point.z;
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 ) {
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 != "[" ) {
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 != "]" ) ) {
while (((token = deserialize_random_set(Input, Break)) != "") && (token != "]"))
{
tokens.emplace_back(token);
}
if( false == tokens.empty() ) {
if (false == tokens.empty())
{
std::shuffle(std::begin(tokens), std::end(tokens), Global.random_engine);
return tokens.front();
}
else {
else
{
// shouldn't ever get here but, eh
return "";
}

View File

@@ -61,6 +61,7 @@ inline long Round(double const 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()
@@ -99,12 +100,12 @@ std::string Now();
double CompareTime(double t1h, double t1m, double t2h, double t2m);
/*funkcje logiczne*/
inline
bool TestFlag( int const Flag, int const Value ) {
inline bool TestFlag(int const Flag, int const Value)
{
return ((Flag & Value) == Value);
}
inline
bool TestFlagAny( int const Flag, int const Value ) {
inline bool TestFlagAny(int const Flag, int const Value)
{
return ((Flag & Value) != 0);
}
bool SetFlag(int &Flag, int const Value);
@@ -133,18 +134,18 @@ 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) {
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 ) {
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 ) {
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);
}
@@ -163,17 +164,18 @@ std::string Bezogonkow( std::string Input, bool const Underscorestospaces = fals
std::string win1250_to_utf8(const std::string &input);
inline
std::string
extract_value( std::string const &Key, std::string const &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 ) {
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 ) {
if (lookup != std::string::npos)
{
// trim everything past the value
value.erase(lookup);
}
@@ -181,21 +183,23 @@ extract_value( std::string const &Key, std::string const &Input ) {
return value;
}
template <typename Type_>
bool
extract_value( Type_ &Variable, std::string const &Key, std::string const &Input, std::string const &Default ) {
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() ) {
if (false == value.empty())
{
// set the specified variable to retrieved value
std::stringstream converter;
converter << value;
converter >> Variable;
return true; // located the variable
}
else {
else
{
// set the variable to provided default value
if( false == Default.empty() ) {
if (false == Default.empty())
{
std::stringstream converter;
converter << Default;
converter >> Variable;
@@ -204,9 +208,7 @@ extract_value( Type_ &Variable, std::string const &Key, std::string const &Input
}
}
template <>
bool
extract_value( bool &Variable, std::string const &Key, std::string const &Input, std::string const &Default );
template <> bool extract_value(bool &Variable, std::string const &Key, std::string const &Input, std::string const &Default);
bool FileExists(std::string const &Filename);
@@ -216,16 +218,13 @@ std::pair<std::string, std::string> FileExists( std::vector<std::string> const &
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 );
bool erase_extension(std::string &Filename);
// potentially erase leading slashes from provided file path
void
erase_leading_slashes( std::string &Filename );
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 );
void replace_slashes(std::string &Filename);
// returns potential path part from provided file name
std::string substr_path(std::string const &Filename);
@@ -241,97 +240,96 @@ bool starts_with( std::string_view String, std::string_view Prefix );
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 ) {
template <typename Type_> void SafeDelete(Type_ &Pointer)
{
delete Pointer;
Pointer = nullptr;
}
template <typename Type_>
void SafeDeleteArray( Type_ &Pointer ) {
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 ) {
template <typename Type_> Type_ is_equal(Type_ const &Left, Type_ const &Right, Type_ const Epsilon = 1e-5)
{
if( Epsilon != 0 ) {
if (Epsilon != 0)
{
return glm::epsilonEqual(Left, Right, Epsilon);
}
else {
else
{
return (Left == Right);
}
}
template <typename Type_>
Type_
clamp( Type_ const Value, Type_ const Min, Type_ const Max ) {
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; }
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) ) {
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;
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) ) {
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 ) ) {
while ((p2size <= Max) && (p2size <= Value))
{
size = p2size;
p2size = p2size << 1;
}
return size;
}
template <typename Type_>
Type_
quantize( Type_ const Value, Type_ const Step ) {
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 ) {
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() ) );
if (Left == Right)
{
return Left;
}
template <typename Type_>
Type_
interpolate( Type_ const &First, Type_ const &Second, float const Factor ) {
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 ) {
template <typename Type_> Type_ interpolate(Type_ const &First, Type_ const &Second, double const Factor)
{
return static_cast<Type_>((First * (1.0 - Factor)) + (Second * Factor));
}
@@ -345,43 +343,43 @@ template <typename Type_> Type_ smoothInterpolate(Type_ const &First, Type_ cons
}
// tests whether provided points form a degenerate triangle
template <typename VecType_>
bool
degenerate( VecType_ const &Vertex1, VecType_ const &Vertex2, VecType_ const &Vertex3 ) {
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 ) {
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 ) {
std::for_each(First, Last,
[&](typename Iterator_::value_type &point)
{
Mincorner = glm::min(Mincorner, VecType_{point});
Maxcorner = glm::max( Maxcorner, 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 ) {
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 ) {
if (c1 <= 0.0)
{
return 0.0;
}
auto const c2 = glm::dot(v, v);
if( c2 <= c1 ) {
if (c2 <= c1)
{
return 1.0;
}
return c1 / c2;
@@ -390,30 +388,30 @@ nearest_segment_point( VecType_ const &Segmentstart, VecType_ const &Segmentend,
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 ;" );
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 ) {
template <typename MapType_> void deserialize_map(MapType_ &Map, cParser &Input)
{
while( Input.ok() && !Input.eof() ) {
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 {
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 {
template <typename Type_> struct lockable
{
Type_ data;
std::mutex mutex;
@@ -421,40 +419,36 @@ struct lockable {
// 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 {
class condition_variable
{
public:
// methods
void
wait() {
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 ) {
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() {
m_condition.wait_for(lock, Time, [this]() { return m_spurious == false; });
}
void notify_one()
{
spurious(false);
m_condition.notify_one();
}
void
notify_all() {
void notify_all()
{
spurious(false);
m_condition.notify_all();
}
void
spurious( bool const Spurious ) {
void spurious(bool const Spurious)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_spurious = Spurious; }
m_spurious = Spurious;
}
private:
// members
@@ -463,6 +457,6 @@ private:
bool m_spurious{true};
};
} // threading
} // namespace threading
//---------------------------------------------------------------------------