diff --git a/.github/workflows/pragma-linux-ci.yml b/.github/workflows/pragma-linux-ci.yml index ee76c0158..bbff97d8a 100644 --- a/.github/workflows/pragma-linux-ci.yml +++ b/.github/workflows/pragma-linux-ci.yml @@ -43,7 +43,7 @@ jobs: uses: ./pragma/github_actions/build id: build-pragma with: - build-args: "--with-pfm --with-all-pfm-modules --with-vr --with-networking --with-lua-debugger=0" + build-args: "--with-pfm --with-all-pfm-modules --with-vr --with-networking --with-lua-debugger=0 --with-swiftshader" - name: Create Release Archive shell: bash diff --git a/.github/workflows/pragma-windows-ci.yml b/.github/workflows/pragma-windows-ci.yml index 99807aad4..14b17fdd8 100644 --- a/.github/workflows/pragma-windows-ci.yml +++ b/.github/workflows/pragma-windows-ci.yml @@ -56,7 +56,7 @@ jobs: uses: ./pragma/github_actions/build id: build-pragma with: - build-args: "--with-pfm --with-all-pfm-modules --with-vr --with-networking --with-lua-debugger=0 --cmake-arg=\"-DCMAKE_SYSTEM_VERSION=10.0.22621.0\" --vcvars \"C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars64.bat\"" + build-args: "--with-pfm --with-all-pfm-modules --with-vr --with-networking --with-lua-debugger=0 --with-swiftshader --cmake-arg=\"-DCMAKE_SYSTEM_VERSION=10.0.22621.0\" --vcvars \"C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars64.bat\"" - name: Create Release Archive shell: bash diff --git a/CMakeLists.txt b/CMakeLists.txt index ca2ce91ce..008324e7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1112,7 +1112,6 @@ add_dependencies(pragma_updater sharedutils-static) add_dependencies(pragma_server server) add_dependencies(pragma client server) -add_dependencies(pragma pragma_server) add_dependencies(pragma pragma_updater) set_property(TARGET pragma PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}") @@ -1167,6 +1166,9 @@ endfunction(pragma_install) pragma_install(pragma ".") pragma_install(pragma_server ".") +if(WIN32) + pragma_install(pragma_console ".") +endif() pragma_install(alsoundsystem "${BINARY_OUTPUT_DIR}") pragma_install(client "${BINARY_OUTPUT_DIR}") @@ -1194,8 +1196,12 @@ pragma_install(wgui "${BINARY_OUTPUT_DIR}") pragma_install(util_unicode "${BINARY_OUTPUT_DIR}") message("Custom install targets: ${PRAGMA_INSTALL_CUSTOM_TARGETS}") +set(PRAGMA_INSTALL_DEPENDENCIES pragma pragma_server iclient iserver udm_convert ${PRAGMA_INSTALL_CUSTOM_TARGETS}) +if(WIN32) + list(APPEND PRAGMA_INSTALL_DEPENDENCIES pragma_console) +endif() add_custom_target(pragma-install - DEPENDS pragma iclient iserver udm_convert ${PRAGMA_INSTALL_CUSTOM_TARGETS} + DEPENDS ${PRAGMA_DEPENDENCIES} COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=${PRAGMA_INSTALL_COMPONENT} -DBUILD_TYPE=${CONFIG_BUILD_TYPE} diff --git a/README.md b/README.md index 799df99f3..f2feb43ef 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Windows](https://github.com/Silverlan/pragma/actions/workflows/pragma-windows-ci.yml/badge.svg?branch=main)](https://github.com/Silverlan/pragma/actions/workflows/pragma-windows-ci.yml) [![Build Linux](https://github.com/Silverlan/pragma/actions/workflows/pragma-linux-ci.yml/badge.svg?branch=main)](https://github.com/Silverlan/pragma/actions/workflows/pragma-linux-ci.yml) +[![Build Windows](https://github.com/Silverlan/pragma/actions/workflows/pragma-windows-ci.yml/badge.svg?branch=main)](https://github.com/Silverlan/pragma/actions/workflows/pragma-windows-ci.yml) [![Build Linux](https://github.com/Silverlan/pragma/actions/workflows/pragma-linux-ci.yml/badge.svg?branch=main)](https://github.com/Silverlan/pragma/actions/workflows/pragma-linux-ci.yml) [![License](https://img.shields.io/github/license/Silverlan/pragma)](#license) [![CodeFactor](https://img.shields.io/codefactor/grade/github/Silverlan/pragma)](https://www.codefactor.io/repository/github/Silverlan/pragma) Pragma Logo @@ -98,7 +98,7 @@ Running the build-script with the arguments above will build and install Pragma | Parameter | Description | Default | | --------------------------------------- | -------------------------------------------------------------------------------------------- | ---------------- | | `--help` | Display this help | | -| `--generator ` | The generator to use. | Windows: `Visual Studio 17 2022`
Linux: `Unix Makefiles` | +| `--generator ` | The generator to use. | Windows: `Visual Studio 17 2022`
Linux: `Ninja Multi-Config` | | `--c-compiler` | [Linux only] The C-compiler to use. | `clang-18` | | `--cxx-compiler` | [Linux only] The C++-compiler to use. | `clang++-18` | | `--no-sudo` | [Linux only] Will not run sudo commands. System packages will have to be installed manually. | `0` | @@ -111,6 +111,8 @@ Running the build-script with the arguments above will build and install Pragma | `--with-vr <1/0>` | Include Virtual Reality support. | `0` | | `--with-networking <1/0>` | Include networking module(s) for multiplayer support. | `0` | | `--with-lua-debugger <1/0>` | Include Lua-debugger support. | `0` | +| `--with-swiftshader <1/0>` | Include SwiftShader support for CPU-only rendering. | `0` | +| `--build-swiftshader <1/0>` | Builds SwiftShader from source instead of downloading prebuilt binaries. | `0` | | `--build-cycles <1/0>` | Build the Cycles library (otherwise uses pre-built binaries). Requires --with-all-pfm-modules| `0` | | `--build <1/0>` | Build Pragma after configurating and generating build files. | `1` | | `--build-all <1/0>` | Build all dependencies instead of downloading prebuilt binaries where available. | `0` | diff --git a/build_scripts/build.py b/build_scripts/build.py index 50ec7d956..6ba57f542 100644 --- a/build_scripts/build.py +++ b/build_scripts/build.py @@ -19,8 +19,6 @@ else: defaultGenerator = "Visual Studio 17 2022" parser.add_argument('--generator', help='The generator to use.', default=defaultGenerator) -if platform == "win32": - parser.add_argument('--vcvars', help='Path to vcvars64.bat.', default="\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat\"") parser.add_argument("--with-essential-client-modules", type=str2bool, nargs='?', const=True, default=True, help="Include essential modules required to run Pragma.") parser.add_argument("--with-common-modules", type=str2bool, nargs='?', const=True, default=True, help="Include non-essential but commonly used modules (e.g. audio and physics modules).") parser.add_argument("--with-pfm", type=str2bool, nargs='?', const=True, default=False, help="Include the Pragma Filmmaker.") @@ -30,9 +28,11 @@ parser.add_argument("--with-networking", type=str2bool, nargs='?', const=True, default=False, help="Include networking module(s) for multiplayer support.") parser.add_argument("--with-common-entities", type=str2bool, nargs='?', const=True, default=True, help="Include addons with support for common entity types.") parser.add_argument("--with-lua-debugger", type=str2bool, nargs='?', const=True, default=False, help="Include Lua-debugger support.") +parser.add_argument("--with-swiftshader", type=str2bool, nargs='?', const=True, default=False, help="Include SwiftShader support for CPU-only rendering.") parser.add_argument('--vtune-include-path', help='The include path to the VTune profiler (required for CPU profiling).', default='') parser.add_argument('--vtune-library-path', help='The path to the "libittnotify" library of the VTune profiler (required for CPU profiling).', default='') parser.add_argument("--build", type=str2bool, nargs='?', const=True, default=True, help="Build Pragma after configurating and generating build files.") +parser.add_argument("--build-swiftshader", type=str2bool, nargs='?', const=True, default=False, help="Builds SwiftShader from source instead of downloading prebuilt binaries.") parser.add_argument("--build-all", type=str2bool, nargs='?', const=True, default=False, help="Build all dependencies instead of downloading prebuilt binaries where available. Enabling this may significantly increase the disk space requirement and build time.") parser.add_argument('--build-config', help='The build configuration to use.', default='RelWithDebInfo') parser.add_argument('--build-directory', help='Directory to write the build files to. Can be relative or absolute.', default='build') @@ -87,8 +87,6 @@ no_sudo = args["no_sudo"] no_confirm = args["no_confirm"] generator = args["generator"] -#if platform == "win32": -# vcvars = args["vcvars with_essential_client_modules = args["with_essential_client_modules"] with_common_modules = args["with_common_modules"] with_pfm = args["with_pfm"] @@ -98,6 +96,8 @@ with_networking = args["with_networking"] with_common_entities = args["with_common_entities"] with_lua_debugger = args["with_lua_debugger"] +with_swiftshader = args["with_swiftshader"] +build_swiftshader = args["build_swiftshader"] vtune_include_path = args["vtune_include_path"] vtune_library_path = args["vtune_library_path"] build = args["build"] @@ -122,8 +122,6 @@ print("c_compiler: " +c_compiler) print("generator: " +generator) -#if platform == "win32": -# print("vcvars: " +vcvars) print("with_essential_client_modules: " +str(with_essential_client_modules)) print("with_common_modules: " +str(with_common_modules)) print("with_pfm: " +str(with_pfm)) @@ -131,6 +129,8 @@ print("with_all_pfm_modules: " +str(with_all_pfm_modules)) print("with_vr: " +str(with_vr)) print("with_lua_debugger: " +str(with_lua_debugger)) +print("with_swiftshader: " +str(with_swiftshader)) +print("build_swiftshader: " +str(build_swiftshader)) print("rerun: " +str(rerun)) print("update: " +str(update)) print("build: " +str(build)) @@ -493,6 +493,40 @@ def execscript(filepath): os.chdir("../../") os.chdir("../../") +########## SwiftShader ########## +if with_swiftshader: + os.chdir(deps_dir) + swiftshader_root = normalize_path(os.getcwd() +"/swiftshader") + swiftshader_modules_dir = install_dir +"/modules/swiftshader/" + + swiftshader_bin_dir = swiftshader_root +"/build/bin/" + if build_swiftshader: + if not Path(swiftshader_root).is_dir(): + print_msg("SwiftShader not found. Downloading...") + git_clone("https://github.com/Silverlan/swiftshader.git") + os.chdir("swiftshader") + reset_to_commit("8f431ea") + + print_msg("Building SwiftShader...") + os.chdir("build") + cmake_configure("..",generator) + cmake_build("Release") + else: + if not Path(swiftshader_root).is_dir(): + mkpath(swiftshader_bin_dir) + os.chdir(swiftshader_bin_dir) + print_msg("Downloading prebuilt SwiftShader...") + if platform == "win32": + http_extract("https://github.com/Silverlan/swiftshader/releases/download/latest/swiftshader.zip") + else: + http_extract("https://github.com/Silverlan/swiftshader/releases/download/latest/swiftshader.tar.gz",format="tar.gz") + print_msg("Installing SwiftShader...") + mkpath(swiftshader_modules_dir) + if platform == "win32": + cp(swiftshader_bin_dir +"/vulkan-1.dll",swiftshader_modules_dir) + else: + cp(swiftshader_bin_dir +"/libvulkan.so.1",swiftshader_modules_dir) + ########## vcpkg ########## os.chdir(deps_dir) if platform == "win32": @@ -703,8 +737,6 @@ def execbuildscript(filepath): l["install_system_packages"] = install_system_packages # l["harfbuzz_include_dir"] = harfbuzz_include_dir # l["harfbuzz_lib"] = harfbuzz_lib - #else: - # l["vcvars"] = "vcvars" if platform == "win32": l["determine_vs_installation_path"] = determine_vs_installation_path diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index d2fb198cc..49f0cac05 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -26,5 +26,8 @@ set_target_properties(client PROPERTIES FOLDER core) set_target_properties(pragma PROPERTIES FOLDER core) set_target_properties(pragma_server PROPERTIES FOLDER core) set_target_properties(wms_shared PROPERTIES FOLDER core) +if(WIN32) + set_target_properties(pragma_console PROPERTIES FOLDER core) +endif() set(CMAKE_CXX_STANDARD 20) diff --git a/core/client/src/c_launchparameters.cpp b/core/client/src/c_launchparameters.cpp index 9227665e5..414a12241 100644 --- a/core/client/src/c_launchparameters.cpp +++ b/core/client/src/c_launchparameters.cpp @@ -18,6 +18,7 @@ std::optional g_launchParamHeight {}; std::optional g_titleBarColor {}; std::optional g_borderColor {}; bool g_launchParamExperimentalMemoryOptimizationEnabled = false; +bool g_cpuRendering = false; bool g_windowless = false; static void LPARAM_windowed(const std::vector &argv) { g_launchParamWindowedMode = true; } @@ -111,7 +112,14 @@ static void LPARAM_border_bar_color(const std::vector &argv) g_borderColor = Color::CreateFromHexColor(strHex); } -static void LPARAM_EXPERIMENTAL_MEMORY_OPTIMIZATION(const std::vector &argv) {g_launchParamExperimentalMemoryOptimizationEnabled = (argv.empty() || util::to_boolean(argv.front())); } +static void LPARAM_EXPERIMENTAL_MEMORY_OPTIMIZATION(const std::vector &argv) { g_launchParamExperimentalMemoryOptimizationEnabled = (argv.empty() || util::to_boolean(argv.front())); } + +static void LPARAM_cpu_rendering(const std::vector &argv) +{ + g_cpuRendering = (argv.empty() || util::to_boolean(argv.front())); + // Without optimizations enabled, loading with CPU rendering will take a very long time + g_launchParamExperimentalMemoryOptimizationEnabled = true; +} REGISTER_LAUNCH_PARAMETER_HELP(-windowed, LPARAM_windowed, "-window -startwindowed -sw", "start in windowed mode"); REGISTER_LAUNCH_PARAMETER(-window, LPARAM_windowed); @@ -138,3 +146,4 @@ REGISTER_LAUNCH_PARAMETER_HELP(-windowless, LPARAM_windowless, "<1/0>", "If enab REGISTER_LAUNCH_PARAMETER_HELP(-title_bar_color, LPARAM_title_bar_color, "", "Hex color for the window title bar."); REGISTER_LAUNCH_PARAMETER_HELP(-border_color, LPARAM_border_bar_color, "", "Hex color for the window border."); REGISTER_LAUNCH_PARAMETER_HELP(-experimental_memory_optimization, LPARAM_EXPERIMENTAL_MEMORY_OPTIMIZATION, "<1/0>", "Enables experimental code for RAM usage reduction."); +REGISTER_LAUNCH_PARAMETER_HELP(-cpu_rendering, LPARAM_cpu_rendering, "<1/0>", "If enabled, the CPU will be used for rendering instead of GPU."); diff --git a/core/client/src/rendering/c_render_context.cpp b/core/client/src/rendering/c_render_context.cpp index 74ef65e03..aa5b0b3ff 100644 --- a/core/client/src/rendering/c_render_context.cpp +++ b/core/client/src/rendering/c_render_context.cpp @@ -27,6 +27,7 @@ static spdlog::logger &LOGGER_VALIDATION = pragma::register_logger("prosper_vali RenderContext::RenderContext() : m_monitor(nullptr), m_renderAPI {"vulkan"} {} RenderContext::~RenderContext() { m_graphicsAPILib = nullptr; } DLLNETWORK std::optional g_customTitle; +extern bool g_cpuRendering; void RenderContext::InitializeRenderAPI() { auto &renderAPI = GetRenderAPI(); @@ -38,7 +39,24 @@ void RenderContext::InitializeRenderAPI() std::string location; std::string modulePath; getRenderApiPath(renderAPI, location, modulePath); - m_graphicsAPILib = util::load_library_module(modulePath, util::get_default_additional_library_search_directories(modulePath), {}, &outErr); + + auto additionalSearchDirectories = util::get_default_additional_library_search_directories(modulePath); + if(g_cpuRendering) { + if(renderAPI == "vulkan") { + if(filemanager::exists("modules/swiftshader/")) { + auto p = util::Path::CreatePath(util::get_program_path()); + p += "modules/swiftshader/"; + additionalSearchDirectories.push_back(p.GetString()); + + spdlog::info("-cpu_rendering option has been specified. SwiftShader will be used for rendering instead of Vulkan driver."); + } + else + spdlog::error("-cpu_rendering option requires SwiftShader module, which is not installed! Ignoring option..."); + } + else + spdlog::error("-cpu_rendering option is only supported for Vulkan render API! Ignoring option..."); + } + m_graphicsAPILib = util::load_library_module(modulePath, additionalSearchDirectories, {}, &outErr); return (m_graphicsAPILib != nullptr); }; std::string err; diff --git a/core/pragma/CMakeLists.txt b/core/pragma/CMakeLists.txt index 61d4eed7a..8a8103ea5 100644 --- a/core/pragma/CMakeLists.txt +++ b/core/pragma/CMakeLists.txt @@ -11,4 +11,14 @@ else() set_target_properties(pragma PROPERTIES VS_DEBUGGER_COMMAND_ARGUMENTS "-console -luaext") endif() +if(WIN32) + def_project(pragma_console exe ${APP_ICON_RESOURCE_WINDOWS}) + set_target_properties(pragma_console PROPERTIES VS_DEBUGGER_COMMAND_ARGUMENTS "-console -luaext") + + # This is required so that the executable can be launched from within a Windows terminal + set_target_properties(pragma_console PROPERTIES LINK_FLAGS "/SUBSYSTEM:CONSOLE") + + set_target_properties(pragma_console PROPERTIES OUTPUT_NAME "pragma" SUFFIX ".com") + target_sources(pragma_console PRIVATE ${APP_ICON_RESOURCE_WINDOWS}) +endif() diff --git a/core/pragma/include/pragma/pragma_executable.hpp b/core/pragma/include/pragma/pragma_executable.hpp index f58b977ff..811274cf1 100644 --- a/core/pragma/include/pragma/pragma_executable.hpp +++ b/core/pragma/include/pragma/pragma_executable.hpp @@ -110,23 +110,6 @@ namespace pragma { const char *runEngineSymbol = server ? "RunEngine" : "RunCEngine"; #ifdef _WIN32 - if(!server) { - // Check if Vulkan drivers are installed - auto bt = IDTRYAGAIN; - while(bt == IDTRYAGAIN) { - auto hVulkan = LoadLibrary("vulkan-1.dll"); - if(hVulkan != NULL) - break; - std::stringstream msg; - msg << "Vulkan drivers not found! Please make sure your GPU drivers are up to date, and that your graphics vendor supports Vulkan for your GPU model."; - msg << " You may have to explicitly enable the Vulkan RT during driver installation."; - bt = MessageBox(nullptr, msg.str().c_str(), "Critical Error", MB_CANCELTRYCONTINUE | MB_ICONERROR); - if(bt == IDCANCEL) - return MODULE_NULL; - else if(bt == IDCONTINUE) - break; - } - } #if ENABLE_GDEBUGGER_SUPPORT == 1 HINSTANCE hEngine = LoadLibrary(library); #else diff --git a/core/pragma/src/main.cpp b/core/pragma/src/main.cpp index 81cae01c8..7af279378 100644 --- a/core/pragma/src/main.cpp +++ b/core/pragma/src/main.cpp @@ -14,6 +14,7 @@ try { "materials/logo/pragma_window_icon.png", "-title_bar_color", "#262626", + "-console", }; auto cargs = pragma::merge_arguments(argc, argv, extraArgs); auto hModule = pragma::launch_pragma(cargs.size(), cargs.data()); diff --git a/core/pragma_server/CMakeLists.txt b/core/pragma_server/CMakeLists.txt index a1979f43b..daa751601 100644 --- a/core/pragma_server/CMakeLists.txt +++ b/core/pragma_server/CMakeLists.txt @@ -9,4 +9,7 @@ if(UNIX) target_link_libraries(pragma_server "pthread") else() set_target_properties(pragma_server PROPERTIES VS_DEBUGGER_COMMAND_ARGUMENTS "-console -luaext") + + # This is required so that the executable can be launched from within a Windows terminal + set_target_properties(pragma_server PROPERTIES LINK_FLAGS "/SUBSYSTEM:CONSOLE") endif() diff --git a/core/shared/include/pragma/util/rig_config.hpp b/core/shared/include/pragma/util/rig_config.hpp index 072b1955c..8709f3e9d 100644 --- a/core/shared/include/pragma/util/rig_config.hpp +++ b/core/shared/include/pragma/util/rig_config.hpp @@ -79,6 +79,7 @@ namespace pragma::ik { SwivelHingeJoint, TwistJoint, AngularJoint, + DistanceJoint, Count, }; @@ -89,7 +90,11 @@ namespace pragma::ik { std::optional axisB {}; std::optional maxAngle {}; float rigidity = 1.f; - std::optional anchorPosition {}; + union { + std::optional anchorPosition {}; + std::optional anchorPositionA; + }; + std::optional anchorPositionB {}; std::optional measurementAxisA {}; }; @@ -130,6 +135,7 @@ namespace pragma::ik { PRigConfigJoint AddTwistLimit(const pragma::GString &bone0, const pragma::GString &bone1, const Vector3 &axisA, const Vector3 &axisB, umath::Degree maxAngle, float rigidity = 1.f, const std::optional &measurementAxisA = {}); PRigConfigJoint AddSwivelHingeJoint(const pragma::GString &bone0, const pragma::GString &bone1, const Vector3 &axisA, const Vector3 &axisB, float rigidity = 1.f); PRigConfigJoint AddTwistJoint(const pragma::GString &bone0, const pragma::GString &bone1, const Vector3 &axisA, const Vector3 &axisB, float rigidity); + PRigConfigJoint AddDistanceJoint(const pragma::GString &bone0, const pragma::GString &bone1, float rigidity = 1.f); PRigConfigJoint AddAngularJoint(const pragma::GString &bone0, const pragma::GString &bone1, float rigidity = 1.f); void RemoveConstraints(const pragma::GString &bone); diff --git a/core/shared/src/console/console.cpp b/core/shared/src/console/console.cpp index afc068da1..1c170a7b5 100644 --- a/core/shared/src/console/console.cpp +++ b/core/shared/src/console/console.cpp @@ -18,6 +18,16 @@ #include #endif +#ifdef _WIN32 +#pragma comment(lib, "Dbghelp.lib") +bool is_console_subsystem() +{ + // See https://stackoverflow.com/a/1440163/1879228 + PIMAGE_NT_HEADERS nth = ImageNtHeader((PVOID)GetModuleHandle(NULL)); + return nth->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI; +} +#endif + DebugConsole::DebugConsole() : _cinbuf(0), _coutbuf(0), _cerrbuf(0) {} DebugConsole::~DebugConsole() {} @@ -25,19 +35,21 @@ DebugConsole::~DebugConsole() {} void DebugConsole::open() { #ifdef _WIN32 - AllocConsole(); - AttachConsole(GetCurrentProcessId()); - this->_cinbuf = std::cin.rdbuf(); - this->_console_cin.open("CONIN$"); - std::cin.rdbuf(this->_console_cin.rdbuf()); - this->_coutbuf = std::cout.rdbuf(); - this->_console_cout.open("CONOUT$"); - std::cout.rdbuf(this->_console_cout.rdbuf()); - this->_cerrbuf = std::cerr.rdbuf(); - this->_console_cerr.open("CONOUT$"); - std::cerr.rdbuf(this->_console_cerr.rdbuf()); + if(!is_console_subsystem()) { + AllocConsole(); + AttachConsole(GetCurrentProcessId()); + this->_cinbuf = std::cin.rdbuf(); + this->_console_cin.open("CONIN$"); + std::cin.rdbuf(this->_console_cin.rdbuf()); + this->_coutbuf = std::cout.rdbuf(); + this->_console_cout.open("CONOUT$"); + std::cout.rdbuf(this->_console_cout.rdbuf()); + this->_cerrbuf = std::cerr.rdbuf(); + this->_console_cerr.open("CONOUT$"); + std::cerr.rdbuf(this->_console_cerr.rdbuf()); - freopen("CON", "w", stdout); // Redirect printf, etc. + freopen("CON", "w", stdout); // Redirect printf, etc. + } // Enable ANSI color codes under Windows HANDLE handleOut = GetStdHandle(STD_OUTPUT_HANDLE); @@ -79,37 +91,44 @@ void DebugConsole::open() } #else - int flags = fcntl(0, F_GETFL, 0); - fcntl(0, F_SETFL, flags | O_NONBLOCK); - //this->_cinbuf = std::cin.rdbuf(); - //this->_coutbuf = std::cout.rdbuf(); - //this->_cerrbuf = std::cerr.rdbuf(); + int flags = fcntl(0, F_GETFL, 0); + fcntl(0, F_SETFL, flags | O_NONBLOCK); + //this->_cinbuf = std::cin.rdbuf(); + //this->_coutbuf = std::cout.rdbuf(); + //this->_cerrbuf = std::cerr.rdbuf(); #endif } void DebugConsole::close() { #ifdef _WIN32 - this->_console_cout.close(); - std::cout.rdbuf(this->_coutbuf); - //this->_console_cin.close(); // This used to work until windows 7, now it blocks the process until new input is received - //std::cin.rdbuf(this->_cinbuf); - this->_console_cerr.close(); - std::cerr.rdbuf(this->_cerrbuf); + auto isConsoleSubSys = is_console_subsystem(); + if(!isConsoleSubSys) { + this->_console_cout.close(); + std::cout.rdbuf(this->_coutbuf); + //this->_console_cin.close(); // This used to work until windows 7, now it blocks the process until new input is received + //std::cin.rdbuf(this->_cinbuf); + this->_console_cerr.close(); + std::cerr.rdbuf(this->_cerrbuf); + } INPUT_RECORD input; unsigned long numEvents; - WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &input, 0, &numEvents); // Workaround: Writes to the console to make sure the thread can end properly - //CloseHandle(GetStdHandle(STD_INPUT_HANDLE)); // Doesn't work? - fclose(stdin); - fclose(stdout); - fclose(stderr); - FreeConsole(); + // Workaround: Writes to the console to make sure the thread can end properly + // Note: This only seems to work with the windows-subsystem + WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &input, 0, &numEvents); + if(!isConsoleSubSys) { + //CloseHandle(GetStdHandle(STD_INPUT_HANDLE)); // Doesn't work? + fclose(stdin); + fclose(stdout); + fclose(stderr); + FreeConsole(); + } #else - int flags = fcntl(0, F_GETFL, 0); - fcntl(0, F_SETFL, flags & ~O_NONBLOCK); + int flags = fcntl(0, F_GETFL, 0); + fcntl(0, F_SETFL, flags & ~O_NONBLOCK); //see https://stackoverflow.com/questions/55602283/how-to-write-data-to-stdin-to-be-consumed-by-a-separate-thread-waiting-on-input for details - //std::cout.rdbuf(this->_coutbuf); - //ssstd::cerr.rdbuf(this->_cerrbuf); + //std::cout.rdbuf(this->_coutbuf); + //ssstd::cerr.rdbuf(this->_cerrbuf); //fwrite("\n", 1, 1, stdin); //std::cin.putback('\n'); //This actually does not work. //We have to fiddle with the owning pts directly. diff --git a/core/shared/src/console/debugconsole.cpp b/core/shared/src/console/debugconsole.cpp index f391c0f1d..bd7520287 100644 --- a/core/shared/src/console/debugconsole.cpp +++ b/core/shared/src/console/debugconsole.cpp @@ -22,65 +22,63 @@ #endif #ifdef __linux__ - //https://stackoverflow.com/a/76104592 - // Returns 1 on success, 0 when not done, and -1 on failure (check errno) +//https://stackoverflow.com/a/76104592 +// Returns 1 on success, 0 when not done, and -1 on failure (check errno) // str is initially expected to be an empty string and should only altered by this function. -static int getline_async_thread_safe( std::string& str,const int& fd = 0, char delim = '\n') { - int chars_read; - do { - char buf[2] = { 0 }; - pollfd fd_stdin {0,POLLIN,0}; - - //sigemptyset(&signalset); - ppoll(&fd_stdin,1,NULL,nullptr); - chars_read = (int) read(fd, buf, 1); - if (chars_read == 1) { - if (*buf == delim) { - return 1; - } - str.append(buf); - } else { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - chars_read = 0; - break; - } - } - } while (chars_read > 0); - - return chars_read; +static int getline_async_thread_safe(std::string &str, const int &fd = 0, char delim = '\n') +{ + int chars_read; + do { + char buf[2] = {0}; + pollfd fd_stdin {0, POLLIN, 0}; + + //sigemptyset(&signalset); + ppoll(&fd_stdin, 1, NULL, nullptr); + chars_read = (int)read(fd, buf, 1); + if(chars_read == 1) { + if(*buf == delim) { + return 1; + } + str.append(buf); + } + else { + if(errno == EAGAIN || errno == EWOULDBLOCK) { + chars_read = 0; + break; + } + } + } while(chars_read > 0); + + return chars_read; } #endif - - extern Engine *engine; static std::atomic_bool bCheckInput = true; static void KeyboardInput() { //TODO: Rewrite this to use non-blocking algorythms - std::string line; + std::string line; #ifdef _WIN32 while(bCheckInput) { std::getline(std::cin, line); if(bCheckInput) engine->ConsoleInput(line); - } + } #else - int retval; - while(bCheckInput) { - retval = getline_async_thread_safe(line); - if (retval > 0) { - // Process std::string output - // Make sure to reset string if continuing through loop - if(bCheckInput) - engine->ConsoleInput(line); - - line = ""; - - } - // line = ""; - - } + int retval; + while(bCheckInput) { + retval = getline_async_thread_safe(line); + if(retval > 0) { + // Process std::string output + // Make sure to reset string if continuing through loop + if(bCheckInput) + engine->ConsoleInput(line); + + line = ""; + } + // line = ""; + } #endif } @@ -106,6 +104,9 @@ Engine::ConsoleInstance::ConsoleInstance() util::set_thread_name(*consoleThread, "pr_console_input_listener"); } +#ifdef _WIN32 +bool is_console_subsystem(); +#endif Engine::ConsoleInstance::~ConsoleInstance() { #ifdef __linux__ @@ -113,7 +114,14 @@ Engine::ConsoleInstance::~ConsoleInstance() bCheckInput = false; #endif console->close(); -#ifdef __linux__ +#ifdef _WIN32 + if(is_console_subsystem() && consoleThread) { + // There's no way to cancel the blocking std::getline in the console thread if it is attached + // to a parent console, so we have to force terminate the thread. + // TODO: Do this properly by implementing an asynchronous non-blocking input method. + TerminateThread(consoleThread->native_handle(), 0); + } +#else //It is impossible to unblock KeyboardInput by putting \n. I have to cancel the thread. auto natConsoleThread = consoleThread->native_handle(); pthread_cancel(natConsoleThread); diff --git a/core/shared/src/util/rig_config.cpp b/core/shared/src/util/rig_config.cpp index 342ae0e0d..a614d5819 100644 --- a/core/shared/src/util/rig_config.cpp +++ b/core/shared/src/util/rig_config.cpp @@ -165,8 +165,15 @@ std::optional pragma::ik::RigConfig::load_from_udm_data(u joint = rig.AddAngularJoint(bone0, bone1, rigidity); break; } + case RigConfigJoint::Type::DistanceJoint: + { + float rigidity = 1.f; + udmJoint["rigidity"] >> rigidity; + joint = rig.AddDistanceJoint(bone0, bone1, rigidity); + break; + } } - static_assert(umath::to_integral(RigConfigJoint::Type::Count) == 6u, "Update this list when new joint types are added!"); + static_assert(umath::to_integral(RigConfigJoint::Type::Count) == 7u, "Update this list when new joint types are added!"); if(joint) udmJoint["measurementAxisA"] >> joint->measurementAxisA; @@ -340,6 +347,16 @@ pragma::ik::PRigConfigJoint pragma::ik::RigConfig::AddTwistJoint(const pragma::G j->type = RigConfigJoint::Type::TwistJoint; return j; } +pragma::ik::PRigConfigJoint pragma::ik::RigConfig::AddDistanceJoint(const pragma::GString &bone0, const pragma::GString &bone1, float rigidity) +{ + m_joints.push_back(std::make_shared()); + auto &j = m_joints.back(); + j->bone0 = bone0; + j->bone1 = bone1; + j->rigidity = rigidity; + j->type = RigConfigJoint::Type::DistanceJoint; + return j; +} pragma::ik::PRigConfigJoint pragma::ik::RigConfig::AddAngularJoint(const pragma::GString &bone0, const pragma::GString &bone1, float rigidity) { m_joints.push_back(std::make_shared());