From 3ae4515a582209afb1a15811ab461e1de2b5dd31 Mon Sep 17 00:00:00 2001 From: Angelo Theodorou Date: Thu, 18 Jul 2019 01:11:18 +0200 Subject: [PATCH] Initial commit --- .clang-format | 128 +++ CMakeLists.txt | 111 +++ Info.plist.in | 34 + LICENSE | 19 + README.md | 5 + android/build.gradle.in | 103 +++ android/gradle.properties.in | 4 + android/src/main/AndroidManifest.xml.in | 31 + android/src/main/cpp/CMakeLists.txt.in | 51 ++ android/src/main/java/LoadLibraries.java.in | 67 ++ android/src/main/java/LoadLibrariesTV.java.in | 14 + android/src/main/res/values/strings.xml.in | 4 + azure-pipelines.yml | 735 ++++++++++++++++++ cmake/FindGLFW.cmake | 61 ++ cmake/FindSDL2.cmake | 255 ++++++ cmake/FindVorbis.cmake | 33 + cmake/FindWebP.cmake | 53 ++ cmake/find_ncine.cmake | 130 ++++ cmake/package_build_android.cmake | 192 +++++ cmake/package_check_info.cmake | 26 + cmake/package_copy_targets.cmake | 36 + cmake/package_generated_sources.cmake | 73 ++ cmake/package_get_version.cmake | 60 ++ cmake/package_imported_targets.cmake | 423 ++++++++++ cmake/package_info.cmake | 35 + cmake/package_installation.cmake | 91 +++ cmake/package_options.cmake | 33 + cmake/package_strip_binaries.cmake | 35 + emscripten_shell.html.in | 64 ++ package.desktop | 10 + src/CollisionManager.cpp | 106 +++ src/CollisionManager.h | 27 + src/Configuration.h | 50 ++ src/EnemyPool.cpp | 139 ++++ src/EnemyPool.h | 67 ++ src/Game.cpp | 161 ++++ src/Game.h | 81 ++ src/Player.cpp | 46 ++ src/Player.h | 48 ++ src/ProjectilePool.cpp | 62 ++ src/ProjectilePool.h | 37 + src/SpritePool.cpp | 53 ++ src/SpritePool.h | 62 ++ src/invaders.cpp | 139 ++++ src/invaders.h | 44 ++ 45 files changed, 4038 insertions(+) create mode 100644 .clang-format create mode 100644 CMakeLists.txt create mode 100644 Info.plist.in create mode 100644 LICENSE create mode 100644 README.md create mode 100644 android/build.gradle.in create mode 100644 android/gradle.properties.in create mode 100644 android/src/main/AndroidManifest.xml.in create mode 100644 android/src/main/cpp/CMakeLists.txt.in create mode 100644 android/src/main/java/LoadLibraries.java.in create mode 100644 android/src/main/java/LoadLibrariesTV.java.in create mode 100644 android/src/main/res/values/strings.xml.in create mode 100644 azure-pipelines.yml create mode 100644 cmake/FindGLFW.cmake create mode 100644 cmake/FindSDL2.cmake create mode 100644 cmake/FindVorbis.cmake create mode 100644 cmake/FindWebP.cmake create mode 100644 cmake/find_ncine.cmake create mode 100644 cmake/package_build_android.cmake create mode 100644 cmake/package_check_info.cmake create mode 100644 cmake/package_copy_targets.cmake create mode 100644 cmake/package_generated_sources.cmake create mode 100644 cmake/package_get_version.cmake create mode 100644 cmake/package_imported_targets.cmake create mode 100644 cmake/package_info.cmake create mode 100644 cmake/package_installation.cmake create mode 100644 cmake/package_options.cmake create mode 100644 cmake/package_strip_binaries.cmake create mode 100644 emscripten_shell.html.in create mode 100644 package.desktop create mode 100644 src/CollisionManager.cpp create mode 100644 src/CollisionManager.h create mode 100644 src/Configuration.h create mode 100644 src/EnemyPool.cpp create mode 100644 src/EnemyPool.h create mode 100644 src/Game.cpp create mode 100644 src/Game.h create mode 100644 src/Player.cpp create mode 100644 src/Player.h create mode 100644 src/ProjectilePool.cpp create mode 100644 src/ProjectilePool.h create mode 100644 src/SpritePool.cpp create mode 100644 src/SpritePool.h create mode 100644 src/invaders.cpp create mode 100644 src/invaders.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..dbdeb79 --- /dev/null +++ b/.clang-format @@ -0,0 +1,128 @@ +--- +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: DontAlign +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: No +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakAfterJavaFieldAnnotations: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom # Allman +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: AfterColon +BreakStringLiterals: false +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^:${PACKAGE_UPPER_NAME}_DEBUG>") + +if(MSVC) + target_compile_options(${PACKAGE_EXE_NAME} PRIVATE /MP) +endif() + +if(APPLE) + file(RELATIVE_PATH RELPATH_TO_LIB ${CMAKE_INSTALL_PREFIX}/${RUNTIME_INSTALL_DESTINATION}/ ${CMAKE_INSTALL_PREFIX}/${LIBRARY_INSTALL_DESTINATION}) + set_target_properties(${PACKAGE_EXE_NAME} PROPERTIES INSTALL_RPATH "@executable_path/${RELPATH_TO_LIB}") +endif() + +if(DEFAULT_DATA_DIR_DIST) + if(MSVC OR APPLE) + # Relative path from tests to data on Windows and OS X, where the user can choose the installation directory + file(RELATIVE_PATH PACKAGE_DEFAULT_DATA_DIR + ${CMAKE_INSTALL_PREFIX}/${RUNTIME_INSTALL_DESTINATION} + ${CMAKE_INSTALL_PREFIX}/${DATA_INSTALL_DESTINATION}) # Always strips trailing slash + set(PACKAGE_DEFAULT_DATA_DIR "${PACKAGE_DEFAULT_DATA_DIR}/") + else() + set(PACKAGE_DEFAULT_DATA_DIR "${CMAKE_INSTALL_PREFIX}/${DATA_INSTALL_DESTINATION}/") + endif() +elseif(NOT PACKAGE_DEFAULT_DATA_DIR) + set(PACKAGE_DEFAULT_DATA_DIR "${PACKAGE_DATA_DIR}/data/") +endif() + +if(PACKAGE_DEFAULT_DATA_DIR) + file(TO_CMAKE_PATH "${PACKAGE_DEFAULT_DATA_DIR}" PACKAGE_DEFAULT_DATA_DIR) # Always strips trailing slash + set(PACKAGE_DEFAULT_DATA_DIR "${PACKAGE_DEFAULT_DATA_DIR}/") + + message(STATUS "Default data directory: ${PACKAGE_DEFAULT_DATA_DIR}") + target_compile_definitions(${PACKAGE_EXE_NAME} PRIVATE "PACKAGE_DEFAULT_DATA_DIR=\"${PACKAGE_DEFAULT_DATA_DIR}\"") +endif() + +if(EMSCRIPTEN) + target_compile_options(${PACKAGE_EXE_NAME} PRIVATE "SHELL:--preload-file ${PACKAGE_DATA_DIR}/data@ --no-heap-copy") + target_link_options(${PACKAGE_EXE_NAME} PRIVATE "SHELL:--preload-file ${PACKAGE_DATA_DIR}/data@ --no-heap-copy") + configure_file(${CMAKE_SOURCE_DIR}/emscripten_shell.html.in ${CMAKE_BINARY_DIR}/${PACKAGE_EXE_NAME}.html @ONLY) + install(FILES ${CMAKE_BINARY_DIR}/${PACKAGE_EXE_NAME}.html DESTINATION ${RUNTIME_INSTALL_DESTINATION}) + install(FILES ${CMAKE_BINARY_DIR}/${PACKAGE_EXE_NAME}.data DESTINATION ${RUNTIME_INSTALL_DESTINATION}) + install(FILES ${CMAKE_BINARY_DIR}/${PACKAGE_EXE_NAME}.wasm DESTINATION ${RUNTIME_INSTALL_DESTINATION}) + if(EXISTS ${PACKAGE_DATA_DIR}/icons/ncInvaders.ico) + file(COPY ${PACKAGE_DATA_DIR}/icons/ncInvaders.ico DESTINATION ${CMAKE_BINARY_DIR}) + file(RENAME ${CMAKE_BINARY_DIR}/ncInvaders.ico ${CMAKE_BINARY_DIR}/favicon.ico) + install(FILES ${CMAKE_BINARY_DIR}/favicon.ico DESTINATION ${RUNTIME_INSTALL_DESTINATION}) + endif() +endif() + +install(TARGETS ${PACKAGE_EXE_NAME} RUNTIME DESTINATION ${RUNTIME_INSTALL_DESTINATION}) +install(FILES README.md DESTINATION ${README_INSTALL_DESTINATION}) +if(MSVC OR APPLE) + install(FILES LICENSE DESTINATION . RENAME LICENSE.txt) +endif() +if(NOT EMSCRIPTEN) + install(DIRECTORY ${PACKAGE_DATA_DIR}/data/ DESTINATION ${DATA_INSTALL_DESTINATION}) +endif() +install(FILES ${PACKAGE_DATA_DIR}/README.md DESTINATION ${DATA_INSTALL_DESTINATION}) +if(IS_DIRECTORY ${NCINE_SHADERS_DIR}) + install(DIRECTORY ${NCINE_SHADERS_DIR} DESTINATION ${SHADERS_INSTALL_DESTINATION}) +endif() + +if(MSVC) + install(FILES ${NCINE_LOCATION} DESTINATION ${RUNTIME_INSTALL_DESTINATION}) + install(DIRECTORY ${MSVC_BINDIR}/ DESTINATION ${RUNTIME_INSTALL_DESTINATION} FILES_MATCHING PATTERN "*.dll") +elseif(APPLE) + install(FILES ${NCINE_LOCATION} DESTINATION ${LIBRARY_INSTALL_DESTINATION}) + install(DIRECTORY ${FRAMEWORKS_DIR}/ DESTINATION ${FRAMEWORKS_INSTALL_DESTINATION}) +endif() + +include(package_copy_targets) +include(package_build_android) +include(package_strip_binaries) diff --git a/Info.plist.in b/Info.plist.in new file mode 100644 index 0000000..a1f6b73 --- /dev/null +++ b/Info.plist.in @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + @CPACK_BUNDLE_NAME@ + CFBundleIconFile + @CPACK_BUNDLE_NAME@.icns + CFBundleIdentifier + io.github.ncine.ncinvaders + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + @CPACK_BUNDLE_NAME@ + CFBundlePackageType + APPL + CFBundleShortVersionString + @PACKAGE_VERSION@ + CFBundleSignature + ???? + CFBundleVersion + + CSResourcesFileMapped + + LSApplicationCategoryType + public.app-category.games + NSHumanReadableCopyright + Copyright ©2011-2019 Angelo Theodorou + NSHighResolutionCapable + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..40731ed --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016-2019 Angelo Theodorou + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..19cef27 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# ncInvaders +ncInvaders is a simplified version of **Space Invaders** made with the nCine. +It has a slightly more complicated game logic than the `ncPong` example. + +You can control the ship with the mouse or keyboard on PC, tapping the screen on Android or with the joystick on both platforms. diff --git a/android/build.gradle.in b/android/build.gradle.in new file mode 100644 index 0000000..69e0e68 --- /dev/null +++ b/android/build.gradle.in @@ -0,0 +1,103 @@ +buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.4.1' + } +} +allprojects { + repositories { + google() + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + buildToolsVersion "@GRADLE_BUILDTOOLS_VERSION@" + compileSdkVersion @GRADLE_COMPILESDK_VERSION@ + + defaultConfig { + minSdkVersion @GRADLE_MINSDK_VERSION@ + targetSdkVersion @GRADLE_TARGETSDK_VERSION@ + versionCode @GRADLE_VERSIONCODE@ + versionName "@GRADLE_VERSION@" + } + + buildTypes { + release { + minifyEnabled true + shrinkResources true + } + + debug { + applicationIdSuffix ".debug" + } + } + + splits { + abi { + enable true + reset() + include '@GRADLE_NDK_ARCHITECTURES@' + universalApk false + } + } + + final def cmakeBuildDir = new File(project.buildDir, "@PACKAGE_LOWER_NAME@") + final def architectures = ['@GRADLE_NDK_ARCHITECTURES@'] + final def libcppIsShared = @GRADLE_LIBCPP_SHARED@ + + def androidPlatform = @GRADLE_MINSDK_VERSION@ + while (file("$ndkDir/platforms/android-" + androidPlatform.toString()).exists() == false && androidPlatform > 0) + androidPlatform -= 1 + + def cmakeBuildTasks = [] + for (arch in architectures) { + final def archDir = new File(cmakeBuildDir, arch) + def cmakeBuildDependsTasks = [] + + if (arch == "armeabi-v7a") + androidPlatform = androidPlatform >= 16 ? androidPlatform : 16 + else if (arch == "arm64-v8a") + androidPlatform = androidPlatform >= 21 ? androidPlatform : 21 + else if (arch == "x86_64") + androidPlatform = androidPlatform >= 21 ? androidPlatform : 21 + + if (libcppIsShared) { + tasks.create(name:'libcppCopy_' + arch, type: Exec, dependsOn: 'cmakeConfigure_' + arch) { + commandLine cmakeCommand, '-E', 'copy_if_different', ndkDir + '/sources/cxx-stl/llvm-libc++/libs/' + arch + '/libc++_shared.so', archDir.path + } + cmakeBuildDependsTasks.add('libcppCopy_' + arch) + } + + tasks.create(name:'cmakeConfigure_' + arch, type: Exec) { + commandLine cmakeCommand, '-H' + file('src/main/cpp').absolutePath, '-B' + archDir, '-DCMAKE_TOOLCHAIN_FILE=' + ndkDir + '/build/cmake/android.toolchain.cmake', '-DANDROID_PLATFORM=android-' + androidPlatform.toString(), '-DANDROID_ABI=' + arch, '@GRADLE_PASSTHROUGH_ARGS@', '@GRADLE_CMAKE_ARGS@' + + if (arch == "armeabi-v7a") + commandLine += ['@GRADLE_ARM_ARGS@'] + } + cmakeBuildDependsTasks.add('cmakeConfigure_' + arch) + + tasks.create(name:'cmakeBuild_' + arch, type: Exec, dependsOn: cmakeBuildDependsTasks) { + commandLine cmakeCommand, '--build', archDir + } + cmakeBuildTasks.add('cmakeBuild_' + arch) + } + + sourceSets.main { + jniLibs.srcDirs = [cmakeBuildDir, @GRADLE_JNILIBS_DIRS@] + jni.srcDirs = [] // disable automatic ndk-build call + } + + tasks.withType(JavaCompile) { + compileTask -> compileTask.dependsOn cmakeBuildTasks + } + + clean { + delete cmakeBuildDir + } +} diff --git a/android/gradle.properties.in b/android/gradle.properties.in new file mode 100644 index 0000000..54f11ba --- /dev/null +++ b/android/gradle.properties.in @@ -0,0 +1,4 @@ +org.gradle.daemon=true +org.gradle.jvmargs=-Xms256m -Xmx2048m +cmakeCommand=@GRADLE_CMAKE_COMMAND@ +ndkDir=@GRADLE_NDK_DIR@ diff --git a/android/src/main/AndroidManifest.xml.in b/android/src/main/AndroidManifest.xml.in new file mode 100644 index 0000000..0f217a0 --- /dev/null +++ b/android/src/main/AndroidManifest.xml.in @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/src/main/cpp/CMakeLists.txt.in b/android/src/main/cpp/CMakeLists.txt.in new file mode 100644 index 0000000..b1907b9 --- /dev/null +++ b/android/src/main/cpp/CMakeLists.txt.in @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.7) +project(@PACKAGE_NAME@-Android-${ANDROID_ABI}) + +if(NCINE_DYNAMIC_LIBRARY) + add_library(ncine SHARED IMPORTED) + set_target_properties(ncine PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ncine/${ANDROID_ABI}/libncine.so" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/ncine/include") +else() + add_library(ncine STATIC IMPORTED) + set_target_properties(ncine PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ncine/${ANDROID_ABI}/libncine.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/ncine/include") + target_compile_definitions(ncine INTERFACE "NCINE_STATIC") +endif() + +add_library(ncine_main STATIC IMPORTED) +set_target_properties(ncine_main PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/ncine/${ANDROID_ABI}/libncine_main.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/ncine/include") + +set(SOURCES +@ANDROID_PACKAGE_SOURCES@ +) + +add_library(native_app_glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) +target_link_libraries(native_app_glue PRIVATE android log) +add_library(game SHARED ${SOURCES}) +target_link_libraries(game PRIVATE native_app_glue ncine_main ncine) +target_include_directories(game PRIVATE @NCPONG_INCLUDE_DIR@ ${ANDROID_NDK}/sources/android/native_app_glue) + +# Convert strings back to the original lists +string(REPLACE " " ";" GENERATED_SOURCES "${GENERATED_SOURCES}") +string(REPLACE " " ";" ANDROID_GENERATED_FLAGS "${ANDROID_GENERATED_FLAGS}") + +target_sources(game PRIVATE ${GENERATED_SOURCES}) +if(IS_DIRECTORY ${GENERATED_INCLUDE_DIR}) + target_include_directories(game PRIVATE ${GENERATED_INCLUDE_DIR}) +endif() + +if(NOT NCINE_DYNAMIC_LIBRARY) + include(package_imported_targets) + target_link_libraries(game PRIVATE + ${EGL_LIBRARY} ${GLES3_LIBRARY} ${OPENSLES_LIBRARY} ${ZLIB_LIBRARY} + PNG::PNG WebP::WebP OpenAL::AL Vorbis::Vorbisfile Lua::Lua) +endif() + +# Export ANativeActivity_onCreate() - https://github.com/android-ndk/ndk/issues/381 +set_property(TARGET game APPEND_STRING PROPERTY LINK_FLAGS "-u ANativeActivity_onCreate") + +include(package_strip_binaries) diff --git a/android/src/main/java/LoadLibraries.java.in b/android/src/main/java/LoadLibraries.java.in new file mode 100644 index 0000000..de56b94 --- /dev/null +++ b/android/src/main/java/LoadLibraries.java.in @@ -0,0 +1,67 @@ +package @JAVA_PACKAGE@; + +import android.app.NativeActivity; +import android.os.Bundle; +import android.view.View; + +public class LoadLibraries extends NativeActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //Hide toolbar + int SDK_INT = android.os.Build.VERSION.SDK_INT; + if(SDK_INT >= 19) + { + setImmersiveSticky(); + + View decorView = getWindow().getDecorView(); + decorView.setOnSystemUiVisibilityChangeListener( + new View.OnSystemUiVisibilityChangeListener() { + @Override + public void onSystemUiVisibilityChange(int visibility) { + setImmersiveSticky(); + } + } + ); + } + } + + @Override + protected void onResume() { + super.onResume(); + + //Hide toolbar + int SDK_INT = android.os.Build.VERSION.SDK_INT; + if(SDK_INT >= 11 && SDK_INT < 14) + { + getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_HIDDEN); + } + else if(SDK_INT >= 14 && SDK_INT < 19) + { + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_LOW_PROFILE); + } + else if(SDK_INT >= 19) + { + setImmersiveSticky(); + } + } + + void setImmersiveSticky() { + View decorView = getWindow().getDecorView(); + decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + } + + static { + System.loadLibrary("openal"); + System.loadLibrary("ncine"); + } + +} diff --git a/android/src/main/java/LoadLibrariesTV.java.in b/android/src/main/java/LoadLibrariesTV.java.in new file mode 100644 index 0000000..f46fe32 --- /dev/null +++ b/android/src/main/java/LoadLibrariesTV.java.in @@ -0,0 +1,14 @@ +package @JAVA_PACKAGE@; + +import android.app.NativeActivity; +import android.os.Bundle; +import android.view.View; + +public class LoadLibrariesTV extends NativeActivity { + + static { + System.loadLibrary("openal"); + System.loadLibrary("ncine"); + } + +} diff --git a/android/src/main/res/values/strings.xml.in b/android/src/main/res/values/strings.xml.in new file mode 100644 index 0000000..8c8887e --- /dev/null +++ b/android/src/main/res/values/strings.xml.in @@ -0,0 +1,4 @@ + + + @PACKAGE_NAME@ + diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..801a61b --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,735 @@ +variables: + libraries_branch: libraries-OS-COMPILER + ncine_branch: nCine-BRANCHNAME-OS-COMPILER + deploy_message: + "Push artifact from Azure Pipelines build $(Build.BuildNumber) with id $(Build.BuildId) + + - ncInvaders artifact from branch '$(Build.SourceBranchName)' with commit id $(Build.SourceVersion)" + deploy_branch: ncInvaders-BRANCHNAME-OS-COMPILER + ndk_version: r20 + +jobs: +- job: Linux_macOS + displayName: Linux and macOS + + strategy: + matrix: + Linux GCC Debug: + imageName: 'ubuntu-16.04' + CC: gcc + CXX: g++ + BuildType: Debug + + Linux GCC Release: + imageName: 'ubuntu-16.04' + CC: gcc + CXX: g++ + BuildType: Release + + Linux GCC BinDist: + imageName: 'ubuntu-16.04' + CC: gcc + CXX: g++ + BuildType: BinDist + + Linux Clang Debug: + imageName: 'ubuntu-16.04' + CC: clang + CXX: clang++ + BuildType: Debug + + Linux Clang Release: + imageName: 'ubuntu-16.04' + CC: clang + CXX: clang++ + BuildType: Release + + Linux Clang BinDist: + imageName: 'ubuntu-16.04' + CC: clang + CXX: clang++ + BuildType: BinDist + + macOS Debug: + imageName: 'macOS-10.14' + BuildType: Debug + + macOS Release: + imageName: 'macOS-10.14' + BuildType: Release + + macOS BinDist: + imageName: 'macOS-10.14' + BuildType: BinDist + + pool: + vmImage: $(imageName) + + steps: + - script: | + curl -fsSL https://cmake.org/files/v3.14/cmake-3.14.5-Linux-x86_64.sh -o cmakeinstall.sh && \ + chmod +x cmakeinstall.sh && \ + sudo ./cmakeinstall.sh --prefix=/usr/local --exclude-subdir && \ + rm cmakeinstall.sh + + displayName: 'Install Latest CMake on Linux' + condition: eq( variables['Agent.OS'], 'Linux' ) + + - script: | + git fetch --unshallow || true + + displayName: 'Unshallow Git Repository for Versioning' + condition: eq( variables['BuildType'], 'BinDist' ) + + - script: | + if [ "$(Agent.OS)" == "Linux" ]; then + export OS=linux + elif [ "$(Agent.OS)" == "Darwin" ]; then + export OS=darwin + export CC=appleclang + fi + + export LIBRARIES_BRANCH=`echo $LIBRARIES_BRANCH | sed 's/OS/'"$OS"'/'` + export LIBRARIES_BRANCH=`echo $LIBRARIES_BRANCH | sed 's/COMPILER/'"$CC"'/'` + + cd .. + git clone https://github.com/nCine/nCine-libraries-artifacts.git + cd nCine-libraries-artifacts + git checkout $LIBRARIES_BRANCH + LIBRARIES_FILE=$(ls -t | head -n 1) && tar xpzf $LIBRARIES_FILE + mv nCine-external .. + + cd .. + rm -rf nCine-libraries-artifacts + + export NCINE_BRANCH=`echo $NCINE_BRANCH | sed 's/BRANCHNAME/'"master"'/'` + export NCINE_BRANCH=`echo $NCINE_BRANCH | sed 's/OS/'"$OS"'/'` + export NCINE_BRANCH=`echo $NCINE_BRANCH | sed 's/COMPILER/'"$CC"'/'` + git clone https://github.com/nCine/nCine-artifacts.git + cd nCine-artifacts + git checkout $NCINE_BRANCH + + if [ "$(Agent.OS)" == "Linux" ]; then + NCINE_FILE=$(ls -t | head -n 1) + tar xpzf $NCINE_FILE + mv nCine-*-Linux ../nCine + elif [ "$(Agent.OS)" == "Darwin" ]; then + NCINE_FILE=$(ls -t | head -n 1) + hdiutil convert *.dmg -format UDTO -o nCine + hdiutil attach -readonly nCine.cdr + cp -Rp /Volumes/nCine-*-Darwin/nCine.app .. + hdiutil detach /Volumes/nCine-*-Darwin; + fi + + cd .. + rm -rf nCine-artifacts + + git clone https://github.com/nCine/ncInvaders-data.git + + displayName: 'Download nCine-libraries and nCine Artifacts and ncInvaders-data' + + - script: | + if [ "$(Agent.OS)" == "Linux" ]; then + export NCINE_DIRECTORY=$(pwd)/../nCine/lib/cmake/nCine + elif [ "$(Agent.OS)" == "Darwin" ]; then + export NCINE_DIRECTORY=$(pwd)/../nCine.app/Contents/Resources/cmake + fi + + if [[ "$(BuildType)" == "BinDist" ]]; then + cmake -B ../ncInvaders-build-$(BuildType) -D PACKAGE_OPTIONS_PRESETS=$(BuildType) -D nCine_DIR=$NCINE_DIRECTORY + else + cmake -B ../ncInvaders-build-$(BuildType) -D CMAKE_BUILD_TYPE=$(BuildType) -D nCine_DIR=$NCINE_DIRECTORY + fi + + displayName: 'CMake Configuration' + + - script: | + if [ "$(Agent.OS)" == "Linux" ]; then + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/../nCine-external/lib + fi + + make -j2 -C ../ncInvaders-build-$(BuildType) + + displayName: 'Make' + + - script: | + make package -C ../ncInvaders-build-$(BuildType) + + displayName: 'Package' + condition: eq( variables['BuildType'], 'BinDist' ) + + - script: | + git config --global user.email "pipelines@azure.com" + git config --global user.name "Azure Pipelines" + + if [ "$(Agent.OS)" == "Linux" ]; then + export OS=linux + export PACKAGE_EXT=tar.gz + elif [ "$(Agent.OS)" == "Darwin" ]; then + export OS=darwin + export CC=appleclang + export PACKAGE_EXT=dmg + fi + + cd .. + export DEPLOY_BRANCH=`echo $DEPLOY_BRANCH | sed 's/BRANCHNAME/'"$(Build.SourceBranchName)"'/'` + export DEPLOY_BRANCH=`echo $DEPLOY_BRANCH | sed 's/OS/'"$OS"'/'` + export DEPLOY_BRANCH=`echo $DEPLOY_BRANCH | sed 's/COMPILER/'"$CC"'/'` + git clone https://$MAPPED_GH_TOKEN@github.com/nCine/ncInvaders-artifacts.git >/dev/null 2>&1 + cd ncInvaders-artifacts + + git checkout $DEPLOY_BRANCH || git checkout --orphan $DEPLOY_BRANCH + git reset + git rm * + mv -f ../ncInvaders-build-$(BuildType)/*.$PACKAGE_EXT . + git add *.$PACKAGE_EXT + git commit --amend -m "$(DEPLOY_MESSAGE)" || git commit -m "$(DEPLOY_MESSAGE)" + git push --force || git push --set-upstream origin $DEPLOY_BRANCH + + displayName: 'Push Artifacts' + condition: and( eq( variables['BuildType'], 'BinDist' ), succeeded() ) + env: + MAPPED_GH_TOKEN: $(GH_TOKEN) + +- job: Android + displayName: Android + + strategy: + matrix: + Debug armeabi-v7a: + imageName: 'ubuntu-16.04' + CC: gcc + CXX: g++ + BuildType: Debug + Arch: armeabi-v7a + + Debug arm64-v8a: + imageName: 'ubuntu-16.04' + CC: gcc + CXX: g++ + BuildType: Debug + Arch: arm64-v8a + + Debug x86_64: + imageName: 'ubuntu-16.04' + CC: gcc + CXX: g++ + BuildType: Debug + Arch: x86_64 + + Release armeabi-v7a: + imageName: 'ubuntu-16.04' + CC: gcc + CXX: g++ + BuildType: Release + Arch: armeabi-v7a + + Release arm64-v8a: + imageName: 'ubuntu-16.04' + CC: gcc + CXX: g++ + BuildType: Release + Arch: arm64-v8a + + Release x86_64: + imageName: 'ubuntu-16.04' + CC: gcc + CXX: g++ + BuildType: Release + Arch: x86_64 + + pool: + vmImage: $(imageName) + + steps: + - script: | + curl -sL https://cmake.org/files/v3.14/cmake-3.14.5-Linux-x86_64.sh -o cmakeinstall.sh \ + && chmod +x cmakeinstall.sh \ + && sudo ./cmakeinstall.sh --prefix=/usr/local --exclude-subdir \ + && rm cmakeinstall.sh + + displayName: 'Install Latest CMake' + + - script: | + git fetch --unshallow || true + + displayName: 'Unshallow Git Repository for Versioning' + + - script: | + export OS=linux + + export LIBRARIES_BRANCH=`echo $LIBRARIES_BRANCH | sed 's/OS/'"$OS"'/'` + export LIBRARIES_BRANCH=`echo $LIBRARIES_BRANCH | sed 's/COMPILER/'"$CC"'/'` + + cd .. + git clone https://github.com/nCine/nCine-libraries-artifacts.git + cd nCine-libraries-artifacts + git checkout $LIBRARIES_BRANCH + LIBRARIES_FILE=$(ls -t | head -n 1) && tar xpzf $LIBRARIES_FILE + mv nCine-external .. + + cd .. + rm -rf nCine-libraries-artifacts + + export NCINE_BRANCH=`echo $NCINE_BRANCH | sed 's/BRANCHNAME/'"master"'/'` + export NCINE_BRANCH=`echo $NCINE_BRANCH | sed 's/OS/'"$OS"'/'` + export NCINE_BRANCH=`echo $NCINE_BRANCH | sed 's/COMPILER/'"$CC"'/'` + git clone https://github.com/nCine/nCine-artifacts.git + cd nCine-artifacts + git checkout $NCINE_BRANCH + + NCINE_FILE=$(ls -t | head -n 1) + tar xpzf $NCINE_FILE + mv nCine-*-Linux ../nCine + + cd .. + rm -rf nCine-artifacts + + git clone https://github.com/nCine/ncInvaders-data.git + + displayName: 'Download nCine-libraries and nCine Artifacts and ncInvaders-data' + + - script: | + export NDK_URL=https://dl.google.com/android/repository/android-ndk-$NDK_VERSION-linux-x86_64.zip + + cd .. + curl -fsSL $NDK_URL -o ndk.zip + unzip -q ndk.zip && rm -f ndk.zip + + displayName: 'Download Android NDK' + + - script: | + export ANDROID_NDK_HOME=$(pwd)/../android-ndk-$NDK_VERSION + + cmake -B ../ncInvaders-build-$(BuildType) -D CMAKE_BUILD_TYPE=$(BuildType) -D PACKAGE_NDK_ARCHITECTURES=$(Arch) -D PACKAGE_BUILD_ANDROID=ON -D PACKAGE_ASSEMBLE_APK=ON -D nCine_DIR=$(pwd)/../nCine/lib/cmake/nCine + + displayName: 'CMake Configuration' + + - script: | + cd ../ncInvaders-build-$(BuildType)/android + gradle assemble$(BuildType) + + displayName: 'Gradle Assemble' + + - script: | + git config --global user.email "pipelines@azure.com" + git config --global user.name "Azure Pipelines" + + cd .. + export DEPLOY_BRANCH=ncInvaders-$(Build.SourceBranchName)-android-$(Arch)-$(BuildType) + git clone https://$MAPPED_GH_TOKEN@github.com/nCine/ncInvaders-artifacts.git >/dev/null 2>&1 + cd ncInvaders-artifacts + + git checkout $DEPLOY_BRANCH || git checkout --orphan $DEPLOY_BRANCH + git reset + git rm * + mv -f ../ncInvaders-build-$(BuildType)/android/build/outputs/apk/*/*.apk . + git add *.apk + git commit --amend -m "$(DEPLOY_MESSAGE)" || git commit -m "$(DEPLOY_MESSAGE)" + git push --force || git push --set-upstream origin $DEPLOY_BRANCH + + displayName: 'Push Artifacts' + condition: succeeded() + env: + MAPPED_GH_TOKEN: $(GH_TOKEN) + +- job: Emscripten + displayName: Emscripten + + strategy: + matrix: + Emscripten Debug: + imageName: 'ubuntu-16.04' + OS: emscripten + CC: emcc + BuildType: Debug + + Emscripten Release: + imageName: 'ubuntu-16.04' + OS: emscripten + CC: emcc + BuildType: Release + + Emscripten BinDist: + imageName: 'ubuntu-16.04' + OS: emscripten + CC: emcc + BuildType: BinDist + + pool: + vmImage: $(imageName) + + steps: + - script: | + curl -fsSL https://cmake.org/files/v3.14/cmake-3.14.5-Linux-x86_64.sh -o cmakeinstall.sh && \ + chmod +x cmakeinstall.sh && \ + sudo ./cmakeinstall.sh --prefix=/usr/local --exclude-subdir && \ + rm cmakeinstall.sh + + displayName: 'Install Latest CMake on Linux' + condition: eq( variables['Agent.OS'], 'Linux' ) + + - script: | + cd .. + git clone https://github.com/emscripten-core/emsdk.git + cd emsdk + ./emsdk install latest + ./emsdk activate latest + + displayName: 'Install Emscripten SDK' + + - script: | + git fetch --unshallow || true + + displayName: 'Unshallow Git Repository for Versioning' + condition: eq( variables['BuildType'], 'BinDist' ) + + - script: | + export LIBRARIES_BRANCH=`echo $LIBRARIES_BRANCH | sed 's/OS/'"$OS"'/'` + export LIBRARIES_BRANCH=`echo $LIBRARIES_BRANCH | sed 's/COMPILER/'"$CC"'/'` + + cd .. + git clone https://github.com/nCine/nCine-libraries-artifacts.git + cd nCine-libraries-artifacts + git checkout $LIBRARIES_BRANCH + LIBRARIES_FILE=$(ls -t | head -n 1) && tar xpzf $LIBRARIES_FILE + mv nCine-external-emscripten .. + + cd .. + rm -rf nCine-libraries-artifacts + + # Only the nCine `develop` branch has Emscripten support at the moment + export NCINE_BRANCH=`echo $NCINE_BRANCH | sed 's/BRANCHNAME/'"develop"'/'` + export NCINE_BRANCH=`echo $NCINE_BRANCH | sed 's/OS/'"$OS"'/'` + export NCINE_BRANCH=`echo $NCINE_BRANCH | sed 's/COMPILER/'"$CC"'/'` + git clone https://github.com/nCine/nCine-artifacts.git + cd nCine-artifacts + git checkout $NCINE_BRANCH + + NCINE_FILE=$(ls -t | head -n 1) + tar xpzf $NCINE_FILE + mv nCine-*-Emscripten ../nCine + + cd .. + rm -rf nCine-artifacts + + git clone https://github.com/nCine/ncInvaders-data.git + + displayName: 'Download nCine-libraries and nCine Artifacts and ncInvaders-data' + + - script: | + source ../emsdk/emsdk_env.sh + export NCINE_DIRECTORY=$(pwd)/../nCine/cmake + + if [[ "$(BuildType)" == "BinDist" ]]; then + emcmake cmake -B ../ncInvaders-build-$(BuildType) -D PACKAGE_OPTIONS_PRESETS=$(BuildType) -D nCine_DIR=$NCINE_DIRECTORY + else + emcmake cmake -B ../ncInvaders-build-$(BuildType) -D CMAKE_BUILD_TYPE=$(BuildType) -D nCine_DIR=$NCINE_DIRECTORY + fi + + displayName: 'CMake Configuration' + + - script: | + source ../emsdk/emsdk_env.sh + + make -j2 -C ../ncInvaders-build-$(BuildType) + + displayName: 'Make' + + - script: | + make package -C ../ncInvaders-build-$(BuildType) + + displayName: 'Package' + condition: eq( variables['BuildType'], 'BinDist' ) + + - script: | + git config --global user.email "pipelines@azure.com" + git config --global user.name "Azure Pipelines" + + cd .. + export DEPLOY_BRANCH=`echo $DEPLOY_BRANCH | sed 's/BRANCHNAME/'"$(Build.SourceBranchName)"'/'` + export DEPLOY_BRANCH=`echo $DEPLOY_BRANCH | sed 's/OS/'"$OS"'/'` + export DEPLOY_BRANCH=`echo $DEPLOY_BRANCH | sed 's/COMPILER/'"$CC"'/'` + git clone https://$MAPPED_GH_TOKEN@github.com/nCine/ncInvaders-artifacts.git >/dev/null 2>&1 + cd ncInvaders-artifacts + + git checkout $DEPLOY_BRANCH || git checkout --orphan $DEPLOY_BRANCH + git reset + git rm * + mv -f ../ncInvaders-build-$(BuildType)/*.tar.gz . + git add *.tar.gz + git commit --amend -m "$(DEPLOY_MESSAGE)" || git commit -m "$(DEPLOY_MESSAGE)" + git push --force || git push --set-upstream origin $DEPLOY_BRANCH + + displayName: 'Push Artifacts' + condition: and( eq( variables['BuildType'], 'BinDist' ), succeeded() ) + env: + MAPPED_GH_TOKEN: $(GH_TOKEN) + +- job: Windows_MinGW + displayName: Windows and MinGW + + strategy: + matrix: + VS2017 Debug: + imageName: 'vs2017-win2016' + Compiler: vs2017 + BuildType: Debug + + VS2017 Release: + imageName: 'vs2017-win2016' + Compiler: vs2017 + BuildType: Release + + VS2017 BinDist: + imageName: 'vs2017-win2016' + Compiler: vs2017 + BuildType: BinDist + + VS2019 Debug: + imageName: 'windows-2019' + Compiler: vs2019 + BuildType: Debug + + VS2019 Release: + imageName: 'windows-2019' + Compiler: vs2019 + BuildType: Release + + VS2019 BinDist: + imageName: 'windows-2019' + Compiler: vs2019 + BuildType: BinDist + + MinGW GCC Debug: + imageName: 'vs2017-win2016' + Compiler: mingw64 + CC: gcc + CXX: g++ + BuildType: Debug + + MinGW GCC Release: + imageName: 'vs2017-win2016' + Compiler: mingw64 + CC: gcc + CXX: g++ + BuildType: Release + + MinGW GCC BinDist: + imageName: 'vs2017-win2016' + Compiler: mingw64 + CC: gcc + CXX: g++ + BuildType: BinDist + + MinGW Clang Debug: + imageName: 'vs2017-win2016' + Compiler: mingw64 + CC: clang + CXX: clang++ + BuildType: Debug + + MinGW Clang Release: + imageName: 'vs2017-win2016' + Compiler: mingw64 + CC: clang + CXX: clang++ + BuildType: Release + + MinGW Clang BinDist: + imageName: 'vs2017-win2016' + Compiler: mingw64 + CC: clang + CXX: clang++ + BuildType: BinDist + + pool: + vmImage: $(imageName) + + steps: + - powershell: | + choco install --no-progress msys2 --params="/InstallDir:C:\msys64 /NoUpdate /NoPath" + C:\msys64\usr\bin\pacman --noconfirm -Syy + + displayName: 'Install and Update MSYS2 on Windows' + condition: eq( variables['Compiler'], 'mingw64' ) + + - powershell: | + C:\msys64\usr\bin\pacman --noconfirm -S mingw-w64-x86_64-glew mingw-w64-x86_64-glfw mingw-w64-x86_64-SDL2 mingw-w64-x86_64-openal mingw-w64-x86_64-libvorbis mingw-w64-x86_64-libwebp mingw-w64-x86_64-lua mingw-w64-x86_64-cmake make + + if ($env:CC -eq "gcc") { C:\msys64\usr\bin\pacman --noconfirm -S mingw-w64-x86_64-gcc } + else { C:\msys64\usr\bin\pacman --noconfirm -S mingw-w64-x86_64-clang } + + C:\msys64\usr\bin\pacman --noconfirm -Scc + + displayName: 'Install Build Dependencies on MinGW' + condition: eq( variables['Compiler'], 'mingw64' ) + + - powershell: | + $env:GIT_REDIRECT_STDERR = '2>&1' + git fetch --unshallow; if (-not $?) { return } + + displayName: 'Unshallow Git Repository for Versioning' + condition: eq( variables['BuildType'], 'BinDist' ) + + - powershell: | + $env:GIT_REDIRECT_STDERR = '2>&1' + $env:vsversion = switch ("$(imageName)") + { + "windows-2019" {"vs2019"} + "vs2017-win2016" {"vs2017"} + } + + cd .. + git clone https://github.com/nCine/nCine-libraries-artifacts.git + cd nCine-libraries-artifacts + + if ($env:Compiler -ne "mingw64") + { + $env:LIBRARIES_BRANCH = $env:LIBRARIES_BRANCH -replace "OS","windows" -replace "COMPILER",$env:vsversion + git checkout $env:LIBRARIES_BRANCH + $env:LIBRARIES_FILE = Get-ChildItem -Path $(pwd) -Name -File | Select-Object -First 1 + 7z x $env:LIBRARIES_FILE + + Move-Item -Path nCine-external -Destination .. + } + + cd .. + Remove-Item nCine-libraries-artifacts -Recurse -Force + + git clone https://github.com/nCine/nCine-artifacts.git + cd nCine-artifacts + + if ($env:Compiler -ne "mingw64") + { + $env:NCINE_BRANCH = $env:NCINE_BRANCH -replace "BRANCHNAME","master" -replace "OS","windows" -replace "COMPILER",$env:vsversion + git checkout $env:NCINE_BRANCH + $env:NCINE_FILE = Get-ChildItem -Path $(pwd) -Filter *.zip -Name -File | Select-Object -First 1 + 7z x $env:NCINE_FILE + } + else + { + $env:NCINE_BRANCH = $env:NCINE_BRANCH -replace "BRANCHNAME","master" -replace "OS","mingw64" -replace "COMPILER",$env:CC + git checkout $env:NCINE_BRANCH + $env:NCINE_FILE = Get-ChildItem -Path $(pwd) -Name -File | Select-Object -First 1 + 7z x $env:NCINE_FILE + $env:NCINE_FILE = $env:NCINE_FILE -replace ".gz","" + 7z x $env:NCINE_FILE -aos + } + + Move-Item -Path nCine-*-win64 -Destination ../nCine + + cd .. + Remove-Item nCine-artifacts -Recurse -Force + + git clone https://github.com/nCine/ncInvaders-data.git + + displayName: 'Download nCine-libraries and nCine Artifacts and ncInvaders-data' + + - powershell: | + $env:generator = switch ("$(imageName)") + { + "windows-2019" {"Visual Studio 16 2019"} + "vs2017-win2016" {"Visual Studio 15 2017"} + } + + if ($env:BuildType -eq "BinDist") + { cmake -G "$env:generator" -A x64 -B ../ncInvaders-build-$(BuildType) -D PACKAGE_OPTIONS_PRESETS=$(BuildType) -D nCine_DIR=$(pwd)/../nCine/cmake } + else + { cmake -G "$env:generator" -A x64 -B ../ncInvaders-build-$(BuildType) -D nCine_DIR=$(pwd)/../nCine/cmake } + + displayName: 'CMake Configuration on Windows' + condition: ne( variables['Compiler'], 'mingw64' ) + + - powershell: | + $env:PATH = "C:\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem" + $env:MSYSTEM = "MINGW64" + + if ($env:BuildType -eq "BinDist") + { C:\msys64\usr\bin\bash.exe -lc 'cmake -G \"MSYS Makefiles\" -B ../ncInvaders-build-$(BuildType) -D PACKAGE_OPTIONS_PRESETS=$(BuildType) -D nCine_DIR=$(pwd)/../nCine/lib/cmake/nCine' } + else + { C:\msys64\usr\bin\bash.exe -lc 'cmake -G \"MSYS Makefiles\" -B ../ncInvaders-build-$(BuildType) -D CMAKE_BUILD_TYPE=$(BuildType) -D nCine_DIR=$(pwd)/../nCine/lib/cmake/nCine' } + + displayName: 'CMake Configuration on MinGW' + condition: eq( variables['Compiler'], 'mingw64' ) + env: + CHERE_INVOKING: on + + - powershell: | + if ($env:BuildType -eq "BinDist") + { cmake --build ../ncInvaders-build-$(BuildType) --config Release } + else + { cmake --build ../ncInvaders-build-$(BuildType) --config $(BuildType) } + + displayName: 'CMake Build on Windows' + condition: ne( variables['Compiler'], 'mingw64' ) + + - powershell: | + $env:PATH = "C:\msys64\x86_64\bin;C:\msys64\usr\bin;$env:PATH" + $env:MSYSTEM = "MINGW64" + C:\msys64\usr\bin\bash.exe -lc 'make -j2 -C ../ncInvaders-build-$(BuildType)' + + displayName: 'Make on MinGW' + condition: eq( variables['Compiler'], 'mingw64' ) + env: + CHERE_INVOKING: on + + - powershell: | + cmake --build ../ncInvaders-build-$(BuildType) --config Release --target package + + displayName: 'Package on Windows' + condition: and( eq( variables['BuildType'], 'BinDist' ), ne( variables['Compiler'], 'mingw64' ) ) + + - powershell: | + $env:PATH = "C:\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem" + $env:MSYSTEM = "MINGW64" + $env:CHERE_INVOKING = 1 + + C:\msys64\usr\bin\bash.exe -lc 'make package -C ../ncInvaders-build-$(BuildType)' + + displayName: 'Package on MinGW' + condition: and( eq( variables['BuildType'], 'BinDist' ), eq( variables['Compiler'], 'mingw64' ) ) + + - powershell: | + git config --global user.email "pipelines@azure.com" + git config --global user.name "Azure Pipelines" + + $env:GIT_REDIRECT_STDERR = '2>&1' + $env:vsversion = switch ("$(imageName)") + { + "windows-2019" {"vs2019"} + "vs2017-win2016" {"vs2017"} + } + + $env:DEPLOY_BRANCH = $env:DEPLOY_BRANCH -replace "BRANCHNAME","$(Build.SourceBranchName)" + if ($env:Compiler -ne "mingw64") + { $env:DEPLOY_BRANCH = $env:DEPLOY_BRANCH -replace "OS","windows" -replace "COMPILER",$env:vsversion } + else + { $env:DEPLOY_BRANCH = $env:DEPLOY_BRANCH -replace "OS","mingw64" -replace "COMPILER",$env:CC } + + cd .. + git clone https://$env:MAPPED_GH_TOKEN@github.com/nCine/ncInvaders-artifacts.git 2>&1>$null + cd ncInvaders-artifacts + git checkout $env:DEPLOY_BRANCH; if (-not $?) { git checkout --orphan $env:DEPLOY_BRANCH } + git reset + git rm * + + if ($env:Compiler -ne "mingw64") + { + Move-Item -Path ..\ncInvaders-build-$(BuildType)\*.exe -Destination . + Move-Item -Path ..\ncInvaders-build-$(BuildType)\*.zip -Destination . + git add *.exe *.zip + } + else + { + Move-Item -Path ..\ncInvaders-build-$(BuildType)\*.tar.gz -Destination . + git add *.tar.gz + } + + git commit --amend -m "$env:DEPLOY_MESSAGE"; if (-not $?) { git commit -m "$env:DEPLOY_MESSAGE" } + git push --force; if (-not $?) { git push --set-upstream origin $env:DEPLOY_BRANCH } + + displayName: 'Push Artifacts' + condition: and( eq( variables['BuildType'], 'BinDist' ), succeeded() ) + env: + MAPPED_GH_TOKEN: $(GH_TOKEN) diff --git a/cmake/FindGLFW.cmake b/cmake/FindGLFW.cmake new file mode 100644 index 0000000..4967c7a --- /dev/null +++ b/cmake/FindGLFW.cmake @@ -0,0 +1,61 @@ +# Locate the glfw library +# This module defines the following variables: +# GLFW_LIBRARY, the name of the library; +# GLFW_INCLUDE_DIR, where to find glfw include files. +# GLFW_FOUND, true if both the GLFW_LIBRARY and GLFW_INCLUDE_DIR have been found. +# +# To help locate the library and include file, you could define an environment variable called +# GLFW_ROOT which points to the root of the glfw library installation. This is pretty useful +# on a Windows platform. +# +# +# Usage example to compile an "executable" target to the glfw library: +# +# FIND_PACKAGE (glfw REQUIRED) +# INCLUDE_DIRECTORIES (${GLFW_INCLUDE_DIR}) +# ADD_EXECUTABLE (executable ${EXECUTABLE_SRCS}) +# TARGET_LINK_LIBRARIES (executable ${GLFW_LIBRARY}) +# +# TODO: +# Allow the user to select to link to a shared library or to a static library. + +#Search for the include file... +FIND_PATH(GLFW_INCLUDE_DIR GLFW/glfw3.h DOC "Path to GLFW include directory." + HINTS + $ENV{GLFW_ROOT} + PATH_SUFFIX include #For finding the include file under the root of the glfw expanded archive, typically on Windows. + PATHS + /usr/include/ + /usr/local/include/ + # By default headers are under GL subfolder + /usr/include/GLFW + /usr/local/include/GLFW + ${GLFW_ROOT_DIR}/include/ # added by ptr +) +MARK_AS_ADVANCED(GLFW_INCLUDE_DIR) + +FIND_LIBRARY(GLFW_LIBRARY DOC "Absolute path to GLFW library." + NAMES glfw glfw3 glfw3.lib + HINTS + $ENV{GLFW_ROOT} + PATH_SUFFIXES lib/win32 #For finding the library file under the root of the glfw expanded archive, typically on Windows. + PATHS + /usr/local/lib + /usr/lib + ${GLFW_ROOT_DIR}/lib-msvc100/release # added by ptr +) +MARK_AS_ADVANCED(GLFW_LIBRARY) + +# handle the QUIETLY and REQUIRED arguments and set WEBFOUND_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLFW DEFAULT_MSG GLFW_LIBRARY GLFW_INCLUDE_DIR) + +# On Mac OS X GLFW depends on the Cocoa framework +if(APPLE) + SET(GLFW_LIBRARY "-framework Cocoa -framework IOKit" ${GLFW_LIBRARY}) +endif() + +SET(GLFW_LIBRARIES ${GLFW_LIBRARY}) +SET(GLFW_INCLUDE_DIRS ${GLFW_INCLUDE_DIR}) + diff --git a/cmake/FindSDL2.cmake b/cmake/FindSDL2.cmake new file mode 100644 index 0000000..872e3c4 --- /dev/null +++ b/cmake/FindSDL2.cmake @@ -0,0 +1,255 @@ +# Locate SDL2 library +# This module defines +# SDL2_LIBRARY, the name of the library to link against +# SDL2_FOUND, if false, do not try to link to SDL2 +# SDL2_INCLUDE_DIR, where to find SDL.h +# +# This module responds to the the flag: +# SDL2_BUILDING_LIBRARY +# If this is defined, then no SDL2_main will be linked in because +# only applications need main(). +# Otherwise, it is assumed you are building an application and this +# module will attempt to locate and set the the proper link flags +# as part of the returned SDL2_LIBRARY variable. +# +# Don't forget to include SDL2main.h and SDL2main.m your project for the +# OS X framework based version. (Other versions link to -lSDL2main which +# this module will try to find on your behalf.) Also for OS X, this +# module will automatically add the -framework Cocoa on your behalf. +# +# +# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration +# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library +# (SDL2.dll, libsdl2.so, SDL2.framework, etc). +# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. +# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value +# as appropriate. These values are used to generate the final SDL2_LIBRARY +# variable, but when these values are unset, SDL2_LIBRARY does not get created. +# +# +# $SDL2 is an environment variable that would +# correspond to the ./configure --prefix=$SDL2 +# used in building SDL2. +# l.e.galup 9-20-02 +# +# Modified by Eric Wing. +# Added code to assist with automated building by using environmental variables +# and providing a more controlled/consistent search behavior. +# Added new modifications to recognize OS X frameworks and +# additional Unix paths (FreeBSD, etc). +# Also corrected the header search path to follow "proper" SDL2 guidelines. +# Added a search for SDL2main which is needed by some platforms. +# Added a search for threads which is needed by some platforms. +# Added needed compile switches for MinGW. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of +# SDL2_LIBRARY to override this selection or set the CMake environment +# CMAKE_INCLUDE_PATH to modify the search paths. +# +# Note that the header path has changed from SDL2/SDL.h to just SDL.h +# This needed to change because "proper" SDL2 convention +# is #include "SDL.h", not . This is done for portability +# reasons because not all systems place things in SDL2/ (see FreeBSD). +# +# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake +# module with the minor edit of changing "SDL" to "SDL2" where necessary. This +# was not created for redistribution, and exists temporarily pending official +# SDL2 CMake modules. +# +# Note that on windows this will only search for the 32bit libraries, to search +# for 64bit change x86/i686-w64 to x64/x86_64-w64 + +#============================================================================= +# Copyright 2003-2009 Kitware, Inc. +# +# CMake - Cross Platform Makefile Generator +# Copyright 2000-2014 Kitware, Inc. +# Copyright 2000-2011 Insight Software Consortium +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +FIND_PATH(SDL2_INCLUDE_DIR SDL.h + HINTS + ${SDL2} + $ENV{SDL2} + PATH_SUFFIXES include/SDL2 include SDL2 + i686-w64-mingw32/include/SDL2 + x86_64-w64-mingw32/include/SDL2 + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include/SDL2 + /usr/include/SDL2 + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) + +# Lookup the 64 bit libs on x64 +IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2 + HINTS + ${SDL2} + $ENV{SDL2} + PATH_SUFFIXES lib64 lib + lib/x64 + x86_64-w64-mingw32/lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) +# On 32bit build find the 32bit libs +ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8) + FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2 + HINTS + ${SDL2} + $ENV{SDL2} + PATH_SUFFIXES lib + lib/x86 + i686-w64-mingw32/lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) +ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8) + +IF(NOT SDL2_BUILDING_LIBRARY) + IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDL2main for compatibility even though they don't + # necessarily need it. + # Lookup the 64 bit libs on x64 + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + FIND_LIBRARY(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + ${SDL2} + $ENV{SDL2} + PATH_SUFFIXES lib64 lib + lib/x64 + x86_64-w64-mingw32/lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) + # On 32bit build find the 32bit libs + ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8) + FIND_LIBRARY(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + ${SDL2} + $ENV{SDL2} + PATH_SUFFIXES lib + lib/x86 + i686-w64-mingw32/lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) + ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8) + ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") +ENDIF(NOT SDL2_BUILDING_LIBRARY) + +# SDL2 may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + +# MinGW needs an additional library, mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows +# (Actually on second look, I think it only needs one of the m* libraries.) +IF(MINGW) + SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") +ENDIF(MINGW) + +SET(SDL2_FOUND "NO") + IF(SDL2_LIBRARY_TEMP) + # For SDL2main + IF(NOT SDL2_BUILDING_LIBRARY) + IF(SDL2MAIN_LIBRARY) + SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(SDL2MAIN_LIBRARY) + ENDIF(NOT SDL2_BUILDING_LIBRARY) + + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + IF(APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") + ENDIF(APPLE) + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + IF(NOT APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(NOT APPLE) + + # For MinGW library + IF(MINGW) + SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(MINGW) + + # Set the final string here so the GUI reflects the final state. + SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") + # Set the temp variable to INTERNAL so it is not seen in the CMake GUI + SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") + + SET(SDL2_FOUND "YES") +ENDIF(SDL2_LIBRARY_TEMP) + +INCLUDE(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) + diff --git a/cmake/FindVorbis.cmake b/cmake/FindVorbis.cmake new file mode 100644 index 0000000..df7de08 --- /dev/null +++ b/cmake/FindVorbis.cmake @@ -0,0 +1,33 @@ +# - Find vorbis +# Find the native vorbis includes and libraries +# +# VORBIS_INCLUDE_DIR - where to find vorbis.h, etc. +# VORBIS_LIBRARIES - List of libraries when using vorbis(file). +# VORBIS_FOUND - True if vorbis found. + +if(VORBIS_INCLUDE_DIR) + # Already in cache, be silent + set(VORBIS_FIND_QUIETLY TRUE) +endif(VORBIS_INCLUDE_DIR) + +find_path(VORBIS_INCLUDE_DIR vorbis/vorbisfile.h) + +find_library(OGG_LIBRARY NAMES ogg) +find_library(VORBIS_LIBRARY NAMES vorbis) +find_library(VORBISFILE_LIBRARY NAMES vorbisfile) + +# Handle the QUIETLY and REQUIRED arguments and set VORBIS_FOUND to TRUE if +# all listed variables are TRUE. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(VORBIS DEFAULT_MSG + VORBIS_INCLUDE_DIR OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY) + +if(VORBIS_FOUND) + set(VORBIS_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY}) +else(VORBIS_FOUND) + set(VORBIS_LIBRARIES) +endif(VORBIS_FOUND) + +mark_as_advanced(VORBIS_INCLUDE_DIR) +mark_as_advanced(OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY) + diff --git a/cmake/FindWebP.cmake b/cmake/FindWebP.cmake new file mode 100644 index 0000000..ab3b481 --- /dev/null +++ b/cmake/FindWebP.cmake @@ -0,0 +1,53 @@ +# - Find WebP library +# Find the native WebP headers and libraries. +# +# WEBP_INCLUDE_DIRS - where to find webp/decode.h, etc. +# WEBP_LIBRARIES - List of libraries when using webp. +# WEBP_FOUND - True if webp is found. + +#============================================================================= +#Copyright 2000-2009 Kitware, Inc., Insight Software Consortium +#All rights reserved. +# +#Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +#* Redistributions of source code must retain the above copyright notice, +#this list of conditions and the following disclaimer. +# +#* Redistributions in binary form must reproduce the above copyright notice, +#this list of conditions and the following disclaimer in the documentation +#and/or other materials provided with the distribution. +# +#* Neither the names of Kitware, Inc., the Insight Software Consortium, nor +#the names of their contributors may be used to endorse or promote products +#derived from this software without specific prior written permission. +# +#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +#AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +#IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +#ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +#LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +#CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +#SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +#INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +#CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +#ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +#POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# Look for the header file. +FIND_PATH(WEBP_INCLUDE_DIR NAMES webp/decode.h) +MARK_AS_ADVANCED(WEBP_INCLUDE_DIR) + +# Look for the library. +FIND_LIBRARY(WEBP_LIBRARY NAMES webp) +MARK_AS_ADVANCED(WEBP_LIBRARY) + +# handle the QUIETLY and REQUIRED arguments and set WEBFOUND_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(WebP DEFAULT_MSG WEBP_LIBRARY WEBP_INCLUDE_DIR) + +SET(WEBP_LIBRARIES ${WEBP_LIBRARY}) +SET(WEBP_INCLUDE_DIRS ${WEBP_INCLUDE_DIR}) diff --git a/cmake/find_ncine.cmake b/cmake/find_ncine.cmake new file mode 100644 index 0000000..112ea13 --- /dev/null +++ b/cmake/find_ncine.cmake @@ -0,0 +1,130 @@ +if(MSVC AND NOT DEFINED CMAKE_PREFIX_PATH AND NOT DEFINED nCine_DIR) + get_filename_component(CMAKE_PREFIX_PATH "[HKEY_LOCAL_MACHINE\\SOFTWARE\\nCine]" ABSOLUTE) +endif() +find_package(nCine REQUIRED) + +set(NCINE_CONFIGURATION "RELEASE" CACHE STRING "Preferred nCine configuration type when more than one has been exported") +get_target_property(NCINE_CONFIGURATIONS ncine::ncine IMPORTED_CONFIGURATIONS) +message(STATUS "nCine exported build configurations: ${NCINE_CONFIGURATIONS} (preferred: ${NCINE_CONFIGURATION})") + +get_target_property(NCINE_LOCATION ncine::ncine IMPORTED_LOCATION_${NCINE_CONFIGURATION}) +if(NOT EXISTS ${NCINE_LOCATION}) + unset(NCINE_CONFIGURATION CACHE) + foreach(NCINE_CFG ${NCINE_CONFIGURATIONS}) + get_target_property(NCINE_LOCATION ncine::ncine IMPORTED_LOCATION_${NCINE_CFG}) + if(EXISTS ${NCINE_LOCATION}) + message(STATUS "Preferred configuration unavailable, changing to ${NCINE_CFG}") + set(NCINE_CONFIGURATION ${NCINE_CFG}) + break() + endif() + endforeach() +endif() +if(NOT DEFINED NCINE_CONFIGURATION) + message(FATAL_ERROR "No nCine build configuration found") +endif() + +message(STATUS "nCine library: ${NCINE_LOCATION}") +if(WIN32 AND NCINE_DYNAMIC_LIBRARY) + get_target_property(NCINE_IMPLIB ncine::ncine IMPORTED_IMPLIB_${NCINE_CONFIGURATION}) + message(STATUS "nCine import library: ${NCINE_IMPLIB}") +endif() +get_target_property(NCINE_INCLUDE_DIR ncine::ncine INTERFACE_INCLUDE_DIRECTORIES) +message(STATUS "nCine include directory: ${NCINE_INCLUDE_DIR}") +get_target_property(NCINE_MAIN_LOCATION ncine::ncine_main IMPORTED_LOCATION_${NCINE_CONFIGURATION}) +message(STATUS "nCine main function library: ${NCINE_MAIN_LOCATION}") + +if(NOT NCINE_EMBEDDED_SHADERS) + if(IS_DIRECTORY ${NCINE_SHADERS_DIR}) + message(STATUS "nCine shaders directory: ${NCINE_SHADERS_DIR}") + else() + message(FATAL_ERROR "nCine shaders directory not found at: ${NCINE_SHADERS_DIR}") + endif() +endif() + +if(PACKAGE_BUILD_ANDROID) + if(IS_DIRECTORY ${NCINE_ANDROID_DIR}) + message(STATUS "nCine Android directory: ${NCINE_ANDROID_DIR}") + else() + message(FATAL_ERROR "nCine Android directory not found at: ${NCINE_ANDROID_DIR}") + endif() + + set(NCINE_EXTERNAL_ANDROID_DIR "" CACHE PATH "Path to the nCine external Android libraries directory") + if(NOT IS_DIRECTORY ${NCINE_EXTERNAL_ANDROID_DIR}) + unset(NCINE_EXTERNAL_ANDROID_DIR CACHE) + get_filename_component(PARENT_DIR ${CMAKE_SOURCE_DIR} DIRECTORY) + find_path(NCINE_EXTERNAL_ANDROID_DIR + NAMES libopenal.so + PATHS ${PARENT_SOURCE_DIR}/nCine-android-external ${PARENT_BINARY_DIR}/nCine-android-external + PATH_SUFFIXES openal/armeabi-v7a openal/arm64-v8a openal/x86_64 + DOC "Path to the nCine external Android libraries directory") + + if(IS_DIRECTORY ${NCINE_EXTERNAL_ANDROID_DIR}) + get_filename_component(NCINE_EXTERNAL_ANDROID_DIR ${NCINE_EXTERNAL_ANDROID_DIR} DIRECTORY) + get_filename_component(NCINE_EXTERNAL_ANDROID_DIR ${NCINE_EXTERNAL_ANDROID_DIR} DIRECTORY) + endif() + endif() + + if(IS_DIRECTORY ${NCINE_EXTERNAL_ANDROID_DIR}) + message(STATUS "nCine external Android libraries directory: ${NCINE_EXTERNAL_ANDROID_DIR}") + else() + message(STATUS "nCine external Android libraries directory not found at: ${NCINE_EXTERNAL_ANDROID_DIR}") + endif() +endif() + +get_filename_component(NCINE_LOCATION_DIR ${NCINE_LOCATION} DIRECTORY) +if(MSVC) + set(MSVC_ARCH_SUFFIX "x86") + if(MSVC_C_ARCHITECTURE_ID MATCHES 64 OR MSVC_CXX_ARCHITECTURE_ID MATCHES 64) + set(MSVC_ARCH_SUFFIX "x64") + endif() + + find_path(MSVC_BINDIR + NAMES glfw3.dll SDL2.dll + PATHS ${NCINE_LOCATION_DIR} ${NCINE_EXTERNAL_DIR} ${PARENT_SOURCE_DIR}/nCine-external ${PARENT_BINARY_DIR}/nCine-external + PATH_SUFFIXES bin/${MSVC_ARCH_SUFFIX} + DOC "Path to the nCine external MSVC DLL libraries directory" + NO_DEFAULT_PATH) # To avoid finding MSYS/MinGW libraries + + find_path(MSVC_LIBDIR + NAMES glfw3.lib SDL2.lib + PATHS ${NCINE_LOCATION_DIR} ${NCINE_EXTERNAL_DIR} ${PARENT_SOURCE_DIR}/nCine-external ${PARENT_BINARY_DIR}/nCine-external + PATH_SUFFIXES lib/${MSVC_ARCH_SUFFIX} + DOC "Path to the nCine external MSVC import libraries directory" + NO_DEFAULT_PATH) # To avoid finding MSYS/MinGW libraries + + get_filename_component(EXTERNAL_MSVC_DIR ${MSVC_LIBDIR} DIRECTORY) + get_filename_component(EXTERNAL_MSVC_DIR ${EXTERNAL_MSVC_DIR} DIRECTORY) + + if(IS_DIRECTORY ${MSVC_BINDIR} AND IS_DIRECTORY ${MSVC_LIBDIR} AND IS_DIRECTORY ${EXTERNAL_MSVC_DIR}) + message(STATUS "nCine external MSVC directory: ${EXTERNAL_MSVC_DIR}") + else() + message(FATAL_ERROR "nCine external MSVC directory not found at: ${EXTERNAL_MSVC_DIR}") + endif() +elseif(APPLE) + find_path(FRAMEWORKS_DIR + NAMES glfw.framework sdl2.framework + PATHS ${NCINE_LOCATION_DIR}/../../Frameworks ${NCINE_EXTERNAL_DIR} ${PARENT_SOURCE_DIR}/nCine-external ${PARENT_BINARY_DIR}/nCine-external + PATH_SUFFIXES bin/${ARCH_SUFFIX} + DOC "Path to the nCine frameworks directory") + + if(IS_DIRECTORY ${FRAMEWORKS_DIR}) + message(STATUS "nCine frameworks directory: ${FRAMEWORKS_DIR}") + else() + message(FATAL_ERROR "nCine frameworks directory not found at: ${FRAMEWORKS_DIR}") + endif() +elseif(EMSCRIPTEN) + find_path(EMSCRIPTEN_LIBDIR + NAMES libwebp.a liblua.a + PATHS ${NCINE_LOCATION_DIR} ${NCINE_EXTERNAL_DIR} ${PARENT_SOURCE_DIR}/nCine-external-emscripten ${PARENT_BINARY_DIR}/nCine-external-emscripten + PATH_SUFFIXES lib + DOC "Path to the nCine external Emscripten libraries directory" + NO_DEFAULT_PATH # To avoid finding MSYS/MinGW libraries + NO_CMAKE_FIND_ROOT_PATH) # To avoid using the cross-compiler root + + get_filename_component(EXTERNAL_EMSCRIPTEN_DIR ${EMSCRIPTEN_LIBDIR} DIRECTORY) + if(IS_DIRECTORY ${EMSCRIPTEN_LIBDIR} AND IS_DIRECTORY ${EXTERNAL_EMSCRIPTEN_DIR}) + message(STATUS "nCine external Emscripten directory: ${EXTERNAL_EMSCRIPTEN_DIR}") + else() + message(FATAL_ERROR "nCine external Emscripten directory not found at: ${EXTERNAL_EMSCRIPTEN_DIR}") + endif() +endif() diff --git a/cmake/package_build_android.cmake b/cmake/package_build_android.cmake new file mode 100644 index 0000000..87a61db --- /dev/null +++ b/cmake/package_build_android.cmake @@ -0,0 +1,192 @@ +if(PACKAGE_BUILD_ANDROID) + if(NOT IS_DIRECTORY ${NDK_DIR}) + unset(NDK_DIR CACHE) + find_path(NDK_DIR + NAMES ndk-build ndk-build.cmd ndk-gdb ndk-gdb.cmd ndk-stack ndk-stack.cmd ndk-which ndk-which.cmd + PATHS $ENV{ANDROID_NDK_HOME} $ENV{ANDROID_NDK_ROOT} $ENV{ANDROID_NDK} + DOC "Path to the Android NDK directory") + + if(NOT IS_DIRECTORY ${NDK_DIR}) + message(FATAL_ERROR "Cannot find the Android NDK directory") + endif() + endif() + message(STATUS "Android NDK directory: ${NDK_DIR}") + + set(ANDROID_TOOLCHAIN clang) + set(ANDROID_STL c++_shared) + + string(REPLACE "/" "." JAVA_PACKAGE "${PACKAGE_JAVA_URL}") + set(ANDROID_MANIFEST_XML_IN ${CMAKE_SOURCE_DIR}/android/src/main/AndroidManifest.xml.in) + set(ANDROID_MANIFEST_XML ${CMAKE_BINARY_DIR}/android/src/main/AndroidManifest.xml) + configure_file(${ANDROID_MANIFEST_XML_IN} ${ANDROID_MANIFEST_XML} @ONLY) + + set(LOAD_LIBRARIES_JAVA_IN ${CMAKE_SOURCE_DIR}/android/src/main/java/LoadLibraries.java.in) + set(LOAD_LIBRARIES_JAVA ${CMAKE_BINARY_DIR}/android/src/main/java/${PACKAGE_JAVA_URL}/LoadLibraries.java) + configure_file(${LOAD_LIBRARIES_JAVA_IN} ${LOAD_LIBRARIES_JAVA} @ONLY) + set(LOAD_LIBRARIES_TV_JAVA_IN ${CMAKE_SOURCE_DIR}/android/src/main/java/LoadLibrariesTV.java.in) + set(LOAD_LIBRARIES_TV_JAVA ${CMAKE_BINARY_DIR}/android/src/main/java/${PACKAGE_JAVA_URL}/LoadLibrariesTV.java) + configure_file(${LOAD_LIBRARIES_TV_JAVA_IN} ${LOAD_LIBRARIES_TV_JAVA} @ONLY) + + set(STRINGS_XML_IN ${CMAKE_SOURCE_DIR}/android/src/main/res/values/strings.xml.in) + set(STRINGS_XML ${CMAKE_BINARY_DIR}/android/src/main/res/values/strings.xml) + configure_file(${STRINGS_XML_IN} ${STRINGS_XML} @ONLY) + + foreach(SOURCE ${PACKAGE_SOURCES}) + list(APPEND ANDROID_PACKAGE_SOURCES "\t${CMAKE_SOURCE_DIR}/${SOURCE}") + endforeach() + string(REPLACE ";" "\n" ANDROID_PACKAGE_SOURCES "${ANDROID_PACKAGE_SOURCES}") + set(CMAKELIST_TXT_IN ${CMAKE_SOURCE_DIR}/android/src/main/cpp/CMakeLists.txt.in) + set(CMAKELIST_TXT ${CMAKE_BINARY_DIR}/android/src/main/cpp/CMakeLists.txt) + configure_file(${CMAKELIST_TXT_IN} ${CMAKELIST_TXT} @ONLY) + + # Reset compilation flags that external tools might have set in environment variables + set(RESET_FLAGS_ARGS -DCMAKE_C_FLAGS="" -DCMAKE_CXX_FLAGS="" -DCMAKE_EXE_LINKER_FLAGS="" + -DCMAKE_MODULE_LINKER_FLAGS="" -DCMAKE_SHARED_LINKER_FLAGS="" -DCMAKE_STATIC_LINKER_FLAGS="") + set(ANDROID_PASSTHROUGH_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} + -DNCINE_DYNAMIC_LIBRARY=${NCINE_DYNAMIC_LIBRARY} -DEXTERNAL_ANDROID_DIR=${NCINE_EXTERNAL_ANDROID_DIR} + -DGENERATED_INCLUDE_DIR=${GENERATED_INCLUDE_DIR} -DPACKAGE_STRIP_BINARIES=${PACKAGE_STRIP_BINARIES}) + set(ANDROID_CMAKE_ARGS -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=${ANDROID_API_LEVEL} + -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=${ANDROID_TOOLCHAIN_VERSION} -DCMAKE_ANDROID_STL_TYPE=${ANDROID_STL_TYPE}) + set(ANDROID_ARM_ARGS -DCMAKE_ANDROID_ARM_MODE=ON -DCMAKE_ANDROID_ARM_NEON=ON) + + if(MSVC) + list(APPEND ANDROID_CMAKE_ARGS "-GNMake Makefiles") + else() + list(APPEND ANDROID_CMAKE_ARGS "-G${CMAKE_GENERATOR}") + endif() + + string(REPLACE ";" "', '" GRADLE_PASSTHROUGH_ARGS "${ANDROID_PASSTHROUGH_ARGS}") + string(REPLACE ";" "', '" GRADLE_CMAKE_ARGS "${ANDROID_CMAKE_ARGS}") + string(REPLACE ";" "', '" GRADLE_ARM_ARGS "${ANDROID_ARM_ARGS}") + string(REPLACE ";" "', '" GRADLE_NDK_ARCHITECTURES "${PACKAGE_NDK_ARCHITECTURES}") + + # Added later to skip the string replace operation and keep them as lists in Gradle too + list(APPEND ANDROID_PASSTHROUGH_ARGS -DGENERATED_SOURCES="${GENERATED_SOURCES}" -DANDROID_GENERATED_FLAGS="${ANDROID_GENERATED_FLAGS}") + set(GRADLE_PASSTHROUGH_ARGS "${GRADLE_PASSTHROUGH_ARGS}', '-DGENERATED_SOURCES=${GENERATED_SOURCES}', '-DANDROID_GENERATED_FLAGS=${ANDROID_GENERATED_FLAGS}") + # Not added to Gradle arguments as it is handled by substituting `GRADLE_NDK_DIR` + list(APPEND ANDROID_CMAKE_ARGS -DCMAKE_ANDROID_NDK=${NDK_DIR}) + + set(GRADLE_BUILDTOOLS_VERSION 28.0.3) + set(GRADLE_COMPILESDK_VERSION 28) + set(GRADLE_MINSDK_VERSION 21) + set(GRADLE_TARGETSDK_VERSION 28) + set(GRADLE_VERSIONCODE 1) + + set(GRADLE_LIBCPP_SHARED "false") + if(ANDROID_STL STREQUAL "c++_shared") + set(GRADLE_LIBCPP_SHARED "true") + endif() + + set(BUILD_GRADLE_IN ${CMAKE_SOURCE_DIR}/android/build.gradle.in) + set(BUILD_GRADLE ${CMAKE_BINARY_DIR}/android/build.gradle) + set(GRADLE_JNILIBS_DIRS "'src/main/cpp/ncine', 'src/main/cpp/openal'") + configure_file(${BUILD_GRADLE_IN} ${BUILD_GRADLE} @ONLY) + set(GRADLE_PROPERTIES_IN ${CMAKE_SOURCE_DIR}/android/gradle.properties.in) + set(GRADLE_PROPERTIES ${CMAKE_BINARY_DIR}/android/gradle.properties) + set(GRADLE_CMAKE_COMMAND ${CMAKE_COMMAND}) + set(GRADLE_NDK_DIR ${NDK_DIR}) + configure_file(${GRADLE_PROPERTIES_IN} ${GRADLE_PROPERTIES} @ONLY) + + file(COPY ${PACKAGE_DATA_DIR}/icons/icon48.png DESTINATION android/src/main/res/mipmap-mdpi) + file(RENAME ${CMAKE_BINARY_DIR}/android/src/main/res/mipmap-mdpi/icon48.png ${CMAKE_BINARY_DIR}/android/src/main/res/mipmap-mdpi/ic_launcher.png) + file(COPY ${PACKAGE_DATA_DIR}/icons/icon72.png DESTINATION android/src/main/res/mipmap-hdpi) + file(RENAME ${CMAKE_BINARY_DIR}/android/src/main/res/mipmap-hdpi/icon72.png ${CMAKE_BINARY_DIR}/android/src/main/res/mipmap-hdpi/ic_launcher.png) + file(COPY ${PACKAGE_DATA_DIR}/icons/icon96.png DESTINATION android/src/main/res/mipmap-xhdpi) + file(RENAME ${CMAKE_BINARY_DIR}/android/src/main/res/mipmap-xhdpi/icon96.png ${CMAKE_BINARY_DIR}/android/src/main/res/mipmap-xhdpi/ic_launcher.png) + file(COPY ${PACKAGE_DATA_DIR}/icons/icon144.png DESTINATION android/src/main/res/mipmap-xxhdpi) + file(RENAME ${CMAKE_BINARY_DIR}/android/src/main/res/mipmap-xxhdpi/icon144.png ${CMAKE_BINARY_DIR}/android/src/main/res/mipmap-xxhdpi/ic_launcher.png) + file(COPY ${PACKAGE_DATA_DIR}/icons/icon192.png DESTINATION android/src/main/res/mipmap-xxxhdpi) + file(RENAME ${CMAKE_BINARY_DIR}/android/src/main/res/mipmap-xxxhdpi/icon192.png ${CMAKE_BINARY_DIR}/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png) + + if(DEFINED PACKAGE_ANDROID_ASSETS) + foreach(ASSET ${PACKAGE_ANDROID_ASSETS}) + file(COPY ${PACKAGE_DATA_DIR}/${ASSET} DESTINATION android/src/main/assets/) + endforeach() + endif() + + find_path(NCINE_ANDROID_NCINE_LIB_DIR + NAMES libncine.so libncine.a + PATHS ${NCINE_ANDROID_DIR}/src/main/cpp/ncine ${NCINE_ANDROID_DIR}/build/ncine/ + PATH_SUFFIXES armeabi-v7a arm64-v8a x86_64 + ) + get_filename_component(NCINE_ANDROID_NCINE_LIB_DIR ${NCINE_ANDROID_NCINE_LIB_DIR} DIRECTORY) + + set(NCINE_ANDROID_LIBNAME libncine.so) + if(NOT NCINE_DYNAMIC_LIBRARY) + set(NCINE_ANDROID_LIBNAME libncine.a) + endif() + + find_path(NCINE_ANDROID_OPENAL_LIB_DIR + NAMES libopenal.so + PATHS ${NCINE_ANDROID_DIR}/src/main/cpp/openal ${NCINE_EXTERNAL_ANDROID_DIR}/openal/ + PATH_SUFFIXES armeabi-v7a arm64-v8a x86_64 + ) + get_filename_component(NCINE_ANDROID_OPENAL_LIB_DIR ${NCINE_ANDROID_OPENAL_LIB_DIR} DIRECTORY) + + foreach(ARCHITECTURE ${PACKAGE_NDK_ARCHITECTURES}) + file(COPY ${NCINE_ANDROID_OPENAL_LIB_DIR}/${ARCHITECTURE}/libopenal.so DESTINATION android/src/main/cpp/openal/${ARCHITECTURE}) + file(COPY ${NCINE_ANDROID_NCINE_LIB_DIR}/${ARCHITECTURE}/${NCINE_ANDROID_LIBNAME} DESTINATION android/src/main/cpp/ncine/${ARCHITECTURE}) + file(COPY ${NCINE_ANDROID_NCINE_LIB_DIR}/${ARCHITECTURE}/libncine_main.a DESTINATION android/src/main/cpp/ncine/${ARCHITECTURE}) + endforeach() + + foreach(INCLUDE_DIR ${NCINE_INCLUDE_DIR}) + if(IS_DIRECTORY ${INCLUDE_DIR}/ncine) + file(COPY ${INCLUDE_DIR}/ncine DESTINATION android/src/main/cpp/ncine/include) + endif() + if(IS_DIRECTORY ${INCLUDE_DIR}/nctl) + file(COPY ${INCLUDE_DIR}/nctl DESTINATION android/src/main/cpp/ncine/include) + endif() + endforeach() + + add_custom_target(package_android ALL) + set_target_properties(package_android PROPERTIES FOLDER "Android") + add_dependencies(package_android ${PACKAGE_EXE_NAME}) + + foreach(ARCHITECTURE ${PACKAGE_NDK_ARCHITECTURES}) + set(ANDROID_BINARY_DIR ${CMAKE_BINARY_DIR}/android/build/${PACKAGE_LOWER_NAME}/${ARCHITECTURE}) + set(ANDROID_ARCH_ARGS -DCMAKE_ANDROID_ARCH_ABI=${ARCHITECTURE}) + if(ARCHITECTURE STREQUAL "armeabi-v7a") + list(APPEND ANDROID_ARCH_ARGS ${ANDROID_ARM_ARGS}) + endif() + add_custom_command(OUTPUT ${ANDROID_BINARY_DIR}/libgame.so + COMMAND ${CMAKE_COMMAND} -H${CMAKE_BINARY_DIR}/android/src/main/cpp/ -B${ANDROID_BINARY_DIR} + -DCMAKE_TOOLCHAIN_FILE=${NDK_DIR}/build/cmake/android.toolchain.cmake + -DANDROID_PLATFORM=android-${GRADLE_MINSDK_VERSION} -DANDROID_ABI=${ARCHITECTURE} + ${RESET_FLAGS_ARGS} ${ANDROID_PASSTHROUGH_ARGS} ${ANDROID_CMAKE_ARGS} ${ANDROID_ARCH_ARGS} + COMMAND ${CMAKE_COMMAND} --build ${ANDROID_BINARY_DIR} + COMMENT "Compiling the Android library for ${ARCHITECTURE}") + add_custom_target(package_android_${ARCHITECTURE} DEPENDS ${ANDROID_BINARY_DIR}/libgame.so) + set_target_properties(package_android_${ARCHITECTURE} PROPERTIES FOLDER "Android") + add_dependencies(package_android package_android_${ARCHITECTURE}) + endforeach() + + if(PACKAGE_ASSEMBLE_APK) + find_program(GRADLE_EXECUTABLE gradle $ENV{GRADLE_HOME}/bin) + if(GRADLE_EXECUTABLE) + message(STATUS "Gradle executable: ${GRADLE_EXECUTABLE}") + + if(CMAKE_BUILD_TYPE MATCHES "Release") + set(GRADLE_TASK assembleRelease) + set(APK_BUILDTYPE_DIR release) + set(APK_FILE_SUFFIX release-unsigned) + else() + set(GRADLE_TASK assembleDebug) + set(APK_BUILDTYPE_DIR debug) + set(APK_FILE_SUFFIX debug) + endif() + + foreach(ARCHITECTURE ${PACKAGE_NDK_ARCHITECTURES}) + list(APPEND APK_FILES ${CMAKE_BINARY_DIR}/android/build/outputs/apk/${APK_BUILDTYPE_DIR}/android-${ARCHITECTURE}-${APK_FILE_SUFFIX}.apk) + endforeach() + + # Invoking Gradle to create the Android APK of the game + add_custom_command(OUTPUT ${APK_FILES} + COMMAND ${GRADLE_EXECUTABLE} -p android ${GRADLE_TASK}) + add_custom_target(package_gradle_apk ALL DEPENDS ${APK_FILES}) + set_target_properties(package_gradle_apk PROPERTIES FOLDER "Android") + add_dependencies(package_gradle_apk package_android) + else() + message(WARNING "Gradle executable not found, Android APK will not be assembled") + endif() + endif() +endif() diff --git a/cmake/package_check_info.cmake b/cmake/package_check_info.cmake new file mode 100644 index 0000000..504d90a --- /dev/null +++ b/cmake/package_check_info.cmake @@ -0,0 +1,26 @@ +if(NOT DEFINED PACKAGE_NAME) + message(FATAL_ERROR "PACKAGE_NAME variable is not set") +endif() + +if(NOT DEFINED PACKAGE_SOURCES) + message(FATAL_ERROR "PACKAGE_SOURCES variable is not set") +endif() + +string(TOLOWER ${PACKAGE_NAME} PACKAGE_LOWER_NAME) +string(TOUPPER ${PACKAGE_NAME} PACKAGE_UPPER_NAME) + +if(NOT DEFINED PACKAGE_EXE_NAME) + set(PACKAGE_EXE_NAME ${PACKAGE_LOWER_NAME}) +endif() + +if(NOT DEFINED PACKAGE_ICON_NAME) + set(PACKAGE_ICON_NAME ${PACKAGE_NAME}) +endif() + +if(NOT DEFINED PACKAGE_DESKTOP_FILE) + set(PACKAGE_DESKTOP_FILE ${PACKAGE_LOWER_NAME}.desktop) +endif() + +if(NOT DEFINED PACKAGE_JAVA_URL) + set(PACKAGE_JAVA_URL ${PACKAGE_LOWER_NAME}) +endif() diff --git a/cmake/package_copy_targets.cmake b/cmake/package_copy_targets.cmake new file mode 100644 index 0000000..4672c83 --- /dev/null +++ b/cmake/package_copy_targets.cmake @@ -0,0 +1,36 @@ +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +if(UNIX) + if(IS_DIRECTORY ${NCINE_SHADERS_DIR}) + add_custom_target(symlink_shaders ALL + COMMAND ${CMAKE_COMMAND} -E create_symlink ${NCINE_SHADERS_DIR} ${PACKAGE_DEFAULT_DATA_DIR}/shaders + COMMENT "Symlinking shaders..." + ) + set_target_properties(symlink_shaders PROPERTIES FOLDER "CustomSymlinkTargets") + endif() +elseif(WIN32) + if(IS_DIRECTORY ${NCINE_SHADERS_DIR}) + add_custom_target(copy_shaders ALL + COMMAND ${CMAKE_COMMAND} -E copy_directory ${NCINE_SHADERS_DIR} ${PACKAGE_DEFAULT_DATA_DIR}/shaders + COMMENT "Copying shaders..." + ) + set_target_properties(copy_shaders PROPERTIES FOLDER "CustomCopyTargets") + endif() + + if(NCINE_DYNAMIC_LIBRARY) + add_custom_target(copy_ncine_dll ALL + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${NCINE_LOCATION} ${CMAKE_BINARY_DIR} + COMMENT "Copying nCine DLL..." + ) + set_target_properties(copy_ncine_dll PROPERTIES FOLDER "CustomCopyTargets") + endif() +endif() + +if(MSVC) + file(GLOB MSVC_DLL_FILES ${MSVC_BINDIR}/*.dll) + + add_custom_target(copy_dlls ALL + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${MSVC_DLL_FILES} ${CMAKE_BINARY_DIR} + COMMENT "Copying DLLs..." + ) + set_target_properties(copy_dlls PROPERTIES FOLDER "CustomCopyTargets") +endif() diff --git a/cmake/package_generated_sources.cmake b/cmake/package_generated_sources.cmake new file mode 100644 index 0000000..abfd0c2 --- /dev/null +++ b/cmake/package_generated_sources.cmake @@ -0,0 +1,73 @@ +# Has to be included after package_get_version.cmake + +set(GENERATED_SOURCE_DIR "${CMAKE_BINARY_DIR}/generated") +set(GENERATED_INCLUDE_DIR "${GENERATED_SOURCE_DIR}/include") + +# Version strings +if(GIT_EXECUTABLE) + message(STATUS "Exporting git version information to C strings") +endif() + +set(VERSION_H_FILE "${GENERATED_INCLUDE_DIR}/version.h") +set(VERSION_CPP_FILE "${GENERATED_SOURCE_DIR}/version.cpp") +if(EXISTS ${VERSION_H_FILE}) + file(REMOVE ${VERSION_H_FILE}) +endif() +if(EXISTS ${VERSION_CPP_FILE}) + file(REMOVE ${VERSION_CPP_FILE}) +endif() + +set(VERSION_STRUCT_NAME "VersionStrings") +set(VERSION_STRING_NAME "Version") +set(REVCOUNT_STRING_NAME "GitRevCount") +set(SHORTHASH_STRING_NAME "GitShortHash") +set(LASTCOMMITDATE_STRING_NAME "GitLastCommitDate") +set(BRANCH_STRING_NAME "GitBranch") +set(TAG_STRING_NAME "GitTag") +set(COMPILATION_DATE_STRING_NAME "CompilationDate") +set(COMPILATION_TIME_STRING_NAME "CompilationTime") + +get_filename_component(VERSION_H_FILENAME ${VERSION_H_FILE} NAME) +file(APPEND ${VERSION_H_FILE} "#ifndef PACKAGE_VERSION\n") +file(APPEND ${VERSION_H_FILE} "#define PACKAGE_VERSION\n\n") +file(APPEND ${VERSION_H_FILE} "struct ${VERSION_STRUCT_NAME}\n{\n") +file(APPEND ${VERSION_CPP_FILE} "#include \"${VERSION_H_FILENAME}\"\n\n") + +if(GIT_EXECUTABLE) + target_compile_definitions(${PACKAGE_EXE_NAME} PRIVATE "WITH_GIT_VERSION") + list(APPEND ANDROID_GENERATED_FLAGS WITH_GIT_VERSION) + + file(APPEND ${VERSION_H_FILE} "\tstatic char const * const ${VERSION_STRING_NAME};\n") + file(APPEND ${VERSION_CPP_FILE} "char const * const ${VERSION_STRUCT_NAME}::${VERSION_STRING_NAME} = \"${PACKAGE_VERSION}\";\n") + file(APPEND ${VERSION_H_FILE} "\tstatic char const * const ${REVCOUNT_STRING_NAME};\n") + file(APPEND ${VERSION_CPP_FILE} "char const * const ${VERSION_STRUCT_NAME}::${REVCOUNT_STRING_NAME} = \"${GIT_REV_COUNT}\";\n") + file(APPEND ${VERSION_H_FILE} "\tstatic char const * const ${SHORTHASH_STRING_NAME};\n") + file(APPEND ${VERSION_CPP_FILE} "char const * const ${VERSION_STRUCT_NAME}::${SHORTHASH_STRING_NAME} = \"${GIT_SHORT_HASH}\";\n") + file(APPEND ${VERSION_H_FILE} "\tstatic char const * const ${LASTCOMMITDATE_STRING_NAME};\n") + file(APPEND ${VERSION_CPP_FILE} "char const * const ${VERSION_STRUCT_NAME}::${LASTCOMMITDATE_STRING_NAME} = \"${GIT_LAST_COMMIT_DATE}\";\n") + file(APPEND ${VERSION_H_FILE} "\tstatic char const * const ${BRANCH_STRING_NAME};\n") + file(APPEND ${VERSION_CPP_FILE} "char const * const ${VERSION_STRUCT_NAME}::${BRANCH_STRING_NAME} = \"${GIT_BRANCH_NAME}\";\n") + if(NOT GIT_NO_TAG) + file(APPEND ${VERSION_H_FILE} "\tstatic char const * const ${TAG_STRING_NAME};\n") + file(APPEND ${VERSION_CPP_FILE} "char const * const ${VERSION_STRUCT_NAME}::${TAG_STRING_NAME} = \"${GIT_TAG_NAME}\";\n") + endif() +endif() +file(APPEND ${VERSION_H_FILE} "\tstatic char const * const ${COMPILATION_DATE_STRING_NAME};\n") +file(APPEND ${VERSION_CPP_FILE} "char const * const ${VERSION_STRUCT_NAME}::${COMPILATION_DATE_STRING_NAME} = __DATE__;\n") +file(APPEND ${VERSION_H_FILE} "\tstatic char const * const ${COMPILATION_TIME_STRING_NAME};\n") +file(APPEND ${VERSION_CPP_FILE} "char const * const ${VERSION_STRUCT_NAME}::${COMPILATION_TIME_STRING_NAME} = __TIME__;\n") + +file(APPEND ${VERSION_H_FILE} "};\n\n") +file(APPEND ${VERSION_H_FILE} "#endif") + +list(APPEND HEADERS ${VERSION_H_FILE}) +list(APPEND GENERATED_SOURCES ${VERSION_CPP_FILE}) + +if(MSVC AND EXISTS ${PACKAGE_DATA_DIR}/icons/${PACKAGE_ICON_NAME}.ico) + message(STATUS "Writing a resource file for executable icon") + + set(RESOURCE_RC_FILE "${GENERATED_SOURCE_DIR}/resource.rc") + file(WRITE ${RESOURCE_RC_FILE} "IDI_ICON1 ICON DISCARDABLE \"${PACKAGE_ICON_NAME}.ico\"") + file(COPY ${PACKAGE_DATA_DIR}/icons/${PACKAGE_ICON_NAME}.ico DESTINATION ${GENERATED_INCLUDE_DIR}) + target_sources(${PACKAGE_EXE_NAME} PRIVATE ${RESOURCE_RC_FILE}) +endif() diff --git a/cmake/package_get_version.cmake b/cmake/package_get_version.cmake new file mode 100644 index 0000000..79e8a5b --- /dev/null +++ b/cmake/package_get_version.cmake @@ -0,0 +1,60 @@ +find_package(Git) +if(GIT_EXECUTABLE AND IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.git) + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_REV_COUNT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_SHORT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + execute_process( + COMMAND ${GIT_EXECUTABLE} log -1 --format=%ad --date=short + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_LAST_COMMIT_DATE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_BRANCH_NAME + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + execute_process( + COMMAND ${GIT_EXECUTABLE} describe --tags --exact-match HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + RESULT_VARIABLE GIT_NO_TAG + OUTPUT_VARIABLE GIT_TAG_NAME + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if(${GIT_NO_TAG}) + string(REPLACE "-" ";" GIT_LAST_COMMIT_DATE_LIST ${GIT_LAST_COMMIT_DATE}) + list(GET GIT_LAST_COMMIT_DATE_LIST 0 PACKAGE_MAJOR_VERSION) + list(GET GIT_LAST_COMMIT_DATE_LIST 1 PACKAGE_MINOR_VERSION) + set(PACKAGE_PATCH_VERSION "r${GIT_REV_COUNT}-${GIT_SHORT_HASH}") + endif() +else() + set(GIT_NO_TAG TRUE) + string(TIMESTAMP PACKAGE_MAJOR_VERSION "%Y") + string(TIMESTAMP PACKAGE_MINOR_VERSION "%m") + string(TIMESTAMP PACKAGE_PATCH_VERSION "%d") +endif() + +if(NOT GIT_EXECUTABLE OR GIT_NO_TAG) + set(PACKAGE_VERSION "${PACKAGE_MAJOR_VERSION}.${PACKAGE_MINOR_VERSION}.${PACKAGE_PATCH_VERSION}") +else() + set(PACKAGE_VERSION "${GIT_TAG_NAME}") +endif() +message(STATUS "${PACKAGE_NAME} version: ${PACKAGE_VERSION}") + +mark_as_advanced(PACKAGE_MAJOR_VERSION PACKAGE_MINOR_VERSION PACKAGE_PATCH_VERSION PACKAGE_VERSION) diff --git a/cmake/package_imported_targets.cmake b/cmake/package_imported_targets.cmake new file mode 100644 index 0000000..2856e9b --- /dev/null +++ b/cmake/package_imported_targets.cmake @@ -0,0 +1,423 @@ +if(EMSCRIPTEN) + add_library(Threads::Threads INTERFACE IMPORTED) + set_target_properties(Threads::Threads PROPERTIES + INTERFACE_COMPILE_OPTIONS "SHELL:-s USE_PTHREADS=1 -s WASM_MEM_MAX=128MB" + INTERFACE_LINK_OPTIONS "SHELL:-s USE_PTHREADS=1 -s WASM_MEM_MAX=128MB") + set(Threads_FOUND 1) + + add_library(OpenGL::GL INTERFACE IMPORTED) + set_target_properties(OpenGL::GL PROPERTIES + INTERFACE_COMPILE_OPTIONS "SHELL:-s USE_WEBGL2=1 -s FULL_ES3=1" + INTERFACE_LINK_OPTIONS "SHELL:-s USE_WEBGL2=1 -s FULL_ES3=1") + set(OPENGL_FOUND 1) + + add_library(GLFW::GLFW INTERFACE IMPORTED) + set_target_properties(GLFW::GLFW PROPERTIES + INTERFACE_COMPILE_OPTIONS "SHELL:-s USE_GLFW=3" + INTERFACE_LINK_OPTIONS "SHELL:-s USE_GLFW=3") + set(GLFW_FOUND 1) + + add_library(SDL2::SDL2 INTERFACE IMPORTED) + set_target_properties(SDL2::SDL2 PROPERTIES + INTERFACE_COMPILE_OPTIONS "SHELL:-s USE_SDL=2" + INTERFACE_LINK_OPTIONS "SHELL:-s USE_SDL=2") + set(SDL2_FOUND 1) + + add_library(OpenAL::AL INTERFACE IMPORTED) + set_target_properties(OpenAL::AL PROPERTIES + INTERFACE_LINK_OPTIONS "SHELL:-lopenal") + set(OPENAL_FOUND 1) + + add_library(PNG::PNG INTERFACE IMPORTED) + set_target_properties(PNG::PNG PROPERTIES + INTERFACE_COMPILE_OPTIONS "SHELL:-s USE_LIBPNG=1" + INTERFACE_LINK_OPTIONS "SHELL:-s USE_LIBPNG=1") + set(PNG_FOUND 1) + + add_library(WebP::WebP STATIC IMPORTED) + set_target_properties(WebP::WebP PROPERTIES + IMPORTED_LOCATION ${EMSCRIPTEN_LIBDIR}/libwebp.a + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_EMSCRIPTEN_DIR}/include") + set(WEBP_FOUND 1) + + add_library(Vorbis::Vorbisfile INTERFACE IMPORTED) + set_target_properties(Vorbis::Vorbisfile PROPERTIES + INTERFACE_COMPILE_OPTIONS "SHELL:-s USE_VORBIS=1" + INTERFACE_LINK_OPTIONS "SHELL:-s USE_VORBIS=1") + set(VORBIS_FOUND 1) + + add_library(Lua::Lua STATIC IMPORTED) + set_target_properties(Lua::Lua PROPERTIES + IMPORTED_LOCATION ${EMSCRIPTEN_LIBDIR}/liblua.a + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_EMSCRIPTEN_DIR}/include") + set(LUA_FOUND 1) + + return() +endif() + +find_package(Threads) +if(NOT MSVC AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Android") # GCC and LLVM + if(APPLE) + set(CMAKE_FRAMEWORK_PATH ${FRAMEWORKS_DIR}) + set(CMAKE_MACOSX_RPATH ON) + endif() + + if(WIN32) + find_package(GLEW REQUIRED) + else() + find_package(GLEW) + endif() + find_package(OpenGL REQUIRED) + find_package(GLFW) + find_package(SDL2) + find_package(PNG) + find_package(OpenAL) + find_package(WebP) + find_package(Vorbis) + find_package(Lua) +endif() + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Android") + find_library(ANDROID_LIBRARY android) + find_library(EGL_LIBRARY EGL) + find_library(GLES3_LIBRARY GLESv3) + find_library(LOG_LIBRARY log) + find_library(OPENSLES_LIBRARY OpenSLES) + find_library(ZLIB_LIBRARY z) + + if(EXISTS ${EXTERNAL_ANDROID_DIR}/png/${ANDROID_ABI}/libpng16.a) + add_library(PNG::PNG STATIC IMPORTED) + set_target_properties(PNG::PNG PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION ${EXTERNAL_ANDROID_DIR}/png/${ANDROID_ABI}/libpng16.a + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_ANDROID_DIR}/png/include" + INTERFACE_LINK_LIBRARIES ${ZLIB_LIBRARY}) + set(PNG_FOUND 1) + endif() + + if(EXISTS ${EXTERNAL_ANDROID_DIR}/webp/${ANDROID_ABI}/libwebp.a) + add_library(WebP::WebP STATIC IMPORTED) + set_target_properties(WebP::WebP PROPERTIES + IMPORTED_LOCATION ${EXTERNAL_ANDROID_DIR}/webp/${ANDROID_ABI}/libwebp.a + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_ANDROID_DIR}/webp/include") + set(WEBP_FOUND 1) + endif() + + if(EXISTS ${EXTERNAL_ANDROID_DIR}/openal/${ANDROID_ABI}/libopenal.so) + add_library(OpenAL::AL SHARED IMPORTED) + set_target_properties(OpenAL::AL PROPERTIES + IMPORTED_LOCATION ${EXTERNAL_ANDROID_DIR}/openal/${ANDROID_ABI}/libopenal.so + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_ANDROID_DIR}/openal/include") + set(OPENAL_FOUND 1) + endif() + + if(EXISTS ${EXTERNAL_ANDROID_DIR}/vorbis/${ANDROID_ABI}/libvorbisfile.a AND + EXISTS ${EXTERNAL_ANDROID_DIR}/vorbis/${ANDROID_ABI}/libvorbis.a AND + EXISTS ${EXTERNAL_ANDROID_DIR}/ogg/${ANDROID_ABI}/libogg.a) + add_library(Vorbis::Vorbisfile STATIC IMPORTED) + set_target_properties(Vorbis::Vorbisfile PROPERTIES + IMPORTED_LOCATION ${EXTERNAL_ANDROID_DIR}/vorbis/${ANDROID_ABI}/libvorbisfile.a + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_ANDROID_DIR}/vorbis/include;${EXTERNAL_ANDROID_DIR}/ogg/include" + INTERFACE_LINK_LIBRARIES "${EXTERNAL_ANDROID_DIR}/vorbis/${ANDROID_ABI}/libvorbis.a;${EXTERNAL_ANDROID_DIR}/ogg/${ANDROID_ABI}/libogg.a") + set(VORBIS_FOUND 1) + endif() + + if(EXISTS ${EXTERNAL_ANDROID_DIR}/lua/${ANDROID_ABI}/liblua.a) + add_library(Lua::Lua STATIC IMPORTED) + set_target_properties(Lua::Lua PROPERTIES + IMPORTED_LOCATION ${EXTERNAL_ANDROID_DIR}/lua/${ANDROID_ABI}/liblua.a + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_ANDROID_DIR}/lua/include") + set(LUA_FOUND 1) + endif() +elseif(MSVC) + if(EXISTS ${MSVC_LIBDIR}/glew32.lib AND EXISTS ${MSVC_BINDIR}/glew32.dll) + add_library(GLEW::GLEW SHARED IMPORTED) + set_target_properties(GLEW::GLEW PROPERTIES + IMPORTED_IMPLIB ${MSVC_LIBDIR}/glew32.lib + IMPORTED_LOCATION ${MSVC_BINDIR}/glew32.dll + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_MSVC_DIR}/include") + set(GLEW_FOUND 1) + endif() + + add_library(OpenGL::GL SHARED IMPORTED) + set_target_properties(OpenGL::GL PROPERTIES + IMPORTED_IMPLIB opengl32.lib + IMPORTED_LOCATION opengl32.dll) + set(OPENGL_FOUND 1) + + if(EXISTS ${MSVC_LIBDIR}/glfw3dll.lib AND EXISTS ${MSVC_BINDIR}/glfw3.dll) + add_library(GLFW::GLFW SHARED IMPORTED) + set_target_properties(GLFW::GLFW PROPERTIES + IMPORTED_IMPLIB ${MSVC_LIBDIR}/glfw3dll.lib + IMPORTED_LOCATION ${MSVC_BINDIR}/glfw3.dll + INTERFACE_COMPILE_DEFINITIONS "GLFW_NO_GLU" + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_MSVC_DIR}/include") + set(GLFW_FOUND 1) + endif() + + if(EXISTS ${MSVC_LIBDIR}/SDL2.lib AND EXISTS ${MSVC_LIBDIR}/SDL2main.lib AND EXISTS ${MSVC_BINDIR}/SDL2.dll) + add_library(SDL2::SDL2 SHARED IMPORTED) + set_target_properties(SDL2::SDL2 PROPERTIES + IMPORTED_IMPLIB ${MSVC_LIBDIR}/SDL2.lib + IMPORTED_LOCATION ${MSVC_BINDIR}/SDL2.dll + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_MSVC_DIR}/include" + INTERFACE_LINK_LIBRARIES ${MSVC_LIBDIR}/SDL2main.lib) + set(SDL2_FOUND 1) + endif() + + if(EXISTS ${MSVC_LIBDIR}/libpng16.lib AND EXISTS ${MSVC_LIBDIR}/zlib.lib AND + EXISTS ${MSVC_BINDIR}/libpng16.dll AND EXISTS ${MSVC_BINDIR}/zlib.dll) + add_library(PNG::PNG SHARED IMPORTED) + set_target_properties(PNG::PNG PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_IMPLIB ${MSVC_LIBDIR}/libpng16.lib + IMPORTED_LOCATION ${MSVC_BINDIR}/libpng16.dll + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_MSVC_DIR}/include" + INTERFACE_LINK_LIBRARIES ${MSVC_LIBDIR}/zlib.lib) + set(PNG_FOUND 1) + endif() + + if(EXISTS ${MSVC_LIBDIR}/OpenAL32.lib AND EXISTS ${MSVC_BINDIR}/OpenAL32.dll) + add_library(OpenAL::AL SHARED IMPORTED) + set_target_properties(OpenAL::AL PROPERTIES + IMPORTED_IMPLIB ${MSVC_LIBDIR}/OpenAL32.lib + IMPORTED_LOCATION ${MSVC_BINDIR}/OpenAL32.dll + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_MSVC_DIR}/include") + set(OPENAL_FOUND 1) + endif() + + if(EXISTS ${MSVC_LIBDIR}/libwebp_dll.lib AND EXISTS ${MSVC_BINDIR}/libwebp.dll AND EXISTS ${MSVC_BINDIR}/libwebpdecoder.dll) + add_library(WebP::WebP SHARED IMPORTED) + set_target_properties(WebP::WebP PROPERTIES + IMPORTED_IMPLIB ${MSVC_LIBDIR}/libwebp_dll.lib + IMPORTED_LOCATION ${MSVC_BINDIR}/libwebp.dll + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_MSVC_DIR}/include") + set(WEBP_FOUND 1) + endif() + + if(EXISTS ${MSVC_LIBDIR}/libogg.lib AND EXISTS ${MSVC_LIBDIR}/libvorbis.lib AND EXISTS ${MSVC_LIBDIR}/libvorbisfile.lib AND + EXISTS ${MSVC_BINDIR}/libogg.dll AND EXISTS ${MSVC_BINDIR}/libvorbis.dll AND EXISTS ${MSVC_BINDIR}/libvorbisfile.dll) + add_library(Vorbis::Vorbisfile SHARED IMPORTED) + set_target_properties(Vorbis::Vorbisfile PROPERTIES + IMPORTED_IMPLIB ${MSVC_LIBDIR}/libvorbisfile.lib + IMPORTED_LOCATION ${MSVC_BINDIR}/libvorbisfile.dll + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_MSVC_DIR}/include" + INTERFACE_LINK_LIBRARIES "${MSVC_LIBDIR}/libvorbis.lib;${MSVC_LIBDIR}/libogg.lib") + set(VORBIS_FOUND 1) + endif() + + if(EXISTS ${MSVC_LIBDIR}/lua53.lib AND EXISTS ${MSVC_BINDIR}/lua53.dll) + add_library(Lua::Lua SHARED IMPORTED) + set_target_properties(Lua::Lua PROPERTIES + IMPORTED_IMPLIB ${MSVC_LIBDIR}/lua53.lib + IMPORTED_LOCATION ${MSVC_BINDIR}/lua53.dll + INTERFACE_INCLUDE_DIRECTORIES "${EXTERNAL_MSVC_DIR}/include") + set(LUA_FOUND 1) + endif() +elseif(MINGW OR MSYS) + function(set_msys_dll PREFIX DLL_NAME) + set(LIB_NAME "${DLL_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(DLL_LIBRARY "$ENV{MINGW_PREFIX}/bin/${LIB_NAME}") + if(EXISTS ${DLL_LIBRARY} AND NOT IS_DIRECTORY ${DLL_LIBRARY}) + set(${PREFIX}_DLL_LIBRARY ${DLL_LIBRARY} PARENT_SCOPE) + else() + message(WARNING "Could not find: ${DLL_LIBRARY}") + endif() + endfunction() + + if(GLFW_FOUND) + set_msys_dll(GLFW glfw3) + add_library(GLFW::GLFW SHARED IMPORTED) + set_target_properties(GLFW::GLFW PROPERTIES + IMPORTED_IMPLIB ${GLFW_LIBRARY} + IMPORTED_LOCATION ${GLFW_DLL_LIBRARY} + INTERFACE_COMPILE_DEFINITIONS "GLFW_NO_GLU" + INTERFACE_INCLUDE_DIRECTORIES ${GLFW_INCLUDE_DIR}) + endif() + + if(SDL2_FOUND) + foreach(LIBRARY ${SDL2_LIBRARY}) + string(FIND ${LIBRARY} ".a" FOUND_STATIC_LIB) + if(FOUND_STATIC_LIB GREATER -1 AND NOT ${LIBRARY} STREQUAL ${SDL2MAIN_LIBRARY}) + set(SDL2_IMPORT_LIBRARY ${LIBRARY}) + break() + endif() + endforeach() + + set_msys_dll(SDL2 SDL2) + add_library(SDL2::SDL2 SHARED IMPORTED) + set_target_properties(SDL2::SDL2 PROPERTIES + IMPORTED_IMPLIB ${SDL2_IMPORT_LIBRARY} + IMPORTED_LOCATION ${SDL2_DLL_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES ${SDL2MAIN_LIBRARY}) + endif() + + if(OPENAL_FOUND) + set_msys_dll(OPENAL libopenal-1) + add_library(OpenAL::AL SHARED IMPORTED) + set_target_properties(OpenAL::AL PROPERTIES + IMPORTED_IMPLIB ${OPENAL_LIBRARY} + IMPORTED_LOCATION ${OPENAL_DLL_LIBRARY} + IMPORTED_LOCATION ${OPENAL_LIB_PATH}/${OPENAL_LIB_NAME}.dll + INTERFACE_INCLUDE_DIRECTORIES ${OPENAL_INCLUDE_DIR}) + endif() + + if(WEBP_FOUND) + set_msys_dll(WEBP libwebp-7) + add_library(WebP::WebP SHARED IMPORTED) + set_target_properties(WebP::WebP PROPERTIES + IMPORTED_IMPLIB ${WEBP_LIBRARY} + IMPORTED_LOCATION ${WEBP_DLL_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${WEBP_INCLUDE_DIR}) + endif() + + if(VORBIS_FOUND) + set_msys_dll(VORBISFILE libvorbisfile-3) + add_library(Vorbis::Vorbisfile SHARED IMPORTED) + set_target_properties(Vorbis::Vorbisfile PROPERTIES + IMPORTED_IMPLIB ${VORBISFILE_LIBRARY} + IMPORTED_LOCATION ${VORBISFILE_DLL_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${VORBIS_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES "${VORBIS_LIBRARY};${OGG_LIBRARY}") + endif() + + if(LUA_FOUND) + set_msys_dll(LUA lua53) + add_library(Lua::Lua SHARED IMPORTED) + set_target_properties(Lua::Lua PROPERTIES + IMPORTED_IMPLIB ${LUA_LIBRARY} + IMPORTED_LOCATION ${LUA_DLL_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${LUA_INCLUDE_DIR}) + endif() +else() # GCC and LLVM + function(split_extra_libraries PREFIX LIBRARIES) + foreach(LIBRARY ${LIBRARIES}) + string(FIND ${LIBRARY} "-l" FOUND_LINKER_ARG) + string(FIND ${LIBRARY} ".a" FOUND_STATIC_LIB) + if(FOUND_LINKER_ARG GREATER -1 OR FOUND_STATIC_LIB GREATER -1) + list(APPEND EXTRA_LIBRARIES ${LIBRARY}) + else() + list(APPEND LIBRARY_FILE ${LIBRARY}) + endif() + endforeach() + set(${PREFIX}_EXTRA_LIBRARIES ${EXTRA_LIBRARIES} PARENT_SCOPE) + set(${PREFIX}_LIBRARY_FILE ${LIBRARY_FILE} PARENT_SCOPE) + endfunction() + + if(GLFW_FOUND) + add_library(GLFW::GLFW SHARED IMPORTED) + set_target_properties(GLFW::GLFW PROPERTIES + IMPORTED_LOCATION "${GLFW_LIBRARY}" # On macOS it's a list + INTERFACE_COMPILE_DEFINITIONS "GLFW_NO_GLU" + INTERFACE_INCLUDE_DIRECTORIES ${GLFW_INCLUDE_DIR}) + endif() + + if(SDL2_FOUND) + split_extra_libraries(SDL2 "${SDL2_LIBRARY}") + add_library(SDL2::SDL2 SHARED IMPORTED) + set_target_properties(SDL2::SDL2 PROPERTIES + IMPORTED_LOCATION "${SDL2_LIBRARY_FILE}" # On macOS it's a list + INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES "${SDL2_EXTRA_LIBRARIES}") + endif() + + if(OPENAL_FOUND) + add_library(OpenAL::AL SHARED IMPORTED) + set_target_properties(OpenAL::AL PROPERTIES + IMPORTED_LOCATION ${OPENAL_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${OPENAL_INCLUDE_DIR}) + endif() + + if(WEBP_FOUND) + add_library(WebP::WebP SHARED IMPORTED) + set_target_properties(WebP::WebP PROPERTIES + IMPORTED_LOCATION ${WEBP_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${WEBP_INCLUDE_DIR}) + endif() + + if(VORBIS_FOUND) + add_library(Vorbis::Vorbisfile SHARED IMPORTED) + set_target_properties(Vorbis::Vorbisfile PROPERTIES + IMPORTED_LOCATION ${VORBISFILE_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${VORBIS_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES "${VORBIS_LIBRARY};${OGG_LIBRARY}") + endif() + + if(LUA_FOUND) + add_library(Lua::Lua SHARED IMPORTED) + set_target_properties(Lua::Lua PROPERTIES + IMPORTED_LOCATION ${LUA_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${LUA_INCLUDE_DIR}) + endif() + + if(APPLE) + function(split_extra_frameworks PREFIX LIBRARIES) + foreach(LIBRARY ${LIBRARIES}) + string(FIND ${LIBRARY} "-framework " FOUND) + if(FOUND GREATER -1) + list(APPEND FRAMEWORK_LINKS ${LIBRARY}) + else() + list(APPEND FRAMEWORK_DIR ${LIBRARY}) + endif() + endforeach() + set(${PREFIX}_FRAMEWORK_LINKS ${FRAMEWORK_LINKS} PARENT_SCOPE) + set(${PREFIX}_FRAMEWORK_DIR ${FRAMEWORK_DIR} PARENT_SCOPE) + endfunction() + + if(GLEW_FOUND) + get_target_property(GLEW_LIBRARY_RELEASE GLEW::GLEW IMPORTED_LOCATION_RELEASE) + set_target_properties(GLEW::GLEW PROPERTIES + IMPORTED_LOCATION ${GLEW_LIBRARY_RELEASE}/glew + IMPORTED_LOCATION_RELEASE ${GLEW_LIBRARY_RELEASE}/glew + IMPORTED_LOCATION_DEBUG ${GLEW_LIBRARY_RELEASE}/glew) + endif() + + if(GLFW_FOUND) + split_extra_frameworks(GLFW "${GLFW_LIBRARY}") + set_target_properties(GLFW::GLFW PROPERTIES + IMPORTED_LOCATION ${GLFW_FRAMEWORK_DIR}/glfw + INTERFACE_LINK_LIBRARIES ${GLFW_FRAMEWORK_LINKS}) + endif() + + if(SDL2_FOUND) + split_extra_frameworks(SDL2 "${SDL2_LIBRARY}") + set_target_properties(SDL2::SDL2 PROPERTIES + IMPORTED_LOCATION ${SDL2_FRAMEWORK_DIR}/sdl2 + INTERFACE_LINK_LIBRARIES ${SDL2_FRAMEWORK_LINKS}) + endif() + + if(OPENAL_FOUND) + set_target_properties(OpenAL::AL PROPERTIES + IMPORTED_LOCATION ${OPENAL_LIBRARY}/openal) + endif() + + if(PNG_FOUND) + get_target_property(ZLIB_LIBRARY_RELEASE ZLIB::ZLIB IMPORTED_LOCATION_RELEASE) + set_target_properties(ZLIB::ZLIB PROPERTIES + IMPORTED_LOCATION ${ZLIB_LIBRARY_RELEASE}/zlib + IMPORTED_LOCATION_RELEASE ${ZLIB_LIBRARY_RELEASE}/zlib + IMPORTED_LOCATION_DEBUG ${ZLIB_LIBRARY_RELEASE}/zlib) + get_target_property(PNG_LIBRARY_RELEASE PNG::PNG IMPORTED_LOCATION_RELEASE) + set_target_properties(PNG::PNG PROPERTIES + IMPORTED_LOCATION ${PNG_LIBRARY_RELEASE}/png + IMPORTED_LOCATION_RELEASE ${PNG_LIBRARY_RELEASE}/png + IMPORTED_LOCATION_DEBUG ${PNG_LIBRARY_RELEASE}/png) + endif() + + if(WEBP_FOUND) + set_target_properties(WebP::WebP PROPERTIES + IMPORTED_LOCATION ${WEBP_LIBRARY}/webp) + endif() + + if(VORBIS_FOUND) + set_target_properties(Vorbis::Vorbisfile PROPERTIES + IMPORTED_LOCATION ${VORBISFILE_LIBRARY}/vorbisfile) + endif() + + if(LUA_FOUND) + set_target_properties(Lua::Lua PROPERTIES + IMPORTED_LOCATION ${LUA_LIBRARY}/lua) + endif() + endif() +endif() diff --git a/cmake/package_info.cmake b/cmake/package_info.cmake new file mode 100644 index 0000000..88882d7 --- /dev/null +++ b/cmake/package_info.cmake @@ -0,0 +1,35 @@ +set(PACKAGE_NAME "ncInvaders") +set(PACKAGE_EXE_NAME "ncinvaders") +set(PACKAGE_ICON_NAME ${PACKAGE_NAME}) +set(PACKAGE_DESCRIPTION "A simplified version of Space Invaders made with the nCine") +set(PACKAGE_AUTHOR_MAIL "encelo@gmail.com") +set(PACKAGE_DESKTOP_FILE "io.github.ncine.ncinvaders.desktop") +set(PACKAGE_JAVA_URL "io/github/ncine/ncinvaders") + +set(PACKAGE_SOURCES + src/invaders.h + src/invaders.cpp + src/Configuration.h + src/SpritePool.h + src/SpritePool.cpp + src/Game.h + src/Game.cpp + src/ProjectilePool.h + src/ProjectilePool.cpp + src/EnemyPool.h + src/EnemyPool.cpp + src/Player.h + src/Player.cpp + src/CollisionManager.h + src/CollisionManager.cpp +) + +set(PACKAGE_ANDROID_ASSETS + data/DroidSans32_256.fnt + android/DroidSans32_256.webp + data/bomb.png + data/enemy1.png + data/enemy2.png + data/player.png + data/rocket.png +) diff --git a/cmake/package_installation.cmake b/cmake/package_installation.cmake new file mode 100644 index 0000000..3b9cf83 --- /dev/null +++ b/cmake/package_installation.cmake @@ -0,0 +1,91 @@ +set(RUNTIME_INSTALL_DESTINATION bin) +set(LIBRARY_INSTALL_DESTINATION lib) + +if(MSVC OR APPLE OR EMSCRIPTEN) + set(README_INSTALL_DESTINATION .) + set(DATA_INSTALL_DESTINATION data) + set(SHADERS_INSTALL_DESTINATION data/shaders) +else() + set(README_INSTALL_DESTINATION share/${PACKAGE_LOWER_NAME}) + set(DATA_INSTALL_DESTINATION share/${PACKAGE_LOWER_NAME}/data) + set(SHADERS_INSTALL_DESTINATION share/${PACKAGE_LOWER_NAME}/shaders) +endif() + +set(CPACK_PACKAGE_NAME "${PACKAGE_NAME}") +set(CPACK_PACKAGE_VENDOR "") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PACKAGE_DESCRIPTION}) +set(CPACK_PACKAGE_CONTACT ${PACKAGE_AUTHOR_MAIL}) +set(CPACK_PACKAGE_VERSION ${PACKAGE_VERSION}) +set(CPACK_PACKAGE_VERSION_MAJOR ${PACKAGE_MAJOR_VERSION}) +set(CPACK_PACKAGE_VERSION_MINOR ${PACKAGE_MINOR_VERSION}) +set(CPACK_PACKAGE_VERSION_PATCH ${PACKAGE_PATCH_VERSION}) +set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PACKAGE_NAME}") +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") +set(CPACK_PACKAGE_CHECKSUM MD5) + +if(MSVC) + set(CPACK_GENERATOR NSIS ZIP) + set(CPACK_NSIS_MUI_ICON "${PACKAGE_DATA_DIR}/icons/${PACKAGE_ICON_NAME}.ico") + set(CPACK_NSIS_COMPRESSOR "/SOLID lzma") + # Custom NSIS commands needed in order to set the "Start in" property of the start menu shortcut + set(CPACK_NSIS_CREATE_ICONS_EXTRA + "SetOutPath '$INSTDIR\\\\bin' + CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\${PACKAGE_NAME}.lnk' '$INSTDIR\\\\bin\\\\${PACKAGE_EXE_NAME}.exe' + SetOutPath '$INSTDIR'") + set(CPACK_NSIS_DELETE_ICONS_EXTRA "Delete '$SMPROGRAMS\\\\$MUI_TEMP\\\\${PACKAGE_NAME}.lnk'") + + include(InstallRequiredSystemLibraries) +elseif(APPLE) + set(CPACK_GENERATOR "Bundle") + set(CPACK_BUNDLE_NAME ${PACKAGE_NAME}) + set(FRAMEWORKS_INSTALL_DESTINATION ../Frameworks) + + configure_file(${CMAKE_SOURCE_DIR}/Info.plist.in ${CMAKE_BINARY_DIR}/Info.plist @ONLY) + set(CPACK_BUNDLE_PLIST ${CMAKE_BINARY_DIR}/Info.plist) + + file(RELATIVE_PATH RELPATH_TO_BIN ${CMAKE_INSTALL_PREFIX}/MacOS ${CMAKE_INSTALL_PREFIX}/Resources/${RUNTIME_INSTALL_DESTINATION}) + file(WRITE ${CMAKE_BINARY_DIR}/bundle_executable "#!/usr/bin/env sh\ncd \"$(dirname \"$0\")\" \ncd ${RELPATH_TO_BIN} && ./${PACKAGE_EXE_NAME}") + install(FILES ${CMAKE_BINARY_DIR}/bundle_executable DESTINATION ../MacOS/ RENAME ${CPACK_BUNDLE_NAME} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + + add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PACKAGE_DATA_DIR}/icons/icon1024.png ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_512x512@2x.png + COMMAND sips -z 512 512 ${PACKAGE_DATA_DIR}/icons/icon1024.png --out ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_512x512.png + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_512x512.png ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_256x256@2x.png + COMMAND sips -z 256 256 ${PACKAGE_DATA_DIR}/icons/icon1024.png --out ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_256x256.png + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_256x256.png ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_128x128@2x.png + COMMAND sips -z 128 128 ${PACKAGE_DATA_DIR}/icons/icon1024.png --out ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_128x128.png + COMMAND sips -z 64 64 ${PACKAGE_DATA_DIR}/icons/icon1024.png --out ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_32x32@2x.png + COMMAND sips -z 32 32 ${PACKAGE_DATA_DIR}/icons/icon1024.png --out ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_32x32.png + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_32x32.png ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_16x16@2x.png + COMMAND sips -z 16 16 ${PACKAGE_DATA_DIR}/icons/icon1024.png --out ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset/icon_16x16.png + COMMAND iconutil --convert icns --output ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.icns ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset) + add_custom_target(iconutil_convert ALL DEPENDS ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.iconset) + set(CPACK_BUNDLE_ICON ${CMAKE_BINARY_DIR}/${CPACK_BUNDLE_NAME}.icns) +elseif(EMSCRIPTEN) + if(CMAKE_HOST_WIN32) + set(CPACK_GENERATOR ZIP) + else() + set(CPACK_GENERATOR TGZ) + endif() +elseif(UNIX AND NOT APPLE) + set(CPACK_GENERATOR TGZ) + set(ICONS_INSTALL_DESTINATION share/icons/hicolor) + + if(EXISTS ${PACKAGE_DATA_DIR}/icons) + install(FILES ${PACKAGE_DATA_DIR}/icons/icon1024.png DESTINATION ${ICONS_INSTALL_DESTINATION}/1024x1024/apps/ RENAME ${PACKAGE_ICON_NAME}.png) + install(FILES ${PACKAGE_DATA_DIR}/icons/icon192.png DESTINATION ${ICONS_INSTALL_DESTINATION}/192x192/apps/ RENAME ${PACKAGE_ICON_NAME}.png) + install(FILES ${PACKAGE_DATA_DIR}/icons/icon96.png DESTINATION ${ICONS_INSTALL_DESTINATION}/96x96/apps/ RENAME ${PACKAGE_ICON_NAME}.png) + install(FILES ${PACKAGE_DATA_DIR}/icons/icon72.png DESTINATION ${ICONS_INSTALL_DESTINATION}/72x72/apps/ RENAME ${PACKAGE_ICON_NAME}.png) + install(FILES ${PACKAGE_DATA_DIR}/icons/icon48.png DESTINATION ${ICONS_INSTALL_DESTINATION}/48x48/apps/ RENAME ${PACKAGE_ICON_NAME}.png) + endif() + + configure_file(${CMAKE_SOURCE_DIR}/package.desktop ${CMAKE_BINARY_DIR}/${PACKAGE_DESKTOP_FILE} @ONLY) + install(FILES ${CMAKE_BINARY_DIR}/${PACKAGE_DESKTOP_FILE} DESTINATION share/applications) +elseif(MINGW) + set(CPACK_GENERATOR TGZ) +endif() + +include(CPack) diff --git a/cmake/package_options.cmake b/cmake/package_options.cmake new file mode 100644 index 0000000..419a2fe --- /dev/null +++ b/cmake/package_options.cmake @@ -0,0 +1,33 @@ +option(PACKAGE_BUILD_ANDROID "Build the Android version of the game" OFF) +option(PACKAGE_STRIP_BINARIES "Enable symbols stripping from libraries and executables when in release" OFF) + +set(PACKAGE_DATA_DIR "${PARENT_SOURCE_DIR}/${PACKAGE_NAME}-data" CACHE PATH "Set the path to the game data directory") + +if(MSVC OR APPLE) + set(NCINE_EXTERNAL_DIR "${PARENT_SOURCE_DIR}/nCine-external" CACHE PATH "Set the path to the nCine MSVC libraries or macOS frameworks directory") +endif() + +if(PACKAGE_BUILD_ANDROID) + set(NDK_DIR "" CACHE PATH "Set the path to the Android NDK") + set(PACKAGE_NDK_ARCHITECTURES arm64-v8a CACHE STRING "Set the NDK target architectures") + option(PACKAGE_ASSEMBLE_APK "Assemble the Android APK of the game with Gradle" OFF) +endif() + +# Package options presets +set(PACKAGE_OPTIONS_PRESETS "Default" CACHE STRING "Presets for CMake options") +set_property(CACHE PACKAGE_OPTIONS_PRESETS PROPERTY STRINGS Default BinDist) + +if(PACKAGE_OPTIONS_PRESETS STREQUAL BinDist) + message(STATUS "Options presets: ${PACKAGE_OPTIONS_PRESETS}") + + set(CMAKE_BUILD_TYPE Release) + set(CMAKE_CONFIGURATION_TYPES Release) + set(DEFAULT_DATA_DIR_DIST ON) + set(PACKAGE_BUILD_ANDROID OFF) + set(PACKAGE_STRIP_BINARIES ON) + + if(EMSCRIPTEN) + set(PACKAGE_BUILD_ANDROID OFF) + set(PACKAGE_STRIP_BINARIES OFF) + endif() +endif() diff --git a/cmake/package_strip_binaries.cmake b/cmake/package_strip_binaries.cmake new file mode 100644 index 0000000..b51673e --- /dev/null +++ b/cmake/package_strip_binaries.cmake @@ -0,0 +1,35 @@ +if(PACKAGE_STRIP_BINARIES AND CMAKE_BUILD_TYPE MATCHES "Release" AND EXISTS ${CMAKE_STRIP}) + message(STATUS "Strip command: " ${CMAKE_STRIP}) + + get_directory_property(NCINE_BUILD_TARGETS ${CMAKE_BUILD_DIR} BUILDSYSTEM_TARGETS) + foreach(BUILD_TARGET ${NCINE_BUILD_TARGETS}) + get_target_property(BUILD_TARGET_TYPE ${BUILD_TARGET} TYPE) + if(BUILD_TARGET_TYPE STREQUAL "STATIC_LIBRARY") + set(STRIP_MESSAGE "Stripping debug symbols from static library") + set(STRIP_ARGUMENTS "--strip-debug") + if(APPLE) + set(STRIP_ARGUMENTS "-S") + endif() + elseif(BUILD_TARGET_TYPE STREQUAL "SHARED_LIBRARY") + set(STRIP_MESSAGE "Stripping unneeded symbols from shared library") + set(STRIP_ARGUMENTS "--strip-unneeded") + if(APPLE) + set(STRIP_ARGUMENTS "-urS") + endif() + + elseif(BUILD_TARGET_TYPE STREQUAL "EXECUTABLE") + set(STRIP_MESSAGE "Stripping all symbols from executable") + set(STRIP_ARGUMENTS "--strip-all") + if(APPLE) + set(STRIP_ARGUMENTS "-Sx") + endif() + endif() + + if(BUILD_TARGET_TYPE STREQUAL "STATIC_LIBRARY" OR BUILD_TARGET_TYPE STREQUAL "SHARED_LIBRARY" OR + BUILD_TARGET_TYPE STREQUAL "EXECUTABLE") + add_custom_command(TARGET ${BUILD_TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "${STRIP_MESSAGE} ${BUILD_TARGET}" + COMMAND ${CMAKE_STRIP} ${STRIP_ARGUMENTS} $) + endif() + endforeach() +endif() diff --git a/emscripten_shell.html.in b/emscripten_shell.html.in new file mode 100644 index 0000000..eb61f1e --- /dev/null +++ b/emscripten_shell.html.in @@ -0,0 +1,64 @@ + + + + + + @PACKAGE_NAME@ + + + + + + + + diff --git a/package.desktop b/package.desktop new file mode 100644 index 0000000..15de043 --- /dev/null +++ b/package.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=1.0 +Name=@PACKAGE_NAME@ +Comment=@PACKAGE_DESCRIPTION@ +TryExec=@PACKAGE_EXE_NAME@ +Exec=@PACKAGE_EXE_NAME@ +Icon=@PACKAGE_ICON_NAME@ +Terminal=false +Type=Application +Categories=Game; diff --git a/src/CollisionManager.cpp b/src/CollisionManager.cpp new file mode 100644 index 0000000..0126ec9 --- /dev/null +++ b/src/CollisionManager.cpp @@ -0,0 +1,106 @@ +#include "CollisionManager.h" +#include "ProjectilePool.h" +#include "EnemyPool.h" +#include "Player.h" +#include "Configuration.h" + +#include + +bool CollisionManager::performCollisions(ProjectilePool *rocketPool, EnemyPool *enemyPool) +{ + ASSERT(rocketPool); + ASSERT(enemyPool); + + SpritePool &rockets = rocketPool->projectiles_; + SpritePool &enemies = enemyPool->enemies_; + const nc::Vector2f halfRocketSize(rockets.spriteWidth() * 0.5f, rockets.spriteHeight() * 0.5f); + const nc::Vector2f halfEnemySize(enemies.spriteWidth() * 0.5f, enemies.spriteHeight() * 0.5f); + + for (int rocketIdx = rockets.acquiredSize() - 1; rocketIdx >= 0; rocketIdx--) + { + const nc::Sprite &r = rockets[rocketIdx]; + + // Skip check for rockets outside the bounding box of alive enemies + if (r.x + halfRocketSize.x < enemyPool->xMin_ || + r.x - halfRocketSize.x > enemyPool->xMax_ || + r.y + halfRocketSize.y < enemyPool->yMin_ || + r.y - halfRocketSize.y > enemyPool->yMax_) + { + continue; + } + + for (int enemyIdx = enemies.acquiredSize() - 1; enemyIdx >= 0; enemyIdx--) + { + const nc::Sprite &e = enemies[enemyIdx]; + + if (r.x + halfRocketSize.x >= e.x - halfEnemySize.x && r.x - halfRocketSize.x <= e.x + halfEnemySize.x && + r.y + halfRocketSize.y >= e.y - halfEnemySize.y && r.y - halfRocketSize.y <= e.y + halfEnemySize.y) + { + rockets.release(rocketIdx); + enemies.release(enemyIdx); + return true; // collision detected + } + } + } + + return false; // no collisions +} + +bool CollisionManager::performCollisions(Player *player, ProjectilePool *bombPool) +{ + ASSERT(player); + ASSERT(bombPool); + + SpritePool &bombs = bombPool->projectiles_; + const nc::Sprite &p = player->sprite_; + const nc::Vector2f halfPlayerSize(p.width() * 0.5f, p.height() * 0.5f); + const nc::Vector2f halfBombSize(bombs.spriteWidth() * 0.5f, bombs.spriteHeight() * 0.5f); + + for (int bombIdx = bombs.acquiredSize() - 1; bombIdx >= 0; bombIdx--) + { + const nc::Sprite &b = bombs[bombIdx]; + + if (p.x + halfPlayerSize.x >= b.x - halfBombSize.x && p.x - halfPlayerSize.x <= b.x + halfBombSize.x && + p.y + halfPlayerSize.y >= b.y - halfBombSize.y && p.y - halfPlayerSize.y <= b.y + halfBombSize.y) + { + bombs.release(bombIdx); + return true; // collision detected + } + } + + return false; // no collisions +} + +bool CollisionManager::performCollisions(Player *player, EnemyPool *enemyPool) +{ + ASSERT(player); + ASSERT(enemyPool); + + SpritePool &enemies = enemyPool->enemies_; + const nc::Sprite &p = player->sprite_; + const nc::Vector2f halfPlayerSize(p.width() * 0.5f, p.height() * 0.5f); + const nc::Vector2f halfEnemySize(enemies.spriteWidth() * 0.5f, enemies.spriteHeight() * 0.5f); + + // Skip check if the player is outside the bounding box of alive enemies + if (p.x + halfPlayerSize.x < enemyPool->xMin_ || + p.x - halfPlayerSize.x > enemyPool->xMax_ || + p.y + halfPlayerSize.y < enemyPool->yMin_ || + p.y - halfPlayerSize.y > enemyPool->yMax_) + { + return false; // no collisions + } + + for (int enemyIdx = enemies.acquiredSize() - 1; enemyIdx >= 0; enemyIdx--) + { + const nc::Sprite &e = enemies[enemyIdx]; + + if (p.x + halfPlayerSize.x >= e.x - halfEnemySize.x && p.x - halfPlayerSize.x <= e.x + halfEnemySize.x && + p.y + halfPlayerSize.y >= e.y - halfEnemySize.y && p.y - halfPlayerSize.y <= e.y + halfEnemySize.y) + { + enemies.release(enemyIdx); + return true; // collision detected + } + } + + return false; // no collisions +} diff --git a/src/CollisionManager.h b/src/CollisionManager.h new file mode 100644 index 0000000..157d4f7 --- /dev/null +++ b/src/CollisionManager.h @@ -0,0 +1,27 @@ +#ifndef _COLLISION_MANAGER_H +#define _COLLISION_MANAGER_H + +class ProjectilePool; +class EnemyPool; +class Player; + +/// Collection of functions that check for collisions between entities in the game +/*! \note Assuming no more than one collision per frame per function. */ +namespace CollisionManager { + +/// Performs the collision detection between rockets and enemies +/*! \warning It relies on a the bounding box of alive enemies and thus it needs to be called after `EnemyPool::update()` */ +/*! \returns `true` if a collision has been detected */ +bool performCollisions(ProjectilePool *rocketPool, EnemyPool *enemyPool); + +/// Performs the collision detection between the player and bombs +/*! \returns `true` if a collision has been detected */ +bool performCollisions(Player *player, ProjectilePool *bombPool); + +/// Performs the collision detection between the player and enemies +/*! \returns `true` if a collision has been detected */ +bool performCollisions(Player *player, EnemyPool *enemyPool); + +}; + +#endif diff --git a/src/Configuration.h b/src/Configuration.h new file mode 100644 index 0000000..2c0fc64 --- /dev/null +++ b/src/Configuration.h @@ -0,0 +1,50 @@ +#ifndef _CONFIGURATION_H +#define _CONFIGURATION_H + +namespace Configuration { + +// Sprite images +const char *const PlayerSpriteImage = "player.png"; +const char *const RocketSpriteImage = "rocket.png"; +const char *const BombSpriteImage = "bomb.png"; +const char *const EnemySpriteOneImage = "enemy1.png"; +const char *const EnemySpriteTwoImage = "enemy2.png"; + +// Screen text +#ifndef __ANDROID__ +const char *const FontTextureFile = "DroidSans32_256.png"; +#else +const char *const FontTextureFile = "DroidSans32_256.webp"; +#endif +const char *const FontFntFile = "DroidSans32_256.fnt"; + +// Game class +const int MaxNumberEnemies = 50; + +// Player class +const float PlayerSpeed = 160.0f; +const float PlayerShootTime = 1.0f; +const unsigned int PlayerStartingLives = 3; +const unsigned int PlayerDamage = 1; +const unsigned int PlayerKillScore = 10; + +// RocketPool class +const unsigned int RocketPoolSize = 3; +const float RocketSpeed = 300.0f; + +// BombPool class +const unsigned int BombPoolSize = 4; +const float BombSpeed = 100.0f; + +// EnemyPool class +const float EnemyHMargin = 0.075f; +const float EnemyStartingHSpeed = 100.0f; +const float EnemyStartingVSpeed = 1.0f; +const float EnemySpeedIncrease = 1.00035f; +const float EnemyShootTime = 0.75f; + +} + +namespace Conf = Configuration; + +#endif diff --git a/src/EnemyPool.cpp b/src/EnemyPool.cpp new file mode 100644 index 0000000..3b9484c --- /dev/null +++ b/src/EnemyPool.cpp @@ -0,0 +1,139 @@ +#include "Configuration.h" +#include "EnemyPool.h" +#include "ProjectilePool.h" + +#include +#include +#include + +EnemyPool::EnemyPool(unsigned int maxSize, ProjectilePool *bombPool, nc::Texture *enemyTextureOne, nc::Texture *enemyTextureTwo) + : size_(0), width_(0), enemies_(maxSize, enemyTextureOne), state_(MOVE_RIGHT), + xMin_(0.0f), xMax_(0.0f), yMin_(0.0f), yMax_(0.0f), + horizontalSpeed_(Conf::EnemyStartingHSpeed), verticalSpeed_(Conf::EnemyStartingVSpeed), bombPool_(bombPool), + enemyTextureOne_(enemyTextureOne), enemyTextureTwo_(enemyTextureTwo) +{ + ASSERT(bombPool_); + const float aspect = nc::theApplication().width() / nc::theApplication().height(); + width_ = static_cast(sqrtf(static_cast(maxSize) * aspect)); + const int height = static_cast(width_ / aspect); + + size_ = static_cast(width_ * height); + ASSERT(size_ > 0); + + reset(); +} + +void EnemyPool::reset() +{ + horizontalSpeed_ = Conf::EnemyStartingHSpeed; + verticalSpeed_ = Conf::EnemyStartingVSpeed; + + enemies_.reserve(size_); + + float paddingX = (nc::theApplication().width() * (1.0f - Conf::EnemyHMargin * 2.0f)) / width_; + for (unsigned int i = 0; i < size_; i++) + { + enemies_[i].x = (Conf::EnemyHMargin * nc::theApplication().width()) + paddingX * (i % width_) + paddingX * 0.5f * (i / width_ % 2); + enemies_[i].y = nc::theApplication().height() - enemies_[i].height() * (i / width_ + 0.5f); // Shift down vertically half the sprite size to make text readable + enemies_[i].setTexture((i % 2) ? enemyTextureOne_ : enemyTextureTwo_); + enemies_[i].setEnabled(true); + } + for (unsigned int i = size_; i < enemies_.totalSize(); i++) + enemies_[i].setEnabled(false); + + // Ready to shoot + shootTimer_.start(); +} + +void EnemyPool::update(float interval) +{ + // Bounding box of alive enemies + xMin_ = nc::theApplication().width(); + xMax_ = 0.0f; + yMin_ = nc::theApplication().height(); + yMax_ = 0.0f; + + // Check if enough time has passed before shooting again and randomize the shooter + if (enemies_.acquiredSize() > 0 && shootTimer_.interval() >= Conf::EnemyShootTime) + { + const unsigned int shootIdx = nc::random().integer(0, enemies_.acquiredSize()); + bombPool_->shoot(enemies_[shootIdx].x, enemies_[shootIdx].y - enemies_[shootIdx].height() * 0.5f); + shootTimer_.start(); + } + + const nc::Vector2f halfEnemySize(enemies_.spriteWidth() * 0.5f, enemies_.spriteHeight() * 0.5f); + for (unsigned int i = 0; i < enemies_.acquiredSize(); i++) + { + // Update the bounding box + if (enemies_[i].x - halfEnemySize.x < xMin_) + xMin_ = enemies_[i].x - halfEnemySize.x; + if (enemies_[i].x + halfEnemySize.x > xMax_) + xMax_ = enemies_[i].x + halfEnemySize.x; + if (enemies_[i].y - halfEnemySize.y < yMin_) + yMin_ = enemies_[i].y - halfEnemySize.y; + if (enemies_[i].y + halfEnemySize.y > yMax_) + yMax_ = enemies_[i].y + halfEnemySize.y; + + switch (state_) + { + case MOVE_RIGHT: + { + enemies_[i].x += roundf(interval * horizontalSpeed_); + break; + } + case MOVE_LEFT: + { + enemies_[i].x -= roundf(interval * horizontalSpeed_); + break; + } + case MOVE_DOWNRIGHT: + case MOVE_DOWNLEFT: + { + enemies_[i].y -= roundf(verticalSpeed_); + horizontalSpeed_ *= Conf::EnemySpeedIncrease; + verticalSpeed_ *= Conf::EnemySpeedIncrease; + break; + } + } + } + + // Check if a state transition is needed + switch (state_) + { + case MOVE_RIGHT: + { + if (xMax_ >= nc::theApplication().width() - enemies_.spriteWidth() * 0.5f) + state_ = MOVE_DOWNLEFT; + break; + } + case MOVE_LEFT: + { + if (xMin_ <= enemies_.spriteWidth() * 0.5f) + state_ = MOVE_DOWNRIGHT; + break; + } + case MOVE_DOWNRIGHT: + { + state_ = MOVE_RIGHT; + break; + } + case MOVE_DOWNLEFT: + { + state_ = MOVE_LEFT; + break; + } + } +} + +void EnemyPool::draw() +{ + for (unsigned int i = 0; i < enemies_.acquiredSize(); i++) + enemies_[i].setEnabled(true); + for (unsigned int i = enemies_.acquiredSize(); i < enemies_.totalSize(); i++) + enemies_[i].setEnabled(false); +} + +bool EnemyPool::reachedBottom() const +{ + return yMin_ >= nc::theApplication().height() - enemies_.spriteHeight(); +} diff --git a/src/EnemyPool.h b/src/EnemyPool.h new file mode 100644 index 0000000..3a35ae4 --- /dev/null +++ b/src/EnemyPool.h @@ -0,0 +1,67 @@ +#ifndef ENEMY_POOL_H +#define ENEMY_POOL_H + +#include "SpritePool.h" +#include "CollisionManager.h" + +#include +#include + +namespace nc = ncine; + +class ProjectilePool; + +/// The class that handles enemy sprites and their update logic +class EnemyPool +{ + public: + /*! \param maxSize Maximum number of enemies allowed */ + /*! \note Number of enemies on screen might be less than \p maxSize as they are organized in a grid taking aspect ratio into account */ + EnemyPool(unsigned int maxSize, ProjectilePool *bombPool, nc::Texture *enemyTextureOne, nc::Texture *enemyTextureTwo); + + void reset(); + + /// Updates positions, transitions states and calculates the bounding box + void update(float interval); + /*! \note This is the only drawing method that contains logic to use more than a single sprite for pool entities */ + void draw(); + + /// Checks if the enemies have reached the bottom of the window + bool reachedBottom() const; + /// Checks if the player has killed all enemies + inline bool isEmpty() const { return enemies_.acquiredSize() == 0; } + + private: + enum EnemyState + { + MOVE_RIGHT, + MOVE_LEFT, + MOVE_DOWNRIGHT, + MOVE_DOWNLEFT + }; + + unsigned int size_; + int width_; + SpritePool enemies_; + EnemyState state_; + + float xMin_, xMax_; + float yMin_, yMax_; + + float horizontalSpeed_; + float verticalSpeed_; + + ProjectilePool *bombPool_; + + nc::Texture *enemyTextureOne_; + nc::Texture *enemyTextureTwo_; + nc::Timer shootTimer_; + + EnemyPool(const EnemyPool &) = delete; + EnemyPool &operator=(const EnemyPool &) = delete; + + friend bool CollisionManager::performCollisions(ProjectilePool *rocketPool, EnemyPool *enemyPool); + friend bool CollisionManager::performCollisions(Player *player, EnemyPool *enemyPool); +}; + +#endif diff --git a/src/Game.cpp b/src/Game.cpp new file mode 100644 index 0000000..00ffdcf --- /dev/null +++ b/src/Game.cpp @@ -0,0 +1,161 @@ +#include "Configuration.h" +#include "Game.h" +#include "ProjectilePool.h" +#include "EnemyPool.h" +#include "Player.h" + +#include +#include +#include +#include +#include +#include + +Game::Game() + : state_(GameState::START), screenString_(128), playerMovement_(PlayerMovement::STOP) +{ + bombTexture_ = nctl::makeUnique((nc::IFile::dataPath() + Conf::BombSpriteImage).data()); + enemy1Texture_ = nctl::makeUnique((nc::IFile::dataPath() + Conf::EnemySpriteOneImage).data()); + enemy2Texture_ = nctl::makeUnique((nc::IFile::dataPath() + Conf::EnemySpriteTwoImage).data()); + rocketTexture_ = nctl::makeUnique((nc::IFile::dataPath() + Conf::RocketSpriteImage).data()); + playerTexture_ = nctl::makeUnique((nc::IFile::dataPath() + Conf::PlayerSpriteImage).data()); + + font_ = nctl::makeUnique((nc::IFile::dataPath() + Conf::FontTextureFile).data(), + (nc::IFile::dataPath() + Conf::FontFntFile).data()); + + screenText_ = nctl::makeUnique(&nc::theApplication().rootNode(), font_.get()); + screenText_->setPosition((nc::theApplication().width() - screenText_->width()) * 0.5f, + nc::theApplication().height() - screenText_->fontLineHeight() * 0.5f * 2.0f); + screenText_->setColor(255, 255, 0, 255); + screenText_->setAlignment(nc::TextNode::Alignment::CENTER); + + bombPool_ = nctl::makeUnique(Conf::BombPoolSize, bombTexture_.get()); + enemyPool_ = nctl::makeUnique(Conf::MaxNumberEnemies, bombPool_.get(), enemy1Texture_.get(), enemy2Texture_.get()); + rocketPool_ = nctl::makeUnique(Conf::RocketPoolSize, rocketTexture_.get()); + player_ = nctl::makeUnique(rocketPool_.get(), playerTexture_.get()); +} + +Game::~Game() = default; + +void Game::update(float interval) +{ + switch (state_) + { + case GameState::START: + { + screenText_->setString("Press fire to start"); + break; + } + case GameState::RESET: + { + bombPool_->reset(); + enemyPool_->reset(); + rocketPool_->reset(); + player_->reset(); + state_ = GameState::RUN; + + break; + } + case GameState::RUN: + { + screenString_.format("Score: %u - Lives: %u", player_->score(), player_->lives()); + screenText_->setString(screenString_); + + bombPool_->updateBombs(interval); + bombPool_->draw(); + enemyPool_->update(interval); + enemyPool_->draw(); + rocketPool_->updateRockets(interval); + rocketPool_->draw(); + + if (playerMovement_ == PlayerMovement::RIGHT) + player_->move(interval); + else if (playerMovement_ == PlayerMovement::LEFT) + player_->move(-interval); + + bool rocketEnemyCollision = CollisionManager::performCollisions(rocketPool_.get(), enemyPool_.get()); + bool bombPlayerCollision = CollisionManager::performCollisions(player_.get(), bombPool_.get()); + bool enemyPlayerCollision = CollisionManager::performCollisions(player_.get(), enemyPool_.get()); + + if (rocketEnemyCollision || enemyPlayerCollision) + player_->increaseScore(Conf::PlayerKillScore); + if (bombPlayerCollision || enemyPlayerCollision) + player_->damage(Conf::PlayerDamage); + + if (player_->lives() <= 0 || enemyPool_->reachedBottom()) + state_ = GameState::GAME_OVER; + else if (enemyPool_->isEmpty()) + state_ = GameState::CLEARED; + + break; + } + case GameState::PAUSE: + { + // Note: Pause state is currently not pausing shooting timers, which might result in wrong behavior + + screenString_.format("PAUSED (Score: %u - Lives: %u)", player_->score(), player_->lives()); + screenText_->setString(screenString_); + + break; + } + case GameState::CLEARED: + { + screenString_.format("Level cleared, press fire to start again (score: %u)", player_->score()); + screenText_->setString(screenString_); + break; + } + case GameState::GAME_OVER: + { + screenString_.format("Game over, press fire to start again (score: %u)", player_->score()); + screenText_->setString(screenString_); + break; + } + } +} + +void Game::togglePause() +{ + if (state_ == GameState::RUN) + state_ = GameState::PAUSE; + else if (state_ == GameState::PAUSE) + state_ = GameState::RUN; +} + +void Game::firePressed() +{ + switch (state_) + { + case GameState::RUN: + { + player_->shoot(); + break; + } + default: + break; + } +} + +void Game::fireReleased() +{ + switch (state_) + { + case GameState::START: + { + state_ = GameState::RESET; + break; + } + case GameState::CLEARED: + { + state_ = GameState::RESET; + break; + } + case GameState::GAME_OVER: + { + player_->resetScore(); + state_ = GameState::RESET; + break; + } + default: + break; + } +} diff --git a/src/Game.h b/src/Game.h new file mode 100644 index 0000000..1f892de --- /dev/null +++ b/src/Game.h @@ -0,0 +1,81 @@ +#ifndef GAME_H +#define GAME_H + +#include +#include + +namespace ncine { + +class Texture; +class Font; +class TextNode; + +} + +namespace nc = ncine; + +class ProjectilePool; +class EnemyPool; +class Player; + +/// The class that handles all game states and objects +class Game +{ + public: + enum class GameState + { + /// The game has just started (waiting for a keypress) + START, + /// A transition state used to reset game entities + RESET, + /// The game is running, game entities are updating + RUN, + /// The game was running but now is in pause + PAUSE, + /// The player has killed all enemies (waiting for a keypress) + CLEARED, + /// The player is dead (waiting for a keypress) + GAME_OVER + }; + + enum class PlayerMovement + { + LEFT, + RIGHT, + STOP + }; + + Game(); + ~Game(); + void update(float interval); + inline void reset() { state_ = GameState::START; } + void togglePause(); + + inline void movePlayer(PlayerMovement movement) { playerMovement_ = movement; } + void firePressed(); + void fireReleased(); + + private: + GameState state_; + nctl::String screenString_; + PlayerMovement playerMovement_; + + nctl::UniquePtr bombTexture_; + nctl::UniquePtr enemy1Texture_; + nctl::UniquePtr enemy2Texture_; + nctl::UniquePtr rocketTexture_; + nctl::UniquePtr playerTexture_; + + nctl::UniquePtr font_; + nctl::UniquePtr screenText_; + + nctl::UniquePtr bombPool_; + nctl::UniquePtr enemyPool_; + nctl::UniquePtr rocketPool_; + nctl::UniquePtr player_; + + Game(const Game &) = delete; + Game &operator=(const Game &) = delete; +}; + +#endif diff --git a/src/Player.cpp b/src/Player.cpp new file mode 100644 index 0000000..8a4fd3b --- /dev/null +++ b/src/Player.cpp @@ -0,0 +1,46 @@ +#include "Configuration.h" +#include "Player.h" +#include "ProjectilePool.h" + +#include +#include + +Player::Player(ProjectilePool *rocketPool, nc::Texture *playerTexture) + : lives_(0), score_(0), rocketPool_(rocketPool), sprite_(&nc::theApplication().rootNode(), playerTexture) +{ + ASSERT(rocketPool_); + reset(); +} + +void Player::move(float interval) +{ + sprite_.x += roundf(interval * Conf::PlayerSpeed); + + // Clamping to window horizontal borders + if (sprite_.x < 0.0f) + sprite_.x = 0.0f; + else if (sprite_.x >= nc::theApplication().width() - sprite_.width()) + sprite_.x = nc::theApplication().width() - sprite_.width(); +} + +void Player::shoot() +{ + // Check if enough time has passed before shooting again + if (shootTimer_.interval() >= Conf::PlayerShootTime) + { + rocketPool_->shoot(sprite_.x, sprite_.y + sprite_.height() * 0.5f); + shootTimer_.start(); + } +} + +void Player::reset() +{ + // Centered at the bottom of the window + sprite_.x = (nc::theApplication().width() - sprite_.width()) * 0.5f; + sprite_.y = sprite_.height(); + + lives_ = Conf::PlayerStartingLives; + + // Ready to shoot + shootTimer_.start(); +} diff --git a/src/Player.h b/src/Player.h new file mode 100644 index 0000000..4c5bbe4 --- /dev/null +++ b/src/Player.h @@ -0,0 +1,48 @@ +#ifndef PLAYER_H +#define PLAYER_H + +#include "CollisionManager.h" + +#include +#include + +namespace nc = ncine; + +class RocketPool; + +/// The class the handles the logic behind the ship controlled by the player +class Player +{ + public: + Player(ProjectilePool *rocketPool, nc::Texture *playerTexture); + + /// Move the player by an amount specified by the time passed since last frame + void move(float interval); + void shoot(); + + inline unsigned int lives() const { return lives_; } + void damage(unsigned int amount) { lives_ -= amount; } + + inline unsigned int score() const { return score_; } + void increaseScore(unsigned int amount) { score_ += amount; } + void resetScore() { score_ = 0; } + + void reset(); + + private: + unsigned int lives_; + unsigned int score_; + + ProjectilePool *rocketPool_; + + nc::Sprite sprite_; + nc::Timer shootTimer_; + + Player(const Player &) = delete; + Player &operator=(const Player &) = delete; + + friend bool CollisionManager::performCollisions(Player *player, ProjectilePool *bombPool); + friend bool CollisionManager::performCollisions(Player *player, EnemyPool *enemyPool); +}; + +#endif diff --git a/src/ProjectilePool.cpp b/src/ProjectilePool.cpp new file mode 100644 index 0000000..7476cf4 --- /dev/null +++ b/src/ProjectilePool.cpp @@ -0,0 +1,62 @@ +#include "Configuration.h" +#include "ProjectilePool.h" + +#include +#include + +ProjectilePool::ProjectilePool(unsigned int maxSize, nc::Texture *texture) + : projectiles_(maxSize, texture) +{ + reset(); +} + +bool ProjectilePool::shoot(float x, float y) +{ + nc::Sprite *projectile = projectiles_.acquire(); + + if (projectile) + { + projectile->x = x; + projectile->y = y; + } + + // Was a projectile found and dropped? + return (projectile != nullptr); +} + +void ProjectilePool::updateBombs(float interval) +{ + // Traverse the array backwards to release sprites + for (int i = projectiles_.acquiredSize() - 1; i >= 0; i--) + { + projectiles_[i].y -= roundf(interval * Conf::BombSpeed); + if (projectiles_[i].y + projectiles_.spriteHeight() * 0.5f < 0.0f) + projectiles_.release(i); + } +} + +void ProjectilePool::updateRockets(float interval) +{ + // Traverse the array backwards to release sprites + for (int i = projectiles_.acquiredSize() - 1; i >= 0; i--) + { + projectiles_[i].y += roundf(interval * Conf::RocketSpeed); + if (projectiles_[i].y - projectiles_.spriteHeight() * 0.5f > nc::theApplication().height()) + projectiles_.release(i); + } +} + +void ProjectilePool::draw() +{ + for (unsigned int i = 0; i < projectiles_.acquiredSize(); i++) + projectiles_[i].setEnabled(true); + for (unsigned int i = projectiles_.acquiredSize(); i < projectiles_.totalSize(); i++) + projectiles_[i].setEnabled(false); +} + +void ProjectilePool::reset() +{ + for (unsigned int i = 0; i < projectiles_.totalSize(); i++) + projectiles_[i].setEnabled(false); + projectiles_.releaseAll(); +} diff --git a/src/ProjectilePool.h b/src/ProjectilePool.h new file mode 100644 index 0000000..df376db --- /dev/null +++ b/src/ProjectilePool.h @@ -0,0 +1,37 @@ +#ifndef PROJECTILE_POOL_H +#define PROJECTILE_POOL_H + +#include "SpritePool.h" +#include "CollisionManager.h" + +namespace nc = ncine; + +/// The class that handles projectile sprites and their update logic +class ProjectilePool +{ + public: + ProjectilePool(unsigned int maxSize, nc::Texture *texture); + + /// Attempts to shoot a projectile from the pool + /*! \returns `false` if none is available */ + bool shoot(float x, float y); + + /// Updates positions and returns to the pool a bomb that goes beyond the bottom of the window + void updateBombs(float interval); + /// Updates positions and returns to the pool a rocket that goes beyond the top of the window + void updateRockets(float interval); + + void draw(); + void reset(); + + private: + SpritePool projectiles_; + + ProjectilePool(const ProjectilePool &) = delete; + ProjectilePool &operator=(const ProjectilePool &) = delete; + + friend bool CollisionManager::performCollisions(ProjectilePool *rocketPool, EnemyPool *enemyPool); + friend bool CollisionManager::performCollisions(Player *player, ProjectilePool *bombPool); +}; + +#endif diff --git a/src/SpritePool.cpp b/src/SpritePool.cpp new file mode 100644 index 0000000..11e4071 --- /dev/null +++ b/src/SpritePool.cpp @@ -0,0 +1,53 @@ +#include "SpritePool.h" + +#include +#include +#include + +SpritePool::SpritePool(unsigned int size, nc::Texture *texture) + : size_(size), nextFree_(0), + width_(texture->width()), height_(texture->height()) +{ + ASSERT(size_ > 0); + + sprites_.setCapacity(size); + for (unsigned int i = 0; i < size; i++) + sprites_[i] = nctl::makeUnique(&nc::theApplication().rootNode(), texture); +} + +nc::Sprite *SpritePool::acquire() +{ + nc::Sprite *sprite = nullptr; + + if (nextFree_ < size_) + { + sprite = sprites_[nextFree_].get(); + nextFree_++; + } + + return sprite; +} + +void SpritePool::release(unsigned int index) +{ + ASSERT(index < nextFree_); + + swap(sprites_[index], sprites_[nextFree_ - 1]); + nextFree_--; +} + +void SpritePool::reserve(unsigned int index) +{ + ASSERT(index <= size_); + nextFree_ = index; +} + +void SpritePool::acquireAll() +{ + nextFree_ = size_; +} + +void SpritePool::releaseAll() +{ + nextFree_ = 0; +} diff --git a/src/SpritePool.h b/src/SpritePool.h new file mode 100644 index 0000000..fdab778 --- /dev/null +++ b/src/SpritePool.h @@ -0,0 +1,62 @@ +#ifndef SPRITE_POOL_H +#define SPRITE_POOL_H + +#include +#include + +namespace ncine { +class Sprite; +class Texture; +} + +namespace nc = ncine; + +/// The class that handles the pools of game sprites, such as rockets, bombs and enemies +/*! All entities are contained in a contiguous array, released ones are always at the back. */ +class SpritePool +{ + public: + SpritePool(unsigned int size, nc::Texture *texture); + + /// Acquires a single sprite from the pool + nc::Sprite *acquire(); + /// Releases a single sprite so it can return to the pool + /*! \param index The index inside the pool array of the sprite to be released */ + /*! \warning A cycle over pool entities that might call `release()` should always start from the last acquired one and go backwards. */ + void release(unsigned int index); + /// Sets the number of entities to consider as acquired + void reserve(unsigned int index); + /// Acquires all entities in one go + void acquireAll(); + /// Releases all entities in one go + void releaseAll(); + + /// Subscript operator to make the pool act as an ordinary array + inline nc::Sprite &operator[](unsigned int i) + { + ASSERT(i < nextFree_); + return *sprites_[i]; + } + + /// Returns the total size of the pool + inline unsigned int totalSize() const { return size_; } + /// Returns the number of acquired entities + inline unsigned int acquiredSize() const { return nextFree_; } + + inline int spriteWidth() const { return width_; } + inline int spriteHeight() const { return height_; } + + private: + unsigned int size_; + /// The index of the first free sprite, the next one to be acquired + unsigned int nextFree_; + + nctl::Array> sprites_; + int width_; + int height_; + + SpritePool(const SpritePool &) = delete; + SpritePool &operator=(const SpritePool &) = delete; +}; + +#endif diff --git a/src/invaders.cpp b/src/invaders.cpp new file mode 100644 index 0000000..7500313 --- /dev/null +++ b/src/invaders.cpp @@ -0,0 +1,139 @@ +#include + +#include "invaders.h" +#include "Game.h" + +#include +#include +#include +#include +#include +#include +#include // for dataPath() + +nc::IAppEventHandler *createAppEventHandler() +{ + return new MyEventHandler; +} + +void MyEventHandler::onPreInit(nc::AppConfiguration &config) +{ +#if defined(__ANDROID__) + config.dataPath() = "asset::"; +#elif defined(__EMSCRIPTEN__) + config.dataPath() = "/"; +#else + #ifdef PACKAGE_DEFAULT_DATA_DIR + config.dataPath() = PACKAGE_DEFAULT_DATA_DIR; + #else + config.dataPath() = "data/"; + #endif +#endif + + config.windowTitle = "ncInvaders"; + config.windowIconFilename = "icon48.png"; + + config.setResolution(1920, 1080); +} + +void MyEventHandler::onInit() +{ + game_ = nctl::makeUnique(); +} + +void MyEventHandler::onFrameStart() +{ + if (shouldStop_) + game_->movePlayer(Game::PlayerMovement::STOP); + + // Keyboard input handling + const nc::KeyboardState &keyState = nc::theApplication().inputManager().keyboardState(); + if (keyState.isKeyDown(nc::KeySym::RIGHT)) + game_->movePlayer(Game::PlayerMovement::RIGHT); + else if (keyState.isKeyDown(nc::KeySym::LEFT)) + game_->movePlayer(Game::PlayerMovement::LEFT); + + const nc::JoyMappedState &joyState = nc::theApplication().inputManager().joyMappedState(0); + const float axisValue = joyState.axisValue(nc::AxisName::LX); + if (axisValue > nc::IInputManager::LeftStickDeadZone) + game_->movePlayer(Game::PlayerMovement::RIGHT); + else if (axisValue < -nc::IInputManager::LeftStickDeadZone) + game_->movePlayer(Game::PlayerMovement::LEFT); + + const float interval = nc::theApplication().interval(); + game_->update(interval); +} + +#ifdef __ANDROID__ +void MyEventHandler::onTouchDown(const nc::TouchEvent &event) +{ + if (event.pointers[0].x > nc::theApplication().width() * 0.75f) + game_->movePlayer(Game::PlayerMovement::RIGHT); + else if (event.pointers[0].x < nc::theApplication().width() * 0.25f) + game_->movePlayer(Game::PlayerMovement::LEFT); + else + game_->firePressed(); + + shouldStop_ = false; +} + +void MyEventHandler::onTouchUp(const nc::TouchEvent &event) +{ + game_->fireReleased(); + shouldStop_ = true; +} +#endif + +void MyEventHandler::onKeyPressed(const nc::KeyboardEvent &event) +{ + if (event.sym == nc::KeySym::SPACE) + game_->firePressed(); +} + +void MyEventHandler::onKeyReleased(const nc::KeyboardEvent &event) +{ + if (event.sym == nc::KeySym::ESCAPE || event.sym == nc::KeySym::Q) + nc::theApplication().quit(); + else if (event.sym == nc::KeySym::P) + game_->togglePause(); + else if (event.sym == nc::KeySym::SPACE) + game_->fireReleased(); + else if (event.sym == nc::KeySym::R) + game_->reset(); +} + +void MyEventHandler::onMouseButtonPressed(const nc::MouseEvent &event) +{ + if (event.x > nc::theApplication().width() * 0.75f) + game_->movePlayer(Game::PlayerMovement::RIGHT); + else if (event.x < nc::theApplication().width() * 0.25f) + game_->movePlayer(Game::PlayerMovement::LEFT); + else + game_->firePressed(); + + shouldStop_ = false; +} + +void MyEventHandler::onMouseButtonReleased(const nc::MouseEvent &event) +{ + game_->fireReleased(); + shouldStop_ = true; +} + +void MyEventHandler::onJoyMappedButtonPressed(const nc::JoyMappedButtonEvent &event) +{ + if (event.buttonName == nc::ButtonName::A) + game_->firePressed(); +} + +void MyEventHandler::onJoyMappedButtonReleased(const nc::JoyMappedButtonEvent &event) +{ + if (event.buttonName == nc::ButtonName::START) + game_->reset(); + else if (event.buttonName == nc::ButtonName::GUIDE) + nc::theApplication().quit(); + else if (event.buttonName == nc::ButtonName::BACK) + game_->togglePause(); + else if (event.buttonName == nc::ButtonName::A) + game_->fireReleased(); +} diff --git a/src/invaders.h b/src/invaders.h new file mode 100644 index 0000000..9bafccf --- /dev/null +++ b/src/invaders.h @@ -0,0 +1,44 @@ +#ifndef CLASS_MYEVENTHANDLER +#define CLASS_MYEVENTHANDLER + +#include +#include +#include + +namespace ncine { + +class AppConfiguration; + +} + +namespace nc = ncine; + +class Game; + +/// My nCine event handler +class MyEventHandler : + public nc::IAppEventHandler, + public nc::IInputEventHandler +{ + public: + void onPreInit(nc::AppConfiguration &config) override; + void onInit() override; + void onFrameStart() override; + +#ifdef __ANDROID__ + virtual void onTouchDown(const nc::TouchEvent &event) override; + virtual void onTouchUp(const nc::TouchEvent &event) override; +#endif + void onKeyPressed(const nc::KeyboardEvent &event) override; + void onKeyReleased(const nc::KeyboardEvent &event) override; + void onMouseButtonPressed(const nc::MouseEvent &event) override; + void onMouseButtonReleased(const nc::MouseEvent &event) override; + void onJoyMappedButtonPressed(const nc::JoyMappedButtonEvent &event) override; + void onJoyMappedButtonReleased(const nc::JoyMappedButtonEvent &event) override; + + private: + nctl::UniquePtr game_; + bool shouldStop_ = true; +}; + +#endif