diff --git a/CMakeLists.txt b/CMakeLists.txt index 56d4b12ffb..52810e6b0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -298,6 +298,10 @@ include(RttrTestingCfg) set(rttrContribBoostDir ${CMAKE_CURRENT_SOURCE_DIR}/contrib/boost) if(EXISTS ${rttrContribBoostDir} AND IS_DIRECTORY ${rttrContribBoostDir}) set(BOOST_ROOT ${rttrContribBoostDir} CACHE PATH "Path to find boost at") + if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") + set(Boost_INCLUDE_DIR "${rttrContribBoostDir}/include" CACHE PATH "Path to find boost at") + set(Boost_LIBRARY_DIR "${rttrContribBoostDir}/lib" CACHE PATH "Path to find boost at") + endif () endif() set(BoostPackages filesystem iostreams locale program_options) @@ -306,7 +310,7 @@ if(BUILD_TESTING) list(APPEND BoostPackages unit_test_framework) endif() -find_package(Boost 1.71 COMPONENTS ${BoostPackages}) +find_package(Boost 1.86 COMPONENTS ${BoostPackages}) if(NOT Boost_FOUND) message(FATAL_ERROR "You have to install boost (>=1.71) into contrib/boost or set (as CMake or environment variable) " "BOOST_ROOT (currently: '${BOOST_ROOT}', Environment: '$ENV{BOOST_ROOT}'), " @@ -315,7 +319,9 @@ if(NOT Boost_FOUND) endif() option(RTTR_USE_SYSTEM_BOOST_NOWIDE "Use system installed Boost.Nowide. Fails if not found!" "${RTTR_USE_SYSTEM_LIBS}") - +if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") +set(RTTR_USE_SYSTEM_BOOST_NOWIDE ON) +endif () if(Boost_VERSION_MINOR GREATER_EQUAL "74" OR RTTR_USE_SYSTEM_BOOST_NOWIDE) # Boost 1.73 contains Boost.Nowide, 1.74 a required fix find_package(Boost COMPONENTS nowide) diff --git a/README.md b/README.md index b92e100024..5675c48d9e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,14 @@ Copyright (C) 2005 - 2025 Settlers Freaks SPDX-License-Identifier: GPL-2.0-or-later --> +# emscripten build (wasm) + +1. checkout and build `boost-1.86.0` with following command +```bash +docker run --rm -v $(pwd):/src emscripten-image sh -c './b2 install toolset=emscripten link=static runtime-link=static variant=release -j 1 --prefix=/src/contrib/boost --with-program_options --with-nowide --with-filesystem --with-iostreams --with-locale --with-thread --with-system --with-regex' +``` +2. download lua `lua-5.4.7` update config its Makefile add both C and LD flags `-fwasm-exceptions -sSUPPORT_LONGJMP=wasm` and then build +3. run standard build # Return To The Roots diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 29b9de5b03..26b57c01bf 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -51,7 +51,9 @@ add_subdirectory(liblobby) add_subdirectory(libsiedler2) add_subdirectory(libutil) add_subdirectory(mygettext) +if (NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten") add_subdirectory(s25edit) +endif () if(RTTR_BUILD_UPDATER) add_subdirectory(s25update) endif() diff --git a/extras/CMakeLists.txt b/extras/CMakeLists.txt index edbac0174a..ae6d382ae7 100644 --- a/extras/CMakeLists.txt +++ b/extras/CMakeLists.txt @@ -11,7 +11,9 @@ set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) add_subdirectory(audioDrivers) add_subdirectory(videoDrivers) +if (NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten") add_subdirectory(ai-battle) +endif () if(RTTR_BUNDLE AND APPLE) add_subdirectory(macosLauncher) endif() diff --git a/extras/audioDrivers/SDL/AudioSDL.cpp b/extras/audioDrivers/SDL/AudioSDL.cpp index e7e2ee73f3..3e11fb176f 100644 --- a/extras/audioDrivers/SDL/AudioSDL.cpp +++ b/extras/audioDrivers/SDL/AudioSDL.cpp @@ -29,7 +29,7 @@ void FreeAudioInstance(driver::IAudioDriver* driver) delete driver; } -const char* GetDriverName() +const char* GetAudioDriverName() { return "(SDL2) Audio via SDL2_mixer-Library"; } @@ -62,7 +62,7 @@ AudioSDL::~AudioSDL() */ const char* AudioSDL::GetName() const { - return GetDriverName(); + return GetAudioDriverName(); } static AudioSDL* currentInstance = nullptr; diff --git a/extras/audioDrivers/SDL/CMakeLists.txt b/extras/audioDrivers/SDL/CMakeLists.txt index d0db8ee59b..f13fee4244 100644 --- a/extras/audioDrivers/SDL/CMakeLists.txt +++ b/extras/audioDrivers/SDL/CMakeLists.txt @@ -3,8 +3,11 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(SDL_BUILDING_LIBRARY ON) +if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") + set(SDL_MIXER_FOUND TRUE) +else () find_package(SDL_mixer 2.0.1) - +endif () if(NOT SDL_MIXER_FOUND) message(WARNING "SDL_mixer library not found: Not building SDL audiodriver") else() @@ -16,8 +19,18 @@ else() gather_dll_by_name(OGG libogg-0.dll) endif() - add_library(audioSDL SHARED ${RTTR_DRIVER_INTERFACE} AudioSDL.cpp AudioSDL.h) - target_link_libraries(audioSDL PRIVATE audiodrv SDL_mixer::SDL_mixer) + if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") + add_library(audioSDL STATIC ${RTTR_DRIVER_INTERFACE} AudioSDL.cpp AudioSDL.h) + set(USE_FLAGS "--use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg") + set_target_properties(audioSDL PROPERTIES + COMPILE_FLAGS "${USE_FLAGS}" + LINK_FLAGS "${USE_FLAGS}" + ) + target_link_libraries(audioSDL PRIVATE audiodrv) + else () + add_library(audioSDL SHARED ${RTTR_DRIVER_INTERFACE} AudioSDL.cpp AudioSDL.h) + target_link_libraries(audioSDL PRIVATE audiodrv SDL_mixer::SDL_mixer) + endif () enable_warnings(audioSDL) install(TARGETS audioSDL diff --git a/extras/videoDrivers/SDL2/CMakeLists.txt b/extras/videoDrivers/SDL2/CMakeLists.txt index 1c89931c66..05ec5c9202 100644 --- a/extras/videoDrivers/SDL2/CMakeLists.txt +++ b/extras/videoDrivers/SDL2/CMakeLists.txt @@ -3,11 +3,24 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(SDL2_BUILDING_LIBRARY ON) +if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") + set(SDL2_FOUND TRUE) +else () find_package(SDL2 2.0.5) - +endif () if(SDL2_FOUND) - add_library(videoSDL2 SHARED ${RTTR_DRIVER_INTERFACE} VideoSDL2.cpp VideoSDL2.h icon.h icon.cpp) - target_link_libraries(videoSDL2 PRIVATE videodrv s25util::common glad Boost::nowide SDL2::SDL2) + if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") + add_library(videoSDL2 STATIC ${RTTR_DRIVER_INTERFACE} VideoSDL2.cpp VideoSDL2.h icon.h icon.cpp) + set(USE_FLAGS "--use-port=sdl2 --use-port=sdl2_image") + set_target_properties(videoSDL2 PROPERTIES + COMPILE_FLAGS "${USE_FLAGS}" + LINK_FLAGS "${USE_FLAGS}" + ) + target_link_libraries(videoSDL2 PRIVATE videodrv s25util::common Boost::nowide) + else () + add_library(videoSDL2 SHARED ${RTTR_DRIVER_INTERFACE} VideoSDL2.cpp VideoSDL2.h icon.h icon.cpp) + target_link_libraries(videoSDL2 PRIVATE videodrv s25util::common glad Boost::nowide SDL2::SDL2) + endif () enable_warnings(videoSDL2) if(WIN32) diff --git a/extras/videoDrivers/SDL2/VideoSDL2.cpp b/extras/videoDrivers/SDL2/VideoSDL2.cpp index 3f06c5c206..6c7a712d74 100644 --- a/extras/videoDrivers/SDL2/VideoSDL2.cpp +++ b/extras/videoDrivers/SDL2/VideoSDL2.cpp @@ -10,7 +10,9 @@ #include "helpers/LSANUtils.h" #include "helpers/containerUtils.h" #include "icon.h" +#if !__EMSCRIPTEN__ #include "openglCfg.hpp" +#endif #include #include #include @@ -54,7 +56,7 @@ void FreeVideoInstance(IVideoDriver* driver) delete driver; } -const char* GetDriverName() +const char* GetVideoDriverName() { return "(SDL2) OpenGL via SDL2-Library"; } @@ -68,7 +70,7 @@ VideoSDL2::~VideoSDL2() const char* VideoSDL2::GetName() const { - return GetDriverName(); + return GetVideoDriverName(); } bool VideoSDL2::Initialize() @@ -112,6 +114,7 @@ bool VideoSDL2::CreateScreen(const std::string& title, const VideoMode& size, bo if(!initialized) return false; +#if !__EMSCRIPTEN__ // GL-Attributes CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RTTR_OGL_MAJOR)); CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RTTR_OGL_MINOR)); @@ -128,6 +131,7 @@ bool VideoSDL2::CreateScreen(const std::string& title, const VideoMode& size, bo CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8)); CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8)); CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)); +#endif int wndPos = SDL_WINDOWPOS_CENTERED; @@ -170,6 +174,11 @@ bool VideoSDL2::CreateScreen(const std::string& title, const VideoMode& size, bo context = SDL_GL_CreateContext(window); + if(!context) + { + PrintError(SDL_GetError()); + return false; + } #ifdef _WIN32 SetWindowTextW(GetConsoleWindow(), boost::nowide::widen(title).c_str()); #endif diff --git a/libs/libGamedata/CMakeLists.txt b/libs/libGamedata/CMakeLists.txt index eff5db1cd7..ae1e037db2 100644 --- a/libs/libGamedata/CMakeLists.txt +++ b/libs/libGamedata/CMakeLists.txt @@ -15,8 +15,12 @@ AddDirectory(lua) file(GLOB SOURCES_OTHER *.cpp *.h) source_group(other FILES ${SOURCES_OTHER}) -find_package(Lua 5.1 REQUIRED) - +if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") + set(LUA_LIBRARIES /src/contrib/lua-5.4.7/build/lib/liblua.a) + set(LUA_INCLUDE_DIR /src/contrib/lua-5.4.7/build/include) +else () + find_package(Lua 5.1 REQUIRED) +endif () include(GatherDll) gather_dll(Lua) diff --git a/libs/rttrConfig/src/RttrConfig.cpp b/libs/rttrConfig/src/RttrConfig.cpp index d6a6da1fb5..5ce625c54f 100644 --- a/libs/rttrConfig/src/RttrConfig.cpp +++ b/libs/rttrConfig/src/RttrConfig.cpp @@ -37,6 +37,7 @@ bfs::path RttrConfig::GetPrefixPath() // Get path to current executable (at least for checks) bfs::path fullExeFilepath = System::getExecutablePath(); // This should always work unless we have some missing implementation or a bad error +#if !__EMSCRIPTEN__ if(fullExeFilepath.empty()) { LOG.write("Could not get path to current executable\n", LogTarget::Stderr); @@ -47,7 +48,7 @@ bfs::path RttrConfig::GetPrefixPath() LOG.write("Executable not at '%1%'\n", LogTarget::Stderr) % fullExeFilepath; return ""; } - +#endif bfs::path rttrBinDir(RTTR_BINDIR); // Allow overwrite with RTTR_PREFIX_DIR diff --git a/libs/s25client/CMakeLists.txt b/libs/s25client/CMakeLists.txt index a29b0eed0f..a154425e9b 100644 --- a/libs/s25client/CMakeLists.txt +++ b/libs/s25client/CMakeLists.txt @@ -19,7 +19,27 @@ else() endif() add_executable(s25client s25client.cpp commands.cpp ${s25client_RC}) -target_link_libraries(s25client PRIVATE s25Main Boost::program_options Boost::nowide rttr::vld) +if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") + set(USE_FLAGS "-fwasm-exceptions \ + --use-port=sdl2 --use-port=sdl2_image --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg") + + set_target_properties(s25client PROPERTIES + COMPILE_FLAGS "${USE_FLAGS}" + LINK_FLAGS "${USE_FLAGS} \ + -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=256mb -sMEMORY_GROWTH_LINEAR_STEP=128mb \ + -sSTACK_SIZE=1024kb \ + -sENVIRONMENT=web \ + -sMODULARIZE \ + -sEXPORT_ALL -sEXPORTED_RUNTIME_METHODS=callMain,addRunDependency,removeRunDependency \ + -sEXIT_RUNTIME \ + -sFULL_ES2 -L/src/contrib/gl4es/lib/ -lgl4es \ + -lwebsocket.js -lidbfs.js -sCASE_INSENSITIVE_FS --profiling-funcs" + SUFFIX ".js" + ) + target_link_libraries(s25client PRIVATE s25Main Boost::program_options rttr::vld) +else () + target_link_libraries(s25client PRIVATE s25Main Boost::program_options Boost::nowide rttr::vld) +endif () add_dependencies(s25client drivers) if(WIN32) diff --git a/libs/s25client/s25client.cpp b/libs/s25client/s25client.cpp index d6063849a2..13b497f18f 100644 --- a/libs/s25client/s25client.cpp +++ b/libs/s25client/s25client.cpp @@ -50,6 +50,10 @@ # include #endif +#ifdef __EMSCRIPTEN__ +# include +#endif + namespace bfs = boost::filesystem; namespace bnw = boost::nowide; namespace po = boost::program_options; @@ -435,6 +439,17 @@ bool InitGame(GameManager& gameManager) return true; } +#ifdef __EMSCRIPTEN__ +static void mainLoop() +{ + killme = false; + if (!GAMEMANAGER.Run()) + { + emscripten_cancel_main_loop(); + } +} +#endif + int RunProgram(po::variables_map& options) { LOG.write("%1%\n\n", LogTarget::Stdout) % GetProgramDescription(); @@ -466,11 +481,10 @@ int RunProgram(po::variables_map& options) } } - SetGlobalInstanceWrapper gameManager(setGlobalGameManager, LOG, SETTINGS, VIDEODRIVER, AUDIODRIVER, - WINDOWMANAGER); + setGlobalGameManager(new GameManager(LOG, SETTINGS, VIDEODRIVER, AUDIODRIVER, WINDOWMANAGER)); try { - if(!InitGame(gameManager)) + if(!InitGame(GAMEMANAGER)) return 2; if(options.count("map")) @@ -484,16 +498,21 @@ int RunProgram(po::variables_map& options) } // Hauptschleife - - while(gameManager.Run()) +#ifdef __EMSCRIPTEN__ + EM_ASM({ + Module?.gameReady?.(); + }); + emscripten_set_main_loop(&mainLoop, 0, true); +#else + while(GAMEMANAGER.Run()) { #ifndef _WIN32 killme = false; #endif // !_WIN32 } - +#endif // Spiel beenden - gameManager.Stop(); + GAMEMANAGER.Stop(); libsiedler2::setAllocator(nullptr); } catch(const RTTR_AssertError& error) { diff --git a/libs/s25main/CMakeLists.txt b/libs/s25main/CMakeLists.txt index 0d42d75cef..d70b068037 100644 --- a/libs/s25main/CMakeLists.txt +++ b/libs/s25main/CMakeLists.txt @@ -59,11 +59,22 @@ target_link_libraries(s25Main PUBLIC s25Common rttrConfig gamedata - glad driver Boost::filesystem Boost::disable_autolinking PRIVATE BZip2::BZip2 Boost::iostreams Boost::locale Boost::nowide samplerate_cpp ) +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + target_link_libraries(s25Main PUBLIC videoSDL2 audioSDL) + set(USE_FLAGS "--use-port=sdl2 --use-port=sdl2_image \ + --use-port=sdl2_mixer -sSDL2_MIXER_FORMATS=ogg") + set_target_properties(s25Main PROPERTIES + COMPILE_FLAGS "${USE_FLAGS} -I/src/contrib/gl4es/include" + LINK_FLAGS "${USE_FLAGS}" + ) + add_definitions(-DRTTR_OGL_ES=0) +else () + target_link_libraries(s25Main PUBLIC glad) +endif () if(WIN32 OR CYGWIN) include(CheckIncludeFiles) diff --git a/libs/s25main/GameManager.cpp b/libs/s25main/GameManager.cpp index 4c33ee0552..578543b608 100644 --- a/libs/s25main/GameManager.cpp +++ b/libs/s25main/GameManager.cpp @@ -41,7 +41,11 @@ bool GameManager::Start() settings_.Load(); /// Videotreiber laden +#ifdef __EMSCRIPTEN__ + if(!videoDriver_.LoadDriver()) +#else if(!videoDriver_.LoadDriver(settings_.driver.video)) +#endif { s25util::error(_("Video driver couldn't be loaded!")); return false; @@ -57,7 +61,11 @@ bool GameManager::Start() videoDriver_.setGuiScalePercent(settings_.video.guiScale); /// Audiodriver laden +#ifdef __EMSCRIPTEN__ + if(!audioDriver_.LoadDriver()) +#else if(!audioDriver_.LoadDriver(settings_.driver.audio)) +#endif { s25util::warning(_("Audio driver couldn't be loaded!")); // return false; @@ -195,7 +203,7 @@ void GameManager::ResetAverageGFPS() gfCounter_ = FrameCounter(FrameCounter::clock::duration::max()); // Never update } -static GameManager* globalGameManager = nullptr; +GameManager* globalGameManager; GameManager& getGlobalGameManager() { diff --git a/libs/s25main/GameManager.h b/libs/s25main/GameManager.h index de13b0f603..9d99f9d853 100644 --- a/libs/s25main/GameManager.h +++ b/libs/s25main/GameManager.h @@ -49,6 +49,8 @@ class GameManager boost::optional lastSkipReport; }; +extern GameManager *globalGameManager; + GameManager& getGlobalGameManager(); void setGlobalGameManager(GameManager* gameManager); diff --git a/libs/s25main/GlobalGameSettings.cpp b/libs/s25main/GlobalGameSettings.cpp index 730af63fe2..78d5875c35 100644 --- a/libs/s25main/GlobalGameSettings.cpp +++ b/libs/s25main/GlobalGameSettings.cpp @@ -16,6 +16,9 @@ #include #include #include +#if __EMSCRIPTEN__ +#include +#endif GlobalGameSettings::GlobalGameSettings() : speed(GameSpeed::Normal), objective(GameObjective::None), startWares(StartWares::Normal), lockedTeams(false), @@ -192,6 +195,9 @@ void GlobalGameSettings::SaveSettings() const SETTINGS.addons.configuration.clear(); for(const AddonWithState& addon : addons) SETTINGS.addons.configuration.insert(std::make_pair(static_cast(addon.addon->getId()), addon.status)); +#if __EMSCRIPTEN__ + EM_ASM(Module.requireSync?.()); +#endif } /** diff --git a/libs/s25main/Replay.cpp b/libs/s25main/Replay.cpp index ebc28a5a5c..7543406589 100644 --- a/libs/s25main/Replay.cpp +++ b/libs/s25main/Replay.cpp @@ -12,6 +12,9 @@ #include #include #include +#if __EMSCRIPTEN__ +#include +#endif std::string Replay::GetSignature() const { @@ -107,7 +110,13 @@ bool Replay::StopRecording() // All done. Replace uncompressed replay compressedReplay.Close(); file.Close(); +#if __EMSCRIPTEN__ // prevent replay rename fault with link external fs exception + boost::filesystem::copy_file(tmpReplayFile.filePath, filepath_, boost::filesystem::copy_options::overwrite_existing); + boost::filesystem::remove(tmpReplayFile.filePath); + EM_ASM(Module.requireSync?.()); +#elif boost::filesystem::rename(tmpReplayFile.filePath, filepath_); +#endif return true; } catch(const std::exception& e) { diff --git a/libs/s25main/Savegame.cpp b/libs/s25main/Savegame.cpp index 29244bb5b1..ba539a4f54 100644 --- a/libs/s25main/Savegame.cpp +++ b/libs/s25main/Savegame.cpp @@ -7,6 +7,9 @@ #include "s25util/BinaryFile.h" #include #include +#if __EMSCRIPTEN__ +#include +#endif std::string Savegame::GetSignature() const { @@ -46,7 +49,9 @@ bool Savegame::Save(BinaryFile& file, const std::string& mapName) WritePlayerData(file); WriteGGS(file); WriteGameData(file); - +#if __EMSCRIPTEN__ + EM_ASM(Module.requireSync?.()); +#endif return true; } diff --git a/libs/s25main/TerrainRenderer.cpp b/libs/s25main/TerrainRenderer.cpp index 588655194e..640de5a1da 100644 --- a/libs/s25main/TerrainRenderer.cpp +++ b/libs/s25main/TerrainRenderer.cpp @@ -20,7 +20,12 @@ #include "libsiedler2/Archiv.h" #include "libsiedler2/ArchivItem_PaletteAnimation.h" #include "s25util/Log.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include #include @@ -225,7 +230,11 @@ void TerrainRenderer::UpdateVertexColor(const MapPoint pt, const GameWorldViewer break; case Visibility::Visible: // Normal sichtbar +#if __EMSCRIPTEN__ + GetVertex(pt).color = clr; +#else GetVertex(pt).color = clr / 2.f; +#endif break; } } @@ -792,7 +801,11 @@ void TerrainRenderer::Draw(const Position& firstPt, const Position& lastPt, cons } // Modulate2x +#if __EMSCRIPTEN__ + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +#else glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); +#endif glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, 2.0f); // Disable alpha blending @@ -983,50 +996,41 @@ void TerrainRenderer::DrawWays(const PreparedRoads& sorted_roads) const { if(itRoad.value().empty()) continue; - Tex2C3Ver2* curVertexData = vertexData.get(); const glArchivItem_Bitmap& texture = *roadTextures[itRoad.index()]; PointF scaledTexSize = texture.GetSize() / PointF(texture.GetTexSize()); + VIDEODRIVER.BindTexture(texture.GetTextureNoCreate()); + + glBegin(GL_QUADS); for(const auto& it : itRoad.value()) { - curVertexData->tx = 0.0f; - curVertexData->ty = 0.0f; - curVertexData->r = curVertexData->g = curVertexData->b = it.color1; - Position tmpP = it.pos + begin_end_coords[it.dir][0]; - curVertexData->x = GLfloat(tmpP.x); - curVertexData->y = GLfloat(tmpP.y); - - curVertexData++; - - curVertexData->tx = 0.0f; - curVertexData->ty = scaledTexSize.y; - curVertexData->r = curVertexData->g = curVertexData->b = it.color1; - tmpP = it.pos + begin_end_coords[it.dir][1]; - curVertexData->x = GLfloat(tmpP.x); - curVertexData->y = GLfloat(tmpP.y); - - curVertexData++; - - curVertexData->tx = scaledTexSize.x; - curVertexData->ty = scaledTexSize.y; - curVertexData->r = curVertexData->g = curVertexData->b = it.color2; - tmpP = it.pos2 + begin_end_coords[it.dir][2]; - curVertexData->x = GLfloat(tmpP.x); - curVertexData->y = GLfloat(tmpP.y); - - curVertexData++; - curVertexData->tx = scaledTexSize.x; - curVertexData->ty = 0.0f; - curVertexData->r = curVertexData->g = curVertexData->b = it.color2; - tmpP = it.pos2 + begin_end_coords[it.dir][3]; - curVertexData->x = GLfloat(tmpP.x); - curVertexData->y = GLfloat(tmpP.y); - - curVertexData++; - } + const auto& coords = begin_end_coords[it.dir]; - VIDEODRIVER.BindTexture(texture.GetTextureNoCreate()); - glDrawArrays(GL_QUADS, 0, itRoad.value().size() * 4); + GLfloat r1 = it.color1; + GLfloat r2 = it.color2; + + Position p0 = it.pos + coords[0]; + Position p1 = it.pos + coords[1]; + Position p2 = it.pos2 + coords[2]; + Position p3 = it.pos2 + coords[3]; + + // Vertex 0 + glColor3f(r1, r1, r1); + glTexCoord2f(0.0f, 0.0f); glVertex2f(GLfloat(p0.x), GLfloat(p0.y)); + + // Vertex 1 + glColor3f(r1, r1, r1); + glTexCoord2f(0.0f, scaledTexSize.y); glVertex2f(GLfloat(p1.x), GLfloat(p1.y)); + + // Vertex 2 + glColor3f(r2, r2, r2); + glTexCoord2f(scaledTexSize.x, scaledTexSize.y); glVertex2f(GLfloat(p2.x), GLfloat(p2.y)); + + // Vertex 3 + glColor3f(r2, r2, r2); + glTexCoord2f(scaledTexSize.x, 0.0f); glVertex2f(GLfloat(p3.x), GLfloat(p3.y)); + } + glEnd(); } // Note: No glDisableClientState as we did not enable it } diff --git a/libs/s25main/WindowManager.cpp b/libs/s25main/WindowManager.cpp index d6db704823..b110868bc1 100644 --- a/libs/s25main/WindowManager.cpp +++ b/libs/s25main/WindowManager.cpp @@ -25,6 +25,9 @@ #include "s25util/Log.h" #include "s25util/MyTime.h" #include +#ifdef __EMSCRIPTEN__ +#include +#endif WindowManager::WindowManager() : cursor_(Cursor::Hand), disable_mouse(false), lastMousePos(Position::Invalid()), curRenderSize(0, 0), @@ -55,7 +58,7 @@ void WindowManager::DrawCursor() default: break; } if(resId) - LOADER.GetImageN("resource", resId)->DrawFull(VIDEODRIVER.GetMousePos()); + LOADER.GetImageN("resource", resId)->DrawCursor(VIDEODRIVER.GetMousePos()); } /** diff --git a/libs/s25main/desktops/dskCampaignSelection.cpp b/libs/s25main/desktops/dskCampaignSelection.cpp index 36e294798c..83509afd6d 100644 --- a/libs/s25main/desktops/dskCampaignSelection.cpp +++ b/libs/s25main/desktops/dskCampaignSelection.cpp @@ -86,7 +86,7 @@ dskCampaignSelection::dskCampaignSelection(CreateServerInfo csi) Extent(secondColumnExtentX, mutlilineExtentY), TextureColor::Grey, NormalFont); AddTextButton(ID_btBack, DrawPoint(380, 560), Extent(200, 22), TextureColor::Red1, _("Back"), NormalFont); - AddTextButton(ID_Next, DrawPoint(590, 560), Extent(200, 22), TextureColor::Green2, _("Continue"), NormalFont); + AddTextButton(ID_Next, DrawPoint(590, 560), Extent(200, 22), TextureColor::Green2, _("Continue"), NormalFont)->SetEnabled(false); showCampaignInfo(false); diff --git a/libs/s25main/desktops/dskMainMenu.cpp b/libs/s25main/desktops/dskMainMenu.cpp index 2f886ff5b1..1b72b17ae0 100644 --- a/libs/s25main/desktops/dskMainMenu.cpp +++ b/libs/s25main/desktops/dskMainMenu.cpp @@ -39,9 +39,12 @@ dskMainMenu::dskMainMenu() // "Einzelspieler" AddTextButton(ID_btSingleplayer, DrawPoint(115, 180), Extent(220, 22), TextureColor::Green2, _("Singleplayer"), NormalFont); + +#if !__EMSCRIPTEN__ // again requires emscripten libwebsocket patching and dedicated websocket <-> tcp/udp proxy server // "Mehrspieler" AddTextButton(ID_btMultiplayer, DrawPoint(115, 210), Extent(220, 22), TextureColor::Green2, _("Multiplayer"), NormalFont); +#endif // "Optionen" AddTextButton(ID_btOptions, DrawPoint(115, 250), Extent(220, 22), TextureColor::Green2, _("Options"), NormalFont); // "Intro" @@ -51,9 +54,10 @@ dskMainMenu::dskMainMenu() AddTextButton(ID_btReadme, DrawPoint(115, 310), Extent(220, 22), TextureColor::Green2, _("Readme"), NormalFont); // "Credits" AddTextButton(ID_btCredits, DrawPoint(115, 340), Extent(220, 22), TextureColor::Green2, _("Credits"), NormalFont); +#if !__EMSCRIPTEN__ // "Programm verlassen" AddTextButton(ID_btQuit, DrawPoint(115, 390), Extent(220, 22), TextureColor::Red1, _("Quit program"), NormalFont); - +#endif AddImage(ID_logo, DrawPoint(20, 20), LOADER.GetImageN("logo", 0)); using namespace std::chrono_literals; diff --git a/libs/s25main/desktops/dskOptions.cpp b/libs/s25main/desktops/dskOptions.cpp index aafc3417b4..f985a24354 100644 --- a/libs/s25main/desktops/dskOptions.cpp +++ b/libs/s25main/desktops/dskOptions.cpp @@ -113,7 +113,7 @@ constexpr auto sectionSpacingCommon = 10; constexpr auto tabButtonsStartPosition = DrawPoint(80, 510); constexpr auto optionRowsStartPosition = DrawPoint(80, 80); } // namespace - +#if !__EMSCRIPTEN__ static VideoMode getAspectRatio(const VideoMode& vm) { // First some a bit off values where the aspect ratio is defined by convention @@ -133,7 +133,7 @@ static VideoMode getAspectRatio(const VideoMode& vm) else return ratio; } - +#endif dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) { AddText(ID_txtOptions, DrawPoint(400, 10), _("Options"), COLOR_YELLOW, FontStyle::CENTER, LargeFont); @@ -307,6 +307,7 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) mainGroup->SetSelection(SETTINGS.global.showGFInfo); curPos = optionRowsStartPosition; +#if !__EMSCRIPTEN__ // all this stuff in emscripten build is controlled by browser's API and launcher; emscripten adds too bloated and buggy shim in lib lhtml5 groupGraphics->AddText(ID_txtResolution, curPos, _("Fullscreen resolution:"), COLOR_YELLOW, FontStyle{}, NormalFont); groupGraphics->AddComboBox(ID_cbResolution, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont, 150); @@ -317,7 +318,7 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) mainGroup->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("Fullscreen"), NormalFont); mainGroup->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Windowed"), NormalFont); curPos.y += rowHeight + sectionSpacing; - +#endif groupGraphics->AddText(ID_txtFramerate, curPos, _("Limit Framerate:"), COLOR_YELLOW, FontStyle{}, NormalFont); groupGraphics->AddComboBox(ID_cbFramerate, curPos + ctrlOffset, ctrlSizeLarge, TextureColor::Grey, NormalFont, 150); curPos.y += rowHeight + sectionSpacing; @@ -402,7 +403,7 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) // Graphics // { - +#if !__EMSCRIPTEN__ loadVideoModes(); // Und zu der Combobox hinzufügen @@ -427,7 +428,7 @@ dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0)) // "Vollbild" setzen groupGraphics->GetCtrl(ID_grpFullscreen)->SetSelection(SETTINGS.video.fullscreen); //-V807 - +#endif // "Limit Framerate" füllen auto* cbFrameRate = groupGraphics->GetCtrl(ID_cbFramerate); if(VIDEODRIVER.HasVSync()) diff --git a/libs/s25main/desktops/dskTextureTest.cpp b/libs/s25main/desktops/dskTextureTest.cpp index e7abfac918..7134870503 100644 --- a/libs/s25main/desktops/dskTextureTest.cpp +++ b/libs/s25main/desktops/dskTextureTest.cpp @@ -14,7 +14,12 @@ #include "lua/GameDataLoader.h" #include "ogl/FontStyle.h" #include "ogl/glArchivItem_Bitmap.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include diff --git a/libs/s25main/drivers/AudioDriverWrapper.cpp b/libs/s25main/drivers/AudioDriverWrapper.cpp index 41f813bf8c..9e8a24f45d 100644 --- a/libs/s25main/drivers/AudioDriverWrapper.cpp +++ b/libs/s25main/drivers/AudioDriverWrapper.cpp @@ -14,6 +14,10 @@ #include #include +#ifdef __EMSCRIPTEN__ +#include "../../../extras/audioDrivers/SDL/AudioSDL.h" +#endif + using ovectorstream = boost::interprocess::basic_ovectorstream>; AudioDriverWrapper::AudioDriverWrapper() : audiodriver_(nullptr, nullptr) {} @@ -90,6 +94,12 @@ bool AudioDriverWrapper::LoadDriver(std::unique_ptr audioD return Init(); } +bool AudioDriverWrapper::LoadDriver() +{ + audiodriver_ = Handle(CreateAudioInstance(this, VIDEODRIVER.GetMapPointer()), &FreeAudioInstance); + return Init(); +} + bool AudioDriverWrapper::LoadDriver(std::string& preference) { RTTR_Assert(!audiodriver_); diff --git a/libs/s25main/drivers/AudioDriverWrapper.h b/libs/s25main/drivers/AudioDriverWrapper.h index 65602d292f..a4218f2a8a 100644 --- a/libs/s25main/drivers/AudioDriverWrapper.h +++ b/libs/s25main/drivers/AudioDriverWrapper.h @@ -36,6 +36,7 @@ class AudioDriverWrapper : /// Loads the driver bool LoadDriver(std::unique_ptr audioDriver); bool LoadDriver(std::string& preference); + bool LoadDriver(); /// Unloads the driver resetting all open handles void UnloadDriver(); diff --git a/libs/s25main/drivers/DriverWrapper.cpp b/libs/s25main/drivers/DriverWrapper.cpp index 5c6634bf2d..0ce62d5a05 100644 --- a/libs/s25main/drivers/DriverWrapper.cpp +++ b/libs/s25main/drivers/DriverWrapper.cpp @@ -106,8 +106,17 @@ bool DriverWrapper::CheckLibrary(const bfs::path& path, DriverType dt, std::stri nameOrError = _("Invalid API version!"); return false; } + std::string driverNameGetter; + if(dt == DriverType::Video) + { + driverNameGetter = "GetVideoDriverName"; + } + else + { + driverNameGetter = "GetAudioDriverName"; + } - auto GetDriverName = dll.get("GetDriverName"); + auto GetDriverName = dll.get(driverNameGetter); std::string createName, freeName; if(dt == DriverType::Video) { diff --git a/libs/s25main/drivers/VideoDriverWrapper.cpp b/libs/s25main/drivers/VideoDriverWrapper.cpp index 7daa4801cf..57ee0b3c61 100644 --- a/libs/s25main/drivers/VideoDriverWrapper.cpp +++ b/libs/s25main/drivers/VideoDriverWrapper.cpp @@ -12,10 +12,16 @@ #include "mygettext/mygettext.h" #include "ogl/DummyRenderer.h" #include "ogl/OpenGLRenderer.h" -#include "openglCfg.hpp" #include "s25util/Log.h" #include "s25util/error.h" +#ifdef __EMSCRIPTEN__ +#include +#include "../../../extras/videoDrivers/SDL2/VideoSDL2.h" +#include "SDL/SDL_opengl.h" +#else +#include "openglCfg.hpp" #include +#endif #include #if !defined(NDEBUG) && defined(HAVE_MEMCHECK_H) # include @@ -62,6 +68,17 @@ bool VideoDriverWrapper::LoadDriver(IVideoDriver* existingDriver) return Initialize(); } +bool VideoDriverWrapper::LoadDriver() +{ +#ifdef __EMSCRIPTEN__ + initialize_gl4es(); +#endif + UnloadDriver(); + videodriver = Handle(CreateVideoInstance(&WINDOWMANAGER), FreeVideoInstance); + return Initialize(); +} + + bool VideoDriverWrapper::LoadDriver(std::string& preference) { UnloadDriver(); @@ -366,6 +383,7 @@ bool VideoDriverWrapper::LoadAllExtensions() renderer_ = std::make_unique(); else renderer_ = std::make_unique(); +#if !__EMSCRIPTEN__ if(!renderer_->initOpenGL(videodriver->GetLoaderFunction())) return false; LOG.write(_("OpenGL %1%.%2% supported\n")) % GLVersion.major % GLVersion.minor; @@ -375,6 +393,9 @@ bool VideoDriverWrapper::LoadAllExtensions() % ((RTTR_OGL_ES) ? "ES" : "") % RTTR_OGL_MAJOR % RTTR_OGL_MINOR; return false; } +#else + LOG.write(_("OpenGL ES 2.0 (%1%)\n")) % reinterpret_cast(glGetString(GL_VERSION)); +#endif // auf VSync-Extension testen #ifdef _WIN32 diff --git a/libs/s25main/drivers/VideoDriverWrapper.h b/libs/s25main/drivers/VideoDriverWrapper.h index 8b21b9d596..570463d82f 100644 --- a/libs/s25main/drivers/VideoDriverWrapper.h +++ b/libs/s25main/drivers/VideoDriverWrapper.h @@ -32,6 +32,7 @@ class VideoDriverWrapper : public Singleton +#if __EMSCRIPTEN__ +#include +#endif namespace bfs = boost::filesystem; @@ -223,4 +226,7 @@ void iwPlayReplay::Msg_MsgBoxResult(const unsigned msgbox_id, const MsgboxResult PopulateTable(); } } +#if __EMSCRIPTEN__ + EM_ASM(Module.requireSync?.()); +#endif } diff --git a/libs/s25main/ogl/BufferHandle.h b/libs/s25main/ogl/BufferHandle.h index 1deccdd2b2..14a2763123 100644 --- a/libs/s25main/ogl/BufferHandle.h +++ b/libs/s25main/ogl/BufferHandle.h @@ -4,7 +4,12 @@ #pragma once +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include namespace ogl { diff --git a/libs/s25main/ogl/DummyRenderer.cpp b/libs/s25main/ogl/DummyRenderer.cpp index 9c933f1352..cf53aafb16 100644 --- a/libs/s25main/ogl/DummyRenderer.cpp +++ b/libs/s25main/ogl/DummyRenderer.cpp @@ -3,13 +3,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "DummyRenderer.h" -#include "openglCfg.hpp" #include +#if !__EMSCRIPTEN__ +#include "openglCfg.hpp" #include +#endif namespace rttrOglMock { RTTR_IGNORE_DIAGNOSTIC("-Wmissing-declarations") - +#if !__EMSCRIPTEN__ void APIENTRY glGenTextures(GLsizei n, GLuint* textures) { static GLuint cur = 0; @@ -29,10 +31,12 @@ void APIENTRY glGetTexLevelParameteriv(GLenum, GLint, GLenum, GLint* params) { *params = 1; } +#endif RTTR_POP_DIAGNOSTIC } // namespace rttrOglMock +#if !__EMSCRIPTEN__ bool DummyRenderer::initOpenGL(OpenGL_Loader_Proc) { GLVersion = {RTTR_OGL_MAJOR, RTTR_OGL_MINOR}; @@ -50,3 +54,4 @@ bool DummyRenderer::initOpenGL(OpenGL_Loader_Proc) MOCK(glGetTexLevelParameteriv); return true; } +#endif diff --git a/libs/s25main/ogl/DummyRenderer.h b/libs/s25main/ogl/DummyRenderer.h index 91c3a31e69..3f445ce9eb 100644 --- a/libs/s25main/ogl/DummyRenderer.h +++ b/libs/s25main/ogl/DummyRenderer.h @@ -11,7 +11,9 @@ class glArchivItem_Bitmap; class DummyRenderer : public IRenderer { public: +#if !__EMSCRIPTEN__ bool initOpenGL(OpenGL_Loader_Proc) override; +#endif void Draw3DBorder(const Rect&, bool /*elevated*/, glArchivItem_Bitmap& /*texture*/) override {} void Draw3DContent(const Rect&, bool /*elevated*/, glArchivItem_Bitmap& /*texture*/, bool /*illuminated*/, unsigned /*color*/) override diff --git a/libs/s25main/ogl/IRenderer.h b/libs/s25main/ogl/IRenderer.h index 78a3106d71..e7f10e6e96 100644 --- a/libs/s25main/ogl/IRenderer.h +++ b/libs/s25main/ogl/IRenderer.h @@ -18,7 +18,9 @@ class IRenderer using OpenGL_Loader_Proc = void* (*)(const char*); virtual ~IRenderer() = default; +#if !__EMSCRIPTEN__ virtual bool initOpenGL(OpenGL_Loader_Proc) = 0; +#endif /// Synchronize the rendering pipeline. Usually not required unless measuring something virtual void synchronize(){}; /// Draw a border around rect with 3D effect diff --git a/libs/s25main/ogl/OpenGLRenderer.cpp b/libs/s25main/ogl/OpenGLRenderer.cpp index f02fba2b76..65505ef8f4 100644 --- a/libs/s25main/ogl/OpenGLRenderer.cpp +++ b/libs/s25main/ogl/OpenGLRenderer.cpp @@ -6,8 +6,13 @@ #include "DrawPoint.h" #include "drivers/VideoDriverWrapper.h" #include "glArchivItem_Bitmap.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include "openglCfg.hpp" #include +#endif void OpenGLRenderer::synchronize() { @@ -121,11 +126,13 @@ void OpenGLRenderer::DrawLine(DrawPoint pt1, DrawPoint pt2, unsigned width, unsi glEnable(GL_TEXTURE_2D); } +#if !__EMSCRIPTEN__ bool OpenGLRenderer::initOpenGL(OpenGL_Loader_Proc loader) { -#if RTTR_OGL_ES +# if RTTR_OGL_ES return gladLoadGLES2Loader(loader) != 0; -#else +# else return gladLoadGLLoader(loader) != 0; -#endif +# endif } +#endif \ No newline at end of file diff --git a/libs/s25main/ogl/OpenGLRenderer.h b/libs/s25main/ogl/OpenGLRenderer.h index 6cfba22e54..1648e96f54 100644 --- a/libs/s25main/ogl/OpenGLRenderer.h +++ b/libs/s25main/ogl/OpenGLRenderer.h @@ -11,7 +11,9 @@ class glArchivItem_Bitmap; class OpenGLRenderer : public IRenderer { public: +#if !__EMSCRIPTEN__ bool initOpenGL(OpenGL_Loader_Proc) override; +#endif void synchronize() override; void Draw3DBorder(const Rect& rect, bool elevated, glArchivItem_Bitmap& texture) override; void Draw3DContent(const Rect& rect, bool elevated, glArchivItem_Bitmap& texture, bool illuminated, diff --git a/libs/s25main/ogl/VBO.h b/libs/s25main/ogl/VBO.h index e1cd4974ff..c9ecd6fd1c 100644 --- a/libs/s25main/ogl/VBO.h +++ b/libs/s25main/ogl/VBO.h @@ -8,7 +8,12 @@ #include "enum_cast.hpp" #include "ogl/BufferHandle.h" #include "ogl/constants.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include diff --git a/libs/s25main/ogl/constants.h b/libs/s25main/ogl/constants.h index 0b12d9d2c0..af55310f12 100644 --- a/libs/s25main/ogl/constants.h +++ b/libs/s25main/ogl/constants.h @@ -4,7 +4,12 @@ #pragma once +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif namespace ogl { enum class Target : GLenum diff --git a/libs/s25main/ogl/glArchivItem_Bitmap.cpp b/libs/s25main/ogl/glArchivItem_Bitmap.cpp index 86a50fd4fc..bd13034fea 100644 --- a/libs/s25main/ogl/glArchivItem_Bitmap.cpp +++ b/libs/s25main/ogl/glArchivItem_Bitmap.cpp @@ -6,7 +6,12 @@ #include "Point.h" #include "drivers/VideoDriverWrapper.h" #include "libsiedler2/PixelBufferBGRA.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif glArchivItem_Bitmap::glArchivItem_Bitmap() = default; @@ -56,11 +61,25 @@ void glArchivItem_Bitmap::Draw(Rect dstArea, Rect srcArea, unsigned color /*= CO texCoords[0].y = texCoords[3].y = srcOrig.y; texCoords[1].y = texCoords[2].y = srcEndPt.y; - glVertexPointer(2, GL_FLOAT, 0, vertices.data()); - glTexCoordPointer(2, GL_FLOAT, 0, texCoords.data()); - VIDEODRIVER.BindTexture(GetTexture()); - glColor4ub(GetRed(color), GetGreen(color), GetBlue(color), GetAlpha(color)); - glDrawArrays(GL_QUADS, 0, 4); + if (isCursor) + { + glVertexPointer(2, GL_FLOAT, 0, vertices.data()); + glTexCoordPointer(2, GL_FLOAT, 0, texCoords.data()); + VIDEODRIVER.BindTexture(GetTexture()); + glColor4ub(GetRed(color), GetGreen(color), GetBlue(color), GetAlpha(color)); + glDrawArrays(GL_QUADS, 0, 4); + } + else + { + VIDEODRIVER.BindTexture(GetTexture()); + glColor4ub(GetRed(color), GetGreen(color), GetBlue(color), GetAlpha(color)); + glBegin(GL_QUADS); + glTexCoord2f(texCoords[0].x, texCoords[0].y); glVertex2f(vertices[0].x, vertices[0].y); + glTexCoord2f(texCoords[1].x, texCoords[1].y); glVertex2f(vertices[1].x, vertices[1].y); + glTexCoord2f(texCoords[2].x, texCoords[2].y); glVertex2f(vertices[2].x, vertices[2].y); + glTexCoord2f(texCoords[3].x, texCoords[3].y); glVertex2f(vertices[3].x, vertices[3].y); + glEnd(); + } } void glArchivItem_Bitmap::DrawFull(const Rect& destArea, unsigned color) @@ -73,6 +92,14 @@ void glArchivItem_Bitmap::DrawFull(const DrawPoint& dstPos, unsigned color) DrawFull(Rect(dstPos, GetSize()), color); } +void glArchivItem_Bitmap::DrawCursor(const DrawPoint& dstPos) +{ + isCursor = true; + DrawFull(Rect(dstPos, GetSize()), COLOR_WHITE); + isCursor = false; +} + + void glArchivItem_Bitmap::DrawPart(const Rect& destArea, const DrawPoint& offset, unsigned color) { Draw(destArea, Rect(offset, destArea.getSize()), color); diff --git a/libs/s25main/ogl/glArchivItem_Bitmap.h b/libs/s25main/ogl/glArchivItem_Bitmap.h index 21d95b29a9..1311f99d42 100644 --- a/libs/s25main/ogl/glArchivItem_Bitmap.h +++ b/libs/s25main/ogl/glArchivItem_Bitmap.h @@ -22,6 +22,8 @@ class glArchivItem_Bitmap : public virtual libsiedler2::baseArchivItem_Bitmap, p void DrawFull(const Rect& destArea, unsigned color = COLOR_WHITE); /// Draw the texture to the given position with full size void DrawFull(const DrawPoint& dstPos, unsigned color = COLOR_WHITE) override; + /// Draw cursor to the given position (todo: figure out why glBegin/End not rendering cursor, fix it, remove DrawCursor as it's dirty fix) + void DrawCursor(const DrawPoint& dstPos); /// Draw a rectangular part of the texture. offset specifies the offset from the origin of the texture void DrawPart(const Rect& destArea, const DrawPoint& offset, unsigned color = COLOR_WHITE); /// Draw a rectangular part of the texture from the origin of it @@ -36,4 +38,6 @@ class glArchivItem_Bitmap : public virtual libsiedler2::baseArchivItem_Bitmap, p protected: void FillTexture() override; Extent CalcTextureSize() const override; + + bool isCursor = false; }; diff --git a/libs/s25main/ogl/glArchivItem_BitmapBase.cpp b/libs/s25main/ogl/glArchivItem_BitmapBase.cpp index a17663a133..8ab97cac31 100644 --- a/libs/s25main/ogl/glArchivItem_BitmapBase.cpp +++ b/libs/s25main/ogl/glArchivItem_BitmapBase.cpp @@ -5,7 +5,12 @@ #include "glArchivItem_BitmapBase.h" #include "Loader.h" #include "drivers/VideoDriverWrapper.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif /** @class glArchivItem_BitmapBase * diff --git a/libs/s25main/ogl/glArchivItem_Bitmap_Direct.cpp b/libs/s25main/ogl/glArchivItem_Bitmap_Direct.cpp index 4ad5bc8a91..0ce0b3abac 100644 --- a/libs/s25main/ogl/glArchivItem_Bitmap_Direct.cpp +++ b/libs/s25main/ogl/glArchivItem_Bitmap_Direct.cpp @@ -5,7 +5,12 @@ #include "glArchivItem_Bitmap_Direct.h" #include "drivers/VideoDriverWrapper.h" #include "libsiedler2/PixelBufferBGRA.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include glArchivItem_Bitmap_Direct::glArchivItem_Bitmap_Direct() : isUpdating_(false) {} diff --git a/libs/s25main/ogl/glArchivItem_Bitmap_Player.cpp b/libs/s25main/ogl/glArchivItem_Bitmap_Player.cpp index f064e28a94..95da9f6479 100644 --- a/libs/s25main/ogl/glArchivItem_Bitmap_Player.cpp +++ b/libs/s25main/ogl/glArchivItem_Bitmap_Player.cpp @@ -7,7 +7,12 @@ #include "Point.h" #include "drivers/VideoDriverWrapper.h" #include "libsiedler2/PixelBufferBGRA.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif namespace { struct GL_RGBAColor @@ -106,13 +111,15 @@ void glArchivItem_Bitmap_Player::Draw(Rect dstArea, Rect srcArea, unsigned color colors[4].a = GetAlpha(player_color); colors[7] = colors[6] = colors[5] = colors[4]; - glEnableClientState(GL_COLOR_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, vertices.data()); - glTexCoordPointer(2, GL_FLOAT, 0, texCoords.data()); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors.data()); VIDEODRIVER.BindTexture(GetTexture()); - glDrawArrays(GL_QUADS, 0, 8); - glDisableClientState(GL_COLOR_ARRAY); + + glBegin(GL_QUADS); + for (int i = 0; i < 8; ++i) { + glColor4ub(colors[i].r, colors[i].g, colors[i].b, colors[i].a); + glTexCoord2f(texCoords[i].x, texCoords[i].y); + glVertex2f(vertices[i].x, vertices[i].y); + } + glEnd(); } void glArchivItem_Bitmap_Player::FillTexture() diff --git a/libs/s25main/ogl/glFont.cpp b/libs/s25main/ogl/glFont.cpp index 9fcce96f5e..b7133976b1 100644 --- a/libs/s25main/ogl/glFont.cpp +++ b/libs/s25main/ogl/glFont.cpp @@ -248,11 +248,15 @@ void glFont::Draw(DrawPoint pos, const std::string& text, FontStyle format, unsi for(GlPoint& pt : texList.texCoords) pt /= texSize; - glVertexPointer(2, GL_FLOAT, 0, texList.vertices.data()); - glTexCoordPointer(2, GL_FLOAT, 0, texList.texCoords.data()); VIDEODRIVER.BindTexture(texture); glColor4ub(GetRed(color), GetGreen(color), GetBlue(color), GetAlpha(color)); - glDrawArrays(GL_QUADS, 0, texList.vertices.size()); + + glBegin(GL_QUADS); + for (size_t i = 0, l = texList.vertices.size(); i < l; ++i) { + glTexCoord2f(texList.texCoords[i].x, texList.texCoords[i].y); + glVertex2f(texList.vertices[i].x, texList.vertices[i].y); + } + glEnd(); } template diff --git a/libs/s25main/ogl/glFont.h b/libs/s25main/ogl/glFont.h index f79c08ced3..e6061623d6 100644 --- a/libs/s25main/ogl/glFont.h +++ b/libs/s25main/ogl/glFont.h @@ -9,7 +9,12 @@ #include "ogl/FontStyle.h" #include "ogl/glArchivItem_Bitmap.h" #include "s25util/colors.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include #include diff --git a/libs/s25main/ogl/glSmartBitmap.cpp b/libs/s25main/ogl/glSmartBitmap.cpp index 1784a29a4f..ef80ac2fe6 100644 --- a/libs/s25main/ogl/glSmartBitmap.cpp +++ b/libs/s25main/ogl/glSmartBitmap.cpp @@ -11,7 +11,12 @@ #include "libsiedler2/ArchivItem_Bitmap_Player.h" #include "libsiedler2/PixelBufferBGRA.h" #include "s25util/colors.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include @@ -327,11 +332,11 @@ void glSmartBitmap::drawRect(Rect dstArea, Rect srcArea, unsigned color /*= 0xFF } else numQuads = 4; - glEnableClientState(GL_COLOR_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, vertices.data()); - glTexCoordPointer(2, GL_FLOAT, 0, curTexCoords.data()); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors.data()); VIDEODRIVER.BindTexture(texture); - glDrawArrays(GL_QUADS, 0, numQuads); - glDisableClientState(GL_COLOR_ARRAY); + glBegin(GL_QUADS); + for(int i = 0; i < numQuads; ++i) { + glColor4ub(colors[i].r, colors[i].g, colors[i].b, colors[i].a); + glTexCoord2f(curTexCoords[i].x, curTexCoords[i].y); glVertex2f(vertices[i].x, vertices[i].y); + } + glEnd(); } diff --git a/libs/s25main/ogl/glTexturePacker.cpp b/libs/s25main/ogl/glTexturePacker.cpp index 2641a2dfa1..33df6bf36e 100644 --- a/libs/s25main/ogl/glTexturePacker.cpp +++ b/libs/s25main/ogl/glTexturePacker.cpp @@ -8,7 +8,12 @@ #include "ogl/glTexturePackerNode.h" #include "ogl/saveBitmap.h" #include "libsiedler2/PixelBufferBGRA.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include #include diff --git a/libs/s25main/world/GameWorldView.cpp b/libs/s25main/world/GameWorldView.cpp index 5907cc91f6..da2b09ae99 100644 --- a/libs/s25main/world/GameWorldView.cpp +++ b/libs/s25main/world/GameWorldView.cpp @@ -30,7 +30,12 @@ #include "gameData/MapConsts.h" #include "s25util/error.h" #include "s25util/warningSuppression.h" +#ifdef __EMSCRIPTEN__ +#include "SDL/SDL.h" +#include "SDL/SDL_opengl.h" +#else #include +#endif #include #include @@ -109,17 +114,43 @@ float GameWorldView::GetCurrentTargetZoomFactor() const return targetZoomFactor_; } -struct ObjectBetweenLines +struct ObjDraw { - noBase& obj; - DrawPoint pos; // Zeichenposition + noBase* obj; + DrawPoint pos; + ObjDraw(noBase* obj, const DrawPoint& pos) : obj(obj), pos(pos) {} +}; + +struct ObjFOW +{ + const FOWObject* obj; + DrawPoint pos; + ObjFOW(const FOWObject* obj, const DrawPoint& pos) : obj(obj), pos(pos) {} +}; + +struct ObjConstructionAid +{ + ITexture* obj; + DrawPoint pos; + ObjConstructionAid(ITexture* obj, const DrawPoint& pos) : obj(obj), pos(pos) {} +}; - ObjectBetweenLines(noBase& obj, const DrawPoint& pos) : obj(obj), pos(pos) {} +struct ObjBS +{ + glSmartBitmap& obj; + DrawPoint pos; + unsigned color; + unsigned player_color; + ObjBS(glSmartBitmap& obj, const DrawPoint& pos, unsigned color, unsigned player_color) : obj(obj), pos(pos), color(color), player_color(player_color) {} }; void GameWorldView::Draw(const RoadBuildState& rb, const MapPoint selected, bool drawMouse, unsigned* water) { SetNextZoomFactor(); + std::vector renderQueueObjects; + std::vector renderQueueFOW; + std::vector renderQueueBQ; + std::vector renderQueueBS; const auto windowSize = VIDEODRIVER.GetWindowSize(); const auto& guiScale = VIDEODRIVER.getGuiScale(); @@ -154,9 +185,6 @@ void GameWorldView::Draw(const RoadBuildState& rb, const MapPoint selected, bool for(int y = firstPt.y; y <= lastPt.y; ++y) { - // Figuren speichern, die in dieser Zeile gemalt werden müssen - // und sich zwischen zwei Zeilen befinden, da sie dazwischen laufen - std::vector between_lines; for(int x = firstPt.x; x <= lastPt.x; ++x) { @@ -175,31 +203,72 @@ void GameWorldView::Draw(const RoadBuildState& rb, const MapPoint selected, bool Visibility visibility = gwv.GetVisibility(curPt); - DrawBoundaryStone(curPt, curPos, visibility); + DrawBoundaryStone(curPt, curPos, visibility, renderQueueBS); if(visibility == Visibility::Visible) { - DrawObject(curPt, curPos); - DrawMovingFiguresFromBelow(terrainRenderer, Position(x, y), between_lines); - DrawFigures(curPt, curPos, between_lines); + renderQueueObjects.push_back(ObjDraw(GetWorld().GetNode(curPt).obj, curPos)); + + DrawMovingFiguresFromBelow(Direction::NorthEast, terrainRenderer, Position(x, y), renderQueueObjects); + DrawMovingFiguresFromBelow(Direction::NorthWest, terrainRenderer, Position(x, y), renderQueueObjects); + + //Draw figures + for(noBase& figure : GetWorld().GetFigures(curPt)) + { + if(figure.IsMoving()) + { + // Drawn from above + Direction curMoveDir = static_cast(figure).GetCurMoveDir(); + if(Direction::NorthEast == curMoveDir || curMoveDir == Direction::NorthWest) + continue; + // Draw later + renderQueueObjects.push_back(ObjDraw(&figure, curPos)); + } + else + { + // Ansonsten jetzt schon zeichnen (not now, but in different layer) + renderQueueObjects.push_back(ObjDraw(&figure, curPos)); + } + } // Construction aid mode if(show_bq) - DrawConstructionAid(curPt, curPos); + { + DrawConstructionAid(curPt, curPos, renderQueueBQ); + } } else if(visibility == Visibility::FogOfWar) { - const FOWObject* fowobj = gwv.GetYoungestFOWObject(MapPoint(curPt)); - if(fowobj) - fowobj->Draw(curPos); + renderQueueFOW.push_back(ObjFOW(gwv.GetYoungestFOWObject(MapPoint(curPt)), curPos)); } for(IDrawNodeCallback* callback : drawNodeCallbacks) + { callback->onDraw(curPt, curPos); + } } + } + // just group draw calls this gives +5fps in emscripten build + for(auto& bs : renderQueueBS) + { + bs.obj.draw(bs.pos, bs.color, bs.player_color); + } + + for(auto& renderQueueObject : renderQueueObjects) + { + if (!renderQueueObject.obj) continue; + renderQueueObject.obj->Draw(renderQueueObject.pos); + } + + for(auto& fow : renderQueueFOW) + { + if (!fow.obj) continue; + fow.obj->Draw(fow.pos); + } - // Figuren zwischen den Zeilen zeichnen - for(auto& between_line : between_lines) - between_line.obj.Draw(between_line.pos); + for(auto& bq : renderQueueBQ) + { + if (!bq.obj) continue; + bq.obj->DrawFull(bq.pos); } if(show_names || show_productivity) @@ -439,45 +508,19 @@ void GameWorldView::DrawProductivity(const noBaseBuilding& no, const DrawPoint& } } -void GameWorldView::DrawFigures(const MapPoint& pt, const DrawPoint& curPos, - std::vector& between_lines) const +void GameWorldView::DrawMovingFiguresFromBelow(Direction dir, const TerrainRenderer& terrainRenderer, const DrawPoint& curPos, + std::vector& rq) { - for(noBase& figure : GetWorld().GetFigures(pt)) - { - if(figure.IsMoving()) - { - // Drawn from above - Direction curMoveDir = static_cast(figure).GetCurMoveDir(); - if(curMoveDir == Direction::NorthEast || curMoveDir == Direction::NorthWest) - continue; - // Draw later - between_lines.push_back(ObjectBetweenLines(figure, curPos)); - } else if(figure.GetGOT() == GO_Type::Ship) - between_lines.push_back(ObjectBetweenLines(figure, curPos)); // TODO: Why special handling for ships? - else - // Ansonsten jetzt schon zeichnen - figure.Draw(curPos); - } -} + // Get figures opposite the current dir and check if they are moving in this dir + // Coordinates transform + Position curOffset; + MapPoint curPt = terrainRenderer.ConvertCoords(GetNeighbour(curPos, dir + 3u), &curOffset); + Position figPos = GetWorld().GetNodePos(curPt) - offset + curOffset; -void GameWorldView::DrawMovingFiguresFromBelow(const TerrainRenderer& terrainRenderer, const DrawPoint& curPos, - std::vector& between_lines) -{ - // First draw figures moving towards this point from below - static const std::array aboveDirs = {{Direction::NorthEast, Direction::NorthWest}}; - for(Direction dir : aboveDirs) + for(noBase& figure : GetWorld().GetFigures(curPt)) { - // Get figures opposite the current dir and check if they are moving in this dir - // Coordinates transform - Position curOffset; - MapPoint curPt = terrainRenderer.ConvertCoords(GetNeighbour(curPos, dir + 3u), &curOffset); - Position figPos = GetWorld().GetNodePos(curPt) - offset + curOffset; - - for(noBase& figure : GetWorld().GetFigures(curPt)) - { - if(figure.IsMoving() && static_cast(figure).GetCurMoveDir() == dir) - between_lines.push_back(ObjectBetweenLines(figure, figPos)); - } + if(figure.IsMoving() && static_cast(figure).GetCurMoveDir() == dir) + rq.push_back(ObjDraw(&figure, figPos)); } } @@ -493,7 +536,7 @@ constexpr auto getBqImgs() return imgs; } -void GameWorldView::DrawConstructionAid(const MapPoint& pt, const DrawPoint& curPos) +void GameWorldView::DrawConstructionAid(const MapPoint& pt, const DrawPoint& curPos, std::vector &rq) { BuildingQuality bq = gwv.GetBQ(pt); if(bq != BuildingQuality::Nothing) @@ -501,59 +544,59 @@ void GameWorldView::DrawConstructionAid(const MapPoint& pt, const DrawPoint& cur constexpr auto bqImgs = getBqImgs(); auto* bm = LOADER.GetMapTexture(bqImgs[bq]); // Draw building quality icon - bm->DrawFull(curPos); + //bm->DrawFull(curPos); + rq.push_back(ObjConstructionAid(bm, curPos)); // Show ability to construct military buildings if(GetWorld().GetGGS().isEnabled(AddonId::MILITARY_AID)) { if(!GetWorld().IsMilitaryBuildingNearNode(pt, gwv.GetPlayerId()) - && (bq == BuildingQuality::Hut || bq == BuildingQuality::House || bq == BuildingQuality::Castle + && (bq == BuildingQuality::Hut + || bq == BuildingQuality::House + || bq == BuildingQuality::Castle || bq == BuildingQuality::Harbor)) - LOADER.GetImageN("map_new", 20000)->DrawFull(curPos - DrawPoint(-1, bm->GetSize().y + 5)); + { + //LOADER.GetImageN("map_new", 20000)->DrawFull(curPos - DrawPoint(-1, bm->GetSize().y + 5)); + rq.push_back(ObjConstructionAid(LOADER.GetImageN("map_new", 20000), curPos - DrawPoint(-1, bm->GetSize().y + 5))); + } } } } -void GameWorldView::DrawObject(const MapPoint& pt, const DrawPoint& curPos) const +void GameWorldView::DrawBoundaryStone(const MapPoint& pt, const DrawPoint pos, Visibility vis, std::vector& rq) { - noBase* obj = GetWorld().GetNode(pt).obj; - if(!obj) - return; - - obj->Draw(curPos); -} - -void GameWorldView::DrawBoundaryStone(const MapPoint& pt, const DrawPoint pos, Visibility vis) -{ - if(vis == Visibility::Invisible) - return; + if(vis == Visibility::Invisible) return; const bool isFoW = vis == Visibility::FogOfWar; - const BoundaryStones& boundary_stones = - isFoW ? gwv.GetYoungestFOWNode(pt).boundary_stones : GetWorld().GetNode(pt).boundary_stones; + const BoundaryStones& boundary_stones = isFoW + ? gwv.GetYoungestFOWNode(pt).boundary_stones + : GetWorld().GetNode(pt).boundary_stones; const unsigned char owner = boundary_stones[BorderStonePos::OnPoint]; - if(!owner) - return; + if(!owner) return; const Nation nation = GetWorld().GetPlayer(owner - 1).nation; unsigned player_color = GetWorld().GetPlayer(owner - 1).color; if(isFoW) + { player_color = CalcPlayerFOWDrawColor(player_color); + } const auto curVertexPos = gwv.GetTerrainRenderer().GetVertexPos(pt); for(const auto bPos : helpers::EnumRange{}) { DrawPoint curPos; if(bPos == BorderStonePos::OnPoint) + { curPos = pos; + } else if(boundary_stones[bPos]) - curPos = pos - - DrawPoint((curVertexPos - gwv.GetTerrainRenderer().GetNeighbourVertexPos(pt, toDirection(bPos))) - / 2.0f); - else - continue; - LOADER.boundary_stone_cache[nation].draw(curPos, isFoW ? FOW_DRAW_COLOR : COLOR_WHITE, player_color); + { + curPos = pos - DrawPoint((curVertexPos - gwv.GetTerrainRenderer().GetNeighbourVertexPos(pt, toDirection(bPos))) / 2.0f); + } + else continue; + rq.push_back(ObjBS(LOADER.boundary_stone_cache[nation], curPos, isFoW ? FOW_DRAW_COLOR : COLOR_WHITE, player_color)); + //LOADER.boundary_stone_cache[nation].draw(curPos, isFoW ? FOW_DRAW_COLOR : COLOR_WHITE, player_color); } } diff --git a/libs/s25main/world/GameWorldView.h b/libs/s25main/world/GameWorldView.h index b7d3302e41..c6cd664223 100644 --- a/libs/s25main/world/GameWorldView.h +++ b/libs/s25main/world/GameWorldView.h @@ -5,6 +5,7 @@ #pragma once #include "DrawPoint.h" +#include "gameTypes/Direction.h" #include "gameTypes/MapCoordinates.h" #include "gameTypes/MapTypes.h" #include @@ -26,7 +27,10 @@ class IDrawNodeCallback virtual void onDraw(const MapPoint& pt, const DrawPoint& displayPt) = 0; }; -struct ObjectBetweenLines; +struct ObjDraw; +struct ObjFOW; +struct ObjConstructionAid; +struct ObjBS; class GameWorldView { @@ -126,12 +130,9 @@ class GameWorldView private: void CalcFxLx(); - void DrawBoundaryStone(const MapPoint& pt, DrawPoint pos, Visibility vis); - void DrawObject(const MapPoint& pt, const DrawPoint& curPos) const; - void DrawConstructionAid(const MapPoint& pt, const DrawPoint& curPos); - void DrawFigures(const MapPoint& pt, const DrawPoint& curPos, std::vector& between_lines) const; - void DrawMovingFiguresFromBelow(const TerrainRenderer& terrainRenderer, const DrawPoint& curPos, - std::vector& between_lines); + void DrawBoundaryStone(const MapPoint& pt, DrawPoint pos, Visibility vis, std::vector& rq); + void DrawConstructionAid(const MapPoint& pt, const DrawPoint& curPos, std::vector& rq); + void DrawMovingFiguresFromBelow(Direction dir, const TerrainRenderer& terrainRenderer, const DrawPoint& curPos, std::vector& rq); void DrawNameProductivityOverlay(const TerrainRenderer& terrainRenderer); void DrawProductivity(const noBaseBuilding& no, const DrawPoint& curPos);