1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-09-30 19:22:14 +00:00
This commit is contained in:
sfence 2025-09-25 02:24:08 +03:00 committed by GitHub
commit 96f937aef8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 566 additions and 35 deletions

61
.github/workflows/ios.yml vendored Normal file
View file

@ -0,0 +1,61 @@
name: iOS
# build on c/cpp changes or workflow changes
on:
push:
paths:
- 'lib/**.[ch]'
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- 'irr/**.[ch]'
- 'irr/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'po/**.po'
- '.github/workflows/ios.yml'
pull_request:
paths:
- 'lib/**.[ch]'
- 'lib/**.cpp'
- 'src/**.[ch]'
- 'src/**.cpp'
- 'irr/**.[ch]'
- 'irr/**.cpp'
- '**/CMakeLists.txt'
- 'cmake/Modules/**'
- 'po/**.po'
- '.github/workflows/ios.yml'
jobs:
build-ios:
strategy:
matrix:
osver: [18.4]
xcodever: [16.5]
runs-on: macos-15
steps:
- uses: actions/checkout@v4
- name: Prepare environment
run: |
echo "REPDIR=$(pwd)" >> $GITHUB_ENV
echo "osver=${{matrix.osver}}" >> $GITHUB_ENV
echo "xcodever=${{matrix.xcodever}}" >> $GITHUB_ENV
- name: Install deps
run: |
source ./util/ci/common.sh
install_ios_deps $osver
- name: Build and Archive with Xcode
run: |
mkdir build
cd build
export DEPS_DIR=${REPDIR}/ios${osver}_deps/iPhoneSimulator
../util/ci/build_ios.sh
- name: Run in iPhoneSimulator
run: |
./util/ci/run_ios.sh "iPad Air 13-inch (M2)" 150
cat log.txt

View file

@ -1,5 +1,8 @@
mark_as_advanced(ZSTD_LIBRARY ZSTD_INCLUDE_DIR)
message(STATUS "ZSTD_LIBRARY: ${ZSTD_LIBRARY}")
message(STATUS "ZSTD_INCLUDE_DIR: ${ZSTD_INCLUDE_DIR}")
find_path(ZSTD_INCLUDE_DIR NAMES zstd.h)
find_library(ZSTD_LIBRARY NAMES zstd)

View file

@ -24,6 +24,7 @@ General options and their default values:
PRECOMPILED_HEADERS_PATH= - Path to a file listing all headers to precompile (default points to src/precompiled_headers.txt)
USE_SDL2=TRUE - Build with SDL2; Enables IrrlichtMt device SDL2
USE_SDL2_STATIC=TRUE - Links with SDL2::SDL2-static instead of SDL2::SDL2
USE_ANGLE=TRUE - Build with Google ANGLE; IrrlichMt device SDL2 on iOS
ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http
ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal)
ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations

34
doc/compiling/ios.md Normal file
View file

@ -0,0 +1,34 @@
# Compiling for iOS
THIS DOCUMENT IS NOT FINISHED!!!
## Requirements
- macOS
- [Homebrew](https://brew.sh/)
- XCode
- iOS/IPhoneSimulator SDK
- iOS Simulator for newest iOS is supported only on Apple Silicon deviecs.
Install dependencies with homebrew:
```
brew install cmake git
```
## Generate Xcode project
This script will download and build all requested dependencies for iOS or IPhoneSimulator and generate Xcode project for Luanti.
```bash
/path/to/ios_build_with_deps.sh https://github.com/luanti-org/luanti.git master ../luanti_ios ../luanti_ios_deps Debug "iPhoneSimulator" "18.2" "all"
```
### Build and Run
* Open generated Xcode project
* Select scheme `luanti`
* Configure signing Team etc.
* Run Build command
* Open application from `build/build/Debug/` directory or run it from Xcode

View file

@ -32,6 +32,7 @@ We aim to support these platforms:
* Windows via MinGW
* Linux (GL or GLES)
* macOS
* iOS (GLES)
* Android
This doesn't mean other platforms don't work or won't be supported, if you find something that doesn't work contributions are welcome.
@ -51,7 +52,7 @@ Driver (rows) vs Device (columns)
Notes:
* [1] `CIrrDeviceSDL`: supports Android, Linux, macOS, Windows
* [1] `CIrrDeviceSDL`: supports Android, Linux, macOS, iOS, Windows
* [2] `CIrrDeviceLinux`: supports Linux
* [3] `CIrrDeviceOSX`: supports macOS
* [4] `CIrrDeviceWin32`: supports Windows

View file

@ -22,7 +22,7 @@
#include <direct.h> // for _chdir
#include <io.h> // for _access
#include <tchar.h>
#elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_) || defined(_IRR_ANDROID_PLATFORM_))
#elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_) || defined(_IRR_IOS_PLATFORM_) || defined(_IRR_ANDROID_PLATFORM_) )
#include <cstdio>
#include <cstdlib>
#include <cstring>

View file

@ -1,4 +1,5 @@
// Copyright (C) 2002-2012 Nikolaus Gebhardt
// Copyright (C) 2025 Luanti contributors
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
@ -29,6 +30,13 @@
#include "CSDLManager.h"
#ifdef _IRR_COMPILE_WITH_ANGLE_
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES3/gl3.h>
#endif
// Since SDL doesn't have mouse keys as keycodes we need to fall back to EKEY_CODE in some cases.
static inline bool is_fake_key(EKEY_CODE key) {
switch (key) {
@ -409,13 +417,32 @@ CIrrDeviceSDL::~CIrrDeviceSDL()
for (u32 i = 0; i < numJoysticks; ++i)
SDL_JoystickClose(Joysticks[i]);
#endif
#ifndef _IRR_COMPILE_WITH_ANGLE_
if (Window && Context) {
SDL_GL_MakeCurrent(Window, NULL);
SDL_GL_DeleteContext(Context);
}
#else
if (Display != EGL_NO_DISPLAY) {
eglMakeCurrent(Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
if (Surface != EGL_NO_SURFACE) {
eglDestroySurface(Display, Surface);
}
if (Context != EGL_NO_CONTEXT) {
eglDestroyContext(Display, Context);
}
if (Display != EGL_NO_DISPLAY) {
eglTerminate(Display);
}
if (View) {
SDL_Metal_DestroyView(View);
}
if (Window) {
SDL_DestroyWindow(Window);
}
#endif
if (--SDLDeviceInstances == 0) {
SDL_Quit();
@ -529,21 +556,9 @@ bool CIrrDeviceSDL::createWindow()
return false;
}
bool CIrrDeviceSDL::createWindowWithContext()
{
u32 SDL_Flags = 0;
SDL_Flags |= SDL_WINDOW_ALLOW_HIGHDPI;
SDL_Flags |= getFullscreenFlag(CreationParams.Fullscreen);
if (Resizable)
SDL_Flags |= SDL_WINDOW_RESIZABLE;
if (CreationParams.WindowMaximized)
SDL_Flags |= SDL_WINDOW_MAXIMIZED;
SDL_Flags |= SDL_WINDOW_OPENGL;
SDL_GL_ResetAttributes();
#ifdef _IRR_EMSCRIPTEN_PLATFORM_
bool CIrrDeviceSDL::createWindowWithContextEmscripten()
{
if (Width != 0 || Height != 0)
emscripten_set_canvas_size(Width, Height);
else {
@ -581,7 +596,11 @@ bool CIrrDeviceSDL::createWindowWithContext()
emscripten_set_mouseleave_callback("#canvas", (void *)this, false, MouseLeaveCallback);
return true;
#else // !_IRR_EMSCRIPTEN_PLATFORM_
}
#else // _IRR_EMSCRIPTEN_PLATFORM_
#ifndef _IRR_COMPILE_WITH_ANGLE_
bool CIrrDeviceSDL::createWindowWithContextSDL()
{
switch (CreationParams.DriverType) {
case video::EDT_OPENGL:
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
@ -629,7 +648,12 @@ bool CIrrDeviceSDL::createWindowWithContext()
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
}
Window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, Width, Height, SDL_Flags);
if (!SDL_GL_LoadLibrary(NULL)) {
os::Printer::log("Could not load OpenGL ES library", SDL_GetError(), ELL_WARNING);
return false;
}
Window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, Width, Height, SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI);
if (!Window) {
os::Printer::log("Could not create window", SDL_GetError(), ELL_WARNING);
return false;
@ -643,6 +667,121 @@ bool CIrrDeviceSDL::createWindowWithContext()
return false;
}
int major, minor;
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
os::Printer::log("OpenGL ES version", std::to_string(major) + "." + std::to_string(minor), ELL_INFORMATION);
const char* error = SDL_GetError();
if (*error != '\0') {
os::Printer::log("SDL Error", error, ELL_WARNING);
SDL_ClearError();
}
}
#else // _IRR_COMPILE_WITH_ANGLE_
bool CIrrDeviceSDL::createWindowWithContextANGLE()
{
Window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, Width, Height, SDL_WINDOW_METAL | SDL_WINDOW_ALLOW_HIGHDPI);
if (!Window) {
os::Printer::log("Could not create window", SDL_GetError(), ELL_WARNING);
return false;
}
auto metal_view = SDL_Metal_CreateView(Window);
auto metal_layer = SDL_Metal_GetLayer(metal_view);
EGLAttrib egl_display_attribs[] = {
EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE,
EGL_POWER_PREFERENCE_ANGLE, EGL_HIGH_POWER_ANGLE,
EGL_NONE
};
Display = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE, (void*) EGL_DEFAULT_DISPLAY, egl_display_attribs);
if (Display == EGL_NO_DISPLAY)
{
os::Printer::log("Failed to get EGL display");
return false;
}
if (eglInitialize(Display, NULL, NULL) == false)
{
os::Printer::log("Failed to initialize EGL");
return false;
}
EGLint egl_config_attribs[] = {
EGL_RED_SIZE, (CreationParams.Bits == 16 ? 5 : 8),
EGL_GREEN_SIZE, (CreationParams.Bits == 16 ? 5 : 8),
EGL_BLUE_SIZE, (CreationParams.Bits == 16 ? 5 : 8),
EGL_ALPHA_SIZE, (CreationParams.WithAlphaChannel ? (CreationParams.Bits == 16 ? 1 : 8) : 0),
EGL_DEPTH_SIZE, CreationParams.ZBufferBits,
EGL_STENCIL_SIZE, CreationParams.Stencilbuffer ? 8 : 0,
EGL_SAMPLE_BUFFERS, CreationParams.AntiAlias > 1 ? 1 : 0,
EGL_SAMPLES, CreationParams.AntiAlias > 1 ? CreationParams.AntiAlias : 0,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
EGL_NONE
};
EGLConfig config;
EGLint configs_count;
if (!eglChooseConfig(Display, egl_config_attribs, &config, 1, &configs_count))
{
os::Printer::log("Failed to choose EGL config");
return false;
}
EGLint egl_context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};
Context = eglCreateContext(Display, config, EGL_NO_CONTEXT, egl_context_attribs);
if (Context == EGL_NO_CONTEXT) {
os::Printer::log("Failed to create EGL context");
return false;
}
Surface = eglCreateWindowSurface(Display, config, metal_layer, NULL);
if (Surface == EGL_NO_SURFACE)
{
os::Printer::log("Failed to create EGL surface");
return false;
}
if (!eglMakeCurrent(Display, Surface, Surface, Context))
{
os::Printer::log("Failed to make EGL context current");
return false;
}
}
#endif // _IRR_COMPILE_WITH_ANGLE_
#endif // _IRR_EMSCRIPTEN_PLATFORM_
bool CIrrDeviceSDL::createWindowWithContext()
{
u32 SDL_Flags = 0;
SDL_Flags |= SDL_WINDOW_ALLOW_HIGHDPI;
SDL_Flags |= getFullscreenFlag(CreationParams.Fullscreen);
if (Resizable)
SDL_Flags |= SDL_WINDOW_RESIZABLE;
if (CreationParams.WindowMaximized)
SDL_Flags |= SDL_WINDOW_MAXIMIZED;
SDL_Flags |= SDL_WINDOW_OPENGL;
//SDL_Flags |= SDL_WINDOW_METAL;
SDL_GL_ResetAttributes();
#ifdef _IRR_EMSCRIPTEN_PLATFORM_
createWindowWithContextEmscripten();
#else // !_IRR_EMSCRIPTEN_PLATFORM_
#ifndef _IRR_COMPILE_WITH_ANGLE_
createWindowWithContextSDL();
#else
createWindowWithContextANGLE();
#endif
updateSizeAndScale();
if (ScaleX != 1.0f || ScaleY != 1.0f) {
// The given window size is in pixels, not in screen coordinates.
@ -1161,7 +1300,11 @@ float CIrrDeviceSDL::getDisplayDensity() const
void CIrrDeviceSDL::SwapWindow()
{
#ifndef _IRR_COMPILE_WITH_ANGLE_
SDL_GL_SwapWindow(Window);
#else
eglSwapBuffers(Display, Surface);
#endif
}
//! pause execution temporarily

View file

@ -23,6 +23,10 @@
#undef SDL_VIDEO_DRIVER_DIRECTFB
#include <SDL_syswm.h>
#ifdef _IRR_COMPILE_WITH_ANGLE_
#include <EGL/egl.h>
#endif
#include <memory>
#include <unordered_map>
@ -302,13 +306,30 @@ private:
void createDriver();
bool createWindow();
#ifdef _IRR_EMSCRIPTEN_PLATFORM_
bool createWindowWithContextEmscripten();
#else // _IRR_EMSCRIPTEN_PLATFORM_
#ifndef _IRR_COMPILE_WITH_ANGLE_
bool createWindowWithContextSDL();
#else // _IRR_COMPILE_WITH_ANGLE_
bool createWindowWithContextANGLE();
#endif // _IRR_COMPILE_WITH_ANGLE_
#endif // _IRR_EMSCRIPTEN_PLATFORM_
bool createWindowWithContext();
void createKeyMap();
void logAttributes();
SDL_GLContext Context;
SDL_Window *Window;
#ifndef _IRR_COMPILE_WITH_ANGLE_
SDL_GLContext Context;
#else
SDL_MetalView View;
EGLSurface Surface;
EGLContext Context;
EGLDisplay Display;
#endif
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
core::array<SDL_Joystick *> Joysticks;
#endif

View file

@ -1,6 +1,11 @@
set(DEFAULT_SDL2 ON)
if(APPLE AND CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(DEFAULT_ANGLE ON)
endif()
option(USE_SDL2 "Use the SDL2 backend" ${DEFAULT_SDL2})
option(USE_ANGLE "Use the Google ANGLE EGL and OpenGL ES" ${DEFAULT_ANGLE})
option(USE_SDL2_STATIC "Link with SDL2 static libraries" FALSE)
@ -64,8 +69,17 @@ if(WIN32)
add_compile_definitions(_IRR_WINDOWS_ _IRR_WINDOWS_API_)
set(DEVICE "WINDOWS")
elseif(APPLE)
add_compile_definitions(_IRR_OSX_PLATFORM_)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
add_compile_definitions(-D_IRR_OSX_PLATFORM_)
set(DEVICE "OSX")
elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS")
add_compile_definitions(-D_IRR_IOS_PLATFORM_)
if(NOT USE_SDL2)
message(FATAL_ERROR "The iOS build requires SDL2")
endif()
else()
message(FATAL_ERROR "Not supported Apple device.")
endif()
elseif(ANDROID)
add_compile_definitions(_IRR_ANDROID_PLATFORM_)
if(NOT USE_SDL2)
@ -100,6 +114,10 @@ endif()
add_compile_definitions("_IRR_COMPILE_WITH_${DEVICE}_DEVICE_")
if(USE_ANGLE)
add_definitions("-D_IRR_COMPILE_WITH_ANGLE_")
endif()
# X11
if(DEVICE STREQUAL "X11")
@ -125,7 +143,7 @@ endif()
# OpenGL
if(USE_SDL2)
if(NOT ANDROID)
if(NOT ANDROID AND NOT USE_ANGLE)
set(DEFAULT_OPENGL3 TRUE)
endif()
else()
@ -133,17 +151,17 @@ else()
endif()
option(ENABLE_OPENGL3 "Enable OpenGL 3+" ${DEFAULT_OPENGL3})
if(ANDROID OR EMSCRIPTEN)
if(ANDROID OR EMSCRIPTEN OR USE_ANGLE)
set(ENABLE_OPENGL FALSE)
else()
option(ENABLE_OPENGL "Enable OpenGL" TRUE)
endif()
if(APPLE)
if(APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(ENABLE_GLES2 FALSE)
set(ENABLE_WEBGL1 FALSE)
else()
if(ANDROID OR EMSCRIPTEN)
if(ANDROID OR EMSCRIPTEN OR USE_ANGLE)
set(DEFAULT_GLES2 TRUE)
endif()
if(EMSCRIPTEN)
@ -234,7 +252,7 @@ if(USE_SDL2)
if(NOT ANDROID)
find_package(SDL2 REQUIRED)
else()
# provided by AndroidLibs.cmake
# provided by AndroidLibs.cmake or from cmake call
endif()
message(STATUS "Found SDL2: ${SDL2_LIBRARIES}")
@ -280,7 +298,7 @@ endif()
if(ANDROID)
enable_language(C)
elseif(APPLE)
elseif(APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
find_library(COCOA_LIB Cocoa REQUIRED)
find_library(IOKIT_LIB IOKit REQUIRED)
@ -599,6 +617,22 @@ target_link_libraries(IrrlichtMt PRIVATE
"$<$<BOOL:${USE_X11}>:${X11_Xi_LIB}>"
)
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
target_link_libraries(IrrlichtMt PRIVATE
"-framework UIKit"
"-framework Foundation"
"-framework CoreGraphics"
"-framework QuartzCore"
"-framework AudioToolbox"
"-framework AVFoundation"
"-framework CoreAudio"
"-framework CoreMotion"
"-framework IOSurface"
${OPENGLES2_LIBRARY}
SDL2::SDL2main
)
endif()
if(WIN32)
target_compile_definitions(IrrlichtMt INTERFACE _IRR_WINDOWS_API_)
endif()

View file

@ -11,7 +11,7 @@
#include <unistd.h>
#ifndef _IRR_ANDROID_PLATFORM_
#include <sys/types.h>
#ifdef _IRR_OSX_PLATFORM_
#if defined(_IRR_OSX_PLATFORM_) || defined(_IRR_IOS_PLATFORM_)
#include <sys/sysctl.h>
#endif
#endif

View file

@ -33,7 +33,11 @@ bool CSDLManager::activateContext(const SExposedVideoData &videoData, bool resto
void *CSDLManager::getProcAddress(const std::string &procName)
{
#ifndef _IRR_COMPILE_WITH_ANGLE_
return SDL_GL_GetProcAddress(procName.c_str());
#else
return (void *)eglGetProcAddress(procName.c_str());
#endif
}
bool CSDLManager::swapBuffers()

View file

@ -7,7 +7,7 @@
#include "irrTypes.h"
// even though we have mt_opengl.h our driver code still uses GL_* constants
#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_)
#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) && !defined(_IRR_COMPILE_WITH_ANGLE_)
#include <SDL_video.h>
#include <SDL_opengl.h>
#else

View file

@ -23,6 +23,8 @@
#include "mt_opengl.h"
#include <iostream>
namespace video
{
@ -214,14 +216,20 @@ void COpenGL3DriverBase::initQuadsIndices(u32 max_vertex_count)
void COpenGL3DriverBase::initVersion()
{
TEST_GL_ERROR(this);
Name = GL.GetString(GL_VERSION);
TEST_GL_ERROR(this);
printVersion();
// print renderer information
TEST_GL_ERROR(this);
VendorName = GL.GetString(GL_RENDERER);
TEST_GL_ERROR(this);
os::Printer::log("Renderer", VendorName.c_str(), ELL_INFORMATION);
TEST_GL_ERROR(this);
Version = getVersionFromOpenGL();
TEST_GL_ERROR(this);
}
bool COpenGL3DriverBase::isVersionAtLeast(int major, int minor) const noexcept
@ -235,9 +243,13 @@ bool COpenGL3DriverBase::isVersionAtLeast(int major, int minor) const noexcept
bool COpenGL3DriverBase::genericDriverInit(const core::dimension2d<u32> &screenSize, bool stencilBuffer)
{
TEST_GL_ERROR(this);
initVersion();
TEST_GL_ERROR(this);
initFeatures();
TEST_GL_ERROR(this);
printTextureFormats();
TEST_GL_ERROR(this);
if (EnableErrorTest) {
if (KHRDebugSupported) {
@ -259,6 +271,7 @@ bool COpenGL3DriverBase::genericDriverInit(const core::dimension2d<u32> &screenS
StencilBuffer = stencilBuffer;
TEST_GL_ERROR(this);
GL.PixelStorei(GL_PACK_ALIGNMENT, 1);
for (s32 i = 0; i < ETS_COUNT; ++i)
@ -266,15 +279,19 @@ bool COpenGL3DriverBase::genericDriverInit(const core::dimension2d<u32> &screenS
GL.ClearDepthf(1.0f);
TEST_GL_ERROR(this);
GL.FrontFace(GL_CW);
// create material renderers
TEST_GL_ERROR(this);
createMaterialRenderers();
TEST_GL_ERROR(this);
// set the renderstates
setRenderStates3DMode();
// set fog mode
TEST_GL_ERROR(this);
setFog(FogColor, FogType, FogStart, FogEnd, FogDensity, PixelFog, RangeFog);
// We need to reset once more at the beginning of the first rendering.
@ -725,6 +742,7 @@ void COpenGL3DriverBase::draw2DImage(const video::ITexture *texture, const core:
const core::rect<s32> &sourceRect, const core::rect<s32> *clipRect,
const video::SColor *const colors, bool useAlphaChannelOfTexture)
{
TEST_GL_ERROR(this);
if (!texture)
return;
@ -896,6 +914,7 @@ void COpenGL3DriverBase::draw2DImageBatch(const video::ITexture *texture,
if (clipRect)
GL.Disable(GL_SCISSOR_TEST);
TEST_GL_ERROR(this);
}
//! draw a 2d rectangle
@ -976,13 +995,16 @@ void COpenGL3DriverBase::drawArrays(GLenum primitiveType, const VertexType &vert
beginDraw(vertexType, reinterpret_cast<uintptr_t>(vertices));
GL.DrawArrays(primitiveType, 0, vertexCount);
endDraw(vertexType);
TEST_GL_ERROR(this);
}
void COpenGL3DriverBase::drawElements(GLenum primitiveType, const VertexType &vertexType, const void *vertices, int vertexCount, const u16 *indices, int indexCount)
{
TEST_GL_ERROR(this);
beginDraw(vertexType, reinterpret_cast<uintptr_t>(vertices));
GL.DrawRangeElements(primitiveType, 0, vertexCount - 1, indexCount, GL_UNSIGNED_SHORT, indices);
endDraw(vertexType);
TEST_GL_ERROR(this);
}
void COpenGL3DriverBase::drawGeneric(const void *vertices, const void *indexList,
@ -1189,6 +1211,7 @@ void COpenGL3DriverBase::setRenderStates3DMode()
//! Can be called by an IMaterialRenderer to make its work easier.
void COpenGL3DriverBase::setBasicRenderStates(const SMaterial &material, const SMaterial &lastmaterial, bool resetAllRenderStates)
{
TEST_GL_ERROR(this);
// ZBuffer
switch (material.ZBuffer) {
case ECFN_DISABLED:
@ -1341,6 +1364,7 @@ void COpenGL3DriverBase::setBasicRenderStates(const SMaterial &material, const S
// Texture parameters
setTextureRenderStates(material, resetAllRenderStates);
TEST_GL_ERROR(this);
}
//! Compare in SMaterial doesn't check texture parameters, so we should call this on each OnRender call.
@ -1491,6 +1515,7 @@ void COpenGL3DriverBase::setRenderStates2DMode(bool alpha, bool texture, bool al
}
MaterialRenderer2DActive->OnRender(this, video::EVT_STANDARD);
TEST_GL_ERROR(this);
}
void COpenGL3DriverBase::chooseMaterial2D()

View file

@ -163,6 +163,8 @@ public:
inline void irrGlObjectLabel(GLenum identifier, GLuint name, const char *label)
{
// KHR_debug implements ObjectLabelKHR in OpenGL ES
#ifndef _IRR_COMPILE_WITH_OGLES2_
if (KHRDebugSupported) {
u32 len = static_cast<u32>(strlen(label));
// Since our texture strings can get quite long we also truncate
@ -170,6 +172,7 @@ public:
len = std::min(len, std::min(MaxLabelLength, 82U));
GL.ObjectLabel(identifier, name, len, label);
}
#endif
}
bool LODBiasSupported = false;

View file

@ -8,6 +8,8 @@
#include "mt_opengl.h"
#include "CColorConverter.h"
#include <iostream>
namespace video
{

View file

@ -111,7 +111,7 @@
#endif
#endif
#if defined(__APPLE__) && !defined(HAVE_ENDIAN_H)
#if defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define be16toh(x) OSSwapBigToHostInt16((x))
#define htobe16(x) OSSwapHostToBigInt16((x))
@ -119,7 +119,7 @@
#define be32toh(x) OSSwapBigToHostInt32((x))
#define htole32(x) OSSwapHostToLittleInt32(x)
#define htobe32(x) OSSwapHostToBigInt32(x)
#endif /* __APPLE__ && !HAVE_ENDIAN_H */
#endif /* __APPLE__ */
#if defined(_WIN32) && !defined(HAVE_ENDIAN_H)
#include <winsock2.h>

30
misc/ios/Info.plist.in Normal file
View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>luanti</string>
<key>CFBundleIconFile</key>
<string>luanti-icon.icns</string>
<key>CFBundleName</key>
<string>@PROJECT_NAME_CAPITALIZED@</string>
<key>CFBundleDisplayName</key>
<string>@PROJECT_NAME_CAPITALIZED@</string>
<key>CFBundleIdentifier</key>
<string>org.luanti.luanti</string>
<key>CFBundleVersion</key>
<string>@VERSION_STRING@</string>
<key>CFBundleShortVersionString</key>
<string>@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>NSHighResolutionCapable</key>
<false/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
</dict>
</plist>

View file

@ -18,6 +18,8 @@
<string>org.luanti.luanti</string>
<key>CFBundleVersion</key>
<string>@VERSION_STRING@</string>
<key>CFBundleShortVersionString</key>
<string>@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>NSHighResolutionCapable</key>

View file

@ -492,6 +492,10 @@ if(ANDROID)
list(APPEND common_SRCS porting_android.cpp)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
list(APPEND common_SRCS porting_ios.mm)
endif()
if(BUILD_UNITTESTS)
add_subdirectory(unittest)
list(APPEND common_SRCS ${UNITTEST_SRCS})
@ -626,10 +630,17 @@ endif()
if(APPLE)
# Configure the Info.plist file
if(NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
configure_file(
"${CMAKE_SOURCE_DIR}/misc/macos/Info.plist.in"
"${CMAKE_BINARY_DIR}/Info.plist"
)
else()
configure_file(
"${CMAKE_SOURCE_DIR}/misc/ios/Info.plist.in"
"${CMAKE_BINARY_DIR}/Info.plist"
)
endif()
endif()
if(BUILD_CLIENT)
@ -704,6 +715,9 @@ if(BUILD_CLIENT)
# on Android, Luanti depends on SDL2 directly
# on other platforms, only IrrlichtMt depends on SDL2
"$<$<PLATFORM_ID:Android>:${SDL2_LIBRARIES}>"
#"$<$<PLATFORM_ID:iOS>:-framework UIKit -framework Foundation -framework CoreGraphics -framework QuartzCore -framework AudioToolbox -framework AVFoundation -framework CoreAudio -framework CoreMotion -framework Security>"
"$<$<PLATFORM_ID:iOS>:-framework Security -framework CoreHaptics -framework CoreBluetooth -framework GameController -framework Metal -framework Foundation>"
"$<$<PLATFORM_ID:iOS>:${OPENGLES2_LIBRARY}>"
)
target_compile_definitions(${PROJECT_NAME} PRIVATE "MT_BUILDTARGET=1")
if(NOT USE_LUAJIT)

View file

@ -49,6 +49,10 @@ extern "C" {
#endif
}
#if TARGET_OS_IPHONE
#include <SDL2/SDL.h>
#endif
#if !defined(__cpp_rtti) || !defined(__cpp_exceptions)
#error Luanti cannot be built without exceptions or RTTI
#endif

View file

@ -38,11 +38,15 @@
#include <android/api-level.h>
#endif
#if defined(__APPLE__)
#include <TargetConditionals.h>
#include <mach-o/dyld.h>
#include <CoreFoundation/CoreFoundation.h>
// For _NSGetEnviron()
// Related: https://gitlab.haskell.org/ghc/ghc/issues/2458
#include <crt_externs.h>
#if TARGET_OS_IPHONE
#include "porting_ios.h"
#endif
#endif
#if defined(__HAIKU__)
@ -538,6 +542,7 @@ bool setSystemPaths()
}
CFRelease(resources_url);
#if TARGET_OS_MAC
const char *const minetest_user_path = getenv("MINETEST_USER_PATH");
if (minetest_user_path && minetest_user_path[0] != '\0') {
path_user = std::string(minetest_user_path);
@ -547,6 +552,11 @@ bool setSystemPaths()
+ "/Library/Application Support/"
+ "minetest";
}
#elif TARGET_OS_IPHONE
path_user = getAppleDocumentsDirectory();
#else
#error "Not supported Apple OS."
#endif
return true;
}

8
src/porting_ios.h Normal file
View file

@ -0,0 +1,8 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 SFENCE <sfence.software@gmail.com>
#include <string>
std::string getAppleDocumentsDirectory();
std::string getAppleLibraryDirectory();

17
src/porting_ios.mm Normal file
View file

@ -0,0 +1,17 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 SFENCE <sfence.software@gmail.com>
#include "porting_ios.h"
#import <Foundation/Foundation.h>
std::string getAppleDocumentsDirectory() {
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
return std::string([documentsDirectory UTF8String]);
}
std::string getAppleLibraryDirectory() {
NSString *libraryDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Library"];
return std::string([libraryDirectory UTF8String]);
}

View file

@ -38,6 +38,7 @@
#elif !defined(BYTE_ORDER) && defined(__BYTE_ORDER)
#define BYTE_ORDER __BYTE_ORDER
#endif
#undef HAVE_ENDIAN_H
#define FIXEDPOINT_FACTOR 1000.0f

49
util/ci/build_ios.sh Executable file
View file

@ -0,0 +1,49 @@
#!/bin/bash -e
sudo xcode-select -s /Applications/Xcode_${xcodever}.app/Contents/Developer
sdkroot="$(realpath $(xcrun --sdk iphonesimulator --show-sdk-path)/../iPhoneSimulator${osver}.sdk)"
export CMAKE_PREFIX_PATH=${DEPS_DIR}
export SDKROOT="$sdkroot"
cmake .. -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=$osver -DCMAKE_FIND_FRAMEWORK=LAST -DCMAKE_OSX_ARCHITECTURES=arm64 \
-DCMAKE_INSTALL_PREFIX=../build/ios/ -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=${DEPS_DIR} \
-DENABLE_OPENGL=OFF \
-DENABLE_OPENGL3=OFF \
-DENABLE_GLES2=ON \
-DUSE_SDL2=ON \
-DUSE_SDL2_STATIC=ON \
-DSDL2_DIR=${DEPS_DIR}/lib/cmake/SDL2 \
-DOPENGLES2_LIBRARY=${DEPS_DIR}/lib/libGLESv2_static.a \
-DOPENGLES2_INCLUDE_DIR=${DEPS_DIR}/include/ANGLE \
-DCURL_LIBRARY=${DEPS_DIR}/lib/libcurl.a \
-DCURL_INCLUDE_DIR=${DEPS_DIR}/include \
-DFREETYPE_LIBRARY=${DEPS_DIR}/lib/libfreetype.a \
-DFREETYPE_INCLUDE_DIRS=${DEPS_DIR}/include/freetype2 \
-DGETTEXT_INCLUDE_DIR=${DEPS_DIR}/include \
-DGETTEXT_LIBRARY=${DEPS_DIR}/lib/libintl.a \
-DLUA_LIBRARY=${DEPS_DIR}/lib/libluajit-5.1.a \
-DLUA_INCLUDE_DIR=${DEPS_DIR}/include/luajit-2.1 \
-DOGG_LIBRARY=${DEPS_DIR}/lib/libogg.a \
-DOGG_INCLUDE_DIR=${DEPS_DIR}/include \
-DVORBIS_LIBRARY=${DEPS_DIR}/lib/libvorbis.a \
-DVORBISFILE_LIBRARY=${DEPS_DIR}/lib/libvorbisfile.a \
-DVORBIS_INCLUDE_DIR=${DEPS_DIR}/include \
-DZSTD_LIBRARY=${DEPS_DIR}/lib/libzstd.a \
-DZSTD_INCLUDE_DIR=${DEPS_DIR}/include \
-DGMP_LIBRARY=${DEPS_DIR}/lib/libgmp.a \
-DGMP_INCLUDE_DIR=${DEPS_DIR}/include \
-DJSON_LIBRARY=${DEPS_DIR}/lib/libjsoncpp.a \
-DJSON_INCLUDE_DIR=${DEPS_DIR}/include \
-DENABLE_LEVELDB=OFF \
-DENABLE_POSTGRESQL=OFF \
-DENABLE_REDIS=OFF \
-DJPEG_LIBRARY=${DEPS_DIR}/lib/libjpeg.a \
-DJPEG_INCLUDE_DIR=${DEPS_DIR}/include \
-DPNG_LIBRARY=${DEPS_DIR}/lib/libpng.a \
-DPNG_PNG_INCLUDE_DIR=${DEPS_DIR}/include \
-DCMAKE_EXE_LINKER_FLAGS="-lbz2 ${DEPS_DIR}/lib/libANGLE_static.a ${DEPS_DIR}/lib/libEGL_static.a" \
-DXCODE_CODE_SIGN_ENTITLEMENTS=${REPDIR}/misc/ios/entitlements/release.entitlements \
-GXcode
xcodebuild -project luanti.xcodeproj -scheme luanti -configuration Release build

View file

@ -49,3 +49,25 @@ install_macos_deps() {
brew unlink $(brew ls --formula)
brew link "${pkgs[@]}"
}
# iOS build only
install_ios_deps() {
osver=$1
# Uninstall the bundled cmake, it is outdated, and brew does not want to install the newest version with this one present since they are from different taps.
brew uninstall cmake || :
local pkgs=(
cmake gettext wget
)
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
export HOMEBREW_NO_INSTALL_CLEANUP=1
# contrary to how it may look --auto-update makes brew do *less*
brew update --auto-update
brew install --display-times "${pkgs[@]}"
brew unlink $(brew ls --formula)
brew link "${pkgs[@]}"
wget ios${osver}_deps.tar.gz https://github.com/luanti-org/luanti_ios_deps/releases/download/latest/ios${osver}_deps.tar.gz || echo "Ignore stupid error number 4: $?"
tar -xf ios${osver}_deps.tar.gz
}

36
util/ci/run_ios.sh Executable file
View file

@ -0,0 +1,36 @@
#!/bin/sh
DEVICE_NAME=$1
TIMEOUT=$2
xcrun simctl boot "$DEVICE_NAME"
xcrun simctl install booted build/build/Release-iphonesimulator/luanti.app
# Run the iOS app in the background
xcrun simctl launch --console booted org.luanti.luanti --run-unittests 2> log.txt &
APP_PID=$!
# Initialize variables
CHECK_INTERVAL=15
ELAPSED_TIME=0
FOUND_RESULT=false
# Monitor the log file
while [ $ELAPSED_TIME -lt $TIMEOUT ]; do
if grep -q "Unit Test Results:" log.txt; then
FOUND_RESULT=true
break
fi
sleep $CHECK_INTERVAL
ELAPSED_TIME=$((ELAPSED_TIME + CHECK_INTERVAL))
done
# Terminate the app
if $FOUND_RESULT; then
echo "Unit test results found. Terminating the app."
else
echo "Timeout reached. Terminating the app."
fi
xcrun simctl terminate booted org.luanti.luanti
xcrun simctl shutdown booted