From ad9aab4a466afeeae46d45ec5065240e1d2a0fc3 Mon Sep 17 00:00:00 2001 From: frank <420@shampoo.ooo> Date: Mon, 17 Oct 2022 21:57:56 -0400 Subject: [PATCH] android helper scripts and icon assets and fill screen android demo --- README.md | 162 ++++++++++++++++++++++++++----- demo/fill_screen/Makefile | 63 ++++++++++++ demo/fill_screen/fill_screen.cpp | 13 ++- icon/foreground.png | Bin 0 -> 12057 bytes icon/static.png | Bin 0 -> 15547 bytes src/Attributes.hpp | 3 + src/Configuration.cpp | 3 +- src/Configuration.hpp | 2 + src/Display.cpp | 8 ++ src/GLObject.hpp | 11 +++ src/Game.cpp | 14 ++- src/Game.hpp | 3 + src/Log.cpp | 7 +- src/Log.hpp | 3 + src/Texture.cpp | 15 ++- src/Texture.hpp | 6 +- src/VBO.hpp | 8 +- src/android/generate_icon.sh | 61 ++++++++++++ src/android/main_class.sh | 30 ++++++ src/android/revise_skeleton.sh | 42 ++++++++ src/filesystem.hpp | 8 ++ 21 files changed, 415 insertions(+), 47 deletions(-) create mode 100644 icon/foreground.png create mode 100644 icon/static.png create mode 100755 src/android/generate_icon.sh create mode 100755 src/android/main_class.sh create mode 100755 src/android/revise_skeleton.sh diff --git a/README.md b/README.md index 6710592..ab98a5a 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,22 @@ -SPACE BOX -========= +SPACEBOX +========
          /\         +------------------------------------------------------------+ 
     ____/  \____   /| zlib/MIT/Unlicenced game framework licensed to freely use, |
     \          /  / | copy, modify and sell without restriction                  |
   +--\ ^__^   /--+  |                                                            |
-  | ~/        \~ |  | Learn more about [SPACE BOX] at [shampoo.ooo]              |
+  | ~/        \~ |  | Learn more about *SPACEBOX* at [shampoo.ooo][]             |
   | ~~~~~~~~~~~~ |  +------------------------------------------------------------+
   | SPACE ~~~~~  | /
   |  ~~~~~~~ BOX |/
   +--------------+   
-[SPACE BOX] is a C++ framework that makes creating cross-platform games and other interactive applications easier and faster by providing an added layer of abstraction between SDL + OpenGL and the project. +![logo](icon/static.png) -Users can start a project by extending only a single function, the Game class's update function. Using a standard Makefile that can be taken from the examples, the framework can export the same code to multiple platforms like PC, OS/X, Linux, Web GL, Raspberry Pi, and Android. +*SPACEBOX* is a C++ framework that makes creating cross-platform games and other interactive applications easier and faster by providing an added layer of abstraction between SDL + OpenGL and the project. + +Users can start a project by extending only a single function, the Game class's update function. Using a standard Makefile that can be taken from the examples, the framework can export the same code to multiple platforms like, in order of current stability, Linux, Web, Android, OS X, Windows, and Raspberry Pi. It is in an early, untested state. It comes with a few simple examples that demonstrate how to use it. @@ -37,7 +39,7 @@ libSDL2, libSDL2-image, libSDL2-ttf, and libSDL2-mixer must be available to link ### libSDL2 -* Download from +* Download the [SDL source package][] * Run `./configure --prefix=[YOUR LIBRARIES PATH]` (for example, `$HOME/local/sdl`) * Run `make && make install` @@ -54,33 +56,26 @@ libSDL2, libSDL2-image, libSDL2-ttf, and libSDL2-mixer must be available to link * Install GL/GLES according to your platform and link to it during compilation. GLEW is included in the lib/ folder of this framework and should find GL on your platform if it is installed. -Demos ------ +Builds +------ -The `demo/` folder contains programs that demonstrate and test the capabilities of the framework. In order to compile each, you should edit the definitions in the Makefile. +Building a SPACEBOX project is currently best accomplished by duplicating and adapting the fill_screen demo included in this repository or an in-progress game like [Gunkiss][] or [Pepy][]. The library requirements described above (SDL and OpenGL), a copy of this repository, and a Makefile based on the one in the project being duplicated are necessary for compilation. -### cube +SPACEBOX itself currently needs to be compiled along with the project's source, since there aren't any library builds of it. -Switch between GL and SDL contexts by pressing spacebar. The GL context draws a textured, rotating cube, and the SDL context draws basic geometric shapes and a moving 2D sprite. +### Linux -### 2d_collision +This is the platform SPACEBOX is built on, so it has the most straightforward build process. Copy the Makefile from the fill_screen demo, which demonstrates how to compile SPACEBOX and link to required libraries, adapt it to fit a new project, and build a Linux executable with GNU make. -Test collision detection between a 2D sprite and other 2D sprites and boxes. Per-pixel collision can be tested. +### Emscripten -### squircle +Exporting a browser build with [Emscripten][] and its built-in version of SDL has worked well on a few projects. The general process is to create a separate make target that uses Emscripten's C++ to WebAssembly compiler. See the browser webcam demo below for an example. -Map an image from a rectangle to a circle or from a circle to a rectangle using a shader program. Based on the elliptical grid mapping equations at http://squircular.blogspot.com/2015/09/mapping-circle-to-square.html +### Android -### browser webcam +The [fill_screen demo][] has a working example of how to build for Android. It may be worthwhile to read the [SDL wiki Android page][] and [SDL docs Android README][] and compile an SDL example for Linux before doing a SPACEBOX Android build. The source distributions for SDL, SDL image, SDL ttf, and SDL mixer, and the Android SDK are required. -An example for using a C++ program to display a webcam stream in the browser using Emscripten to translate the code from C++ to WebAssembly. Get the frame pixel data from a canvas element, read it into a SPACEBOX object, write the pixel data to an OpenGL texture, and use Emscripten to display the video. - -Android -------- - -See the [SDL wiki Android page][] and the [SDL docs Android README][]. - -### Building an SDL example from Linux +#### Building an SDL example for Linux * Install Java packages @@ -90,7 +85,7 @@ See the [SDL wiki Android page][] and the [SDL docs Android README][]. mkdir -p ~/local/Android/ -* Download [Android command line tools][] to that folder, extract them, and install an NDK (the gradle tool included with SDL seems to be expecting NDK 21.4.7075529). Note that the gradle tool may download Android tools, but this step seems to be necessary for signing the license, which gets created in `licenses` +* Download [Android command line tools][] to that folder, extract them, and install an NDK (the gradle tool included with SDL defaults to NDK 21.4.7075529). Note that the gradle tool may download Android tools, but this step seems to be necessary for signing the license, which gets created in `licenses` $ cd ~/local/Android/ $ unzip commandlinetools-linux-8512546_latest.zip @@ -104,7 +99,7 @@ See the [SDL wiki Android page][] and the [SDL docs Android README][]. * Copy `android-project/` to another folder, symlink the SDL source, - $ cp android-project org.my.testgles + $ cp -r android-project org.my.testgles $ cd org.my.testgles/app/jni $ ln -s ../../.. SDL @@ -133,6 +128,114 @@ See the [SDL wiki Android page][] and the [SDL docs Android README][]. # Install the APK to the running emulator $ ~/local/Android/platform-tools/adb -e install -r app/build/outputs/apk/debug/app-debug.apk +#### fill_screen demo + +The [fill_screen demo][] has a Makefile that should work for building for Android if the paths in the file are adjusted to match the project. Edit the Makefile and run `make build/android/[org.my.app]`. If that isn't working, see below for notes on how the build was originally done manually. + +##### Creating the fill_screen Android build + +These steps were taken to build the fill_screen demo for Android. The Android SDK is assumed to be installed as explained above in the SDL test example. The instructions are based on SDL 2.24.0. There is also a Makefile target that scripts this process in `[demo/fill_screen/Makefile][]`. + +* Copy the included Android project in the SDL source into the root of the fill_screen project folder. + + $ cp -r path/to/SDL2-2.24.0/android-project [fill_screen root]/ooo.shampoo.fill_screen + $ cd [fill_screen root]/ooo.shampoo.fill_screen + +* Edit the application ID + + $ sed -i s/org.libsdl.app/ooo.shampoo.fill_screen/ app/build.gradle app/src/main/AndroidManifest.xml + +* Enable C++ STL + + $ sed -i "s/^#.*\(APP_STL\)/\1/" app/jni/Application.mk + +* Enable C++ 17 features and exceptions + + $ echo "APP_CPPFLAGS := -std=c++17 -fexceptions -frtti" >> app/jni/Application.mk + +* Modify rules to allow OpenGL ES 3.0 (necessary for example for using GL_RGBA8) + + $ sed -i -e 's/^LOCAL_LDLIBS.*/& -lGLESv3/' app/jni/src/Android.mk + $ sed -i 's/0x0002/0x0003/' app/src/main/AndroidManifest.xml + $ sed -i 's/\(minSdkVersion\).*16/\1 18/' app/build.gradle + $ sed -i 's/\(android\)-16/\1-18/' app/build.gradle app/jni/Application.mk + +* `std::filesystem` is only available in NDK 22+, so install NDK version 22 and set the project to use it + + $ ~/local/Android/cmdline-tools/bin/sdkmanager --sdk_root=$HOME/local/Android --install "ndk;22.1.7171670" + $ sed -i '11i\ ndkVersion "22.1.7171670"' app/build.gradle + +* Link to SDL source packages (versions other than the listed ones may work) + + $ ln -s path/to/SDL2-2.24.0 app/jni/SDL + $ ln -s path/to/SDL2_image-2.6.2 app/jni/SDL2_image + $ ln -s path/to/SDL2_mixer-2.6.2 app/jni/SDL2_mixer + $ ln -s path/to/SDL2_ttf-2.20.1 app/jni/SDL2_ttf + +* Add SDL packages as libraries in the Makefile + + $ sed -i 's/^LOCAL_SHARED_LIBRARIES.*/& SDL2_image SDL2_mixer SDL2_ttf/' app/jni/src/Android.mk + +* Add SPACEBOX lib/ and src/ to include search path. In this command, the paths are relative to the path of Android.mk and based on the location of fill_screen as included in the SPACEBOX repository, but they can be edited to other paths if necessary. + + $ sed -i 's#^LOCAL_C_INCLUDES.*#& $(LOCAL_PATH)/../../../../../../lib $(LOCAL_PATH)/../../../../../../src#' \ + app/jni/src/Android.mk + +* Add SPACEBOX source files from lib/ src/ and source files for fill_screen + + $ sed -i 's#YourSourceHere.c#$(LOCAL_PATH)/../../../../fill_screen.cpp#' app/jni/src/Android.mk + $ sed -i 's#^LOCAL_SRC_FILES.*#& $(wildcard $(LOCAL_PATH)/../../../../../../src/*.cpp)#' app/jni/src/Android.mk + $ sed -i 's#^LOCAL_SRC_FILES.*#& $(wildcard $(LOCAL_PATH)/../../../../../../lib/sdl2-gfx/*.c)#' app/jni/src/Android.mk + +* Create a file at `app/src/main/java/ooo/shampoo/fill_screen/FillScreen.java` with the following contents + + package ooo.shampoo.fill_screen; + + import org.libsdl.app.SDLActivity; + + public class FillScreen extends SDLActivity { + protected String getMainFunction() { + return "main"; + } + } + +* Edit the manifest to point to that class + + $ sed -i 's/\(name=\)"SDLActivity"/\1"FillScreen"/' app/src/main/AndroidManifest.xml + +* Run gradle + + $ ANDROID_SDK_ROOT=$HOME/local/Android ./gradlew build + +### OS X, Windows, Raspberry Pi + +Builds for these platforms have only passed the proof of concept phase. An early version of SPACEBOX was compiled for each of them, but none of the demos have been compiled for them in their current form, so there is only some broken code available in the cube demo Makefile. + +Demos +----- + +The `demo/` folder contains programs that demonstrate and test the capabilities of the framework. In order to compile each, you should edit the definitions in the Makefile. + +### fill_screen + +This is intended to be a bare minimum test of the framework which loads the framework and fills the screen with a new color each frame. It currently only builds to Linux but will be ported to every platform in order to test each platform's build process. + +### browser webcam + +An example for using a C++ program to display a webcam stream in the browser using Emscripten to translate the code from C++ to WebAssembly. Get the frame pixel data from a canvas element, read it into a SPACEBOX object, write the pixel data to an OpenGL texture, and use Emscripten to display the video. + +### cube + +Switch between GL and SDL contexts by pressing spacebar. The GL context draws a textured, rotating cube, and the SDL context draws basic geometric shapes and a moving 2D sprite. (Note: support for SDL context is being removed in favor of GL context as even SDL recommends for new projects) + +### 2d_collision + +Test collision detection between a 2D sprite and other 2D sprites and boxes. Per-pixel collision can be tested. (Note: as support for SDL context is being removed in favor of GL context, the Sprite class will be either changing drastically or possibly removed entirely in favor of using a Model class) + +### squircle + +Map an image from a rectangle to a circle or from a circle to a rectangle using a shader program. Based on a [blog post about elliptical grid mapping equations][]. + Other libraries --------------- @@ -229,3 +332,10 @@ Contact [SDL docs Android README]: https://github.com/libsdl-org/SDL/blob/main/docs/README-android.md [SDL source]: https://github.com/libsdl-org/SDL/releases/ [Android command line tools]: https://developer.android.com/studio#command-tools +[SDL source package]: http://libsdl.org/download-2.0.php +[blog post about elliptical grid mapping equations]: http://squircular.blogspot.com/2015/09/mapping-circle-to-square.html +[Emscripten]: https://emscripten.org/ +[Gunkiss]: https://git.nugget.fun/nugget/gunkiss +[Pepy]: https://git.nugget.fun/nugget/pepy +[demo/fill_screen/Makefile]: demo/fill_screen/Makefile +[fill_screen demo]: demo/fill_screen diff --git a/demo/fill_screen/Makefile b/demo/fill_screen/Makefile index 1367861..6102726 100644 --- a/demo/fill_screen/Makefile +++ b/demo/fill_screen/Makefile @@ -1,3 +1,13 @@ +# /\ +------------------------------------------------------------+ +# ____/ \____ /| zlib/MIT/Unlicenced game framework licensed to freely use, | +# \ / / | copy, modify and sell without restriction | +# +--\ ^__^ /--+ | | +# | ~/ \~ | | created for | +# | ~~~~~~~~~~~~ | +------------------------------------------------------------+ +# | SPACE ~~~~~ | / +# | ~~~~~~~ BOX |/ +# +--------------+ +# # Fill screen SPACEBOX example ####################### @@ -80,6 +90,59 @@ linux : $(GLEW_DIR)glew.o $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPri $(CREATE_FONT_SYMLINK) $(CXX) $^ $(LFLAGS) -D__LINUX__ -o fill-screen +################# +# Android build # +################# + +# Detailed info on how this build target was created is in README.md at the root of the repository. It requires the Android SDK and the source packages +# for SDL, SDL_image, SDL_mixer, and SDL_ttf. The paths below should be customized. The icon creation requires Imagemagick's convert tool from +# + +SDL_SRC := $(HOME)/ext/software/SDL2-2.24.0-android +SDL_IMAGE_SRC := $(HOME)/ext/software/SDL2_image-2.6.2-android +SDL_MIXER_SRC := $(HOME)/ext/software/SDL2_mixer-2.6.2-android +SDL_TTF_SRC := $(HOME)/ext/software/SDL2_ttf-2.20.1-android +SDL_ANDROID_PROJECT := $(SDL_SRC)/android-project +ANDROID_MK := app/jni/src/Android.mk +ANDROID_APP_MK := app/jni/Application.mk +ANDROID_MANIFEST := app/src/main/AndroidManifest.xml +ANDROID_SDK := $(HOME)/local/Android +ANDROID_PACKAGE := ooo.shampoo.fill_screen +ANDROID_BUILD_DIR := build/android/$(ANDROID_PACKAGE) +ANDROID_CLASS := FillScreen +ANDROID_CLASS_DIR := app/src/main/java/$(subst .,/,$(ANDROID_PACKAGE)) + +# The skeleton for the Android build is based on the SDL android-project. The libraries are symlinked in. If the script that rewrites the skeleton +# has changed, start with a fresh skeleton. + +$(ANDROID_BUILD_DIR): $(SDL_SRC)/android-project/ $(SB_SRC_DIR)/android/revise_skeleton.sh + -mkdir -p $(ANDROID_BUILD_DIR) + rsync -ar $(SDL_SRC)/android-project/ $(ANDROID_BUILD_DIR) + ln -nsf $(SDL_SRC) $(ANDROID_BUILD_DIR)/app/jni/SDL + ln -nsf $(SDL_IMAGE_SRC) $(ANDROID_BUILD_DIR)/app/jni/SDL2_image + ln -nsf $(SDL_MIXER_SRC) $(ANDROID_BUILD_DIR)/app/jni/SDL2_mixer + ln -nsf $(SDL_TTF_SRC) $(ANDROID_BUILD_DIR)/app/jni/SDL2_ttf + $(SB_SRC_DIR)/android/revise_skeleton.sh $(ANDROID_PACKAGE) $(ANDROID_BUILD_DIR) $(ANDROID_MANIFEST) $(ANDROID_APP_MK) $(ANDROID_MK) $(ANDROID_CLASS) \ + $(ANDROID_APP_NAME) $(ANDROID_MIN_TARGET) $(ANDROID_NDK) + +# Extend the SDLActivity class + +$(ANDROID_BUILD_DIR)/$(ANDROID_CLASS_DIR)/$(ANDROID_CLASS).java: $(SB_SRC_DIR)/android/main_class.sh + $(SB_SRC_DIR)/android/main_class.sh $(ANDROID_PACKAGE) $(ANDROID_CLASS) $(ANDROID_BUILD_DIR)/$(ANDROID_CLASS_DIR) + +# Generate icon + +$(ANDROID_BUILD_DIR)/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: $(SB_SRC_DIR)/android/generate_icon.sh $(SB_DIR)/icons + $(SB_SRC_DIR)/android/generate_icon.sh $(ANDROID_BUILD_DIR) + +# Run gradle and generate an APK + +$(ANDROID_BUILD_DIR)/app-debug.apk: $(ANDROID_BUILD_DIR) $(ANDROID_BUILD_DIR)/$(ANDROID_MK) $(ANDROID_BUILD_DIR)/$(ANDROID_CLASS_DIR)/$(ANDROID_CLASS).java \ + $(ANDROID_BUILD_DIR)/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml + ANDROID_SDK_ROOT=$(ANDROID_SDK) $(ANDROID_BUILD_DIR)/gradlew -p $(ANDROID_BUILD_DIR) build + ln -nsf app/build/outputs/apk/debug/app-debug.apk $(ANDROID_BUILD_DIR) + ln -nsf app/build/outputs/apk/debug/app-release-unsigned.apk $(ANDROID_BUILD_DIR) + ######################### # Clean up object files # ######################### diff --git a/demo/fill_screen/fill_screen.cpp b/demo/fill_screen/fill_screen.cpp index af84684..ceea70d 100644 --- a/demo/fill_screen/fill_screen.cpp +++ b/demo/fill_screen/fill_screen.cpp @@ -28,8 +28,18 @@ public: }; +// #if defined(__ANDROID__) || defined(ANDROID) +// int SDL_main() +// { +// FillScreen fill_screen = FillScreen(); +// fill_screen.load_gl_context(); +// fill_screen.run(); +// fill_screen.quit(); +// return 0; +// } +// #else /* Create a game object, load its GL context, and run the game. */ -int main() +int main(int argc, char* argv[]) { FillScreen fill_screen = FillScreen(); fill_screen.load_gl_context(); @@ -37,3 +47,4 @@ int main() fill_screen.quit(); return 0; } +// #endif diff --git a/icon/foreground.png b/icon/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..932a86b1ea0b388bb90dfb444f6e11f377f71303 GIT binary patch literal 12057 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 +#elif defined(__ANDROID__) || defined(ANDROID) +#include +#include #else #include "glew/glew.h" #endif diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 62de026..1c439c1 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -48,7 +48,8 @@ void Configuration::set_defaults() {"ignore-repeat-keypress", true} }; sys_config["display"] = { - {"dimensions", {960, 540}}, + // {"dimensions", {960, 540}}, + {"dimensions", {480, 800}}, {"framerate", 60}, {"title", "[SPACE BOX]"}, {"debug", false}, diff --git a/src/Configuration.hpp b/src/Configuration.hpp index 285016d..bf305ba 100644 --- a/src/Configuration.hpp +++ b/src/Configuration.hpp @@ -70,6 +70,8 @@ namespace glm /* Extend std::filesystem so nlohmann::json can read and write std::filesystem::path */ #if defined(__MINGW32__) namespace std::experimental::filesystem +// #elif defined(__ANDROID__) || defined(ANDROID) +// namespace std::__fs::filesystem #else namespace std::filesystem #endif diff --git a/src/Display.cpp b/src/Display.cpp index c8a5e4c..23f4b94 100644 --- a/src/Display.cpp +++ b/src/Display.cpp @@ -79,14 +79,22 @@ void sb::Display::screen_pixels(unsigned char* pixels, int w, int h, int x, int if (get_root()->is_gl_context) { GLenum format; + + /* GL_BGRA is not defined in Open GL ES (some info available at + * https://community.khronos.org/t/why-opengles-2-spec-doesnt-support-bgra-texture-format/72853) */ +#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !defined(ANDROID) if constexpr (SDL_BYTEORDER == SDL_BIG_ENDIAN) { format = GL_BGRA; } else { +#endif format = GL_RGBA; +#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !defined(ANDROID) } +#endif + glReadBuffer(GL_FRONT); glReadPixels(x, y, w, h, format, GL_UNSIGNED_BYTE, pixels); /* Debug statement showing the framebuffer status and first pixel read */ diff --git a/src/GLObject.hpp b/src/GLObject.hpp index 45b7214..4ba5582 100644 --- a/src/GLObject.hpp +++ b/src/GLObject.hpp @@ -33,6 +33,17 @@ #include #include #include + +/* include Open GL */ +#if defined(__EMSCRIPTEN__) +#include +#elif defined(__ANDROID__) || defined(ANDROID) +#include +#include +#else +#include "glew/glew.h" +#endif + #include "Log.hpp" namespace sb diff --git a/src/Game.cpp b/src/Game.cpp index 19a3cb4..380f4a8 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -47,12 +47,17 @@ Game::Game() flag_to_end(); } log_message = std::ostringstream(); + + /* Android does not use GLEW */ +#if !defined(__ANDROID__) && !defined(ANDROID) log_message << "GLEW " << glewGetString(GLEW_VERSION); +#endif + sb::Log::log(log_message.str()); glm::ivec2 window_size = configuration()["display"]["dimensions"].get(); - /* Set GL context attributes before creating a window (see SDL_GLattr.html). Don't ask Emscripten for a specific GL context version. */ -#ifndef __EMSCRIPTEN__ + /* Set GL context attributes before creating a window (see SDL_GLattr.html). Don't ask Emscripten of Android for a specific GL context version. */ +#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !defined(ANDROID) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, configuration()["gl"]["major-version"]); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, configuration()["gl"]["minor-version"]); #endif @@ -201,6 +206,9 @@ void Game::load_gl_context() { sb::Log::log("vsync not supported"); } + + /* Android does not use GLEW*/ +#if !defined(__ANDROID__) && !defined(ANDROID) GLenum error = glewInit(); if (error != GLEW_OK) { @@ -208,6 +216,8 @@ void Game::load_gl_context() message << "GLEW could not initialize " << glewGetErrorString(error); sb::Log::log(message, sb::Log::ERROR); } +#endif + log_gl_properties(); is_gl_context = true; log_display_mode(); diff --git a/src/Game.hpp b/src/Game.hpp index 9a1f760..2166718 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -19,6 +19,9 @@ #include #include #include +#elif defined(__ANDROID__) || defined(ANDROID) +#include +#include #else #include "glew/glew.h" #endif diff --git a/src/Log.cpp b/src/Log.cpp index 24d84da..ae64d52 100644 --- a/src/Log.cpp +++ b/src/Log.cpp @@ -6,7 +6,7 @@ | ~~~~~~~~~~~~ | +--------------------------------------------------------------+ | SPACE ~~~~~ | / | ~~~~~~~ BOX |/ - +--------------+ */ + +-------------*/ #include "Log.hpp" @@ -58,6 +58,9 @@ bool sb::Log::gl_errors(const std::string& heading) { message << "GL_OUT_OF_MEMORY, there is not enough memory left to execute the command"; } + + /* The following error codes aren't available in Open GL ES */ +#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !defined(ANDROID) else if (error == GL_STACK_UNDERFLOW) { message << "GL_STACK_UNDERFLOW, an attempt has been made to perform an operation that would " << @@ -68,6 +71,8 @@ bool sb::Log::gl_errors(const std::string& heading) message << "GL_STACK_OVERFLOW, an attempt has been made to perform an operation that would " << "cause an internal stack to overflow"; } +#endif + log(message); } return error_logged; diff --git a/src/Log.hpp b/src/Log.hpp index 66ebd93..770994c 100644 --- a/src/Log.hpp +++ b/src/Log.hpp @@ -22,6 +22,9 @@ /* include Open GL */ #if defined(__EMSCRIPTEN__) #include +#elif defined(__ANDROID__) && defined(ANDROID) +#include +#include #else #include "glew/glew.h" #endif diff --git a/src/Texture.cpp b/src/Texture.cpp index 40b1353..1274ff2 100644 --- a/src/Texture.cpp +++ b/src/Texture.cpp @@ -131,7 +131,13 @@ void Texture::load(void* pixels, glm::vec2 size, GLenum format, GLenum type) sb::Log::gl_errors("after loading texture"); } -#ifndef __EMSCRIPTEN__ +void Texture::bind() const +{ + glBindTexture(GL_TEXTURE_2D, this->id()); +} + +/* glGetTexlevelparameteriv is not available in OpenGL ES 3.0 */ +#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !defined(ANDROID) void Texture::load(void* pixels, GLenum format, GLenum type) { if (!generated()) @@ -143,14 +149,7 @@ void Texture::load(void* pixels, GLenum format, GLenum type) load(pixels, size(), format, type); } } -#endif -void Texture::bind() const -{ - glBindTexture(GL_TEXTURE_2D, this->id()); -} - -#ifndef __EMSCRIPTEN__ glm::vec2 Texture::size() const { if (generated()) diff --git a/src/Texture.hpp b/src/Texture.hpp index 9d3be08..a43903c 100644 --- a/src/Texture.hpp +++ b/src/Texture.hpp @@ -24,10 +24,12 @@ #include #include + #include "glm/vec2.hpp" #include "SDL.h" #include "SDL_image.h" #include "sdl2-gfx/SDL2_rotozoom.h" + #include "filesystem.hpp" #include "GLObject.hpp" #include "Log.hpp" @@ -94,7 +96,9 @@ namespace sb */ void load(void* pixels, glm::vec2 size, GLenum format = GL_RGBA, GLenum type = GL_UNSIGNED_BYTE); -#ifndef __EMSCRIPTEN__ + + /* glGetTexlevelparameteriv is not available in OpenGL ES 3.0 */ +#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !defined(ANDROID) /*! * @overload load(void* pixels, glm::vec2 size, GLenum format, GLenum type) * diff --git a/src/VBO.hpp b/src/VBO.hpp index 624ff31..3c60cb9 100644 --- a/src/VBO.hpp +++ b/src/VBO.hpp @@ -27,15 +27,9 @@ #ifndef SB_VBO_H_ #define SB_VBO_H_ -/* include Open GL */ -#if defined(__EMSCRIPTEN__) -#include -#else -#include "glew/glew.h" -#endif - #include #include + #include "GLObject.hpp" #include "Attributes.hpp" #include "Log.hpp" diff --git a/src/android/generate_icon.sh b/src/android/generate_icon.sh new file mode 100755 index 0000000..2885184 --- /dev/null +++ b/src/android/generate_icon.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +# /\ +------------------------------------------------------------+ +# ____/ \____ /| zlib/MIT/Unlicenced game framework licensed to freely use, | +# \ / / | copy, modify and sell without restriction | +# +--\ ^__^ /--+ | | +# | ~/ \~ | | created for | +# | ~~~~~~~~~~~~ | +------------------------------------------------------------+ +# | SPACE ~~~~~ | / +# | ~~~~~~~ BOX |/ +# +--------------+ +# +# Generate a folder structure of adaptive icons to be used in an Android build. Requires imagemagick. +# +# Create legacy, adaptive, and monochrome adaptive icons as mipmaps, one per device DPI. Draw the foreground onto the background to create +# the legacy icons. Draw the foreground onto a transparent background to create the adaptive icons and save them in mipmap-anydpi-v26 to +# to target Android API 26 and up. Set the adaptive background by writing the color value to XML. + +ANDROID_BUILD_DIR=$1 +FOREGROUND=../../icon/foreground.png +BACKGROUND=#000000 +MANIFEST='\n + + + +' +COLORS=$ANDROID_BUILD_DIR/app/src/main/res/values/colors.xml +XXXHDPI=$ANDROID_BUILD_DIR/app/src/main/res/mipmap-xxxhdpi +XXHDPI=$ANDROID_BUILD_DIR/app/src/main/res/mipmap-xxhdpi +XHDPI=$ANDROID_BUILD_DIR/app/src/main/res/mipmap-xhdpi +HDPI=$ANDROID_BUILD_DIR/app/src/main/res/mipmap-hdpi +MDPI=$ANDROID_BUILD_DIR/app/src/main/res/mipmap-mdpi + +# Create all the XXXHDPI versions +convert xc:$BACKGROUND -resize 432x432 $FOREGROUND -gravity center -composite $XXXHDPI/ic_launcher.png +convert xc:none -resize 432x432 $FOREGROUND -gravity center -composite $XXXHDPI/ic_launcher_foreground.png +convert xc:none -resize 432x432 $FOREGROUND -gravity center -composite -threshold 0 -negate $XXXHDPI/ic_launcher_foreground_mono.png + +# Use the XXXHDPI versions to create the lower resolutions +convert $XXXHDPI/ic_launcher.png -resize 324x324 $XXHDPI/ic_launcher.png +convert $XXXHDPI/ic_launcher_foreground.png -resize 324x324 $XXHDPI/ic_launcher_foreground.png +convert $XXXHDPI/ic_launcher_foreground_mono.png -resize 324x324 $XXHDPI/ic_launcher_foreground_mono.png +convert $XXXHDPI/ic_launcher.png -resize 216x216 $XHDPI/ic_launcher.png +convert $XXXHDPI/ic_launcher_foreground.png -resize 216x216 $XHDPI/ic_launcher_foreground.png +convert $XXXHDPI/ic_launcher_foreground_mono.png -resize 216x216 $XHDPI/ic_launcher_foreground_mono.png +convert $XXXHDPI/ic_launcher.png -resize 162x162 $HDPI/ic_launcher.png +convert $XXXHDPI/ic_launcher_foreground.png -resize 162x162 $HDPI/ic_launcher_foreground.png +convert $XXXHDPI/ic_launcher_foreground_mono.png -resize 162x162 $HDPI/ic_launcher_foreground_mono.png +convert $XXXHDPI/ic_launcher.png -resize 108x108 $MDPI/ic_launcher.png +convert $XXXHDPI/ic_launcher_foreground.png -resize 108x108 $MDPI/ic_launcher_foreground.png +convert $XXXHDPI/ic_launcher_foreground_mono.png -resize 108x108 $MDPI/ic_launcher_foreground_mono.png + +# Create the adaptive icons XML +mkdir -p $ANDROID_BUILD_DIR/app/src/main/res/mipmap-anydpi-v26/ +echo $MANIFEST > $ANDROID_BUILD_DIR/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +if [ -z "$(grep iconBackground $COLORS)" ] +then + sed -i "6i\ $BACKGROUND" $COLORS +else + sed -i "s|\(iconBackground\">\).*\(\)|\1$BACKGROUND\2|" $COLORS +fi diff --git a/src/android/main_class.sh b/src/android/main_class.sh new file mode 100755 index 0000000..2ebeaf9 --- /dev/null +++ b/src/android/main_class.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# /\ +------------------------------------------------------------+ +# ____/ \____ /| zlib/MIT/Unlicenced game framework licensed to freely use, | +# \ / / | copy, modify and sell without restriction | +# +--\ ^__^ /--+ | | +# | ~/ \~ | | created for | +# | ~~~~~~~~~~~~ | +------------------------------------------------------------+ +# | SPACE ~~~~~ | / +# | ~~~~~~~ BOX |/ +# +--------------+ +# +# Extend the SDLActivity class + +ANDROID_PACKAGE=$1 +ANDROID_CLASS=$2 +ANDROID_CLASS_DIR=$3 + +ANDROID_CLASS_SRC="package $ANDROID_PACKAGE; + +import org.libsdl.app.SDLActivity; + +public class $ANDROID_CLASS extends SDLActivity { + protected String getMainFunction() { + return \"main\"; + } +}" + +mkdir -p $ANDROID_CLASS_DIR +echo $ANDROID_CLASS_SRC > $ANDROID_CLASS_DIR/$ANDROID_CLASS.java diff --git a/src/android/revise_skeleton.sh b/src/android/revise_skeleton.sh new file mode 100755 index 0000000..623ed8b --- /dev/null +++ b/src/android/revise_skeleton.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +# /\ +------------------------------------------------------------+ +# ____/ \____ /| zlib/MIT/Unlicenced game framework licensed to freely use, | +# \ / / | copy, modify and sell without restriction | +# +--\ ^__^ /--+ | | +# | ~/ \~ | | created for | +# | ~~~~~~~~~~~~ | +------------------------------------------------------------+ +# | SPACE ~~~~~ | / +# | ~~~~~~~ BOX |/ +# +--------------+ +# +# Text edits for skinning the SDL Android project skeleton + +ANDROID_PACKAGE=$1 +ANDROID_BUILD_DIR=$2 +ANDROID_MANIFEST=$3 +ANDROID_APP_MK=$4 +ANDROID_MK=$5 +ANDROID_CLASS=$6 +ANDROID_APP_NAME="Fill Screen Test" +ANDROID_MIN_TARGET=18 +ANDROID_NDK="22.1.7171670" + +sed -i s/org.libsdl.app/$ANDROID_PACKAGE/ $ANDROID_BUILD_DIR/app/build.gradle $ANDROID_BUILD_DIR/$ANDROID_MANIFEST +sed -i "s/^#.*\(APP_STL\)/\1/" $ANDROID_BUILD_DIR/$ANDROID_APP_MK +echo "APP_CPPFLAGS := -std=c++17 -fexceptions -frtti" >> $ANDROID_BUILD_DIR/$ANDROID_APP_MK +sed -i -e 's/^LOCAL_LDLIBS.*/& -lGLESv3/' $ANDROID_BUILD_DIR/$ANDROID_MK +sed -i 's/0x0002/0x0003/' $ANDROID_BUILD_DIR/$ANDROID_MANIFEST +sed -i "s/\(minSdkVersion\).*16/\1 $ANDROID_MIN_TARGET/" $ANDROID_BUILD_DIR/app/build.gradle $ANDROID_BUILD_DIR/$ANDROID_APP_MK +sed -i "s/\(android\)-16/\1-$ANDROID_MIN_TARGET/" $ANDROID_BUILD_DIR/app/build.gradle $ANDROID_BUILD_DIR/$ANDROID_APP_MK +sed -i "11i\ ndkVersion \"$ANDROID_NDK\"" $ANDROID_BUILD_DIR/app/build.gradle +sed -i 's/1536m/4096m/' $ANDROID_BUILD_DIR/gradle.properties +sed -i "s/^#.*\(org.gradle.parallel\)/\1/" $ANDROID_BUILD_DIR/gradle.properties +sed -i 's/^LOCAL_SHARED_LIBRARIES.*/& SDL2_image SDL2_mixer SDL2_ttf/' $ANDROID_BUILD_DIR/$ANDROID_MK +sed -i 's#^LOCAL_C_INCLUDES.*#& $(LOCAL_PATH)/../../../../../../../../lib $(LOCAL_PATH)/../../../../../../../../src#' \ + $ANDROID_BUILD_DIR/$ANDROID_MK +sed -i 's#YourSourceHere.c#$(LOCAL_PATH)/../../../../../../fill_screen.cpp#' $ANDROID_BUILD_DIR/$ANDROID_MK +sed -i 's#^LOCAL_SRC_FILES.*#& $(wildcard $(LOCAL_PATH)/../../../../../../../../src/*.cpp)#' $ANDROID_BUILD_DIR/$ANDROID_MK +sed -i 's#^LOCAL_SRC_FILES.*#& $(wildcard $(LOCAL_PATH)/../../../../../../../../lib/sdl2-gfx/*.c)#' $ANDROID_BUILD_DIR/$ANDROID_MK +sed -i "s/\(name=\)\"SDLActivity\"/\1\"$ANDROID_CLASS\"/" $ANDROID_BUILD_DIR/$ANDROID_MANIFEST +sed -i "s/Game/$ANDROID_APP_NAME/" $ANDROID_BUILD_DIR/app/src/main/res/values/strings.xml diff --git a/src/filesystem.hpp b/src/filesystem.hpp index bdceda2..de191ec 100644 --- a/src/filesystem.hpp +++ b/src/filesystem.hpp @@ -1,7 +1,15 @@ +/* MinGW filesystem library is in another location */ #if defined(__MINGW32__) #include namespace fs = std::experimental::filesystem; #else #include + +/* Android uses a different path to the filesystem namespace */ +// #if defined(__ANDROID__) || defined(ANDROID) +// namespace fs = std::__fs::filesystem; +// #else namespace fs = std::filesystem; +// #endif + #endif