mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
534 lines
14 KiB
C++
534 lines
14 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "application.h"
|
|
|
|
#include "Globals.h"
|
|
#include "keyboardinput.h"
|
|
#include "mouseinput.h"
|
|
#include "gamepadinput.h"
|
|
#include "Console.h"
|
|
#include "simulation.h"
|
|
#include "World.h"
|
|
#include "PyInt.h"
|
|
#include "sceneeditor.h"
|
|
#include "renderer.h"
|
|
#include "uilayer.h"
|
|
#include "Logs.h"
|
|
#include "screenshot.h"
|
|
#include "motiontelemetry.h"
|
|
|
|
#pragma comment (lib, "glu32.lib")
|
|
#pragma comment (lib, "dsound.lib")
|
|
#pragma comment (lib, "winmm.lib")
|
|
#pragma comment (lib, "setupapi.lib")
|
|
#pragma comment (lib, "dbghelp.lib")
|
|
#pragma comment (lib, "version.lib")
|
|
|
|
#ifdef __linux__
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
eu07_application Application;
|
|
screenshot_manager screenshot_man;
|
|
|
|
namespace input {
|
|
|
|
gamepad_input Gamepad;
|
|
mouse_input Mouse;
|
|
glm::dvec2 mouse_pickmodepos; // stores last mouse position in control picking mode
|
|
keyboard_input Keyboard;
|
|
std::unique_ptr<uart_input> uart;
|
|
user_command command; // currently issued control command, if any
|
|
std::unique_ptr<motiontelemetry> motiontelemetry;
|
|
#ifdef _WIN32
|
|
Console console;
|
|
#endif
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
extern "C"
|
|
{
|
|
GLFWAPI HWND glfwGetWin32Window( GLFWwindow* window );
|
|
}
|
|
|
|
LONG CALLBACK unhandled_handler( ::EXCEPTION_POINTERS* e );
|
|
LRESULT APIENTRY WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
|
|
extern HWND Hwnd;
|
|
extern WNDPROC BaseWindowProc;
|
|
#endif
|
|
|
|
// user input callbacks
|
|
|
|
void window_resize_callback( GLFWwindow *window, int w, int h ) {
|
|
// NOTE: we have two variables which basically do the same thing as we don't have dynamic fullscreen toggle
|
|
// TBD, TODO: merge them?
|
|
Global.iWindowWidth = w;
|
|
Global.iWindowHeight = h;
|
|
Global.fDistanceFactor = std::max( 0.5f, h / 768.0f ); // not sure if this is really something we want to use
|
|
glViewport( 0, 0, w, h );
|
|
}
|
|
|
|
void cursor_pos_callback( GLFWwindow *window, double x, double y ) {
|
|
if( false == Global.ControlPicking ) {
|
|
glfwSetCursorPos( window, 0, 0 );
|
|
}
|
|
|
|
Application.m_cursor_pos.x = x;
|
|
Application.m_cursor_pos.y = y;
|
|
|
|
// give the potential event recipient a shot at it, in the virtual z order
|
|
if( true == scene::Editor.on_mouse_move( x, y ) ) { return; }
|
|
input::Mouse.move( x, y );
|
|
}
|
|
|
|
void mouse_button_callback( GLFWwindow* window, int button, int action, int mods )
|
|
{
|
|
if (UILayer.mouse_button_callback(button, action, mods))
|
|
return;
|
|
|
|
if( ( button != GLFW_MOUSE_BUTTON_LEFT )
|
|
&& ( button != GLFW_MOUSE_BUTTON_RIGHT ) ) {
|
|
// we don't care about other mouse buttons at the moment
|
|
return;
|
|
}
|
|
// give the potential event recipient a shot at it, in the virtual z order
|
|
if( true == scene::Editor.on_mouse_button( button, action ) ) { return; }
|
|
input::Mouse.button( button, action );
|
|
}
|
|
|
|
void key_callback( GLFWwindow *window, int key, int scancode, int action, int mods )
|
|
{
|
|
if (UILayer.key_callback(key, scancode, action, mods))
|
|
return;
|
|
|
|
Global.shiftState = ( mods & GLFW_MOD_SHIFT ) ? true : false;
|
|
Global.ctrlState = ( mods & GLFW_MOD_CONTROL ) ? true : false;
|
|
Global.altState = ( mods & GLFW_MOD_ALT ) ? true : false;
|
|
|
|
// give the ui first shot at the input processing...
|
|
if( true == UILayer.on_key( key, action ) ) { return; }
|
|
if( true == scene::Editor.on_key( key, action ) ) { return; }
|
|
// ...if the input is left untouched, pass it on
|
|
input::Keyboard.key( key, action );
|
|
|
|
if( ( true == Global.InputMouse )
|
|
&& ( ( key == GLFW_KEY_LEFT_ALT )
|
|
|| ( key == GLFW_KEY_RIGHT_ALT ) ) ) {
|
|
// if the alt key was pressed toggle control picking mode and set matching cursor behaviour
|
|
if( action == GLFW_RELEASE ) {
|
|
|
|
if( Global.ControlPicking ) {
|
|
// switch off
|
|
Application.get_cursor_pos( input::mouse_pickmodepos.x, input::mouse_pickmodepos.y );
|
|
Application.set_cursor( GLFW_CURSOR_DISABLED );
|
|
Application.set_cursor_pos( 0, 0 );
|
|
}
|
|
else {
|
|
// enter picking mode
|
|
Application.set_cursor_pos( input::mouse_pickmodepos.x, input::mouse_pickmodepos.y );
|
|
Application.set_cursor( GLFW_CURSOR_NORMAL );
|
|
}
|
|
// actually toggle the mode
|
|
Global.ControlPicking = !Global.ControlPicking;
|
|
}
|
|
}
|
|
|
|
if( ( key == GLFW_KEY_LEFT_SHIFT )
|
|
|| ( key == GLFW_KEY_LEFT_CONTROL )
|
|
|| ( key == GLFW_KEY_LEFT_ALT )
|
|
|| ( key == GLFW_KEY_RIGHT_SHIFT )
|
|
|| ( key == GLFW_KEY_RIGHT_CONTROL )
|
|
|| ( key == GLFW_KEY_RIGHT_ALT ) ) {
|
|
// don't bother passing these
|
|
return;
|
|
}
|
|
|
|
if( action == GLFW_PRESS || action == GLFW_REPEAT ) {
|
|
|
|
World.OnKeyDown( key );
|
|
|
|
switch( key ) {
|
|
case GLFW_KEY_PRINT_SCREEN: {
|
|
Application.queue_screenshot();
|
|
break;
|
|
}
|
|
default: { break; }
|
|
}
|
|
}
|
|
}
|
|
|
|
void char_callback(GLFWwindow *window, unsigned int c)
|
|
{
|
|
if (UILayer.char_callback(c))
|
|
return;
|
|
}
|
|
|
|
void focus_callback( GLFWwindow *window, int focus ) {
|
|
if( Global.bInactivePause ) // jeśli ma być pauzowanie okna w tle
|
|
if( focus )
|
|
Global.iPause &= ~4; // odpauzowanie, gdy jest na pierwszym planie
|
|
else
|
|
Global.iPause |= 4; // włączenie pauzy, gdy nieaktywy
|
|
}
|
|
|
|
void scroll_callback( GLFWwindow* window, double xoffset, double yoffset )
|
|
{
|
|
if (UILayer.scroll_callback(xoffset, yoffset))
|
|
return;
|
|
|
|
if( Global.ctrlState ) {
|
|
// ctrl + scroll wheel adjusts fov in debug mode
|
|
Global.FieldOfView = clamp( static_cast<float>( Global.FieldOfView - yoffset ), 15.0f, 75.0f );
|
|
}
|
|
}
|
|
|
|
// public:
|
|
|
|
void eu07_application::queue_screenshot()
|
|
{
|
|
m_screenshot_queued = true;
|
|
}
|
|
|
|
int
|
|
eu07_application::init( int Argc, char *Argv[] ) {
|
|
|
|
int result { 0 };
|
|
|
|
init_debug();
|
|
init_files();
|
|
if( ( result = init_settings( Argc, Argv ) ) != 0 ) {
|
|
return result;
|
|
}
|
|
if( ( result = init_glfw() ) != 0 ) {
|
|
return result;
|
|
}
|
|
init_callbacks();
|
|
if( ( result = init_gfx() ) != 0 ) {
|
|
return result;
|
|
}
|
|
if( ( result = init_audio() ) != 0 ) {
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int
|
|
eu07_application::run()
|
|
{
|
|
// TODO: move input sources and their initializations to the application mode member
|
|
input::Keyboard.init();
|
|
input::Mouse.init();
|
|
input::Gamepad.init();
|
|
if( true == Global.uart_conf.enable ) {
|
|
input::uart = std::make_unique<uart_input>();
|
|
input::uart->init();
|
|
}
|
|
if (Global.motiontelemetry_conf.enable)
|
|
input::motiontelemetry = std::make_unique<motiontelemetry>();
|
|
#ifdef _WIN32
|
|
Console::On(); // włączenie konsoli
|
|
#endif
|
|
|
|
Global.pWorld = &World; // Ra: wskaźnik potrzebny do usuwania pojazdów
|
|
|
|
if( false == World.Init( m_window ) ) {
|
|
ErrorLog( "Bad init: simulation setup failed" );
|
|
return -1;
|
|
}
|
|
|
|
if( Global.iConvertModels < 0 ) {
|
|
// generate binary files for all 3d models
|
|
Global.iConvertModels = -Global.iConvertModels;
|
|
World.CreateE3D( szModelPath ); // rekurencyjne przeglądanie katalogów
|
|
World.CreateE3D( szDynamicPath, true );
|
|
// auto-close when you're done
|
|
WriteLog( "Binary 3d model generation completed" );
|
|
return 0;
|
|
}
|
|
|
|
// main application loop
|
|
// TODO: split into parts and delegate these to application mode member
|
|
while (!glfwWindowShouldClose(m_window))
|
|
{
|
|
Timer::subsystem.mainloop_total.start();
|
|
|
|
glfwPollEvents();
|
|
|
|
if (!World.Update())
|
|
break;
|
|
|
|
if (!GfxRenderer.Render())
|
|
break;
|
|
|
|
if (m_screenshot_queued)
|
|
{
|
|
m_screenshot_queued = false;
|
|
screenshot_man.make_screenshot();
|
|
}
|
|
|
|
if (input::motiontelemetry)
|
|
input::motiontelemetry->update();
|
|
input::Keyboard.poll();
|
|
simulation::Commands.update();
|
|
if (Global.InputMouse)
|
|
input::Mouse.poll();
|
|
if (Global.InputGamepad)
|
|
input::Gamepad.poll();
|
|
if (input::uart)
|
|
input::uart->poll();
|
|
|
|
// TODO: wrap current command in object, include other input sources
|
|
input::command = (
|
|
input::Mouse.command() != user_command::none ?
|
|
input::Mouse.command() :
|
|
input::Keyboard.command() );
|
|
|
|
GfxRenderer.SwapBuffers();
|
|
|
|
Timer::subsystem.mainloop_total.stop();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
eu07_application::exit() {
|
|
|
|
#ifdef _WIN32
|
|
Console::Off(); // wyłączenie konsoli (komunikacji zwrotnej)
|
|
#endif
|
|
SafeDelete( simulation::Region );
|
|
|
|
glfwDestroyWindow( m_window );
|
|
glfwTerminate();
|
|
|
|
TPythonInterpreter::killInstance();
|
|
}
|
|
|
|
void
|
|
eu07_application::set_cursor( int const Mode ) {
|
|
|
|
UILayer.set_cursor( Mode );
|
|
}
|
|
|
|
void
|
|
eu07_application::set_cursor_pos( double const X, double const Y ) {
|
|
|
|
if( m_window != nullptr ) {
|
|
glfwSetCursorPos( m_window, X, Y );
|
|
}
|
|
}
|
|
|
|
void
|
|
eu07_application::get_cursor_pos( double &X, double &Y ) const {
|
|
X = m_cursor_pos.x;
|
|
Y = m_cursor_pos.y;
|
|
}
|
|
|
|
glm::dvec2 eu07_application::get_cursor_pos() const
|
|
{
|
|
return m_cursor_pos;
|
|
}
|
|
|
|
// private:
|
|
|
|
void
|
|
eu07_application::init_debug() {
|
|
|
|
#if defined(_MSC_VER) && defined (_DEBUG)
|
|
// memory leaks
|
|
_CrtSetDbgFlag( _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ) | _CRTDBG_LEAK_CHECK_DF );
|
|
// floating point operation errors
|
|
auto state { _clearfp() };
|
|
state = _control87( 0, 0 );
|
|
// this will turn on FPE for #IND and zerodiv
|
|
state = _control87( state & ~( _EM_ZERODIVIDE | _EM_INVALID ), _MCW_EM );
|
|
#endif
|
|
#ifdef _WIN32
|
|
::SetUnhandledExceptionFilter( unhandled_handler );
|
|
#endif
|
|
}
|
|
|
|
void
|
|
eu07_application::init_files() {
|
|
|
|
#ifdef _WIN32
|
|
DeleteFile( "log.txt" );
|
|
DeleteFile( "errors.txt" );
|
|
CreateDirectory( "logs", NULL );
|
|
#elif __linux__
|
|
unlink("log.txt");
|
|
unlink("errors.txt");
|
|
mkdir("logs", 0664);
|
|
#endif
|
|
}
|
|
|
|
int
|
|
eu07_application::init_settings( int Argc, char *Argv[] ) {
|
|
|
|
Global.LoadIniFile( "eu07.ini" );
|
|
#ifdef _WIN32
|
|
if( ( Global.iWriteLogEnabled & 2 ) != 0 ) {
|
|
// show output console if requested
|
|
AllocConsole();
|
|
}
|
|
#endif
|
|
|
|
Global.asVersion = VERSION_INFO;
|
|
|
|
// process command line arguments
|
|
for( int i = 1; i < Argc; ++i ) {
|
|
|
|
std::string token { Argv[ i ] };
|
|
|
|
if( token == "-e3d" ) {
|
|
Global.iConvertModels = (
|
|
Global.iConvertModels > 0 ?
|
|
-Global.iConvertModels :
|
|
-7 ); // z optymalizacją, bananami i prawidłowym Opacity
|
|
}
|
|
else if( token == "-s" ) {
|
|
if( i + 1 < Argc ) {
|
|
Global.SceneryFile = ToLower( Argv[ ++i ] );
|
|
}
|
|
}
|
|
else if( token == "-v" ) {
|
|
if( i + 1 < Argc ) {
|
|
Global.asHumanCtrlVehicle = ToLower( Argv[ ++i ] );
|
|
}
|
|
}
|
|
else {
|
|
std::cout
|
|
<< "usage: " << std::string( Argv[ 0 ] )
|
|
<< " [-s sceneryfilepath]"
|
|
<< " [-v vehiclename]"
|
|
<< " [-e3d]"
|
|
<< std::endl;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
eu07_application::init_glfw()
|
|
{
|
|
if( glfwInit() == GLFW_FALSE ) {
|
|
ErrorLog( "Bad init: failed to initialize glfw" );
|
|
return -1;
|
|
}
|
|
|
|
// match requested video mode to current to allow for
|
|
// fullwindow creation when resolution is the same
|
|
auto *monitor { glfwGetPrimaryMonitor() };
|
|
auto const *vmode { glfwGetVideoMode( monitor ) };
|
|
|
|
glfwWindowHint( GLFW_RED_BITS, vmode->redBits );
|
|
glfwWindowHint( GLFW_GREEN_BITS, vmode->greenBits );
|
|
glfwWindowHint( GLFW_BLUE_BITS, vmode->blueBits );
|
|
glfwWindowHint( GLFW_REFRESH_RATE, vmode->refreshRate );
|
|
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
|
|
glfwWindowHint( GLFW_AUTO_ICONIFY, GLFW_FALSE );
|
|
if( Global.iMultisampling > 0 ) {
|
|
glfwWindowHint( GLFW_SAMPLES, 1 << Global.iMultisampling );
|
|
}
|
|
|
|
auto *window {
|
|
glfwCreateWindow(
|
|
Global.iWindowWidth,
|
|
Global.iWindowHeight,
|
|
Global.AppName.c_str(),
|
|
( Global.bFullScreen ?
|
|
monitor :
|
|
nullptr ),
|
|
nullptr ) };
|
|
|
|
if( window == nullptr ) {
|
|
ErrorLog( "Bad init: failed to create glfw window" );
|
|
return -1;
|
|
}
|
|
|
|
glfwMakeContextCurrent( window );
|
|
glfwSwapInterval( Global.VSync ? 1 : 0 ); //vsync
|
|
|
|
#ifdef _WIN32
|
|
// setup wrapper for base glfw window proc, to handle copydata messages
|
|
Hwnd = glfwGetWin32Window( window );
|
|
BaseWindowProc = ( WNDPROC )::SetWindowLongPtr( Hwnd, GWLP_WNDPROC, (LONG_PTR)WndProc );
|
|
// switch off the topmost flag
|
|
::SetWindowPos( Hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
|
|
#endif
|
|
|
|
if (Global.captureonstart)
|
|
{
|
|
Global.ControlPicking = false;
|
|
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
|
}
|
|
else
|
|
Global.ControlPicking = true;
|
|
|
|
// TBD, TODO: move the global pointer to a more appropriate place
|
|
m_window = window;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
eu07_application::init_callbacks() {
|
|
|
|
glfwSetFramebufferSizeCallback( m_window, window_resize_callback );
|
|
glfwSetCursorPosCallback( m_window, cursor_pos_callback );
|
|
glfwSetMouseButtonCallback( m_window, mouse_button_callback );
|
|
glfwSetKeyCallback( m_window, key_callback );
|
|
glfwSetScrollCallback( m_window, scroll_callback );
|
|
glfwSetCharCallback(m_window, char_callback);
|
|
glfwSetWindowFocusCallback( m_window, focus_callback );
|
|
{
|
|
int width, height;
|
|
glfwGetFramebufferSize( m_window, &width, &height );
|
|
window_resize_callback( m_window, width, height );
|
|
}
|
|
}
|
|
|
|
int
|
|
eu07_application::init_gfx() {
|
|
|
|
if( glewInit() != GLEW_OK ) {
|
|
ErrorLog( "Bad init: failed to initialize glew" );
|
|
return -1;
|
|
}
|
|
|
|
if (!UILayer.init(m_window))
|
|
return -1;
|
|
|
|
if (!GfxRenderer.Init(m_window))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
eu07_application::init_audio() {
|
|
|
|
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;
|
|
}
|