diff --git a/CMakeLists.txt b/CMakeLists.txt index 552a4d9e4..0efb55525 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,11 @@ set(CMAKE_RELWITHDEBINFO_POSTFIX "") set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_SKIP_RPATH ON) -add_executable(${PROJECT_NAME}) +if(ANDROID) + add_library(${PROJECT_NAME} SHARED) +else() + add_executable(${PROJECT_NAME}) +endif() if(MSVC) add_definitions(-D_USE_MATH_DEFINES) @@ -59,6 +63,12 @@ elseif(IOS) MACOSX_BUNDLE_BUNDLE_VERSION 1 MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0" XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer") +elseif(ANDROID) + # Android build configuration + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + # Build as shared library for Android + set_target_properties(${PROJECT_NAME} PROPERTIES + LIBRARY_OUTPUT_NAME "opengothic") elseif(APPLE) enable_language(OBJCXX) endif() @@ -96,6 +106,12 @@ endif() if(WIN32) target_link_libraries(${PROJECT_NAME} shlwapi DbgHelp) +elseif(ANDROID) + # Add android_native_app_glue + set(ANDROID_NATIVE_APP_GLUE_DIR "${ANDROID_NDK}/sources/android/native_app_glue") + target_sources(${PROJECT_NAME} PRIVATE "${ANDROID_NATIVE_APP_GLUE_DIR}/android_native_app_glue.c") + target_include_directories(${PROJECT_NAME} PRIVATE "${ANDROID_NATIVE_APP_GLUE_DIR}") + target_link_libraries(${PROJECT_NAME} android log) elseif(UNIX) target_link_libraries(${PROJECT_NAME} -lpthread -ldl) endif() diff --git a/game/camera.cpp b/game/camera.cpp index 4019a37de..8710b0da7 100644 --- a/game/camera.cpp +++ b/game/camera.cpp @@ -80,7 +80,32 @@ void Camera::changeZoom(int delta) { void Camera::setViewport(uint32_t w, uint32_t h) { const float fov = Gothic::options().cameraFov; - proj.perspective(fov, float(w)/float(h), zNear(), zFar()); + + // On Android, we may need to swap width/height for projection matrix due to Vulkan transform issues + uint32_t projW = w, projH = h; + +#if defined(__ANDROID__) + // Check if we're in landscape mode and if Vulkan transform might be incorrect + if(w > h) { + // Landscape mode - Vulkan transform might be ROTATE_90 when it should be IDENTITY + // We'll use the actual window dimensions for correct aspect ratio + Tempest::Log::i("Camera::setViewport: Android landscape mode detected"); + projW = w; + projH = h; + } else { + // Portrait mode + Tempest::Log::i("Camera::setViewport: Android portrait mode detected"); + projW = w; + projH = h; + } +#endif + + float aspectRatio = float(projW)/float(projH); + proj.perspective(fov, aspectRatio, zNear(), zFar()); + + // Log viewport changes for rotation debugging + const char* orientation = (w > h) ? "LANDSCAPE" : "PORTRAIT"; + Tempest::Log::i("Camera::setViewport: ", w, "x", h, " (", orientation, ") aspect=", aspectRatio); // NOTE: usually depth bouds are from 0.95 to 1.0, resilting into ~805675 discrete values static float l = 0.951978f, r = 1; diff --git a/game/main.cpp b/game/main.cpp index de00118e9..f28559a42 100644 --- a/game/main.cpp +++ b/game/main.cpp @@ -14,10 +14,18 @@ #include #endif -#if defined(__IOS__) +#if defined(__IOS__) || defined(__ANDROID__) #include "utils/installdetect.h" #endif +#if defined(__ANDROID__) +#include +#include +extern "C" void tempest_android_main(struct android_app* app); +static struct android_app* g_android_app = nullptr; +int main(int argc, const char** argv); +#endif + #include "utils/crashlog.h" #include "mainwindow.h" #include "gothic.h" @@ -53,7 +61,7 @@ std::unique_ptr mkApi(const CommandLine& g) { break; #endif case CommandLine::Vulkan: -#if !defined(__APPLE__) +#if !defined(__APPLE__) || defined(__ANDROID__) return std::make_unique(flg); #else break; @@ -67,12 +75,35 @@ std::unique_ptr mkApi(const CommandLine& g) { #endif } +#if defined(__ANDROID__) +extern "C" void android_main(struct android_app* app) { + g_android_app = app; + tempest_android_main(app); + + // Set up working directory + auto appdir = InstallDetect::applicationSupportDirectory(); + if(!appdir.empty()) { + std::filesystem::current_path(appdir); + } + + const char* argv[] = {"opengothic", nullptr}; + main(1, argv); +} +#endif + int main(int argc,const char** argv) { #if defined(__IOS__) { auto appdir = InstallDetect::applicationSupportDirectory(); std::filesystem::current_path(appdir); } +#elif defined(__ANDROID__) + { + auto appdir = InstallDetect::applicationSupportDirectory(); + if(!appdir.empty()) { + std::filesystem::current_path(appdir); + } + } #endif try { diff --git a/game/mainwindow.cpp b/game/mainwindow.cpp index 1d22d40e2..763b36e01 100644 --- a/game/mainwindow.cpp +++ b/game/mainwindow.cpp @@ -281,8 +281,17 @@ void MainWindow::paintEvent(PaintEvent& event) { void MainWindow::resizeEvent(SizeEvent&) { for(auto& i:fence) i.wait(); + + auto rectBefore = SystemApi::windowClientRect(hwnd()); + Tempest::Log::i("MainWindow::resizeEvent: Starting resize - window: ", rectBefore.w, "x", rectBefore.h); + swapchain.reset(); renderer.resetSwapchain(); + + auto rectAfter = SystemApi::windowClientRect(hwnd()); + Tempest::Log::i("MainWindow::resizeEvent: After swapchain reset - window: ", rectAfter.w, "x", rectAfter.h); + Tempest::Log::i("MainWindow::resizeEvent: Swapchain size: ", swapchain.w(), "x", swapchain.h()); + if(auto camera = Gothic::inst().camera()) camera->setViewport(swapchain.w(),swapchain.h()); @@ -291,6 +300,8 @@ void MainWindow::resizeEvent(SizeEvent&) { setCursorPosition(rect.w/2,rect.h/2); setCursorShape(fs ? CursorShape::Hidden : CursorShape::Arrow); dMouse = Point(); + + Tempest::Log::i("MainWindow::resizeEvent: Resize completed"); } void MainWindow::mouseDownEvent(MouseEvent &event) { @@ -519,6 +530,7 @@ void MainWindow::keyUpEvent(KeyEvent &event) { if(auto pl = Gothic::inst().player()) rootMenu.setPlayer(*pl); clearInput(); + //event.accept(); } else if(act==KeyCodec::Inventory && !dialogs.isActive()) { if(inventory.isActive()) { diff --git a/game/utils/crashlog.cpp b/game/utils/crashlog.cpp index 4373f8ce0..f667931d6 100644 --- a/game/utils/crashlog.cpp +++ b/game/utils/crashlog.cpp @@ -14,7 +14,7 @@ #include #endif -#if defined(__LINUX__) || defined(__APPLE__) +#if (defined(__LINUX__) || defined(__APPLE__)) && !defined(__ANDROID__) #include // backtrace #include // dladdr #include // __cxa_demangle @@ -167,7 +167,7 @@ void CrashLog::tracebackStd(std::ostream &out) { } void CrashLog::tracebackLinux(std::ostream &out) { -#if defined(__LINUX__) || defined(__APPLE__) +#if (defined(__LINUX__) || defined(__APPLE__)) && !defined(__ANDROID__) // inspired by https://gist.github.com/fmela/591333/36faca4c2f68f7483cd0d3a357e8a8dd5f807edf (BSD) void *callstack[64] = {}; char **symbols = nullptr; diff --git a/game/utils/installdetect.cpp b/game/utils/installdetect.cpp index 9c7f3de39..3d4231def 100644 --- a/game/utils/installdetect.cpp +++ b/game/utils/installdetect.cpp @@ -8,6 +8,11 @@ #include "shlwapi.h" #endif +#ifdef __ANDROID__ +#include +extern "C" struct android_app* tempest_android_get_app(); +#endif + #include #include "utils/fileutil.h" @@ -16,7 +21,7 @@ InstallDetect::InstallDetect() { pfiles = programFiles(false); pfilesX86 = programFiles(true); #endif -#if defined(__OSX__) || defined(__IOS__) +#if defined(__OSX__) || defined(__IOS__) || defined(__ANDROID__) appDir = applicationSupportDirectory(); #endif } @@ -27,7 +32,7 @@ std::u16string InstallDetect::detectG2() { if(ret.empty()) ret = detectG2(pfilesX86); return ret; -#elif defined(__OSX__) || defined(__IOS__) +#elif defined(__OSX__) || defined(__IOS__) || defined(__ANDROID__) if(FileUtil::exists(appDir)) return appDir; return u""; @@ -62,3 +67,27 @@ std::u16string InstallDetect::programFiles(bool x86) { return ret; } #endif + +#ifdef __ANDROID__ +std::u16string InstallDetect::applicationSupportDirectory() { + struct android_app* app = tempest_android_get_app(); + if(app == nullptr || app->activity == nullptr) + return u""; + + // Prefer external data path (accessible without root) + const char* path = app->activity->externalDataPath; + if(path == nullptr) + path = app->activity->internalDataPath; + if(path == nullptr) + return u""; + + // Convert UTF-8 to UTF-16 + std::u16string ret; + size_t len = std::strlen(path); + ret.reserve(len); + for(size_t i = 0; i < len; ++i) { + ret.push_back(static_cast(static_cast(path[i]))); + } + return ret; + } +#endif diff --git a/game/utils/installdetect.h b/game/utils/installdetect.h index 140855660..fdf3709ce 100644 --- a/game/utils/installdetect.h +++ b/game/utils/installdetect.h @@ -8,7 +8,7 @@ class InstallDetect final { InstallDetect(); std::u16string detectG2(); -#if defined(__OSX__) || defined(__IOS__) +#if defined(__OSX__) || defined(__IOS__) || defined(__ANDROID__) static std::u16string applicationSupportDirectory(); #endif @@ -20,7 +20,7 @@ class InstallDetect final { std::u16string pfiles, pfilesX86; #endif -#if defined(__OSX__) || defined(__IOS__) +#if defined(__OSX__) || defined(__IOS__) || defined(__ANDROID__) std::u16string appDir; #endif }; diff --git a/lib/Tempest b/lib/Tempest index 8c571528b..6b613c0b8 160000 --- a/lib/Tempest +++ b/lib/Tempest @@ -1 +1 @@ -Subproject commit 8c571528b41db77503cd23bc579f48414202082a +Subproject commit 6b613c0b85f5663dba5cd93a00a9ca4792447ae2