From e773b519858a9e15004479117e4e5b66f92617f2 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sat, 18 Jul 2026 13:19:08 +0300 Subject: [PATCH] Working on Android version --- proj-android/app/build.gradle | 12 +- proj-android/app/jni/CMakeLists.txt | 5 +- proj-android/app/jni/src/CMakeLists.txt | 161 +++++++++---- proj-android/app/src/main/AndroidManifest.xml | 4 +- proj-android/build.gradle | 2 +- proj-android/gradle.properties | 11 + .../gradle/wrapper/gradle-wrapper.properties | 2 +- src/AudioPlayerAsync.cpp | 37 +-- src/AudioPlayerAsync.h | 9 +- src/BoneAnimatedModelNew.cpp | 4 +- src/Environment.cpp | 2 +- src/Game.cpp | 221 +++++++----------- src/ScriptEngine.cpp | 38 ++- src/ScriptEngine.h | 2 +- src/main.cpp | 137 ++++++++--- src/render/OpenGlExtensions.h | 4 +- src/render/ShadowMap.cpp | 6 +- src/render/TextRenderer.cpp | 23 +- src/render/TextRenderer.h | 2 +- 19 files changed, 418 insertions(+), 264 deletions(-) diff --git a/proj-android/app/build.gradle b/proj-android/app/build.gradle index 13f6b57..71b3cb2 100644 --- a/proj-android/app/build.gradle +++ b/proj-android/app/build.gradle @@ -11,10 +11,10 @@ android { if (buildAsApplication) { namespace "org.libsdl.app" } - compileSdkVersion 34 + compileSdkVersion 37 defaultConfig { - minSdkVersion 19 - targetSdkVersion 34 + minSdkVersion 21 + targetSdkVersion 37 versionCode 1 versionName "1.0" externalNativeBuild { @@ -23,8 +23,8 @@ android { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' }*/ cmake { - arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static" - cppFlags "-std=c++11 -frtti -fexceptions" + arguments /*"-DANDROID_APP_PLATFORM=android-19",*/ "-DANDROID_STL=c++_static" + cppFlags "-std=c++17 -frtti -fexceptions" abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } } @@ -32,7 +32,7 @@ android { buildTypes { release { minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } applicationVariants.all { variant -> diff --git a/proj-android/app/jni/CMakeLists.txt b/proj-android/app/jni/CMakeLists.txt index b40ad99..ec0946b 100644 --- a/proj-android/app/jni/CMakeLists.txt +++ b/proj-android/app/jni/CMakeLists.txt @@ -21,7 +21,7 @@ set(TP_ROOT "${THIRDPARTY_DIR}") # ============================================================================== # --- ZLIB --- -add_subdirectory("${TP_ROOT}/zlib-1.3.1" zlib-build) +#add_subdirectory("${TP_ROOT}/zlib-1.3.2" zlib-build) # --- LIBPNG --- set(PNG_STATIC ON CACHE BOOL "Build static library" FORCE) @@ -39,6 +39,9 @@ add_subdirectory("${TP_ROOT}/libpng-1.6.51" libpng-build) # --- SDL2 --- # Android-версия SDL требует специфичных настроек, но add_subdirectory обычно подхватывает их сама add_subdirectory("${TP_ROOT}/SDL-release-2.32.10" sdl-build) +#add_subdirectory("${TP_ROOT}/SDL_mixer-release-2.8.0" sdl-mixer-build) + +add_subdirectory("${TP_ROOT}/SDL_ttf-release-2.24.0" sdl-ttf-build) # --- LIBZIP --- # Отключаем поиск системных крипто-библиотек, так как в NDK их может не быть в стандартных путях diff --git a/proj-android/app/jni/src/CMakeLists.txt b/proj-android/app/jni/src/CMakeLists.txt index 2744580..9497c26 100644 --- a/proj-android/app/jni/src/CMakeLists.txt +++ b/proj-android/app/jni/src/CMakeLists.txt @@ -33,62 +33,143 @@ endforeach() # Создаем кастомную цель, которая будет запускать процесс копирования add_custom_target(sync_resources ALL DEPENDS ${RES_OUTPUTS}) +set(LUA_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/lua-5.4.8") +file(GLOB LUA_SOURCES "${LUA_SRC_DIR}/*.c") +list(REMOVE_ITEM LUA_SOURCES + "${LUA_SRC_DIR}/lua.c" + "${LUA_SRC_DIR}/luac.c" + "${LUA_SRC_DIR}/onelua.c" +) +add_library(lua_static STATIC ${LUA_SOURCES}) +target_include_directories(lua_static PUBLIC "${LUA_SRC_DIR}") + + add_library(main SHARED - SDL_android_main.c - ../../../../src/BoneAnimatedModel.cpp - ../../../../src/Environment.cpp - ../../../../src/Game.cpp + SDL_android_main.c ../../../../src/main.cpp - ../../../../src/Projectile.cpp - ../../../../src/SparkEmitter.cpp - ../../../../src/TextModel.cpp - ../../../../src/UiManager.cpp - ../../../../src/utils/Perlin.cpp - ../../../../src/utils/TaskManager.cpp - ../../../../src/utils/Utils.cpp - ../../../../src/navigation/PathFinder.cpp - ../../../../src/planet/PlanetData.cpp - ../../../../src/planet/PlanetObject.cpp - ../../../../src/planet/StoneObject.cpp - ../../../../src/render/FrameBuffer.cpp - ../../../../src/render/ShadowMap.cpp + ../../../../src/Game.cpp + ../../../../src/Game.h + ../../../../src/Character.cpp + ../../../../src/Character.h + ../../../../src/CharacterState.cpp + ../../../../src/CharacterState.h + ../../../../src/Environment.cpp + ../../../../src/Environment.h + ../../../../src/Localization.cpp + ../../../../src/Localization.h ../../../../src/render/Renderer.cpp + ../../../../src/render/Renderer.h ../../../../src/render/ShaderManager.cpp + ../../../../src/render/ShaderManager.h ../../../../src/render/TextureManager.cpp + ../../../../src/render/TextureManager.h + ../../../../src/TextModel.cpp + ../../../../src/TextModel.h + ../../../../src/AudioPlayerAsync.cpp + ../../../../src/AudioPlayerAsync.h + ../../../../src/BoneAnimatedModelNew.cpp + ../../../../src/BoneAnimatedModelNew.h ../../../../src/render/OpenGlExtensions.cpp -) - -# Подключаем заголовки -target_include_directories(main PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../SDL/include - ${CMAKE_CURRENT_SOURCE_DIR}/../zlib - ${CMAKE_CURRENT_SOURCE_DIR}/../libpng - ${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/eigen-5.0.0 - ${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/boost_1_90_0 - ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src - ${CMAKE_CURRENT_SOURCE_DIR}/../libzip -) - -# ВАЖНО: Линкуемся с png_static (статика) или png_shared (динамика) -# Так как мы установили PNG_STATIC=ON и PNG_SHARED=OFF, -# должна создаться цель png_static -target_link_libraries(main - png_static # ← ЭТО ПРАВИЛЬНОЕ ИМЯ ЦЕЛИ! - z - SDL2 + ../../../../src/render/OpenGlExtensions.h + ../../../../src/utils/Utils.cpp + ../../../../src/utils/Utils.h + ../../../../src/SparkEmitter.cpp + ../../../../src/SparkEmitter.h + ../../../../src/TeleportZone.h + ../../../../src/TeleportZone.cpp + ../../../../src/utils/TaskManager.cpp + ../../../../src/utils/TaskManager.h + ../../../../src/render/FrameBuffer.cpp + ../../../../src/render/FrameBuffer.h + ../../../../src/render/ShadowMap.cpp + ../../../../src/render/ShadowMap.h + ../../../../src/UiManager.cpp + ../../../../src/UiManager.h + ../../../../src/render/TextRenderer.h + ../../../../src/render/TextRenderer.cpp + ../../../../src/MenuManager.h + ../../../../src/MenuManager.cpp + ../../../../src/Location.h + ../../../../src/Location.cpp + ../../../../src/LocationState.h + ../../../../src/LocationState.cpp + ../../../../src/LocationEditor.h + ../../../../src/LocationEditor.cpp + ../../../../src/NpcCar.h + ../../../../src/NpcCar.cpp + ../../../../src/GameConstants.h + ../../../../src/GameConstants.cpp + ../../../../src/GameState.h + ../../../../src/GameState.cpp + ../../../../src/ScriptEngine.h + ../../../../src/ScriptEngine.cpp + ../../../../src/navigation/PathFinder.h + ../../../../src/navigation/PathFinder.cpp + ../../../../src/items/GameObjectLoader.h + ../../../../src/items/GameObjectLoader.cpp + ../../../../src/items/Item.h + ../../../../src/items/Item.cpp + ../../../../src/items/ItemRegistry.h + ../../../../src/items/ItemRegistry.cpp + ../../../../src/items/InteractiveObject.h + ../../../../src/items/InteractiveObject.cpp + ../../../../src/items/InteractiveObjectState.h + ../../../../src/dialogue/DialogueTypes.h + ../../../../src/dialogue/DialogueDatabase.h + ../../../../src/dialogue/DialogueDatabase.cpp + ../../../../src/dialogue/DialogueRuntime.h + ../../../../src/dialogue/DialogueRuntime.cpp + ../../../../src/dialogue/DialogueOverlay.h + ../../../../src/dialogue/DialogueOverlay.cpp + ../../../../src/dialogue/DialogueSystem.h + ../../../../src/dialogue/DialogueSystem.cpp + ../../../../src/dialogue/TranslationDatabase.h + ../../../../src/dialogue/TranslationDatabase.cpp + ../../../../src/quest/QuestTypes.h + ../../../../src/quest/QuestJournal.h + ../../../../src/quest/QuestJournal.cpp + ../../../../src/cutscene/CutsceneTypes.h + ../../../../src/cutscene/CutsceneDatabase.h + ../../../../src/cutscene/CutsceneDatabase.cpp + ../../../../src/cutscene/CutsceneRuntime.h + ../../../../src/cutscene/CutsceneRuntime.cpp + ../../../../src/cutscene/CutsceneOverlay.h + ../../../../src/cutscene/CutsceneOverlay.cpp + ../../../../src/render/UiQuad.h ) -find_library(OPENGLES2_LIB GLESv2) + + +find_library(OPENGLES3_LIB GLESv3) target_link_libraries(main - ${OPENGLES2_LIB} # OpenGL ES 2.0/3.0 + ${OPENGLES3_LIB} + png_static + z + SDL2 + SDL2_ttf log android OpenSLES dl - zip + zip + lua_static + #freetype +) + +# Подключаем заголовки +target_include_directories(main PRIVATE + #${CMAKE_CURRENT_SOURCE_DIR}/../SDL/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/SDL_ttf-release-2.24.0/external/freetype/include + #${CMAKE_CURRENT_SOURCE_DIR}/../zlib + #${CMAKE_CURRENT_SOURCE_DIR}/../libpng + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/eigen-5.0.0 + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/boost_1_90_0 + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/sol2-3.3.0/include + #${CMAKE_CURRENT_SOURCE_DIR}/../libzip ) add_dependencies(main sync_resources) diff --git a/proj-android/app/src/main/AndroidManifest.xml b/proj-android/app/src/main/AndroidManifest.xml index 5157374..3b0863e 100644 --- a/proj-android/app/src/main/AndroidManifest.xml +++ b/proj-android/app/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ android:installLocation="auto"> - + --> - + +#endif #include #include #include @@ -51,17 +53,18 @@ private: std::queue> taskQueue; #endif bool stop = false; - +#if 0 std::unordered_map soundCache; std::mutex soundCacheMutex; std::string currentTrack; - Mix_Music* currentMusic_ = nullptr; + Mix_Music* currentMusic_ = nullptr; +#endif int musicVolume_ = 128; int soundVolume_ = 128; bool musicEnabled_ = true; bool soundEnabled_ = true; bool initialized = false; -}; \ No newline at end of file +}; diff --git a/src/BoneAnimatedModelNew.cpp b/src/BoneAnimatedModelNew.cpp index 958af73..463908f 100644 --- a/src/BoneAnimatedModelNew.cpp +++ b/src/BoneAnimatedModelNew.cpp @@ -528,12 +528,14 @@ namespace FRG } else { + /* std::ifstream f(fileName, std::ios::binary | std::ios::ate); if (!f.is_open()) throw std::runtime_error("Failed to open binary file: " + fileName); std::streamsize fileSize = f.tellg(); f.seekg(0); fileData.resize(static_cast(fileSize)); - f.read(fileData.data(), fileSize); + f.read(fileData.data(), fileSize);*/ + fileData = readFile(fileName); } const char* ptr = fileData.data(); diff --git a/src/Environment.cpp b/src/Environment.cpp index 4bdf8e1..158d72f 100644 --- a/src/Environment.cpp +++ b/src/Environment.cpp @@ -23,7 +23,7 @@ int Environment::height = CONST_DEFAULT_HEIGHT; float Environment::zoom = DEFAULT_ZOOM; -bool Environment::enableLogging = false; +bool Environment::enableLogging = true; SDL_Window* Environment::window = nullptr; diff --git a/src/Game.cpp b/src/Game.cpp index 79cc94c..c20005a 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -24,11 +24,16 @@ #include "GameConstants.h" +#ifdef __ANDROID__ +extern bool isAppActive; +#endif + namespace FRG { static const float zoomMin = 6.0f; static const float zoomMax = 20.0f; + #ifdef EMSCRIPTEN const char* CONST_ZIP_FILE = "resources.zip"; #else @@ -81,8 +86,10 @@ namespace FRG } void Game::setup() { +#ifndef __ANDROID__ Environment::width = Environment::CONST_DEFAULT_WIDTH; Environment::height = Environment::CONST_DEFAULT_HEIGHT; +#endif Environment::computeProjectionDimensions(); FRG::BindOpenGlFunctions(); @@ -156,7 +163,9 @@ namespace FRG }); loadSteps.push([this]() { -#if defined(EMSCRIPTEN) + + +#if defined(EMSCRIPTEN) || defined(__ANDROID__) renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_web.fragment", CONST_ZIP_FILE); @@ -181,7 +190,14 @@ namespace FRG renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning_web.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow_web.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow_web.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE); -/*#elif defined(__linux__) + + renderer.shaderManager.AddShaderFromFiles("text2d", + "resources/shaders/text2d.vertex", + "resources/shaders/text2d_web.fragment", + CONST_ZIP_FILE + ); + + /*#elif defined(__linux__) renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_desktop.fragment", CONST_ZIP_FILE); @@ -233,6 +249,12 @@ namespace FRG renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning.vertex", "resources/shaders/night_fog_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE); + + renderer.shaderManager.AddShaderFromFiles("text2d", + "resources/shaders/text2d.vertex", + "resources/shaders/text2d_desktop.fragment", + CONST_ZIP_FILE + ); #endif }); @@ -1175,6 +1197,23 @@ namespace FRG Environment::exitGameLoop = true; } +#ifdef __ANDROID__ + if (event.type == SDL_APP_WILLENTERBACKGROUND) { + isAppActive = false; + __android_log_print(ANDROID_LOG_INFO, "Game", "App moving to background, suspending loop"); + break; + } + if (event.type == SDL_APP_DIDENTERFOREGROUND) { + isAppActive = true; + __android_log_print(ANDROID_LOG_INFO, "Game", "App returned to foreground, resuming loop"); + break; + } + /* + __android_log_print(ANDROID_LOG_ERROR, "Game", + "Game::update step 1 event type: %d", event.type); + */ +#endif + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) { const bool wasPortrait = Environment::width < Environment::height; @@ -1211,7 +1250,12 @@ namespace FRG // Suppress all input while portrait overlay is shown //if (mobileIsPortrait) continue; - + /* +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, "Game", + "Game::update step 2 event type: %d", event.type); +#endif +*/ // Ignore pointer input until the initial loading screen has finished. // Otherwise a click on the loading screen (e.g. at the spot where a // main-menu button will later be) gets delivered to the menu UI as @@ -1222,6 +1266,12 @@ namespace FRG continue; } + /* +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, "Game", + "Game::update step 3 event type: %d", event.type); +#endif +*/ #ifdef __ANDROID__ if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) { Environment::exitGameLoop = true; @@ -1232,6 +1282,12 @@ namespace FRG // SDL_HINT_TOUCH_MOUSE_EVENTS="0" is set in main.cpp so these do not // also fire SDL_MOUSEBUTTONDOWN, preventing double-processing. if (event.type == SDL_FINGERDOWN || event.type == SDL_FINGERUP || event.type == SDL_FINGERMOTION) { +/* +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, "Game", + "Game::update step 4 event type: %d", event.type); +#endif + */ int eventX = static_cast(event.tfinger.x * Environment::width); int eventY = static_cast(event.tfinger.y * Environment::height); int mx = static_cast(event.tfinger.x * Environment::projectionWidth); @@ -1249,7 +1305,12 @@ namespace FRG } continue; } - + /* +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, "Game", + "Game::update step 5 event type: %d", event.type); +#endif +*/ // Mouse-left maps to a single virtual touch with MOUSE_FINGER_ID. // Right mouse is intentionally unused now — camera rotation is on hold-and-drag. if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) { @@ -1304,136 +1365,6 @@ namespace FRG if (event.type == SDL_KEYDOWN && event.key.repeat == 0) { switch (event.key.keysym.sym) { - /*case SDLK_8: - if (editorMode == EditorMode::InteractiveObjects && currentLocation()) { - currentLocation()->editor.selectInteractiveObject(8); - } else { - gameState.isDawn = !gameState.isDawn; - if (gameState.isDawn) gameState.isNight = true; - } - break; - case SDLK_9: - if (editorMode == EditorMode::InteractiveObjects && currentLocation()) { - currentLocation()->editor.selectInteractiveObject(9); - } else { - startNightTransition(); - } - break; - case SDLK_0: - - if (editorMode == EditorMode::InteractiveObjects && currentLocation()) { - currentLocation()->editor.selectInteractiveObject(event.key.keysym.sym - SDLK_0); - } - else { - currentLocation()->requestDarklandsTransition(); - } - break; - case SDLK_1: - case SDLK_2: - case SDLK_3: - case SDLK_4: - case SDLK_5: - case SDLK_6: - case SDLK_7: - - if (editorMode == EditorMode::InteractiveObjects && currentLocation()) { - currentLocation()->editor.selectInteractiveObject(event.key.keysym.sym - SDLK_0); - } else { - currentLocation()->switchNavigation(event.key.keysym.sym - SDLK_0); - logger() << "Switched to nav mesh " << (event.key.keysym.sym - SDLK_0) << std::endl; - } - break; - case SDLK_f: - currentLocation()->dialogueSystem.startDialogue("phone_night_aiperi001"); - - - break; - case SDLK_e: - currentLocation()->dialogueSystem.startCutscene("computer_cutscene001"); //.startDialogue("test_cutscene_pan_dialogue"); - break; - - - case SDLK_n: - if (editorMode == EditorMode::None) - editorMode = EditorMode::Navigation; - else if (editorMode == EditorMode::Navigation) - editorMode = EditorMode::InteractiveObjects; - else - editorMode = EditorMode::None; - if (currentLocation()) { - currentLocation()->editorMode = editorMode; - if (editorMode == EditorMode::Navigation) - currentLocation()->editor.buildNavMeshes(); - else if (editorMode == EditorMode::InteractiveObjects) - currentLocation()->editor.buildInteractiveObjectBoundsMeshes(); - } - { - const char* modeName = (editorMode == EditorMode::Navigation) ? "Navigation" - : (editorMode == EditorMode::InteractiveObjects) ? "InteractiveObjects" - : "None"; - logger() << "[EDITOR] Mode: " << modeName << std::endl; - } - break; - - case SDLK_o: - x = x + 0.05; - logger() << "current x: " << x << std::endl; - //y = y + 0.002; - //currentLocation->player->hp = 200; - //currentLocation->npcs[0]->walkSpeed += 0.01f; - //logger() << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl; - break; - - case SDLK_k: - x = x - 0.05; - logger() << "current x: " << x << std::endl; - logger() << "Player pos: " << currentLocation()->player->state.position.transpose() << std::endl; - logger() << "Player rotation: " << currentLocation()->player->state.facingAngle*180.0/M_PI << std::endl; - //currentLocation->npcs[0]->walkSpeed -= 0.01f; - //logger() << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl; - break; - - case SDLK_p: - gameState.currentLocationName = "uni_interior"; - currentLocation()->player->state.position = Eigen::Vector3f(-0.0189243, 0, -13.4314); - currentLocation()->player->setTarget(currentLocation()->player->state.position); - //logger() << "Switched to location " << ((currentLocation == locations["location1"]) ? "1" : "2") << std::endl; - break; - - case SDLK_l: - //x = x - 1; - //logger() << "current x: " << x << std::endl; - logger() << "Azimuth: " << currentLocation()->state.cameraAzimuth << std::endl; - logger() << "Inclination: " << currentLocation()->state.cameraInclination << std::endl; - break; - - case SDLK_c: - logger() << "SLOW-MO activated!" << std::endl; - activateSlowMoEffect(); - break; - - case SDLK_i: - y = y - 1; - logger() << "current y: " << y << std::endl; - break; - - case SDLK_b: - if (editorMode != EditorMode::None && currentLocation()) { - currentLocation()->editor.saveAll(); - } - break; - - case SDLK_j: - currentLocation()->editor.placeTree(); - break; - - case SDLK_v: - if (editorMode != EditorMode::None && currentLocation()) { - currentLocation()->editor.saveObjects(); - } - break; - - case SDLK_RETURN:*/ default: break; } @@ -1586,21 +1517,41 @@ namespace FRG const int uiX = mx; const int uiY = Environment::projectionHeight - my; + +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, "Game", + "Game::onPointerDown step 1"); +#endif menuManager.topUiManager.onTouchDown(fingerId, uiX, uiY); const bool capturedByTopUi = menuManager.topUiManager.isUiInteractionForFinger(fingerId); const bool dialogueActive = currentLocation() && currentLocation()->dialogueSystem.isActive(); if (!dialogueActive && !capturedByTopUi) { +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, "Game", + "Game::onPointerDown step 2"); +#endif menuManager.uiManager.onTouchDown(fingerId, uiX, uiY); } st.capturedByUi = capturedByTopUi || (!dialogueActive && menuManager.uiManager.isUiInteractionForFinger(fingerId)); activePointers[fingerId] = st; - +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, "Game", + "Game::onPointerDown step 3"); +#endif if (st.capturedByUi) { // UI button / slider / text-field grabbed this press; don't drive // gameplay or pinch from it. +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, "Game", + "Game::onPointerDown step 4"); +#endif return; } +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, "Game", + "Game::onPointerDown step 5"); +#endif if (countNonUiPointers() >= 2) { // Second finger landed on the gameplay area: switch to pinch-zoom diff --git a/src/ScriptEngine.cpp b/src/ScriptEngine.cpp index b845897..b80a07d 100644 --- a/src/ScriptEngine.cpp +++ b/src/ScriptEngine.cpp @@ -861,7 +861,39 @@ namespace FRG { } }); - lua.script_file(scriptPath); + //lua.script_file(scriptPath); + +#ifdef __ANDROID__ + // Читаем файл в строку через SDL_RWops + std::string scriptContent = readTextFile(scriptPath); + if (scriptContent.empty()) { + std::cerr << "[script] Failed to read script file or file is empty: " << scriptPath << "\n"; + } + else { + + // Загружаем (компилируем) строку скрипта. Вторым аргументом передаем имя для логов + auto loadResult = lua.load(scriptContent, scriptPath); + if (!loadResult.valid()) { + sol::error err = loadResult; + std::cerr << "[script] Lua compile error: " << err.what() << "\n"; + } + else { + // Если компиляция успешна — вызываем функцию для выполнения кода скрипта + auto runResult = loadResult(); + if (!runResult.valid()) { + sol::error err = runResult; + std::cerr << "[script] Lua runtime error: " << err.what() << "\n"; + } + } + } +#else + // На остальных платформах оставляем стандартную загрузку из файла + auto result = lua.script_file(scriptPath, sol::script_pass_on_error); + if (!result.valid()) { + sol::error err = result; + std::cerr << "[script] Lua load error: " << err.what() << "\n"; + } +#endif } void ScriptEngine::callNpcInteractCallback(int npcIndex) { @@ -885,14 +917,14 @@ namespace FRG { std::cerr << "[SCRIPT] Lua function 'on_npc_interact' not found!" << std::endl; } } - +/* void ScriptEngine::runScript(const std::string& path) { auto result = impl->lua.safe_script_file(path, sol::script_pass_on_error); if (!result.valid()) { sol::error err = result; std::cerr << "[script] Error in " << path << ": " << err.what() << "\n"; } - } + }*/ void ScriptEngine::callActivateFunction(const std::string& functionName) { if (!impl) { diff --git a/src/ScriptEngine.h b/src/ScriptEngine.h index 06a40e2..95587b6 100644 --- a/src/ScriptEngine.h +++ b/src/ScriptEngine.h @@ -22,7 +22,7 @@ public: void init(Location* game, Inventory* inventory, AudioPlayerAsync* audioPlayer, const std::string& scriptPath); // Execute a Lua script file. Logs errors to stderr. - void runScript(const std::string& path); + //void runScript(const std::string& path); void callItemPickupCallback(const std::string& objectName); diff --git a/src/main.cpp b/src/main.cpp index 7885596..715bb4b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -117,6 +117,8 @@ int main(int argc, char* argv[]) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + FRG::Environment::window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); glContext_x = SDL_GL_CreateContext(FRG::Environment::window); @@ -149,40 +151,61 @@ int main(int argc, char* argv[]) { #ifdef __ANDROID__ -extern "C" int SDL_main(int argc, char* argv[]) { - // Инициализация SDL перед получением информации о дисплее +#include +#include + +void Android_InitTouchDevices() { + JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv(); + if (!env) { + __android_log_print(ANDROID_LOG_ERROR, "Game", "[Android] Failed to get JNIEnv for touch init"); + return; + } + + jclass activityClass = env->FindClass("org/libsdl/app/SDLActivity"); + if (!activityClass) { + __android_log_print(ANDROID_LOG_ERROR, "Game", "[Android] Failed to find SDLActivity class"); + return; + } + + jmethodID initTouchMethod = env->GetStaticMethodID(activityClass, "initTouch", "()V"); + if (!initTouchMethod) { + __android_log_print(ANDROID_LOG_ERROR, "Game", "[Android] Failed to find initTouch method"); + return; + } + + env->CallStaticVoidMethod(activityClass, initTouchMethod); + __android_log_print(ANDROID_LOG_INFO, "Game", "[Android] JNI initTouch() successfully invoked"); +} + +// Переменная для отслеживания фокуса приложения на Android +bool isAppActive = true; + + +int main(int argc, char* argv[]) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) { __android_log_print(ANDROID_LOG_ERROR, "Game", "SDL init failed: %s", SDL_GetError()); return 1; } - SDL_DisplayMode displayMode; - if (SDL_GetCurrentDisplayMode(0, &displayMode) != 0) { - __android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GetCurrentDisplayMode failed: %s", SDL_GetError()); - SDL_Quit(); - return 1; - } - FRG::Environment::width = displayMode.w; - FRG::Environment::height = displayMode.h; - - __android_log_print(ANDROID_LOG_INFO, "Game", "Display resolution: %dx%d", - FRG::Environment::width, FRG::Environment::height); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0"); + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); + // Передаем 0, 0 — на Android SDL автоматически растянет окно + // на весь экран с учетом системной ориентации Активити FRG::Environment::window = SDL_CreateWindow( "Shadow Over Bishkek", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - FRG::Environment::width, FRG::Environment::height, + 0, 0, SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN ); @@ -208,32 +231,87 @@ extern "C" int SDL_main(int argc, char* argv[]) { return 1; } - // Настройка VSync + // СТРОГО ТУТ: Запрашиваем у созданного окна реальный размер буфера + // Это исключит баг перевернутых координат ширины и высоты + int realWidth, realHeight; + SDL_GL_GetDrawableSize(FRG::Environment::window, &realWidth, &realHeight); + FRG::Environment::width = realWidth; + FRG::Environment::height = realHeight; + + __android_log_print(ANDROID_LOG_INFO, "Game", "Actual Render Resolution: %dx%d", + FRG::Environment::width, FRG::Environment::height); + + // Теперь, когда окно гарантированно создано в правильной ориентации, + // привязываем тач-устройства на стороне Java + Android_InitTouchDevices(); + + if (SDL_GL_SetSwapInterval(1) < 0) { __android_log_print(ANDROID_LOG_WARN, "Game", "Unable to set VSync: %s", SDL_GetError()); } - // Настройка игры try { - game.setup(); + g_game = new FRG::Game(); + g_game->setup(); } catch (const std::exception& e) { __android_log_print(ANDROID_LOG_ERROR, "Game", "Game setup failed: %s", e.what()); + if (g_game) { + delete g_game; + g_game = nullptr; + } + SDL_GL_DeleteContext(ctx); + SDL_DestroyWindow(FRG::Environment::window); + SDL_Quit(); return 1; } - while (!game.shouldExit()) { - game.update(); - SDL_Delay(16); + while (!g_game->shouldExit()) { + SDL_Event event; + // Обрабатываем системные события Android до обновления кадра + /*while (SDL_PollEvent(&event)) { + // Перенаправляем событие в игру, чтобы сработал ваш внутренний цикл (память, ресайз и т.д.) + // Примечание: внутри Game::update у вас тоже стоит PollEvent. + // Чтобы события не терялись, лучше обрабатывать их в одном месте, + // но жизненно важные для ОС флаги перехватываем здесь: + if (event.type == SDL_QUIT) { + break; + } + + // Сворачивание / Разворачивание приложения в Android + if (event.type == SDL_APP_WILLENTERBACKGROUND) { + isAppActive = false; + __android_log_print(ANDROID_LOG_INFO, "Game", "App moving to background, suspending loop"); + } + if (event.type == SDL_APP_DIDENTERFOREGROUND) { + isAppActive = true; + __android_log_print(ANDROID_LOG_INFO, "Game", "App returned to foreground, resuming loop"); + } + }*/ + + if (isAppActive) { + g_game->update(); + // На Android шаг кадров завязан на VSync (SwapInterval = 1). + // SDL_Delay(16) здесь не нужен, он будет вызывать микро-фризы (stuttering). + } + else { + // Если игра свернута — спим, чтобы не нагружать CPU/GPU + SDL_Delay(100); + } } + delete g_game; + g_game = nullptr; + + SDL_GL_DeleteContext(ctx); + SDL_DestroyWindow(FRG::Environment::window); + SDL_Quit(); + return 0; - } - - #endif +#ifndef __ANDROID__ #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__linux__) // Read the persisted fullscreen preference (if any) before creating the @@ -417,3 +495,4 @@ int main(int argc, char* argv[]) { return 0; } #endif +#endif \ No newline at end of file diff --git a/src/render/OpenGlExtensions.h b/src/render/OpenGlExtensions.h index ddb56c1..2d644c9 100644 --- a/src/render/OpenGlExtensions.h +++ b/src/render/OpenGlExtensions.h @@ -12,8 +12,8 @@ #ifdef __ANDROID__ - #include - #include + #include + #include #else #ifdef __linux__ #include diff --git a/src/render/ShadowMap.cpp b/src/render/ShadowMap.cpp index df5e756..410e671 100644 --- a/src/render/ShadowMap.cpp +++ b/src/render/ShadowMap.cpp @@ -87,13 +87,13 @@ namespace FRG { GL_TEXTURE_2D, depthTexture, 0); // No color buffer for this FBO — depth only. -#ifdef EMSCRIPTEN +//#ifdef EMSCRIPTEN GLenum drawBuffers[] = { GL_NONE }; glDrawBuffers(1, drawBuffers); -#else +/*#else glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); -#endif +#endif*/ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { diff --git a/src/render/TextRenderer.cpp b/src/render/TextRenderer.cpp index 58644c0..0b6af2b 100644 --- a/src/render/TextRenderer.cpp +++ b/src/render/TextRenderer.cpp @@ -1,6 +1,8 @@ #include "render/TextRenderer.h" + #include #include FT_FREETYPE_H +//#include "freetype/freetype.h" #include "Environment.h" #include "render/OpenGlExtensions.h" @@ -50,27 +52,6 @@ bool TextRenderer::init(Renderer& renderer, const std::string& ttfPath, int pixe r = &renderer; - static bool shaderAlreadyAdded = false; - - if (shaderAlreadyAdded == false) - { - shaderAlreadyAdded = true; - -#ifdef EMSCRIPTEN - r->shaderManager.AddShaderFromFiles(shaderName, - "resources/shaders/text2d.vertex", - "resources/shaders/text2d_web.fragment", - zipfilename - ); -#else - r->shaderManager.AddShaderFromFiles(shaderName, - "resources/shaders/text2d.vertex", - "resources/shaders/text2d_desktop.fragment", - zipfilename - ); -#endif - - } FRG::CheckGlError(__FILE__, __LINE__); if (!loadGlyphs(ttfPath, pixelSize, zipfilename)) return false; FRG::CheckGlError(__FILE__, __LINE__); diff --git a/src/render/TextRenderer.h b/src/render/TextRenderer.h index 43f3d37..59fb4b8 100644 --- a/src/render/TextRenderer.h +++ b/src/render/TextRenderer.h @@ -49,7 +49,7 @@ private: VertexRenderStruct textMesh; - std::string shaderName = "text2d"; + const std::string shaderName = "text2d"; // caching for static texts struct CachedText {