diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 000000000000..1fc40538250c --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,15 @@ +image: + - Visual Studio 2019 + +version: 1.1.0.{build} + +install: + - git submodule update --init --recursive + +build_script: + - pwsh: .\build.ps1 -p wasm -xb '--target','cpp_tests' + - pwsh: 7z a build_wasm.zip build_wasm/bin/**/* + +artifacts: + - path: build_wasm.zip + name: build_wasm diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 87500fa74f98..7532311aa942 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -105,4 +105,4 @@ jobs: - name: Build shell: pwsh - run: ./build.ps1 -p tvos -a x64 + run: ./build.ps1 -p tvos -a x64 \ No newline at end of file diff --git a/1k/build1k.ps1 b/1k/build1k.ps1 index 069eccceb683..a876185a1a1b 100644 --- a/1k/build1k.ps1 +++ b/1k/build1k.ps1 @@ -28,7 +28,7 @@ # # The build1k.ps1, will be core script of project https://github.com/axmolengine/build1k # options -# -p: build target platform: win32,winuwp,linux,android,osx,ios,tvos,watchos +# -p: build target platform: win32,winuwp,linux,android,osx,ios,tvos,watchos,wasm # for android: will search ndk in sdk_root which is specified by env:ANDROID_HOME first, # if not found, by default will install ndk-r16b or can be specified by option: -cc 'ndk-r23c' # -a: build arch: x86,x64,armv7,arm64 @@ -204,6 +204,11 @@ foreach ($arg in $args) { } } +$is_wasm = $options.p -eq 'wasm' +if ($is_wasm) { + $options.a = 'any' # forcing arch to 'any', only for identifying +} + $manifest_file = Join-Path $myRoot 'manifest.ps1' if ($b1k.isfile($manifest_file)) { . $manifest_file @@ -246,6 +251,7 @@ $toolchains = @{ 'ios' = 'xcode'; 'tvos' = 'xcode'; 'watchos' = 'xcode'; + 'wasm' = 'emcc'; # wasm llvm-emcc } if (!$TOOLCHAIN) { $TOOLCHAIN = $toolchains[$BUILD_TARGET] @@ -764,6 +770,31 @@ function setup_android_sdk() { return $sdk_root, $ndk_root } +# enable emsdk emcmake +function setup_emsdk() { + $emsdk_cmd = (Get-Command emsdk -ErrorAction SilentlyContinue) + if (!$emsdk_cmd) { + $emsdk_root = Join-Path $prefix 'emsdk' + if (!(Test-Path $emsdk_root -PathType Container)) { + git clone 'https://github.com/emscripten-core/emsdk.git' $emsdk_root + } + else { + git -C $emsdk_root pull + } + } else { + $emsdk_root = Split-Path $emsdk_cmd.Source -Parent + } + + $emcmake = (Get-Command emcmake -ErrorAction SilentlyContinue) + if (!$emcmake) { + Set-Location $emsdk_root + ./emsdk install latest + ./emsdk activate latest + . ./emsdk_env.ps1 + Set-Location - + } +} + # preprocess methods: # -inputOptions [CMAKE_OPTIONS] function preprocess_win([string[]]$inputOptions) { @@ -902,6 +933,10 @@ function preprocess_ios([string[]]$inputOptions) { return $outputOptions } +function preprocess_wasm([string[]]$inputOptions) { + return $inputOptions +} + function validHostAndToolchain() { $appleTable = @{ 'host' = @{'macos' = $True }; @@ -928,6 +963,10 @@ function validHostAndToolchain() { 'ios' = $appleTable; 'tvos' = $appleTable; 'watchos' = $appleTable; + 'wasm' = @{ + 'host' = @{'windows' = $True; 'linux' = $True; 'macos' = $True }; + 'toolchain' = @{'emcc' = $True; }; + }; } $validInfo = $validTable[$BUILD_TARGET] $validOS = $validInfo.host[$HOST_OS_NAME] @@ -949,6 +988,7 @@ $proprocessTable = @{ 'ios' = ${function:preprocess_ios}; 'tvos' = ${function:preprocess_ios}; 'watchos' = ${function:preprocess_ios}; + 'wasm' = ${Function:preprocess_wasm}; } validHostAndToolchain @@ -978,6 +1018,9 @@ elseif ($BUILD_TARGET -eq 'android') { $ninja_prog = setup_ninja } } +elseif($BUILD_TARGET -eq 'wasm') { + . setup_emsdk +} if (!$setupOnly) { $stored_cwd = $(Get-Location).Path @@ -1006,7 +1049,10 @@ if (!$setupOnly) { if ($is_host_target) { # wheither building host target? $BUILD_DIR = "build_$($options.a)" } else { - $BUILD_DIR = "build_${BUILD_TARGET}_$($options.a)" + $BUILD_DIR = "build_${BUILD_TARGET}" + if (!$is_wasm) { + $BUILD_DIR += "_$($options.a)" + } } } else { @@ -1070,7 +1116,11 @@ if (!$setupOnly) { $mainDepChanged = "$storeTime" -ne "$lastWriteTime" $cmakeCachePath = Join-Path $workDir "$BUILD_DIR/CMakeCache.txt" if ($mainDepChanged -or !$b1k.isfile($cmakeCachePath)) { - cmake -B $BUILD_DIR $CONFIG_ALL_OPTIONS | Out-Host + if (!$is_wasm) { + cmake -B $BUILD_DIR $CONFIG_ALL_OPTIONS | Out-Host + } else { + emcmake cmake -B $BUILD_DIR $CONFIG_ALL_OPTIONS | Out-Host + } Set-Content $tempFile $lastWriteTime -NoNewline } diff --git a/README_webgl.md b/README_webgl.md new file mode 100644 index 000000000000..5fb2d31e73e1 --- /dev/null +++ b/README_webgl.md @@ -0,0 +1,17 @@ +# Axmol WASM + +Building axmol for target platform: webgl aka wasm. + +After setup, just run follow command: + +axmol build -p wasm + +## TODO + +```cpp +#ifndef EMSCRIPTEN + const FT_Int spread = DistanceMapSpread; + FT_Property_Set(_FTlibrary, "sdf", "spread", &spread); + FT_Property_Set(_FTlibrary, "bsdf", "spread", &spread); +#endif +``` diff --git a/build.ps1 b/build.ps1 index 7dff97907d9f..2863b9bcbecf 100644 --- a/build.ps1 +++ b/build.ps1 @@ -2,7 +2,7 @@ # This script easy to build win32, linux, winuwp, ios, tvos, osx, android depends on $myRoot/1k/build1k.ps1 # usage: pwsh build.ps1 -p -a # options -# -p: build target platform: win32,winuwp,linux,android,osx,ios,tvos,watchos +# -p: build target platform: win32,winuwp,linux,android,osx,ios,tvos,watchos,wasm # for android: will search ndk in sdk_root which is specified by env:ANDROID_HOME first, # if not found, by default will install ndk-r16b or can be specified by option: -cc 'ndk-r23c' # -a: build arch: x86,x64,armv7,arm64; for android can be list by ';', i.e: 'arm64;x64' diff --git a/cmake/Modules/AXBuildHelpers.cmake b/cmake/Modules/AXBuildHelpers.cmake index 355e4c3720f4..9067218ae6dc 100644 --- a/cmake/Modules/AXBuildHelpers.cmake +++ b/cmake/Modules/AXBuildHelpers.cmake @@ -7,6 +7,10 @@ if(NOT PWSH_COMMAND) message(FATAL_ERROR "Please install it https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell, and run CMake again.") endif() +if(NOT DEFINED WASM) + set(WASM FALSE CACHE BOOL "") +endif() + # copy resource `FILES` and `FOLDERS` to TARGET_FILE_DIR/Resources function(ax_sync_target_res ax_target) set(options SYM_LINK) @@ -33,7 +37,7 @@ function(ax_sync_target_res ax_target) get_filename_component(link_folder_abs ${opt_LINK_TO} ABSOLUTE) add_custom_command(TARGET ${sync_target_name} POST_BUILD COMMAND ${PWSH_COMMAND} ARGS ${_AX_ROOT}/cmake/sync_folder.ps1 - -s ${cc_folder} -d ${link_folder_abs} -l ${opt_SYM_LINK} + -s ${cc_folder} -d ${link_folder_abs} -l ${opt_SYM_LINK} -wasm "${WASM}" ) endforeach() endfunction() @@ -72,18 +76,18 @@ function(ax_sync_lua_scripts ax_target src_dir dst_dir) if(MSVC) add_custom_command(TARGET ${luacompile_target} POST_BUILD COMMAND ${PWSH_COMMAND} ARGS ${_AX_ROOT}/cmake/sync_folder.ps1 - -s ${src_dir} -d ${dst_dir} + -s ${src_dir} -d ${dst_dir} -wasm "${WASM}" ) else() if("${CMAKE_BUILD_TYPE}" STREQUAL "") add_custom_command(TARGET ${luacompile_target} POST_BUILD COMMAND ${PWSH_COMMAND} ARGS ${_AX_ROOT}/cmake/sync_folder.ps1 - -s ${src_dir} -d ${dst_dir} + -s ${src_dir} -d ${dst_dir} -wasm "${WASM}" ) else() add_custom_command(TARGET ${luacompile_target} POST_BUILD COMMAND ${PWSH_COMMAND} ARGS ${_AX_ROOT}/cmake/sync_folder.ps1 - -s ${src_dir} -d ${dst_dir} + -s ${src_dir} -d ${dst_dir} -wasm "${WASM}" ) endif() endif() diff --git a/cmake/Modules/AXConfigDefine.cmake b/cmake/Modules/AXConfigDefine.cmake index 39fcd29fa54e..59871695ed48 100644 --- a/cmake/Modules/AXConfigDefine.cmake +++ b/cmake/Modules/AXConfigDefine.cmake @@ -147,6 +147,8 @@ function(use_ax_compile_define target) target_compile_definitions(${target} PUBLIC _GNU_SOURCE) elseif(ANDROID) target_compile_definitions(${target} PUBLIC USE_FILE32API) + elseif(EMSCRIPTEN) + ax_config_pred(${target} AX_USE_ANGLE) elseif(WINDOWS) ax_config_pred(${target} AX_USE_ANGLE) ax_config_pred(${target} AX_ENABLE_VLC_MEDIA) @@ -187,6 +189,11 @@ endfunction() # endif() # endif() +if(EMSCRIPTEN) + set(CMAKE_C_FLAGS "-s USE_LIBJPEG=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_FREETYPE=1") + set(CMAKE_CXX_FLAGS "-s USE_LIBJPEG=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_FREETYPE=1") +endif() + # Try enable asm & nasm compiler support set(can_use_assembler TRUE) enable_language(ASM) diff --git a/cmake/Modules/AXPlatform.cmake b/cmake/Modules/AXPlatform.cmake index 1c7de327d5df..82bd5fb6e9b1 100644 --- a/cmake/Modules/AXPlatform.cmake +++ b/cmake/Modules/AXPlatform.cmake @@ -38,6 +38,10 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(LINUX TRUE) set(PLATFORM_FOLDER linux) endif() +elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + set(EMSCRIPTEN TRUE) + set(WASM TRUE) + set(PLATFORM_FOLDER emscripten) elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(APPLE TRUE) set(MACOSX TRUE) @@ -91,6 +95,9 @@ elseif(MACOSX) elseif(LINUX) set(platform_name linux) set(platform_spec_path linux) +elseif(EMSCRIPTEN) + set(platform_name emscripten) + set(platform_spec_path emscripten) endif() set(platform_spec_path "${_path_prefix}${platform_spec_path}") diff --git a/cmake/sync_folder.ps1 b/cmake/sync_folder.ps1 index 09025125afd0..df9225205ebb 100644 --- a/cmake/sync_folder.ps1 +++ b/cmake/sync_folder.ps1 @@ -5,17 +5,27 @@ param( [string]$destDir, [Parameter(Mandatory=$false, ValueFromPipeline=$true)] [PSDefaultValue(Value=$null)] - $linkOnly + $linkOnly, + $wasm = $null ) + function ParseBoolFuzzy($value) { + $value = "$value".ToLower() return $value.startsWith('1') -or $value.StartsWith('t') -or $value.StartsWith('y') } +$wasm = ParseBoolFuzzy($wasm) + +if ($wasm) { + Write-Host "sync_folder.ps1: Skipping sync folder for target platform 'wasm'" + return +} + # 0: windows, 1: linux, 2: macos $IsWin = $IsWindows -or ("$env:OS" -eq 'Windows_NT') -$linkOnly = ParseBoolFuzzy("$linkOnly".ToLower()) +$linkOnly = ParseBoolFuzzy($linkOnly) # convert to native path style if ($IsWin) { @@ -27,24 +37,24 @@ if ($IsWin) { } if(!$srcDir -or !(Test-Path $srcDir -PathType Container)) { - throw "The source directory $srcDir not exist" + throw "sync_folder.ps1: The source directory $srcDir not exist" } if (Test-Path $destDir -PathType Container) { # dest already exist if ($linkOnly) { # is symlink and dest exist $directoryInfo = (Get-Item $destDir) if ($directoryInfo.Target -eq $srcDir) { - Write-Host "Symlink $destDir ===> $($directoryInfo.Target) exists" + Write-Host "sync_folder.ps1: Symlink $destDir ===> $($directoryInfo.Target) exists" return } - Write-Host "Removing old link target $($directoryInfo.Target)" + Write-Host "sync_folder.ps1: Removing old link target $($directoryInfo.Target)" # Remove-Item -Path $destDir $directoryInfo.Delete($false) } } if ($linkOnly) { - Write-Host "Linking $srcDir to $destDir ..." + Write-Host "sync_folder.ps1: Linking $srcDir to $destDir ..." if ($IsWin) { cmd.exe /c mklink /J $destDir $srcDir } @@ -54,7 +64,7 @@ if ($linkOnly) { } } else { # copy directory, remove first? - Write-Host "Copying $srcDir to $destDir ..." + Write-Host "sync_folder.ps1: Copying $srcDir to $destDir ..." if (!(Test-Path $destDir -PathType Container)) { Copy-Item $srcDir $destDir -Recurse -Force } else { diff --git a/core/2d/FontFreeType.cpp b/core/2d/FontFreeType.cpp index 466808c5f155..ac6b31544f49 100644 --- a/core/2d/FontFreeType.cpp +++ b/core/2d/FontFreeType.cpp @@ -29,7 +29,9 @@ THE SOFTWARE. #include "2d/FontAtlas.h" #include "base/Director.h" #include "base/UTF8.h" +#ifndef EMSCRIPTEN #include "freetype/ftmodapi.h" +#endif #include "platform/FileUtils.h" #include "platform/FileStream.h" @@ -124,9 +126,11 @@ bool FontFreeType::initFreeType() if (FT_Init_FreeType(&_FTlibrary)) return false; +#ifndef EMSCRIPTEN const FT_Int spread = DistanceMapSpread; FT_Property_Set(_FTlibrary, "sdf", "spread", &spread); FT_Property_Set(_FTlibrary, "bsdf", "spread", &spread); +#endif _FTInitialized = true; } @@ -413,12 +417,15 @@ unsigned char* FontFreeType::getGlyphBitmap(char32_t charCode, #endif if (FT_Load_Glyph(_fontFace, glyphIndex, FT_LOAD_RENDER | FT_LOAD_NO_AUTOHINT)) break; + +#ifndef EMSCRIPTEN if (_distanceFieldEnabled && _fontFace->glyph->bitmap.buffer) { // Require freetype version > 2.11.0, because freetype 2.11.0 sdf has memory access bug, see: // https://gitlab.freedesktop.org/freetype/freetype/-/issues/1077 FT_Render_Glyph(_fontFace->glyph, FT_Render_Mode::FT_RENDER_MODE_SDF); } +#endif auto& metrics = _fontFace->glyph->metrics; outRect.origin.x = static_cast(metrics.horiBearingX >> 6); diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 6709a3da416f..66a35f54ddb9 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -76,7 +76,7 @@ endif() message(STATUS "AX_ENABLE_VLC_MEDIA=${AX_ENABLE_VLC_MEDIA}") -if(NOT APPLE) +if(NOT APPLE AND (NOT EMSCRIPTEN)) set(AX_USE_ALSOFT ON CACHE BOOL "" FORCE) else() option(AX_USE_ALSOFT "Use ALSOFT on apple" OFF) @@ -105,13 +105,13 @@ option(AX_ENABLE_EXT_FAIRYGUI "Build extension FairyGUI" ON) option(AX_ENABLE_EXT_LIVE2D "Build extension Live2D" OFF) option(AX_ENABLE_EXT_EFFEKSEER "Build extension Effekseer" OFF) -if((WINDOWS AND NOT WINRT) OR MACOSX OR LINUX OR ANDROID) +if((WINDOWS AND NOT WINRT) OR MACOSX OR LINUX OR ANDROID OR EMSCRIPTEN) option(AX_ENABLE_EXT_IMGUI "Build extension ImGui" ON) else() set(AX_ENABLE_EXT_IMGUI OFF) endif() -if (ANDROID) +if (ANDROID OR EMSCRIPTEN) set(AX_USE_GLAD TRUE CACHE BOOL "Use glad for android GLESv3 support" FORCE) add_definitions(-DAX_USE_GLAD=1) endif() diff --git a/core/audio/AudioEngine.cpp b/core/audio/AudioEngine.cpp index 4fe7d87f757f..724b9c180783 100644 --- a/core/audio/AudioEngine.cpp +++ b/core/audio/AudioEngine.cpp @@ -55,7 +55,10 @@ AudioEngine::ProfileHelper* AudioEngine::_defaultProfileHelper = nullptr; std::unordered_map AudioEngine::_audioIDInfoMap; AudioEngineImpl* AudioEngine::_audioEngineImpl = nullptr; +#ifndef __EMSCRIPTEN__ AudioEngine::AudioEngineThreadPool* AudioEngine::s_threadPool = nullptr; +#endif + bool AudioEngine::_isEnabled = true; AudioEngine::AudioInfo::AudioInfo() @@ -64,6 +67,7 @@ AudioEngine::AudioInfo::AudioInfo() AudioEngine::AudioInfo::~AudioInfo() {} +#ifndef __EMSCRIPTEN__ class AudioEngine::AudioEngineThreadPool { public: @@ -131,6 +135,7 @@ class AudioEngine::AudioEngineThreadPool std::condition_variable _taskCondition; bool _stop; }; +#endif void AudioEngine::end() { @@ -138,11 +143,13 @@ void AudioEngine::end() // fix #127 uncacheAll(); +#ifndef __EMSCRIPTEN__ if (s_threadPool) { delete s_threadPool; s_threadPool = nullptr; } +#endif delete _audioEngineImpl; _audioEngineImpl = nullptr; @@ -164,10 +171,12 @@ bool AudioEngine::lazyInit() } } +#ifndef __EMSCRIPTEN__ if (s_threadPool == nullptr) { s_threadPool = new AudioEngineThreadPool(); } +#endif return true; } @@ -587,10 +596,12 @@ void AudioEngine::addTask(const std::function& task) { lazyInit(); +#ifndef __EMSCRIPTEN__ if (_audioEngineImpl && s_threadPool) { s_threadPool->addTask(task); } +#endif } int AudioEngine::getPlayingAudioCount() diff --git a/core/audio/AudioEngine.h b/core/audio/AudioEngine.h index df602e07c36d..b14c18170f28 100644 --- a/core/audio/AudioEngine.h +++ b/core/audio/AudioEngine.h @@ -383,8 +383,10 @@ class AX_DLL AudioEngine static AudioEngineImpl* _audioEngineImpl; +#ifndef __EMSCRIPTEN__ class AudioEngineThreadPool; static AudioEngineThreadPool* s_threadPool; +#endif static bool _isEnabled; diff --git a/core/audio/AudioEngineImpl.cpp b/core/audio/AudioEngineImpl.cpp index 310c6ac06728..6473f6e1d4ce 100644 --- a/core/audio/AudioEngineImpl.cpp +++ b/core/audio/AudioEngineImpl.cpp @@ -389,7 +389,7 @@ bool AudioEngineImpl::init() for (int i = 0; i < MAX_AUDIOINSTANCES; ++i) { _unusedSourcesPool.push(_alSources[i]); -#if !AX_USE_ALSOFT +#if defined(__APPLE__) && !AX_USE_ALSOFT alSourceAddNotificationExt(_alSources[i], AL_BUFFERS_PROCESSED, myAlSourceNotificationCallback, nullptr); #endif diff --git a/core/audio/AudioEngineImpl.h b/core/audio/AudioEngineImpl.h index c249d3d5d30c..b69b4318f9a5 100644 --- a/core/audio/AudioEngineImpl.h +++ b/core/audio/AudioEngineImpl.h @@ -72,9 +72,9 @@ class AX_DLL AudioEngineImpl : public ax::Ref void _play2d(AudioCache* cache, AUDIO_ID audioID); void _unscheduleUpdate(); ALuint findValidSource(); -# if defined(__APPLE__) +#if defined(__APPLE__) && !AX_USE_ALSOFT static ALvoid myAlSourceNotificationCallback(ALuint sid, ALuint notificationID, ALvoid* userData); -# endif +#endif ALuint _alSources[MAX_AUDIOINSTANCES]; // available sources diff --git a/core/audio/alconfig.h b/core/audio/alconfig.h index efb20da49d78..285f779c930f 100644 --- a/core/audio/alconfig.h +++ b/core/audio/alconfig.h @@ -26,20 +26,26 @@ ****************************************************************************/ #pragma once -#if !defined(__APPLE__) +#if !defined(__APPLE__) && !defined(__EMSCRIPTEN__) # if !defined(AX_USE_ALSOFT) # define AX_USE_ALSOFT 1 # endif #endif -#if !AX_USE_ALSOFT -# import -# import -# define MAX_AUDIOINSTANCES 24 +#if defined(__EMSCRIPTEN__) + #import + #import + #define MAX_AUDIOINSTANCES 24 #else -# define AL_ALEXT_PROTOTYPES 1 -# include "AL/al.h" -# include "AL/alc.h" -# include "AL/alext.h" -# define MAX_AUDIOINSTANCES 32 -#endif + #if !AX_USE_ALSOFT + # import + # import + # define MAX_AUDIOINSTANCES 24 + #else + # define AL_ALEXT_PROTOTYPES 1 + # include "AL/al.h" + # include "AL/alc.h" + # include "AL/alext.h" + # define MAX_AUDIOINSTANCES 32 + #endif +#endif \ No newline at end of file diff --git a/core/axmol.h b/core/axmol.h index cf1950d3e9aa..1891b909f406 100644 --- a/core/axmol.h +++ b/core/axmol.h @@ -226,6 +226,13 @@ THE SOFTWARE. # include "platform/linux/StdC-linux.h" #endif // AX_TARGET_PLATFORM == AX_PLATFORM_LINUX +#if (AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN) + #include "platform/emscripten/Application-emscripten.h" + #include "platform/emscripten/GLViewImpl-emscripten.h" + #include "platform/emscripten/GL-emscripten.h" + #include "platform/emscripten/StdC-emscripten.h" +#endif // AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN + // script_support #include "base/ScriptSupport.h" diff --git a/core/base/Config.h b/core/base/Config.h index 809c59170392..fde9c89ae329 100644 --- a/core/base/Config.h +++ b/core/base/Config.h @@ -272,7 +272,7 @@ THE SOFTWARE. #ifndef AX_USE_3D_PHYSICS # if (AX_TARGET_PLATFORM == AX_PLATFORM_IOS || AX_TARGET_PLATFORM == AX_PLATFORM_MAC || \ AX_TARGET_PLATFORM == AX_PLATFORM_WIN32 || AX_TARGET_PLATFORM == AX_PLATFORM_WINRT || \ - AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_LINUX) + AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_LINUX || AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN) # define AX_USE_3D_PHYSICS 1 # endif #endif @@ -311,9 +311,11 @@ THE SOFTWARE. /** Support webp or not. If your application don't use webp format picture, you can undefine this macro to save package * size. */ -#ifndef AX_USE_WEBP -# define AX_USE_WEBP 1 -#endif // AX_USE_WEBP +#if AX_TARGET_PLATFORM != AX_PLATFORM_EMSCRIPTEN + #ifndef AX_USE_WEBP + #define AX_USE_WEBP 1 + #endif // AX_USE_WEBP +#endif /** Enable Lua Script binding */ #ifndef AX_ENABLE_SCRIPT_BINDING diff --git a/core/base/Controller.cpp b/core/base/Controller.cpp index 2cbc67943907..bf72beaf5414 100644 --- a/core/base/Controller.cpp +++ b/core/base/Controller.cpp @@ -28,7 +28,7 @@ #if (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_IOS || \ AX_TARGET_PLATFORM == AX_PLATFORM_MAC || AX_TARGET_PLATFORM == AX_PLATFORM_LINUX || \ - AX_TARGET_PLATFORM == AX_PLATFORM_WIN32) + defined(_WIN32) || AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN) # include "base/EventDispatcher.h" # include "base/EventController.h" @@ -128,4 +128,4 @@ void Controller::onAxisEvent(int axisCode, float value, bool isAnalog) NS_AX_END #endif // (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_IOS || AX_TARGET_PLATFORM == - // AX_PLATFORM_MAC || AX_TARGET_PLATFORM == AX_PLATFORM_LINUX || AX_TARGET_PLATFORM == AX_PLATFORM_WIN32) + // AX_PLATFORM_MAC || AX_TARGET_PLATFORM == AX_PLATFORM_LINUX || defined(_WIN32) || AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN) diff --git a/core/base/Controller.h b/core/base/Controller.h index 15d6eb2c1e4d..5649d46dc8de 100644 --- a/core/base/Controller.h +++ b/core/base/Controller.h @@ -27,7 +27,8 @@ #define __cocos2d_libs__CCController__ #if (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_IOS || \ AX_TARGET_PLATFORM == AX_PLATFORM_MAC || AX_TARGET_PLATFORM == AX_PLATFORM_LINUX || \ - defined(_WIN32)) + defined(_WIN32) /* win32 & winuwp */ || \ +AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN) # include "platform/PlatformMacros.h" # include diff --git a/core/base/Random.cpp b/core/base/Random.cpp index 350beac96170..c4b0f2811a17 100644 --- a/core/base/Random.cpp +++ b/core/base/Random.cpp @@ -26,9 +26,12 @@ THE SOFTWARE. #include "base/Random.h" +#ifdef EMSCRIPTEN +#else std::mt19937& ax::RandomHelper::getEngine() { static std::random_device seed_gen; static std::mt19937 engine(seed_gen()); return engine; } +#endif diff --git a/core/base/Random.h b/core/base/Random.h index c5a0bcf59575..2688ab592f40 100644 --- a/core/base/Random.h +++ b/core/base/Random.h @@ -30,7 +30,7 @@ THE SOFTWARE. #include #include -#include "platform/PlatformMacros.h" +#include "platform/PlatformConfig.h" /** * @addtogroup base @@ -38,10 +38,33 @@ THE SOFTWARE. */ NS_AX_BEGIN + + /** * @class RandomHelper * @brief A helper class for creating random number. */ +#ifdef EMSCRIPTEN +#include + +class AX_DLL RandomHelper +{ +public: + template + static T random_real(T min, T max) + { + T randomValue = static_cast(emscripten_random()) / static_cast(0xFFFFFFFF); + return min + randomValue * (max - min); + } + + template + static T random_int(T min, T max) + { + T randomValue = static_cast(emscripten_random()) % (max - min + 1); + return min + randomValue; + } +}; +#else class AX_DLL RandomHelper { public: @@ -64,6 +87,7 @@ class AX_DLL RandomHelper private: static std::mt19937& getEngine(); }; +#endif /** * Returns a random value between `min` and `max`. diff --git a/core/base/astc.cpp b/core/base/astc.cpp index 78f3d87db2d8..3ebe78219de7 100644 --- a/core/base/astc.cpp +++ b/core/base/astc.cpp @@ -22,6 +22,8 @@ #include "astcenc/astcenc_internal_entry.h" #include "yasio/utils.hpp" +#if !defined(__EMSCRIPTEN__) + #define ASTCDEC_NO_CONTEXT 1 #define ASTCDEC_PRINT_BENCHMARK 0 @@ -285,3 +287,60 @@ int astc_decompress_image(const uint8_t* in, return astc_decompress_job_manager::get_instance()->decompress_parallel_sync(in, inlen, out, dim_x, dim_y, block_x, block_y); } +#else +int astc_decompress_image(const uint8_t* in, + uint32_t inlen, + uint8_t* out, + uint32_t dim_x, + uint32_t dim_y, + uint32_t block_x, + uint32_t block_y) +{ + const unsigned int dim_z = 1; + const unsigned int block_z = 1; + + unsigned int xblocks = (dim_x + block_x - 1) / block_x; + unsigned int yblocks = (dim_y + block_y - 1) / block_y; + unsigned int zblocks = (dim_z + block_z - 1) / block_z; + + int row_blocks = xblocks; + int plane_blocks = xblocks * yblocks; + + // Check we have enough output space (16 bytes per block) + size_t size_needed = xblocks * yblocks * zblocks * 16; + if (inlen < size_needed) { + return ASTCENC_ERR_OUT_OF_MEM; + } + + auto bsd = aligned_malloc(sizeof(block_size_descriptor), ASTCENC_VECALIGN); + init_block_size_descriptor(block_x, block_y, 1, false, 0 /*unused for decompress*/, 0, *bsd); + + image_block blk; + void* data[1] = {out}; + astcenc_image image_out{dim_x, dim_y, 1, ASTCENC_TYPE_U8, data}; + const auto total_blocks = zblocks * yblocks * xblocks; + const astcenc_swizzle swz_decode{ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A}; + for (unsigned int i = 0; i < total_blocks; ++i) { + // Decode i into x, y, z block indices + int z = i / plane_blocks; + unsigned int rem = i - (z * plane_blocks); + int y = rem / row_blocks; + int x = rem - (y * row_blocks); + + unsigned int offset = (((z * yblocks + y) * xblocks) + x) * 16; + const uint8_t* bp = in + offset; + physical_compressed_block pcb = *(const physical_compressed_block*) bp; + symbolic_compressed_block scb; + + physical_to_symbolic(*bsd, pcb, scb); + + decompress_symbolic_block(ASTCENC_PRF_LDR, *bsd, x * block_x, y * block_y, z * block_z, scb, blk); + + store_image_block(image_out, blk, *bsd, x * block_x, y * block_y, z * block_z, swz_decode); + } + + aligned_free(bsd); + + return ASTCENC_SUCCESS; +} +#endif diff --git a/core/base/posix_io.h b/core/base/posix_io.h index c7d2bf0da812..3e988d1f2e78 100644 --- a/core/base/posix_io.h +++ b/core/base/posix_io.h @@ -42,7 +42,7 @@ extern "C" int _ftruncate(int fd, int64_t size); # define posix_write ::write # define posix_fd2fh(fd) (fd) # define posix_ftruncate ::ftruncate -# if defined(__APPLE__) +# if defined(__APPLE__) || defined(__EMSCRIPTEN__) # define posix_lseek64 ::lseek # define posix_ftruncate64 ::ftruncate # else diff --git a/core/network/CMakeLists.txt b/core/network/CMakeLists.txt index cdd7792965cd..f2bd3681403e 100644 --- a/core/network/CMakeLists.txt +++ b/core/network/CMakeLists.txt @@ -1,18 +1,38 @@ -set(_AX_NETWORK_HEADER - network/Downloader-curl.h - network/IDownloaderImpl.h - network/Downloader.h - network/Uri.h - network/HttpClient.h - network/HttpResponse.h - network/HttpRequest.h - network/HttpCookie.h +if(EMSCRIPTEN) + set(_AX_NETWORK_HEADER + network/Downloader-emscripten.h + network/IDownloaderImpl.h + network/Downloader.h + network/Uri.h + network/HttpClient.h + network/HttpResponse.h + network/HttpRequest.h ) -set(_AX_NETWORK_SRC - network/HttpClient.cpp - network/Downloader.cpp - network/Downloader-curl.cpp - network/HttpCookie.cpp - network/Uri.cpp + set(_AX_NETWORK_SRC + network/HttpClient-emscripten.cpp + network/Downloader.cpp + network/Downloader-emscripten.cpp + network/HttpCookie.cpp + network/Uri.cpp ) +else() + set(_AX_NETWORK_HEADER + network/Downloader-curl.h + network/IDownloaderImpl.h + network/Downloader.h + network/Uri.h + network/HttpClient.h + network/HttpResponse.h + network/HttpRequest.h + network/HttpCookie.h + ) + + set(_AX_NETWORK_SRC + network/HttpClient.cpp + network/Downloader.cpp + network/Downloader-curl.cpp + network/HttpCookie.cpp + network/Uri.cpp + ) +endif() diff --git a/core/network/Downloader-emscripten.cpp b/core/network/Downloader-emscripten.cpp new file mode 100644 index 000000000000..f36ec6d34076 --- /dev/null +++ b/core/network/Downloader-emscripten.cpp @@ -0,0 +1,265 @@ +/**************************************************************************** + Copyright (c) 2015-2016 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + + +#include "platform/FileUtils.h" +#include "network/Downloader-emscripten.h" +#include + +using namespace std; + +namespace ax { namespace network { + + static int sDownloaderCounter; + + struct DownloadTaskEmscripten : public IDownloadTask + { + explicit DownloadTaskEmscripten(unsigned int id_) + :id(id_) + ,bytesReceived(0) + ,fetch(NULL) + { + DLLOG("Construct DownloadTaskEmscripten: %p", this); + } + virtual ~DownloadTaskEmscripten() + { + DLLOG("Destruct DownloadTaskEmscripten: %p", this); + } + + int bytesReceived; + unsigned int id; + emscripten_fetch_t * fetch; + shared_ptr task; // reference to DownloadTask, when task finish, release + }; + + DownloaderEmscripten::DownloaderEmscripten(const DownloaderHints& hints) + : _id(++sDownloaderCounter) + , hints(hints) + { + DLLOG("Construct DownloaderEmscripten: %p", this); + } + + DownloaderEmscripten::~DownloaderEmscripten() + { + DLLOG("Destruct DownloaderEmscripten: %p", this); + for (auto iter = _taskMap.begin(); iter != _taskMap.end(); ++iter) + { + if(iter->second->fetch != NULL) { + emscripten_fetch_close(iter->second->fetch); + } + } + } + + void DownloaderEmscripten::startTask(std::shared_ptr& task) + { + emscripten_fetch_attr_t attr; + emscripten_fetch_attr_init(&attr); + strcpy(attr.requestMethod, "GET"); + attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY | EMSCRIPTEN_FETCH_PERSIST_FILE; + if(task->storagePath.length() == 0) { + attr.onsuccess = DownloaderEmscripten::onDataLoad; + }else{ + attr.onsuccess = DownloaderEmscripten::onLoad; + } + attr.onprogress = DownloaderEmscripten::onProgress; + attr.onerror = DownloaderEmscripten::onError; + attr.timeoutMSecs = this->hints.timeoutInSeconds * 1000; + emscripten_fetch_t *fetch = emscripten_fetch(&attr, task->requestURL.c_str()); + fetch->userData = this; + + DownloadTaskEmscripten *coTask = new DownloadTaskEmscripten(fetch->id); + coTask->task = task; + + DLLOG("DownloaderEmscripten::createCoTask id: %d", coTask->id); + _taskMap.insert(make_pair(coTask->id, coTask)); + } + + void DownloaderEmscripten::onDataLoad(emscripten_fetch_t *fetch) + { + unsigned int taskId = fetch->id; + uint64_t size = fetch->numBytes; + DLLOG("DownloaderEmscripten::onDataLoad(taskId: %d, size: %d)", taskId, size); + DownloaderEmscripten* downloader = reinterpret_cast(fetch->userData); + auto iter = downloader->_taskMap.find(taskId); + if (downloader->_taskMap.end() == iter) + { + DLLOG("DownloaderEmscripten::onDataLoad can't find task with id: %i, size: %i", taskId, size); + return; + } + DownloadTaskEmscripten *coTask = iter->second; + std::vector buf(reinterpret_cast(fetch->data), reinterpret_cast(fetch->data) + size); + emscripten_fetch_close(fetch); + coTask->fetch = fetch = NULL; + + downloader->_taskMap.erase(iter); + downloader->onTaskFinish(*coTask->task, + DownloadTask::ERROR_NO_ERROR, + 0, + "", + buf + ); + + coTask->task.reset(); + } + + void DownloaderEmscripten::onLoad(emscripten_fetch_t *fetch) + { + unsigned int taskId = fetch->id; + uint64_t size = fetch->numBytes; + DLLOG("DownloaderEmscripten::onLoad(taskId: %i, size: %i)", taskId, size); + DownloaderEmscripten* downloader = reinterpret_cast(fetch->userData); + auto iter = downloader->_taskMap.find(taskId); + if (downloader->_taskMap.end() == iter) + { + DLLOG("DownloaderEmscripten::onLoad can't find task with id: %i, size: %i", taskId, size); + return; + } + DownloadTaskEmscripten *coTask = iter->second; + vector buf; + downloader->_taskMap.erase(iter); + + string storagePath = coTask->task->storagePath; + int errCode = DownloadTask::ERROR_NO_ERROR; + int errCodeInternal = 0; + string errDescription; + do + { + auto util = FileUtils::getInstance(); + if (util->isFileExist(storagePath)) + { + if (false == util->removeFile(storagePath)) + { + errCode = DownloadTask::ERROR_REMOVE_FILE_FAILED; + errCodeInternal = 0; + errDescription = "Can't remove old file: "; + errDescription.append(storagePath); + break; + } + } + + string dir; + size_t found = storagePath.find_last_of("/\\"); + if (found == string::npos) + { + errCode = DownloadTask::ERROR_INVALID_PARAMS; + errCodeInternal = 0; + errDescription = "Can't find dirname in storagePath."; + break; + } + + // ensure directory is exist + dir = storagePath.substr(0, found + 1); + if (false == util->isDirectoryExist(dir)) + { + if (false == util->createDirectory(dir)) + { + errCode = DownloadTask::ERROR_CREATE_DIR_FAILED; + errCodeInternal = 0; + errDescription = "Can't create dir:"; + errDescription.append(dir); + break; + } + } + + // open file + auto _fs = util->openFileStream(storagePath, FileStream::Mode::READ); + if (!_fs) + { + errCode = DownloadTask::ERROR_OPEN_FILE_FAILED; + errCodeInternal = 0; + errDescription = "Can't open file:"; + errDescription.append(storagePath); + break; + } + + _fs->write(fetch->data, static_cast(size)); + + } while (0); + emscripten_fetch_close(fetch); + coTask->fetch = fetch = NULL; + + downloader->onTaskFinish(*coTask->task, + errCode, + errCodeInternal, + errDescription, + buf + ); + coTask->task.reset(); + } + + void DownloaderEmscripten::onProgress(emscripten_fetch_t *fetch) + { + uint64_t dlTotal = fetch->totalBytes; + uint64_t dlNow = fetch->dataOffset; + unsigned int taskId = fetch->id; + DLLOG("DownloaderEmscripten::onProgress(taskId: %i, dlnow: %d, dltotal: %d)", taskId, dlNow, dlTotal); + DownloaderEmscripten* downloader = reinterpret_cast(fetch->userData); + auto iter = downloader->_taskMap.find(taskId); + if (downloader->_taskMap.end() == iter) + { + DLLOG("DownloaderEmscripten::onProgress can't find task with id: %i", taskId); + return; + } + + if (dlTotal == 0) { + DLLOG("DownloaderEmscripten::onProgress dlTotal unknown, usually caused by unknown content-length header %i", taskId); + return; + } + + DownloadTaskEmscripten *coTask = iter->second; + function transferDataToBuffer; // just a placeholder + // int dl = dlNow - coTask->bytesReceived; + coTask->bytesReceived = dlNow; + downloader->onTaskProgress(*coTask->task, transferDataToBuffer); + } + + void DownloaderEmscripten::onError(emscripten_fetch_t *fetch) + { + unsigned int taskId = fetch->id; + DLLOG("DownloaderEmscripten::onLoad(taskId: %i)", taskId); + DownloaderEmscripten* downloader = reinterpret_cast(fetch->userData); + auto iter = downloader->_taskMap.find(taskId); + if (downloader->_taskMap.end() == iter) + { + emscripten_fetch_close(fetch); + DLLOG("DownloaderEmscripten::onLoad can't find task with id: %i", taskId); + return; + } + DownloadTaskEmscripten *coTask = iter->second; + vector buf; + downloader->_taskMap.erase(iter); + downloader->onTaskFinish(*coTask->task, + DownloadTask::ERROR_IMPL_INTERNAL, + fetch->status, + fetch->statusText, + buf + ); + + emscripten_fetch_close(fetch); + coTask->fetch = fetch = NULL; + coTask->task.reset(); + } + } +} // namespace cocos2d::network diff --git a/core/network/Downloader-emscripten.h b/core/network/Downloader-emscripten.h new file mode 100644 index 000000000000..2388b4e14911 --- /dev/null +++ b/core/network/Downloader-emscripten.h @@ -0,0 +1,66 @@ +/**************************************************************************** + Copyright (c) 2015-2016 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +#pragma once + +#include "network/IDownloaderImpl.h" +#include "network/Downloader.h" +#include + +namespace ax { + class Scheduler; +} + +namespace ax { namespace network +{ + class DownloadTaskEmscripten; + + class DownloaderEmscripten : public IDownloaderImpl + { + public: + DownloaderEmscripten(const DownloaderHints& hints); + virtual ~DownloaderEmscripten(); + + // virtual IDownloadTask *createCoTask(std::shared_ptr& task) override; + virtual void startTask(std::shared_ptr& task) override; + + protected: + int _id; + + DownloaderHints hints; + + std::unordered_map _taskMap; + + static void onError(emscripten_fetch_t *fetch); + + static void onProgress(emscripten_fetch_t *fetch); + + static void onDataLoad(emscripten_fetch_t *fetch); + + static void onLoad(emscripten_fetch_t *fetch); + }; + +}} // namespace cocos2d::network + diff --git a/core/network/Downloader.cpp b/core/network/Downloader.cpp index 650513a1db64..c2103107bf49 100644 --- a/core/network/Downloader.cpp +++ b/core/network/Downloader.cpp @@ -26,8 +26,13 @@ #include "network/Downloader.h" +#if EMSCRIPTEN +#include "network/Downloader-emscripten.h" +#define DownloaderImpl DownloaderEmscripten +#else #include "network/Downloader-curl.h" #define DownloaderImpl DownloaderCURL +#endif NS_AX_BEGIN diff --git a/core/network/HttpClient-emscripten.cpp b/core/network/HttpClient-emscripten.cpp new file mode 100644 index 000000000000..6fdeb6a44a98 --- /dev/null +++ b/core/network/HttpClient-emscripten.cpp @@ -0,0 +1,346 @@ +/**************************************************************************** + Copyright (c) 2012 greathqy + Copyright (c) 2012 cocos2d-x.org + Copyright (c) 2013-2016 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +#include "network/HttpClient-emscripten.h" +#include +#include "base/Director.h" +#include "platform/FileUtils.h" + +#if EMSCRIPTEN +#include +#include +#endif + +NS_AX_BEGIN + +namespace network +{ + + struct fetchUserData + { + bool isAlone; + HttpResponse *response; + }; + + static HttpClient *_httpClient = nullptr; // pointer to singleton + + // HttpClient implementation + HttpClient *HttpClient::getInstance() + { + if (_httpClient == nullptr) + { + _httpClient = new (std::nothrow) HttpClient(); + } + + return _httpClient; + } + + HttpClient::HttpClient() + : _timeoutForConnect(30) + , _timeoutForRead(60) + , _threadCount(0) + , _cookie(nullptr) + , _clearRequestPredicate(nullptr) + , _clearResponsePredicate(nullptr) + { + AXLOG("In the constructor of HttpClient!"); + increaseThreadCount(); + } + + HttpClient::~HttpClient() + { + AXLOG("HttpClient destructor"); + } + + void HttpClient::destroyInstance() + { + if (nullptr == _httpClient) + { + AXLOG("HttpClient singleton is nullptr"); + } + + auto thiz = _httpClient; + _httpClient = nullptr; + + Vector requestQueue = thiz->_requestQueue; + for (auto it = requestQueue.begin(); it != requestQueue.end();) + { + (*it)->release(); + it = requestQueue.erase(it); + } + + thiz->decreaseThreadCountAndMayDeleteThis(); + } + + void HttpClient::enableCookies(const char *cookieFile) + { + if (cookieFile) + { + _cookieFilename = std::string(cookieFile); + } + else + { + _cookieFilename = (FileUtils::getInstance()->getWritablePath() + "cookieFile.txt"); + } + } + + void HttpClient::setSSLVerification(std::string_view caFile) + { + AXLOG("HttpClient::setSSLVerification not required on Emscripten"); + // _sslCaFilename = caFile; + } + + //Add a get task to queue + void HttpClient::send(HttpRequest *request) + { + if (!request) + return; + + request->retain(); + + if (_threadCount <= 1) + { + increaseThreadCount(); + HttpResponse *response = new (std::nothrow) HttpResponse(request); + processResponse(response, false); + } + else + { + _requestQueue.pushBack(request); + } + } + + void HttpClient::sendImmediate(HttpRequest *request) + { + if (!request) + return; + + request->retain(); + HttpResponse *response = new (std::nothrow) HttpResponse(request); + processResponse(response, true); + } + + // Process Response + void HttpClient::processResponse(HttpResponse *response, bool isAlone) + { + // copy cookie back to document.cookie in case it is changed + std::string_view cookieFilename = HttpClient::getInstance()->getCookieFilename(); + if (!cookieFilename.empty()) + { + EM_ASM_ARGS({ + document.cookie = FS.readFile(UTF8ToString($0)); + }, cookieFilename.data()); + } + + auto request = response->getHttpRequest(); + emscripten_fetch_attr_t attr; + emscripten_fetch_attr_init(&attr); + + // set http method + switch (request->getRequestType()) + { + case HttpRequest::Type::GET: + strcpy(attr.requestMethod, "GET"); + break; + + case HttpRequest::Type::POST: + strcpy(attr.requestMethod, "POST"); + break; + + case HttpRequest::Type::PUT: + strcpy(attr.requestMethod, "PUT"); + break; + + case HttpRequest::Type::DELETE: + strcpy(attr.requestMethod, "DELETE"); + break; + + default: + AXASSERT(false, "CCHttpClient: unknown request type, only GET, POST, PUT or DELETE is supported"); + break; + } + + attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; + + auto userData = new (std::nothrow) fetchUserData(); + userData->isAlone = isAlone; + userData->response = response; + + // set header + std::vector headers; + for (auto &header : request->getHeaders()) + { + size_t pos = header.find(":"); + if (pos != std::string::npos) + { + std::string key = header.substr(0, pos); + std::string value = header.substr(pos + 1); + headers.push_back(key); + headers.push_back(value); + } + } + + std::vector headersCharptr; + headersCharptr.reserve(headers.size() + 1); + for (auto &header : headers) + { + headersCharptr.push_back(header.c_str()); + } + headersCharptr.push_back(0); + attr.requestHeaders = &headersCharptr[0]; + + // post data + if (request->getRequestDataSize()) + { + attr.requestData = request->getRequestData(); + attr.requestDataSize = request->getRequestDataSize(); + } + + attr.onsuccess = onRequestComplete; + attr.onerror = onRequestComplete; + attr.timeoutMSecs = (HttpClient::getInstance()->getTimeoutForConnect() + HttpClient::getInstance()->getTimeoutForRead()) * 1000; + std::string_view url = response->getHttpRequest()->getUrl(); + emscripten_fetch_t *fetch = emscripten_fetch(&attr, url.data()); + fetch->userData = userData; + } + + void HttpClient::onRequestComplete(emscripten_fetch_t *fetch) + { + fetchUserData *userData = reinterpret_cast(fetch->userData); + HttpResponse *response = userData->response; + HttpRequest *request = response->getHttpRequest(); + + // get response + response->setResponseCode(fetch->status); + // response->setErrorBuffer(fetch->statusText); + response->getResponseData()->assign(reinterpret_cast(fetch->data), reinterpret_cast(fetch->data) + fetch->numBytes); + emscripten_fetch_close(fetch); + + // write cookie back + auto cookieFilename = HttpClient::getInstance()->getCookieFilename(); + if (!cookieFilename.empty()) + { + EM_ASM_ARGS({ + FS.writeFile(UTF8ToString($0), document.cookie); + }, cookieFilename.data()); + } + + if (_httpClient) + { + // call back + const ccHttpRequestCallback &callback = request->getCallback(); + if (callback) + callback(HttpClient::getInstance(), response); + + // call next request + if (!userData->isAlone) + { + Vector requestQueue = _httpClient->_requestQueue; + if (!requestQueue.empty()) + { + HttpRequest *request = requestQueue.at(0); + requestQueue.erase(0); + + HttpResponse *response = new (std::nothrow) HttpResponse(request); + _httpClient->processResponse(response, false); + } + else + { + _httpClient->decreaseThreadCountAndMayDeleteThis(); + } + } + } + + response->release(); + request->release(); + delete userData; + } + + void HttpClient::clearResponseAndRequestQueue() + { + for (auto it = _requestQueue.begin(); it != _requestQueue.end();) + { + if (!_clearRequestPredicate || + _clearRequestPredicate((*it))) + { + (*it)->release(); + it = _requestQueue.erase(it); + } + else + { + it++; + } + } + } + + void HttpClient::increaseThreadCount() + { + ++_threadCount; + } + + void HttpClient::decreaseThreadCountAndMayDeleteThis() + { + --_threadCount; + if (0 == _threadCount) + { + delete this; + } + } + + void HttpClient::setTimeoutForConnect(int value) + { + _timeoutForConnect = value; + } + + int HttpClient::getTimeoutForConnect() + { + return _timeoutForConnect; + } + + void HttpClient::setTimeoutForRead(int value) + { + _timeoutForRead = value; + } + + int HttpClient::getTimeoutForRead() + { + return _timeoutForRead; + } + + std::string_view HttpClient::getCookieFilename() + { + return _cookieFilename; + } + + std::string_view HttpClient::getSSLVerification() + { + return _sslCaFilename; + } + +} // namespace network + +NS_AX_END diff --git a/core/network/HttpClient-emscripten.h b/core/network/HttpClient-emscripten.h new file mode 100644 index 000000000000..4d2d54de0ba7 --- /dev/null +++ b/core/network/HttpClient-emscripten.h @@ -0,0 +1,210 @@ +/**************************************************************************** + Copyright (c) 2012 greathqy + Copyright (c) 2012 cocos2d-x.org + Copyright (c) 2013-2016 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +#pragma once + +#include "base/Vector.h" +#include "network/HttpRequest.h" +#include "network/HttpResponse.h" +#include "network/HttpCookie.h" + +struct emscripten_fetch_t; + +/** + * @addtogroup network + * @{ + */ + +NS_AX_BEGIN + +namespace network { + + + +/** Singleton that handles asynchronous http requests. + * + * Once the request completed, a callback will issued in main thread when it provided during make request. + * + * @lua NA + */ +class AX_DLL HttpClient +{ +public: + /** + * The buffer size of _responseMessage + */ + static const int RESPONSE_BUFFER_SIZE = 256; + + /** + * Get instance of HttpClient. + * + * @return the instance of HttpClient. + */ + static HttpClient* getInstance(); + + /** + * Release the instance of HttpClient. + */ + static void destroyInstance(); + + /** + * Enable cookie support. + * + * @param [nullable] cookieFile the filepath of cookie file. + */ + void enableCookies(const char* cookieFile); + + /** + * Get the cookie filename + * + * @return the cookie filename + */ + std::string_view getCookieFilename(); + + /** + * Set root certificate path for SSL verification. + * + * @param caFile a full path of root certificate.if it is empty, SSL verification is disabled. + */ + void setSSLVerification(std::string_view caFile); + + /** + * Get the ssl CA filename + * + * @return the ssl CA filename + */ + std::string_view getSSLVerification(); + + /** + * Add a get request to task queue + * + * @param request a HttpRequest object, which includes url, response callback etc. + please make sure request->_requestData is clear before calling "send" here. + */ + void send(HttpRequest* request); + + /** + * Immediate send a request + * + * @param request a HttpRequest object, which includes url, response callback etc. + please make sure request->_requestData is clear before calling "sendImmediate" here. + */ + void sendImmediate(HttpRequest* request); + + /** + * Set the timeout value for connecting. + * + * @param value the timeout value for connecting. + */ + void setTimeoutForConnect(int value); + + /** + * Get the timeout value for connecting. + * + * @return int the timeout value for connecting. + */ + int getTimeoutForConnect(); + + /** + * Set the timeout value for reading. + * + * @param value the timeout value for reading. + */ + void setTimeoutForRead(int value); + + /** + * Get the timeout value for reading. + * + * @return int the timeout value for reading. + */ + int getTimeoutForRead(); + + HttpCookie* getCookie() const {return _cookie; } + + typedef std::function ClearRequestPredicate; + typedef std::function ClearResponsePredicate; + + /** + * Clears the pending http responses and http requests + * If defined, the method uses the ClearRequestPredicate and ClearResponsePredicate + * to check for each request/response which to delete + */ + void clearResponseAndRequestQueue(); + + void clearResponseQueue() { clearResponseAndRequestQueue(); } + + /** + * Sets a predicate function that is going to be called to determine if we proceed + * each of the pending requests + * + * @param predicate function that will be called + */ + void setClearRequestPredicate(ClearRequestPredicate predicate) { _clearRequestPredicate = predicate; } + + /** + Sets a predicate function that is going to be called to determine if we proceed + * each of the pending requests + * + * @param cb predicate function that will be called + */ + void setClearResponsePredicate(ClearResponsePredicate predicate) { _clearResponsePredicate = predicate; } + + +private: + HttpClient(); + virtual ~HttpClient(); + + void processResponse(HttpResponse* response, bool isAlone); + static void onRequestComplete(emscripten_fetch_t *fetch); + void increaseThreadCount(); + void decreaseThreadCountAndMayDeleteThis(); + +private: + int _timeoutForConnect; + + int _timeoutForRead; + + int _threadCount; + + Vector _requestQueue; + + std::string _cookieFilename; + + std::string _sslCaFilename; + + HttpCookie* _cookie; + + ClearRequestPredicate _clearRequestPredicate; + ClearResponsePredicate _clearResponsePredicate; +}; + +} // namespace network + +NS_AX_END + +// end group +/// @} \ No newline at end of file diff --git a/core/network/HttpClient.cpp b/core/network/HttpClient.cpp index ae68cb2297ca..0730579522ae 100644 --- a/core/network/HttpClient.cpp +++ b/core/network/HttpClient.cpp @@ -26,6 +26,8 @@ THE SOFTWARE. ****************************************************************************/ +#if !defined(__EMSCRIPTEN__) + #include "network/HttpClient.h" #include #include "base/Utils.h" @@ -191,16 +193,15 @@ yasio::io_service* HttpClient::getInternalService() return _service; } -bool HttpClient::send(HttpRequest* request) +void HttpClient::send(HttpRequest* request) { if (!request) - return false; + return; auto response = new HttpResponse(request); response->setLocation(request->getUrl(), false); processResponse(response, -1); response->release(); - return true; } int HttpClient::tryTakeAvailChannel() @@ -522,3 +523,5 @@ std::string_view HttpClient::getSSLVerification() } // namespace network NS_AX_END + +#endif diff --git a/core/network/HttpClient.h b/core/network/HttpClient.h index 67686f9ec109..d201af50c506 100644 --- a/core/network/HttpClient.h +++ b/core/network/HttpClient.h @@ -26,12 +26,13 @@ THE SOFTWARE. ****************************************************************************/ -#ifndef __CCHTTPCLIENT_H__ -#define __CCHTTPCLIENT_H__ +#pragma once +#if !defined(__EMSCRIPTEN__) #include #include #include + #include "base/Scheduler.h" #include "network/HttpRequest.h" #include "network/HttpResponse.h" @@ -117,7 +118,8 @@ class AX_DLL HttpClient * c. other content type, please see: * https://stackoverflow.com/questions/23714383/what-are-all-the-possible-values-for-http-content-type-header */ - bool send(HttpRequest* request); + void send(HttpRequest* request); + void sendImmediate(HttpRequest* request){ this->send(request); } /** * Set the timeout value for connecting. @@ -250,4 +252,8 @@ NS_AX_END // end group /// @} -#endif //__CCHTTPCLIENT_H__ +#else + +#include "network/HttpClient-emscripten.h" + +#endif diff --git a/core/network/HttpCookie.cpp b/core/network/HttpCookie.cpp index 40385f076699..31ad73737368 100644 --- a/core/network/HttpCookie.cpp +++ b/core/network/HttpCookie.cpp @@ -257,8 +257,7 @@ bool HttpCookie::updateOrAddCookie(std::string_view cookie, const Uri& uri) void HttpCookie::writeFile() { - FILE* out; - out = fopen(_cookieFileName.c_str(), "wb"); + auto out = fopen(_cookieFileName.c_str(), "wb"); fputs( "# Netscape HTTP Cookie File\n" "# http://curl.haxx.se/docs/http-cookies.html\n" diff --git a/core/network/HttpResponse.h b/core/network/HttpResponse.h index aa1c49029df8..53645a9d0f9a 100644 --- a/core/network/HttpResponse.h +++ b/core/network/HttpResponse.h @@ -130,6 +130,8 @@ class AX_DLL HttpResponse : public ax::Ref const ResponseHeaderMap& getResponseHeaders() const { return _responseHeaders; } private: + void setResponseCode(int value) { _responseCode = value; } + void updateInternalCode(int value) { if (_internalCode == 0) diff --git a/core/platform/Application.h b/core/platform/Application.h index 1bcb73a1444e..aa1cb1623a1f 100644 --- a/core/platform/Application.h +++ b/core/platform/Application.h @@ -42,6 +42,8 @@ THE SOFTWARE. # include "platform/winrt/Application-winrt.h" #elif AX_TARGET_PLATFORM == AX_PLATFORM_LINUX # include "platform/linux/Application-linux.h" +#elif AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN +# include "platform/emscripten/Application-emscripten.h" #endif /// @endcond diff --git a/core/platform/ApplicationProtocol.h b/core/platform/ApplicationProtocol.h index ad15d001ab2e..a7c12f5d4419 100644 --- a/core/platform/ApplicationProtocol.h +++ b/core/platform/ApplicationProtocol.h @@ -52,6 +52,7 @@ class AX_DLL ApplicationProtocol Linux, /**< Linux */ macOS, /**< macOS */ Android, /**< Android */ + Emscripten, /**< Emscripten */ iOS, /**< Apple iOS */ }; diff --git a/core/platform/CMakeLists.txt b/core/platform/CMakeLists.txt index 1e78aadd289a..b3c8a09b818f 100644 --- a/core/platform/CMakeLists.txt +++ b/core/platform/CMakeLists.txt @@ -145,6 +145,26 @@ elseif(LINUX) platform/linux/Device-linux.cpp platform/desktop/GLViewImpl-desktop.cpp ) +elseif(EMSCRIPTEN) + include_directories( + PUBLIC "${_AX_ROOT}/thirdparty/angle/include" + PUBLIC "${_AX_ROOT}/core" + ) + set(_AX_PLATFORM_SPECIFIC_HEADER + platform/emscripten/Application-emscripten.h + platform/emscripten/GL-emscripten.h + platform/emscripten/StdC-emscripten.h + platform/emscripten/FileUtils-emscripten.h + platform/emscripten/PlatformDefine-emscripten.h + platform/emscripten/GLViewImpl-emscripten.h + ) + set(_AX_PLATFORM_SPECIFIC_SRC + platform/emscripten/FileUtils-emscripten.cpp + platform/emscripten/Common-emscripten.cpp + platform/emscripten/Application-emscripten.cpp + platform/emscripten/Device-emscripten.cpp + platform/emscripten/GLViewImpl-emscripten.cpp + ) endif() set(_AX_PLATFORM_HEADER diff --git a/core/platform/GL.h b/core/platform/GL.h index 6b4087a6415d..1ba6c9d0367f 100644 --- a/core/platform/GL.h +++ b/core/platform/GL.h @@ -39,6 +39,8 @@ THE SOFTWARE. # include "platform/winrt/GL-winrt.h" #elif AX_TARGET_PLATFORM == AX_PLATFORM_LINUX # include "platform/linux/GL-linux.h" +#elif AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN +# include "platform/emscripten/GL-emscripten.h" #elif AX_TARGET_PLATFORM == AX_PLATFORM_IOS # if AX_USE_ANGLE # include "platform/ios/GL-ios.h" diff --git a/core/platform/PlatformConfig.h b/core/platform/PlatformConfig.h index 858ec2de8a23..aa9b007c54ac 100644 --- a/core/platform/PlatformConfig.h +++ b/core/platform/PlatformConfig.h @@ -50,6 +50,7 @@ THE SOFTWARE. #define AX_PLATFORM_WIN32 3 #define AX_PLATFORM_LINUX 5 #define AX_PLATFORM_MAC 8 +#define AX_PLATFORM_EMSCRIPTEN 10 #define AX_PLATFORM_WINRT 13 // Determine target platform by compile environment macro. @@ -87,6 +88,11 @@ THE SOFTWARE. # define AX_TARGET_PLATFORM AX_PLATFORM_LINUX #endif +#if defined(EMSCRIPTEN) + #undef AX_TARGET_PLATFORM + #define AX_TARGET_PLATFORM AX_PLATFORM_EMSCRIPTEN +#endif + // android, override linux #if defined(__ANDROID__) || defined(ANDROID) # undef AX_TARGET_PLATFORM @@ -120,7 +126,7 @@ Linux: Desktop GL/Vulkan # define AX_USE_ANGLE 0 #endif -#if ((AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID) || (AX_TARGET_PLATFORM == AX_PLATFORM_IOS) || (AX_TARGET_PLATFORM == AX_PLATFORM_WINRT)) +#if ((AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID) || (AX_TARGET_PLATFORM == AX_PLATFORM_IOS) || (AX_TARGET_PLATFORM == AX_PLATFORM_WINRT) || (AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN)) # define AX_PLATFORM_MOBILE #else # define AX_PLATFORM_PC @@ -153,6 +159,9 @@ Linux: Desktop GL/Vulkan # define AX_USE_ANGLE 1 # endif # define AX_USE_GLES +#elif (AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN) + #define AX_USE_GL + #define AX_USE_GLES #else # define AX_USE_GL #endif diff --git a/core/platform/PlatformDefine.h b/core/platform/PlatformDefine.h index 3d6b23f1ccb6..a1bbb83a6d62 100644 --- a/core/platform/PlatformDefine.h +++ b/core/platform/PlatformDefine.h @@ -41,4 +41,6 @@ THE SOFTWARE. # include "platform/linux/PlatformDefine-linux.h" #elif AX_TARGET_PLATFORM == AX_PLATFORM_WINRT # include "platform/winrt/PlatformDefine-winrt.h" +#elif AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN +# include "platform/emscripten/PlatformDefine-emscripten.h" #endif diff --git a/core/platform/desktop/GLViewImpl-desktop.cpp b/core/platform/desktop/GLViewImpl-desktop.cpp index c01a3ed4564b..26b79cc1db1e 100644 --- a/core/platform/desktop/GLViewImpl-desktop.cpp +++ b/core/platform/desktop/GLViewImpl-desktop.cpp @@ -74,7 +74,11 @@ THE SOFTWARE. # endif #endif // #if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC) -#include "glfw3native.h" +#if (AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN) + +#else + #include +#endif #if defined(_WIN32) # include "glfw3ext.h" @@ -553,11 +557,12 @@ bool GLViewImpl::initWithRect(std::string_view viewName, const ax::Rect& rect, f #endif #if defined(AX_USE_GL) - glfwSwapInterval(_glContextAttrs.vsync ? 1 : 0); - // check OpenGL version at first const GLubyte* glVersion = glGetString(GL_VERSION); +#ifndef EMSCRIPTEN + glfwSwapInterval(_glContextAttrs.vsync ? 1 : 0); + if (utils::atof((const char*)glVersion) < 1.5 && nullptr == strstr((const char*)glVersion, "ANGLE")) { char strComplain[256] = {0}; @@ -568,7 +573,8 @@ bool GLViewImpl::initWithRect(std::string_view viewName, const ax::Rect& rect, f utils::killCurrentProcess(); // kill current process, don't cause crash when driver issue. return false; } - +#endif + if (GL_ARB_vertex_shader && GL_ARB_fragment_shader) ax::print("[GL:%s] Ready for GLSL", glVersion); else diff --git a/core/platform/desktop/GLViewImpl-desktop.h b/core/platform/desktop/GLViewImpl-desktop.h index 09b1fa06507c..eb490e9f4d84 100644 --- a/core/platform/desktop/GLViewImpl-desktop.h +++ b/core/platform/desktop/GLViewImpl-desktop.h @@ -28,7 +28,7 @@ THE SOFTWARE. #include "base/Ref.h" #include "platform/Common.h" #include "platform/GLView.h" -#include "glfw3.h" +#include NS_AX_BEGIN diff --git a/core/platform/emscripten/Application-emscripten.cpp b/core/platform/emscripten/Application-emscripten.cpp new file mode 100644 index 000000000000..749499a14469 --- /dev/null +++ b/core/platform/emscripten/Application-emscripten.cpp @@ -0,0 +1,202 @@ +/**************************************************************************** +Copyright (c) 2011 Laschweinski +Copyright (c) 2013-2016 Chukong Technologies Inc. +Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + +http://www.cocos2d-x.org + +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. +****************************************************************************/ + +#include "platform/PlatformConfig.h" +#if AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN + +#include "platform/emscripten/Application-emscripten.h" +#include +#include +#include +#include "base/Director.h" +#include "base/Utils.h" +#include "platform/FileUtils.h" +#include + +NS_AX_BEGIN + + +// sharedApplication pointer +Application * Application::sm_pSharedApplication = nullptr; + +static long getCurrentMillSecond() { + long lLastTime; + struct timeval stCurrentTime; + + gettimeofday(&stCurrentTime,NULL); + lLastTime = stCurrentTime.tv_sec*1000+stCurrentTime.tv_usec*0.001; // milliseconds + return lLastTime; +} + +Application::Application() +: _animationInterval(1.0f/60.0f*1000.0f) +{ + AX_ASSERT(! sm_pSharedApplication); + sm_pSharedApplication = this; +} + +Application::~Application() +{ + AX_ASSERT(this == sm_pSharedApplication); + sm_pSharedApplication = nullptr; +} + +extern "C" void mainLoopIter(void) +{ + auto director = Director::getInstance(); + auto glview = director->getOpenGLView(); + + director->mainLoop(); + glview->pollEvents(); +} + +int Application::run() +{ + initGLContextAttrs(); + // Initialize instance and cocos2d. + if (! applicationDidFinishLaunching()) + { + return 1; + } + + auto director = Director::getInstance(); + auto glview = director->getOpenGLView(); + + // Retain glview to avoid glview being released in the while loop + glview->retain(); + + emscripten_set_main_loop(&mainLoopIter, 0, 1); + // TODO: ? does these cleanup really run? + /* Only work on Desktop + * Director::mainLoop is really one frame logic + * when we want to close the window, we should call Director::end(); + * then call Director::mainLoop to do release of internal resources + */ + if (glview->isOpenGLReady()) + { + director->end(); + director->mainLoop(); + director = nullptr; + } + glview->release(); + return 0; +} + +void Application::setAnimationInterval(float interval) +{ + _animationInterval = interval*1000.0f; +} + +void Application::setResourceRootPath(const std::string& rootResDir) +{ + _resourceRootPath = rootResDir; + if (_resourceRootPath[_resourceRootPath.length() - 1] != '/') + { + _resourceRootPath += '/'; + } + FileUtils* pFileUtils = FileUtils::getInstance(); + std::vector searchPaths = pFileUtils->getSearchPaths(); + searchPaths.insert(searchPaths.begin(), _resourceRootPath); + pFileUtils->setSearchPaths(searchPaths); +} + +const std::string& Application::getResourceRootPath() +{ + return _resourceRootPath; +} + +Application::Platform Application::getTargetPlatform() +{ + return Platform::Emscripten; +} + +std::string Application::getVersion() +{ + return ""; +} + +bool Application::openURL(std::string_view url) +{ + EM_ASM_ARGS({ + window.open(UTF8ToString($0)); + }, url.data()); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// static member function +////////////////////////////////////////////////////////////////////////// +Application* Application::getInstance() +{ + AX_ASSERT(sm_pSharedApplication); + return sm_pSharedApplication; +} + +// @deprecated Use getInstance() instead +Application* Application::sharedApplication() +{ + return Application::getInstance(); +} + +const char * Application::getCurrentLanguageCode() +{ + static char code[3]={0}; + char pLanguageName[16]; + + EM_ASM_ARGS({ + var lang = localStorage.getItem('localization_language'); + if (lang == null) { + stringToUTF8(window.navigator.language.replace(/-.*/, ""), $0, 16); + } else { + stringToUTF8(lang, $0, 16); + } + }, pLanguageName); + strncpy(code,pLanguageName,2); + code[2]='\0'; + return code; +} + +LanguageType Application::getCurrentLanguage() +{ + char pLanguageName[16]; + + EM_ASM_ARGS({ + var lang = localStorage.getItem('localization_language'); + if (lang == null) { + stringToUTF8(window.navigator.language.replace(/-.*/, ""), $0, 16); + } else { + stringToUTF8(lang, $0, 16); + } + }, pLanguageName); + + return utils::getLanguageTypeByISO2(pLanguageName); +} + +NS_AX_END + +#endif // AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN + diff --git a/core/platform/emscripten/Application-emscripten.h b/core/platform/emscripten/Application-emscripten.h new file mode 100644 index 000000000000..920c1f5e21c8 --- /dev/null +++ b/core/platform/emscripten/Application-emscripten.h @@ -0,0 +1,122 @@ +/**************************************************************************** +Copyright (c) 2011 Laschweinski +Copyright (c) 2013-2016 Chukong Technologies Inc. +Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + +http://www.cocos2d-x.org + +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. +****************************************************************************/ + +#ifndef AXAPLICATION_H_ +#define AXAPLICATION_H_ + +#include "platform/PlatformConfig.h" +#if AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN + +#include "platform/Common.h" +#include "platform/ApplicationProtocol.h" +#include + +NS_AX_BEGIN +class Rect; + +class Application : public ApplicationProtocol +{ +public: + /** + * @js ctor + */ + Application(); + /** + * @js NA + * @lua NA + */ + virtual ~Application(); + + /** + @brief Callback by Director for limit FPS. + @param interval The time, which expressed in second in second, between current frame and next. + */ + virtual void setAnimationInterval(float interval) override; + + /** + @brief Run the message loop. + */ + int run(); + + /** + @brief Get current application instance. + @return Current application instance pointer. + */ + static Application* getInstance(); + + /** @deprecated Use getInstance() instead */ + AX_DEPRECATED_ATTRIBUTE static Application* sharedApplication(); + + /* override functions */ + virtual LanguageType getCurrentLanguage() override; + + /** + @brief Get current language iso 639-1 code + @return Current language iso 639-1 code + */ + virtual const char * getCurrentLanguageCode() override; + + /** + @brief Get application version + */ + virtual std::string getVersion() override; + + /** + @brief Open url in default browser + @param String with url to open. + @return true if the resource located by the URL was successfully opened; otherwise false. + */ + virtual bool openURL(std::string_view url) override; + + + /** + * Sets the Resource root path. + * @deprecated Please use FileUtils::getInstance()->setSearchPaths() instead. + */ + AX_DEPRECATED_ATTRIBUTE void setResourceRootPath(const std::string& rootResDir); + + /** + * Gets the Resource root path. + * @deprecated Please use FileUtils::getInstance()->getSearchPaths() instead. + */ + AX_DEPRECATED_ATTRIBUTE const std::string& getResourceRootPath(); + + /** + @brief Get target platform + */ + virtual Platform getTargetPlatform() override; +protected: + long _animationInterval; //micro second + std::string _resourceRootPath; + + static Application * sm_pSharedApplication; +}; + +NS_AX_END + +#endif // AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN + +#endif /* AXAPLICATION_H_ */ diff --git a/core/platform/emscripten/Common-emscripten.cpp b/core/platform/emscripten/Common-emscripten.cpp new file mode 100644 index 000000000000..7a8f2764530a --- /dev/null +++ b/core/platform/emscripten/Common-emscripten.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +Copyright (c) 2011 Laschweinski +Copyright (c) 2013-2016 Chukong Technologies Inc. +Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + +http://www.cocos2d-x.org + +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. +****************************************************************************/ + +#include "platform/PlatformConfig.h" +#if AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN + +#include "platform/Common.h" +#include "platform/emscripten/StdC-emscripten.h" +#include "base/Console.h" +#include + +NS_AX_BEGIN + +void ccMessageBox(const char * msg, const char * title) +{ + EM_ASM_ARGS({ + window.alert(UTF8ToString($0) + ": " + UTF8ToString($1)); + }, title, msg); +} + +void LuaLog(const char * format) +{ + puts(format); +} + +NS_AX_END + +#endif // AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN diff --git a/core/platform/emscripten/Device-emscripten.cpp b/core/platform/emscripten/Device-emscripten.cpp new file mode 100644 index 000000000000..1b4e707b6607 --- /dev/null +++ b/core/platform/emscripten/Device-emscripten.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +Copyright (c) 2010-2012 cocos2d-x.org +Copyright (c) 2013-2016 Chukong Technologies Inc. +Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + +http://www.cocos2d-x.org + +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. +****************************************************************************/ + +#include "platform/PlatformConfig.h" +#if AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN + +#include "platform/Device.h" +#include "platform/FileUtils.h" + +#include +#include + +NS_AX_BEGIN + +int Device::getDPI() +{ + return 160; +} + +void Device::setAccelerometerEnabled(bool isEnabled) +{ + // TODO: https://emscripten.org/docs/api_reference/html5.h.html? +} + +void Device::setAccelerometerInterval(float interval) +{ + // TODO: https://emscripten.org/docs/api_reference/html5.h.html? +} + +Data Device::getTextureDataForText(std::string_view text, const FontDefinition& textDefinition, TextAlign align, int &width, int &height, bool& hasPremultipliedAlpha) +{ + char color[8]; + sprintf(color, "#%02x%02x%02x", textDefinition._fontFillColor.r, textDefinition._fontFillColor.g, textDefinition._fontFillColor.b); + unsigned char* ptr = (unsigned char*)EM_ASM_INT({ + var lines = UTF8ToString($0).split("\n"); + var linesWidth = []; + var fontName = UTF8ToString($1); + var fontSize = $2; + var lineHeight = $2 * 1.2; // Nothing accurate + var color = UTF8ToString($3); + var dimWidth = $4; + var dimHeight = $5; + var align = $6; + + var canvas = Module.cocosSharedCanvas = Module.cocosSharedCanvas || document.createElement("canvas"); + var context = canvas.getContext("2d"); + context.font = fontSize + "px " + fontName; + + var canvasWidth = dimWidth; + var canvasHeight = dimHeight; + for (var i = 0; i < lines.length; i++) { + var lineWidth = context.measureText(lines[i]).width; + linesWidth[i] = lineWidth; + if (lineWidth > canvasWidth && dimWidth <= 0) { + canvasWidth = lineWidth; + } + }; + + if (dimHeight <= 0) { + canvasHeight = lineHeight * lines.length; + } + + canvas.width = canvasWidth; + canvas.height = canvasHeight; + + context.clearRect(0, 0, canvasWidth, canvasHeight); + context.font = fontSize + "px " + fontName; + context.fillStyle = color; + context.textBaseline = "top"; + + //Vertical top + var offsetY = 0; + if ((align & 0xf0) == 0x30) { + //Vertical center + offsetY = (canvasHeight - lineHeight * lines.length) / 2; + } else if ((align & 0xf0) == 0x20) { + //Vertical bottom + offsetY = canvasHeight - lineHeight * lines.length; + } + + for (var i = 0; i < lines.length; i++) { + //Horizonal left + var offsetX = 0; + if ((align & 0x0f) == 0x03) { + //Horizonal center + offsetX = (canvasWidth - linesWidth[i]) / 2; + } else if ((align & 0x0f) == 0x02) { + //Horizonal right + offsetX = canvasWidth - linesWidth[i]; + } + context.fillText(lines[i], offsetX, offsetY + lineHeight * i); + } + + var data = context.getImageData(0, 0, canvasWidth, canvasHeight).data; + var ptr = _malloc(data.byteLength); // Cocos Data object free it + var buffer= new Uint8Array(Module.HEAPU8.buffer, ptr, data.byteLength); + buffer.set(data); + return ptr; + }, text.data(), textDefinition._fontName.c_str(), textDefinition._fontSize, color, textDefinition._dimensions.width, textDefinition._dimensions.height, align); + + width = EM_ASM_INT({ + return Module.cocosSharedCanvas.width; + }); + height = EM_ASM_INT({ + return Module.cocosSharedCanvas.height; + }); + hasPremultipliedAlpha = true; + + Data ret; + ret.fastSet(ptr, width * height * 4); + return ret; +} + +void Device::setKeepScreenOn(bool value) +{ + // Impossible in browser +} + +void Device::vibrate(float duration) +{ + emscripten_vibrate(duration * 1000); +} + +void Device::prepareImpactFeedbackGenerator(ImpactFeedbackStyle style) {} + +void Device::impactOccurred(ImpactFeedbackStyle style) {} + +void Device::prepareNotificationFeedbackGenerator() {} + +void Device::notificationOccurred(NotificationFeedbackType type) {} + +void Device::prepareSelectionFeedbackGenerator() {} + +void Device::selectionChanged() {} + +NS_AX_END + +#endif // AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN diff --git a/core/platform/emscripten/FileUtils-emscripten.cpp b/core/platform/emscripten/FileUtils-emscripten.cpp new file mode 100644 index 000000000000..5ec3fbf8ed76 --- /dev/null +++ b/core/platform/emscripten/FileUtils-emscripten.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +Copyright (c) 2011 Laschweinski +Copyright (c) 2013-2016 Chukong Technologies Inc. +Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + +http://www.cocos2d-x.org + +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. +****************************************************************************/ + +#include "platform/PlatformConfig.h" +#if AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN + +#include "platform/emscripten/FileUtils-emscripten.h" +#include "platform/emscripten/Application-emscripten.h" +#include "platform/Common.h" +#include "base/Macros.h" +#include "base/UTF8.h" +#include + +#include "yasio/string_view.hpp" + +using namespace std; + +NS_AX_BEGIN + +FileUtils* FileUtils::getInstance() +{ + if (s_sharedFileUtils == nullptr) + { + s_sharedFileUtils = new FileUtilsEmscripten(); + if(!s_sharedFileUtils->init()) + { + delete s_sharedFileUtils; + s_sharedFileUtils = nullptr; + AXLOG("ERROR: Could not init CCFileUtilsEmscripten"); + } + } + return s_sharedFileUtils; +} + +FileUtilsEmscripten::FileUtilsEmscripten() +{} + +bool FileUtilsEmscripten::init() +{ + _defaultResRootPath = "/"; + return FileUtils::init(); +} + +string FileUtilsEmscripten::getWritablePath() const +{ + return "/cocos2dxWritablePath/"; +} + +std::string FileUtilsEmscripten::getNativeWritableAbsolutePath() const +{ + struct stat st; + stat(_writablePath.c_str(), &st); + if (!S_ISDIR(st.st_mode)) + { + mkdir(_writablePath.c_str(), 0744); + } + + return _writablePath; +} + +bool FileUtilsEmscripten::isFileExistInternal(std::string_view path) const +{ + if (path.empty()) + { + return false; + } + + std::string strPath(path); + if (strPath[0] != '/') + { // Not absolute path, add the default root path at the beginning. + if (!cxx20::starts_with(strPath, _defaultResRootPath)) + {// Didn't find "assets/" at the beginning of the path, adding it. + strPath.insert(0, _defaultResRootPath); + } + } + + return access(strPath.c_str(), F_OK) != -1 ? true : false; +} + +NS_AX_END + +#endif // AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN diff --git a/core/platform/emscripten/FileUtils-emscripten.h b/core/platform/emscripten/FileUtils-emscripten.h new file mode 100644 index 000000000000..d33bac1f5e7c --- /dev/null +++ b/core/platform/emscripten/FileUtils-emscripten.h @@ -0,0 +1,67 @@ +/**************************************************************************** +Copyright (c) 2011 Laschweinski +Copyright (c) 2013-2016 Chukong Technologies Inc. +Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + +http://www.cocos2d-x.org + +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. +****************************************************************************/ +#ifndef __AX_FILEUTILS_EMSCRIPTEN_H__ +#define __AX_FILEUTILS_EMSCRIPTEN_H__ + +#include "platform/PlatformConfig.h" +#if AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN + +#include "platform/FileUtils.h" +#include "platform/PlatformMacros.h" +#include "base/Types.h" +#include +#include + +NS_AX_BEGIN + +/** + * @addtogroup platform + * @{ + */ + +//! @brief Helper class to handle file operations +class AX_DLL FileUtilsEmscripten : public FileUtils +{ + friend class FileUtils; +protected: + FileUtilsEmscripten(); +public: + /* override functions */ + bool init() override; + virtual std::string getWritablePath() const override; + std::string getNativeWritableAbsolutePath() const override; +private: + virtual bool isFileExistInternal(std::string_view path) const override; +}; + +// end of platform group +/// @} + +NS_AX_END + +#endif // AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN + +#endif // __AX_FILEUTILS_EMSCRIPTEN_H__ diff --git a/core/platform/emscripten/GL-emscripten.h b/core/platform/emscripten/GL-emscripten.h new file mode 100644 index 000000000000..31843d64e74b --- /dev/null +++ b/core/platform/emscripten/GL-emscripten.h @@ -0,0 +1,170 @@ +/**************************************************************************** +Copyright (c) 2010-2012 cocos2d-x.org +Copyright (c) 2013-2016 Chukong Technologies Inc. +Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +Copyright (c) 2022 Bytedance Inc. + +https://axmolengine.github.io/ + +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. +****************************************************************************/ +#pragma once + +#include "platform/PlatformConfig.h" + +#if AX_USE_GLAD + +# include "glad/gl.h" + +# undef GL_DEPTH_STENCIL +# undef GL_DEPTH24_STENCIL8 +# undef GL_UNSIGNED_INT_24_8 +# undef glClearDepth +# undef glMapBuffer +# undef glUnmapBuffer +# undef glBindVertexArray +# undef glDeleteVertexArrays +# undef glGenVertexArrays +# if defined(GL_VERSION_ES_CM_1_0) +# undef glIsRenderbuffer +# undef glBindRenderbuffer +# undef glDeleteRenderbuffers +# undef glGenRenderbuffers +# undef glRenderbufferStorage +# undef glIsFramebuffer +# undef glBindFramebuffer +# undef glDeleteFramebuffers +# undef glGenFramebuffers +# undef glCheckFramebufferStatus +# undef glFramebufferRenderbuffer +# undef glFramebufferTexture2D +# undef glGetFramebufferAttachmentParameteriv +# undef glGenerateMipmap +# endif + +# define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES +# define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES +# define GL_UNSIGNED_INT_24_8 GL_UNSIGNED_INT_24_8_OES +# define glClearDepth glClearDepthf +# define glMapBuffer glMapBufferOES +# define glUnmapBuffer glUnmapBufferOES +# define glBindVertexArray glBindVertexArrayOES +# define glDeleteVertexArrays glDeleteVertexArraysOES +# define glGenVertexArrays glGenVertexArraysOES +# if defined(GL_VERSION_ES_CM_1_0) +# define glIsRenderbuffer glIsRenderbufferOES +# define glBindRenderbuffer glBindRenderbufferOES +# define glDeleteRenderbuffers glDeleteRenderbuffersOES +# define glGenRenderbuffers glGenRenderbuffersOES +# define glRenderbufferStorage glRenderbufferStorageOES +# define glIsFramebuffer glIsFramebufferOES +# define glBindFramebuffer glBindFramebufferOES +# define glDeleteFramebuffers glDeleteFramebuffersOES +# define glGenFramebuffers glGenFramebuffersOES +# define glCheckFramebufferStatus glCheckFramebufferStatusOES +# define glFramebufferRenderbuffer glFramebufferRenderbufferOES +# define glFramebufferTexture2D glFramebufferTexture2DOES +# define glGetFramebufferAttachmentParameteriv glGetFramebufferAttachmentParameterivOES +# define glGenerateMipmap glGenerateMipmapOES +# endif + +#else + +# define glClearDepth glClearDepthf +# define glDeleteVertexArrays glDeleteVertexArraysOES +# define glGenVertexArrays glGenVertexArraysOES +# define glBindVertexArray glBindVertexArrayOES +# define glMapBuffer glMapBufferOES +# define glUnmapBuffer glUnmapBufferOES + +# define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES +# define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES +# define GL_UNSIGNED_INT_24_8 GL_UNSIGNED_INT_24_8_OES +# define GL_WRITE_ONLY GL_WRITE_ONLY_OES + +// GL_GLEXT_PROTOTYPES isn't defined in glplatform.h on android ndk r7 +// we manually define it here +# include +# ifndef GL_GLEXT_PROTOTYPES +# define GL_GLEXT_PROTOTYPES 1 +# endif + +// normal process +# include +# include +// gl2.h doesn't define GLchar on Android +typedef char GLchar; +// android defines GL_BGRA_EXT but not GL_BRGA +# ifndef GL_BGRA +# define GL_BGRA 0x80E1 +# endif + +// declare here while define in EGLView_android.cpp +extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT; +extern PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOESEXT; +extern PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArraysOESEXT; + +# define glGenVertexArraysOES glGenVertexArraysOESEXT +# define glBindVertexArrayOES glBindVertexArrayOESEXT +# define glDeleteVertexArraysOES glDeleteVertexArraysOESEXT + +/* gles3/gl */ +# if !defined(GL_SRGB8) +# define GL_SRGB8 0x8C41 +# endif + +# if !defined(GL_SRGB8_ALPHA8) +# define GL_SRGB8_ALPHA8 0x8C43 +# endif + +# if !defined(GL_COMPRESSED_RGB8_ETC2) +# define GL_COMPRESSED_RGB8_ETC2 0x9274 +# endif + +# if !defined(GL_COMPRESSED_SRGB8_ETC2) +# define GL_COMPRESSED_SRGB8_ETC2 0x9275 +# endif + +# if !defined(GL_COMPRESSED_RGBA8_ETC2_EAC) +# define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +# endif + +# if !defined(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) +# define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +# endif + +/* gles2/glext */ +# ifndef GL_EXT_texture_compression_s3tc_srgb +# define GL_EXT_texture_compression_s3tc_srgb 1 +# define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +# define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +# define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +# define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +# endif /* GL_EXT_texture_compression_s3tc_srgb */ + +// works on device which support OpenGLES 3.0 +# if !defined(GL_RG) +# define GL_RG 0x8227 +# endif + +# if !defined(GL_RG8) +# define GL_RG8 0x822B +# endif + +#endif diff --git a/core/platform/emscripten/GLViewImpl-emscripten.cpp b/core/platform/emscripten/GLViewImpl-emscripten.cpp new file mode 100644 index 000000000000..6543bf91c8c8 --- /dev/null +++ b/core/platform/emscripten/GLViewImpl-emscripten.cpp @@ -0,0 +1,1311 @@ +/**************************************************************************** +Copyright (c) 2010-2012 cocos2d-x.org +Copyright (c) 2013-2016 Chukong Technologies Inc. +Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +Copyright (c) 2020 C4games Ltd. +Copyright (c) 2021-2022 Bytedance Inc. + +https://axmolengine.github.io/ + +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. +****************************************************************************/ + +#include "platform/emscripten/GLViewImpl-emscripten.h" + +#include +#include + +#include "platform/Application.h" +#include "base/Director.h" +#include "base/Touch.h" +#include "base/EventDispatcher.h" +#include "base/EventKeyboard.h" +#include "base/EventMouse.h" +#include "base/IMEDispatcher.h" +#include "base/Utils.h" +#include "base/UTF8.h" +#include "2d/Camera.h" +#if AX_ICON_SET_SUPPORT +# include "platform/Image.h" +#endif /* AX_ICON_SET_SUPPORT */ + +#include "renderer/Renderer.h" + +#if defined(AX_USE_METAL) +# include +# include "renderer/backend/metal/DeviceMTL.h" +# include "renderer/backend/metal/UtilsMTL.h" +#else +# include "renderer/backend/opengl/MacrosGL.h" +#endif // #if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC) + +/** glfw3native.h */ +#if (AX_TARGET_PLATFORM == AX_PLATFORM_WIN32) +# ifndef GLFW_EXPOSE_NATIVE_WIN32 +# define GLFW_EXPOSE_NATIVE_WIN32 +# endif +# ifndef GLFW_EXPOSE_NATIVE_WGL +# define GLFW_EXPOSE_NATIVE_WGL +# endif +#endif /* (AX_TARGET_PLATFORM == AX_PLATFORM_WIN32) */ + +#if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC) +# ifndef GLFW_EXPOSE_NATIVE_NSGL +# define GLFW_EXPOSE_NATIVE_NSGL +# endif +# ifndef GLFW_EXPOSE_NATIVE_COCOA +# define GLFW_EXPOSE_NATIVE_COCOA +# endif +#endif // #if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC) + +#if (AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN) + +#else + #include +#endif + +#if defined(_WIN32) +# include "glfw3ext.h" +#endif + +NS_AX_BEGIN + +class GLFWEventHandler +{ +public: + static void onGLFWError(int errorID, const char* errorDesc) + { + if (_view) + _view->onGLFWError(errorID, errorDesc); + } + + static void onGLFWMouseCallBack(GLFWwindow* window, int button, int action, int modify) + { + if (_view) + _view->onGLFWMouseCallBack(window, button, action, modify); + } + + static void onGLFWMouseMoveCallBack(GLFWwindow* window, double x, double y) + { + if (_view) + _view->onGLFWMouseMoveCallBack(window, x, y); + } + + static void onGLFWMouseScrollCallback(GLFWwindow* window, double x, double y) + { + if (_view) + _view->onGLFWMouseScrollCallback(window, x, y); + } + + static void onGLFWKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) + { + if (_view) + _view->onGLFWKeyCallback(window, key, scancode, action, mods); + } + + static void onGLFWCharCallback(GLFWwindow* window, unsigned int character) + { + if (_view) + _view->onGLFWCharCallback(window, character); + } + + static void onGLFWWindowPosCallback(GLFWwindow* windows, int x, int y) + { + if (_view) + _view->onGLFWWindowPosCallback(windows, x, y); + } + + // Notes: Unused on windows or macos Metal renderer backend + // static void onGLFWframebufferSize(GLFWwindow* window, int w, int h) + // { + // if (_view) + // _view->onGLFWframebufferSize(window, w, h); + // } + + static void onGLFWWindowSizeCallback(GLFWwindow* window, int width, int height) + { + if (_view) + _view->onGLFWWindowSizeCallback(window, width, height); + } + + static void setGLViewImpl(GLViewImpl* view) { _view = view; } + + static void onGLFWWindowIconifyCallback(GLFWwindow* window, int iconified) + { + if (_view) + { + _view->onGLFWWindowIconifyCallback(window, iconified); + } + } + + static void onGLFWWindowFocusCallback(GLFWwindow* window, int focused) + { + if (_view) + { + _view->onGLFWWindowFocusCallback(window, focused); + } + } + +private: + static GLViewImpl* _view; +}; +GLViewImpl* GLFWEventHandler::_view = nullptr; + +const std::string GLViewImpl::EVENT_WINDOW_RESIZED = "glview_window_resized"; +const std::string GLViewImpl::EVENT_WINDOW_FOCUSED = "glview_window_focused"; +const std::string GLViewImpl::EVENT_WINDOW_UNFOCUSED = "glview_window_unfocused"; + +//////////////////////////////////////////////////// + +struct keyCodeItem +{ + int glfwKeyCode; + EventKeyboard::KeyCode keyCode; +}; + +static std::unordered_map g_keyCodeMap; + +static keyCodeItem g_keyCodeStructArray[] = { + /* The unknown key */ + {GLFW_KEY_UNKNOWN, EventKeyboard::KeyCode::KEY_NONE}, + + /* Printable keys */ + {GLFW_KEY_SPACE, EventKeyboard::KeyCode::KEY_SPACE}, + {GLFW_KEY_APOSTROPHE, EventKeyboard::KeyCode::KEY_APOSTROPHE}, + {GLFW_KEY_COMMA, EventKeyboard::KeyCode::KEY_COMMA}, + {GLFW_KEY_MINUS, EventKeyboard::KeyCode::KEY_MINUS}, + {GLFW_KEY_PERIOD, EventKeyboard::KeyCode::KEY_PERIOD}, + {GLFW_KEY_SLASH, EventKeyboard::KeyCode::KEY_SLASH}, + {GLFW_KEY_0, EventKeyboard::KeyCode::KEY_0}, + {GLFW_KEY_1, EventKeyboard::KeyCode::KEY_1}, + {GLFW_KEY_2, EventKeyboard::KeyCode::KEY_2}, + {GLFW_KEY_3, EventKeyboard::KeyCode::KEY_3}, + {GLFW_KEY_4, EventKeyboard::KeyCode::KEY_4}, + {GLFW_KEY_5, EventKeyboard::KeyCode::KEY_5}, + {GLFW_KEY_6, EventKeyboard::KeyCode::KEY_6}, + {GLFW_KEY_7, EventKeyboard::KeyCode::KEY_7}, + {GLFW_KEY_8, EventKeyboard::KeyCode::KEY_8}, + {GLFW_KEY_9, EventKeyboard::KeyCode::KEY_9}, + {GLFW_KEY_SEMICOLON, EventKeyboard::KeyCode::KEY_SEMICOLON}, + {GLFW_KEY_EQUAL, EventKeyboard::KeyCode::KEY_EQUAL}, + {GLFW_KEY_A, EventKeyboard::KeyCode::KEY_A}, + {GLFW_KEY_B, EventKeyboard::KeyCode::KEY_B}, + {GLFW_KEY_C, EventKeyboard::KeyCode::KEY_C}, + {GLFW_KEY_D, EventKeyboard::KeyCode::KEY_D}, + {GLFW_KEY_E, EventKeyboard::KeyCode::KEY_E}, + {GLFW_KEY_F, EventKeyboard::KeyCode::KEY_F}, + {GLFW_KEY_G, EventKeyboard::KeyCode::KEY_G}, + {GLFW_KEY_H, EventKeyboard::KeyCode::KEY_H}, + {GLFW_KEY_I, EventKeyboard::KeyCode::KEY_I}, + {GLFW_KEY_J, EventKeyboard::KeyCode::KEY_J}, + {GLFW_KEY_K, EventKeyboard::KeyCode::KEY_K}, + {GLFW_KEY_L, EventKeyboard::KeyCode::KEY_L}, + {GLFW_KEY_M, EventKeyboard::KeyCode::KEY_M}, + {GLFW_KEY_N, EventKeyboard::KeyCode::KEY_N}, + {GLFW_KEY_O, EventKeyboard::KeyCode::KEY_O}, + {GLFW_KEY_P, EventKeyboard::KeyCode::KEY_P}, + {GLFW_KEY_Q, EventKeyboard::KeyCode::KEY_Q}, + {GLFW_KEY_R, EventKeyboard::KeyCode::KEY_R}, + {GLFW_KEY_S, EventKeyboard::KeyCode::KEY_S}, + {GLFW_KEY_T, EventKeyboard::KeyCode::KEY_T}, + {GLFW_KEY_U, EventKeyboard::KeyCode::KEY_U}, + {GLFW_KEY_V, EventKeyboard::KeyCode::KEY_V}, + {GLFW_KEY_W, EventKeyboard::KeyCode::KEY_W}, + {GLFW_KEY_X, EventKeyboard::KeyCode::KEY_X}, + {GLFW_KEY_Y, EventKeyboard::KeyCode::KEY_Y}, + {GLFW_KEY_Z, EventKeyboard::KeyCode::KEY_Z}, + {GLFW_KEY_LEFT_BRACKET, EventKeyboard::KeyCode::KEY_LEFT_BRACKET}, + {GLFW_KEY_BACKSLASH, EventKeyboard::KeyCode::KEY_BACK_SLASH}, + {GLFW_KEY_RIGHT_BRACKET, EventKeyboard::KeyCode::KEY_RIGHT_BRACKET}, + {GLFW_KEY_GRAVE_ACCENT, EventKeyboard::KeyCode::KEY_GRAVE}, + {GLFW_KEY_WORLD_1, EventKeyboard::KeyCode::KEY_GRAVE}, + {GLFW_KEY_WORLD_2, EventKeyboard::KeyCode::KEY_NONE}, + + /* Function keys */ + {GLFW_KEY_ESCAPE, EventKeyboard::KeyCode::KEY_ESCAPE}, + {GLFW_KEY_ENTER, EventKeyboard::KeyCode::KEY_ENTER}, + {GLFW_KEY_TAB, EventKeyboard::KeyCode::KEY_TAB}, + {GLFW_KEY_BACKSPACE, EventKeyboard::KeyCode::KEY_BACKSPACE}, + {GLFW_KEY_INSERT, EventKeyboard::KeyCode::KEY_INSERT}, + {GLFW_KEY_DELETE, EventKeyboard::KeyCode::KEY_DELETE}, + {GLFW_KEY_RIGHT, EventKeyboard::KeyCode::KEY_RIGHT_ARROW}, + {GLFW_KEY_LEFT, EventKeyboard::KeyCode::KEY_LEFT_ARROW}, + {GLFW_KEY_DOWN, EventKeyboard::KeyCode::KEY_DOWN_ARROW}, + {GLFW_KEY_UP, EventKeyboard::KeyCode::KEY_UP_ARROW}, + {GLFW_KEY_PAGE_UP, EventKeyboard::KeyCode::KEY_PG_UP}, + {GLFW_KEY_PAGE_DOWN, EventKeyboard::KeyCode::KEY_PG_DOWN}, + {GLFW_KEY_HOME, EventKeyboard::KeyCode::KEY_HOME}, + {GLFW_KEY_END, EventKeyboard::KeyCode::KEY_END}, + {GLFW_KEY_CAPS_LOCK, EventKeyboard::KeyCode::KEY_CAPS_LOCK}, + {GLFW_KEY_SCROLL_LOCK, EventKeyboard::KeyCode::KEY_SCROLL_LOCK}, + {GLFW_KEY_NUM_LOCK, EventKeyboard::KeyCode::KEY_NUM_LOCK}, + {GLFW_KEY_PRINT_SCREEN, EventKeyboard::KeyCode::KEY_PRINT}, + {GLFW_KEY_PAUSE, EventKeyboard::KeyCode::KEY_PAUSE}, + {GLFW_KEY_F1, EventKeyboard::KeyCode::KEY_F1}, + {GLFW_KEY_F2, EventKeyboard::KeyCode::KEY_F2}, + {GLFW_KEY_F3, EventKeyboard::KeyCode::KEY_F3}, + {GLFW_KEY_F4, EventKeyboard::KeyCode::KEY_F4}, + {GLFW_KEY_F5, EventKeyboard::KeyCode::KEY_F5}, + {GLFW_KEY_F6, EventKeyboard::KeyCode::KEY_F6}, + {GLFW_KEY_F7, EventKeyboard::KeyCode::KEY_F7}, + {GLFW_KEY_F8, EventKeyboard::KeyCode::KEY_F8}, + {GLFW_KEY_F9, EventKeyboard::KeyCode::KEY_F9}, + {GLFW_KEY_F10, EventKeyboard::KeyCode::KEY_F10}, + {GLFW_KEY_F11, EventKeyboard::KeyCode::KEY_F11}, + {GLFW_KEY_F12, EventKeyboard::KeyCode::KEY_F12}, + {GLFW_KEY_F13, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_F14, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_F15, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_F16, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_F17, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_F18, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_F19, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_F20, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_F21, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_F22, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_F23, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_F24, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_F25, EventKeyboard::KeyCode::KEY_NONE}, + {GLFW_KEY_KP_0, EventKeyboard::KeyCode::KEY_0}, + {GLFW_KEY_KP_1, EventKeyboard::KeyCode::KEY_1}, + {GLFW_KEY_KP_2, EventKeyboard::KeyCode::KEY_2}, + {GLFW_KEY_KP_3, EventKeyboard::KeyCode::KEY_3}, + {GLFW_KEY_KP_4, EventKeyboard::KeyCode::KEY_4}, + {GLFW_KEY_KP_5, EventKeyboard::KeyCode::KEY_5}, + {GLFW_KEY_KP_6, EventKeyboard::KeyCode::KEY_6}, + {GLFW_KEY_KP_7, EventKeyboard::KeyCode::KEY_7}, + {GLFW_KEY_KP_8, EventKeyboard::KeyCode::KEY_8}, + {GLFW_KEY_KP_9, EventKeyboard::KeyCode::KEY_9}, + {GLFW_KEY_KP_DECIMAL, EventKeyboard::KeyCode::KEY_PERIOD}, + {GLFW_KEY_KP_DIVIDE, EventKeyboard::KeyCode::KEY_KP_DIVIDE}, + {GLFW_KEY_KP_MULTIPLY, EventKeyboard::KeyCode::KEY_KP_MULTIPLY}, + {GLFW_KEY_KP_SUBTRACT, EventKeyboard::KeyCode::KEY_KP_MINUS}, + {GLFW_KEY_KP_ADD, EventKeyboard::KeyCode::KEY_KP_PLUS}, + {GLFW_KEY_KP_ENTER, EventKeyboard::KeyCode::KEY_KP_ENTER}, + {GLFW_KEY_KP_EQUAL, EventKeyboard::KeyCode::KEY_EQUAL}, + {GLFW_KEY_LEFT_SHIFT, EventKeyboard::KeyCode::KEY_LEFT_SHIFT}, + {GLFW_KEY_LEFT_CONTROL, EventKeyboard::KeyCode::KEY_LEFT_CTRL}, + {GLFW_KEY_LEFT_ALT, EventKeyboard::KeyCode::KEY_LEFT_ALT}, + {GLFW_KEY_LEFT_SUPER, EventKeyboard::KeyCode::KEY_HYPER}, + {GLFW_KEY_RIGHT_SHIFT, EventKeyboard::KeyCode::KEY_RIGHT_SHIFT}, + {GLFW_KEY_RIGHT_CONTROL, EventKeyboard::KeyCode::KEY_RIGHT_CTRL}, + {GLFW_KEY_RIGHT_ALT, EventKeyboard::KeyCode::KEY_RIGHT_ALT}, + {GLFW_KEY_RIGHT_SUPER, EventKeyboard::KeyCode::KEY_HYPER}, + {GLFW_KEY_MENU, EventKeyboard::KeyCode::KEY_MENU}, + {GLFW_KEY_LAST, EventKeyboard::KeyCode::KEY_NONE}}; + +////////////////////////////////////////////////////////////////////////// +// implement GLViewImpl +////////////////////////////////////////////////////////////////////////// + +GLViewImpl::GLViewImpl(bool initglfw) + : _captured(false) + , _isInRetinaMonitor(false) + , _isRetinaEnabled(false) + , _retinaFactor(1) + , _frameZoomFactor(1.0f) + , _mainWindow(nullptr) + , _monitor(nullptr) + , _mouseX(0.0f) + , _mouseY(0.0f) +{ + _viewName = "AXMOL10"; + g_keyCodeMap.clear(); + for (auto&& item : g_keyCodeStructArray) + { + g_keyCodeMap[item.glfwKeyCode] = item.keyCode; + } + + GLFWEventHandler::setGLViewImpl(this); + if (initglfw) + { + glfwSetErrorCallback(GLFWEventHandler::onGLFWError); +#if defined(AX_USE_GLES) && GLFW_VERSION_MAJOR >= 3 && GLFW_VERSION_MINOR >= 4 + glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, GLFW_ANGLE_PLATFORM_TYPE_D3D11); // since glfw-3.4 +#endif +#if defined(_WIN32) + glfwxInit(); +#else + glfwInit(); +#endif + } +} + +GLViewImpl::~GLViewImpl() +{ + AXLOGINFO("deallocing GLViewImpl: %p", this); + GLFWEventHandler::setGLViewImpl(nullptr); +#if defined(_WIN32) + glfwxTerminate(); +#else + glfwTerminate(); +#endif +} + +#if (AX_TARGET_PLATFORM == AX_PLATFORM_WIN32) +HWND GLViewImpl::getWin32Window() +{ + return glfwGetWin32Window(_mainWindow); +} +#endif /* (AX_TARGET_PLATFORM == AX_PLATFORM_WIN32) */ + +#if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC) +void* GLViewImpl::getCocoaWindow() +{ + return (void*)glfwGetCocoaWindow(_mainWindow); +} +void* GLViewImpl::getNSGLContext() +{ + return (void*)glfwGetNSGLContext(_mainWindow); +} // stevetranby: added +#endif // #if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC) + +GLViewImpl* GLViewImpl::create(std::string_view viewName) +{ + return GLViewImpl::create(viewName, false); +} + +GLViewImpl* GLViewImpl::create(std::string_view viewName, bool resizable) +{ + auto ret = new GLViewImpl; + if (ret->initWithRect(viewName, ax::Rect(0, 0, 960, 640), 1.0f, resizable)) + { + ret->autorelease(); + return ret; + } + AX_SAFE_DELETE(ret); + return nullptr; +} + +GLViewImpl* GLViewImpl::createWithRect(std::string_view viewName, const ax::Rect& rect, float frameZoomFactor, bool resizable) +{ + auto ret = new GLViewImpl; + if (ret->initWithRect(viewName, rect, frameZoomFactor, resizable)) + { + ret->autorelease(); + return ret; + } + AX_SAFE_DELETE(ret); + return nullptr; +} + +GLViewImpl* GLViewImpl::createWithFullScreen(std::string_view viewName) +{ + auto ret = new GLViewImpl(); + if (ret->initWithFullScreen(viewName)) + { + ret->autorelease(); + return ret; + } + AX_SAFE_DELETE(ret); + return nullptr; +} + +GLViewImpl* GLViewImpl::createWithFullScreen(std::string_view viewName, + const GLFWvidmode& videoMode, + GLFWmonitor* monitor) +{ + auto ret = new GLViewImpl(); + if (ret->initWithFullscreen(viewName, videoMode, monitor)) + { + ret->autorelease(); + return ret; + } + AX_SAFE_DELETE(ret); + return nullptr; +} + +bool GLViewImpl::initWithRect(std::string_view viewName, const ax::Rect& rect, float frameZoomFactor, bool resizable) +{ + setViewName(viewName); + + _frameZoomFactor = frameZoomFactor; + + Vec2 frameSize = rect.size; + +#if defined(AX_USE_GLES) + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); +#endif + + glfwWindowHint(GLFW_RESIZABLE, resizable ? GL_TRUE : GL_FALSE); + glfwWindowHint(GLFW_RED_BITS, _glContextAttrs.redBits); + glfwWindowHint(GLFW_GREEN_BITS, _glContextAttrs.greenBits); + glfwWindowHint(GLFW_BLUE_BITS, _glContextAttrs.blueBits); + glfwWindowHint(GLFW_ALPHA_BITS, _glContextAttrs.alphaBits); + glfwWindowHint(GLFW_DEPTH_BITS, _glContextAttrs.depthBits); + glfwWindowHint(GLFW_STENCIL_BITS, _glContextAttrs.stencilBits); + + glfwWindowHint(GLFW_SAMPLES, _glContextAttrs.multisamplingCount); + + glfwWindowHint(GLFW_VISIBLE, _glContextAttrs.visible); + glfwWindowHint(GLFW_DECORATED, _glContextAttrs.decorated); + +#if defined(AX_USE_METAL) + // Don't create gl context. + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); +#endif + + int neededWidth = static_cast(frameSize.width * _frameZoomFactor); + int neededHeight = static_cast(frameSize.height * _frameZoomFactor); + +#if (AX_TARGET_PLATFORM == AX_PLATFORM_WIN32) + glfwxSetParent((HWND)_glContextAttrs.viewParent); +#endif + + _mainWindow = glfwCreateWindow(neededWidth, neededHeight, _viewName.c_str(), + _monitor, nullptr); + + if (_mainWindow == nullptr) + { + std::string message = "Can't create window"; + if (!_glfwError.empty()) + { + message.append("\nMore info: \n"); + message.append(_glfwError); + } + + ccMessageBox(message.c_str(), "Error launch application"); + utils::killCurrentProcess(); // kill current process, don't cause crash when driver issue. + return false; + } + +#if defined(AX_USE_METAL) + int fbWidth, fbHeight; + glfwGetFramebufferSize(_mainWindow, &fbWidth, &fbHeight); + + CGSize size; + size.width = static_cast(fbWidth); + size.height = static_cast(fbHeight); + // Initialize device. + id device = MTLCreateSystemDefaultDevice(); + if (!device) + { + AXLOG("Doesn't support metal."); + return false; + } + + NSView* contentView = [(id)getCocoaWindow() contentView]; + [contentView setWantsLayer:YES]; + CAMetalLayer* layer = [CAMetalLayer layer]; + [layer setDevice:device]; + [layer setPixelFormat:MTLPixelFormatBGRA8Unorm]; + [layer setFramebufferOnly:YES]; + [layer setDrawableSize:size]; + layer.displaySyncEnabled = _glContextAttrs.vsync; + [contentView setLayer:layer]; + backend::DeviceMTL::setCAMetalLayer(layer); +#endif + +#if defined(AX_USE_GL) + glfwMakeContextCurrent(_mainWindow); +#endif + + /* + * Note that the created window and context may differ from what you requested, + * as not all parameters and hints are + * [hard constraints](@ref window_hints_hard). This includes the size of the + * window, especially for full screen windows. To retrieve the actual + * attributes of the created window and context, use queries like @ref + * glfwGetWindowAttrib and @ref glfwGetWindowSize. + * + * see declaration glfwCreateWindow + */ + int realW = 0, realH = 0; + glfwGetWindowSize(_mainWindow, &realW, &realH); + if (realW != neededWidth) + { + frameSize.width = realW / _frameZoomFactor; + } + if (realH != neededHeight) + { + frameSize.height = realH / _frameZoomFactor; + } + + glfwSetMouseButtonCallback(_mainWindow, GLFWEventHandler::onGLFWMouseCallBack); + glfwSetCursorPosCallback(_mainWindow, GLFWEventHandler::onGLFWMouseMoveCallBack); + glfwSetScrollCallback(_mainWindow, GLFWEventHandler::onGLFWMouseScrollCallback); + glfwSetCharCallback(_mainWindow, GLFWEventHandler::onGLFWCharCallback); + glfwSetKeyCallback(_mainWindow, GLFWEventHandler::onGLFWKeyCallback); + glfwSetWindowPosCallback(_mainWindow, GLFWEventHandler::onGLFWWindowPosCallback); + glfwSetWindowSizeCallback(_mainWindow, GLFWEventHandler::onGLFWWindowSizeCallback); + glfwSetWindowIconifyCallback(_mainWindow, GLFWEventHandler::onGLFWWindowIconifyCallback); + glfwSetWindowFocusCallback(_mainWindow, GLFWEventHandler::onGLFWWindowFocusCallback); + + setFrameSize(frameSize.width, frameSize.height); + +#if (AX_TARGET_PLATFORM != AX_PLATFORM_MAC) + loadGL(); +#endif + +#if defined(AX_USE_GL) + // check OpenGL version at first + const GLubyte* glVersion = glGetString(GL_VERSION); + + if (GL_ARB_vertex_shader && GL_ARB_fragment_shader) + ax::print("[GL:%s] Ready for GLSL", glVersion); + else + ax::print("Not totally ready :("); + + // Will cause OpenGL error 0x0500 when use ANGLE-GLES on desktop +# if !defined(AX_USE_GLES) + // Enable point size by default. +# if defined(GL_VERSION_2_0) + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); +# else + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); +# endif + if (_glContextAttrs.multisamplingCount > 0) + glEnable(GL_MULTISAMPLE); +# endif + CHECK_GL_ERROR_DEBUG(); +#endif + // // GLFW v3.2 no longer emits "onGLFWWindowSizeFunCallback" at creation time. Force default viewport: + // setViewPortInPoints(0, 0, neededWidth, neededHeight); + // + return true; +} + +bool GLViewImpl::initWithFullScreen(std::string_view viewName) +{ + // Create fullscreen window on primary monitor at its current video mode. + _monitor = glfwGetPrimaryMonitor(); + if (nullptr == _monitor) + return false; + + const GLFWvidmode* videoMode = glfwGetVideoMode(_monitor); + return initWithRect(viewName, ax::Rect(0, 0, (float)videoMode->width, (float)videoMode->height), 1.0f, false); +} + +bool GLViewImpl::initWithFullscreen(std::string_view viewname, const GLFWvidmode& videoMode, GLFWmonitor* monitor) +{ + // Create fullscreen on specified monitor at the specified video mode. + _monitor = monitor; + if (nullptr == _monitor) + return false; + + // These are soft constraints. If the video mode is retrieved at runtime, the resulting window and context should + // match these exactly. If invalid attribs are passed (eg. from an outdated cache), window creation will NOT fail + // but the actual window/context may differ. + glfwWindowHint(GLFW_REFRESH_RATE, videoMode.refreshRate); + glfwWindowHint(GLFW_RED_BITS, videoMode.redBits); + glfwWindowHint(GLFW_BLUE_BITS, videoMode.blueBits); + glfwWindowHint(GLFW_GREEN_BITS, videoMode.greenBits); + + return initWithRect(viewname, ax::Rect(0, 0, (float)videoMode.width, (float)videoMode.height), 1.0f, false); +} + +bool GLViewImpl::isOpenGLReady() +{ + return nullptr != _mainWindow; +} + +void GLViewImpl::end() +{ + if (_mainWindow) + { + glfwSetWindowShouldClose(_mainWindow, 1); + _mainWindow = nullptr; + } + // Release self. Otherwise, GLViewImpl could not be freed. + release(); +} + +void GLViewImpl::swapBuffers() +{ +#if defined(AX_USE_GL) + if (_mainWindow) + glfwSwapBuffers(_mainWindow); +#endif +} + +bool GLViewImpl::windowShouldClose() +{ + if (_mainWindow) + return glfwWindowShouldClose(_mainWindow) ? true : false; + else + return true; +} + +void GLViewImpl::pollEvents() +{ + glfwPollEvents(); +} + +void GLViewImpl::enableRetina(bool enabled) +{ +#if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC) + _isRetinaEnabled = enabled; + if (_isRetinaEnabled) + { + _retinaFactor = 1; + } + else + { + _retinaFactor = 2; + } + updateFrameSize(); +#endif +} + +void GLViewImpl::setIMEKeyboardState(bool /*bOpen*/) {} + +#if AX_ICON_SET_SUPPORT +void GLViewImpl::setIcon(std::string_view filename) const +{ + this->setIcon(std::vector{filename}); +} + +void GLViewImpl::setIcon(const std::vector& filelist) const +{ + if (filelist.empty()) + return; + std::vector icons; + for (auto const& filename : filelist) + { + Image* icon = new Image(); + if (icon->initWithImageFile(filename)) + { + icons.emplace_back(icon); + } + else + { + AX_SAFE_DELETE(icon); + } + } + + if (icons.empty()) + return; // No valid images + size_t iconsCount = icons.size(); + auto images = new GLFWimage[iconsCount]; + for (size_t i = 0; i < iconsCount; i++) + { + auto& image = images[i]; + auto& icon = icons[i]; + image.width = icon->getWidth(); + image.height = icon->getHeight(); + image.pixels = icon->getData(); + }; + + GLFWwindow* window = this->getWindow(); + glfwSetWindowIcon(window, iconsCount, images); + + AX_SAFE_DELETE_ARRAY(images); + for (auto&& icon : icons) + { + AX_SAFE_DELETE(icon); + } +} + +void GLViewImpl::setDefaultIcon() const +{ + GLFWwindow* window = this->getWindow(); + glfwSetWindowIcon(window, 0, nullptr); +} +#endif /* AX_ICON_SET_SUPPORT */ + +void GLViewImpl::setCursorVisible(bool isVisible) +{ + if (_mainWindow == NULL) + return; + + if (isVisible) + glfwSetInputMode(_mainWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + else + glfwSetInputMode(_mainWindow, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); +} + +void GLViewImpl::setFrameZoomFactor(float zoomFactor) +{ + AXASSERT(zoomFactor > 0.0f, "zoomFactor must be larger than 0"); + + if (std::abs(_frameZoomFactor - zoomFactor) < FLT_EPSILON) + { + return; + } + + _frameZoomFactor = zoomFactor; + updateFrameSize(); +} + +float GLViewImpl::getFrameZoomFactor() const +{ + return _frameZoomFactor; +} + +bool GLViewImpl::isFullscreen() const +{ + return (_monitor != nullptr); +} + +void GLViewImpl::setFullscreen() +{ + setFullscreen(-1, -1, -1); +} + +void GLViewImpl::setFullscreen(int w, int h, int refreshRate) +{ + auto monitor = glfwGetPrimaryMonitor(); + if (nullptr == monitor || monitor == _monitor) + { + return; + } + this->setFullscreen(monitor, w, h, refreshRate); +} + +void GLViewImpl::setFullscreen(int monitorIndex) +{ + setFullscreen(monitorIndex, -1, -1, -1); +} + +void GLViewImpl::setFullscreen(int monitorIndex, int w, int h, int refreshRate) +{ + int count = 0; + GLFWmonitor** monitors = glfwGetMonitors(&count); + if (monitorIndex < 0 || monitorIndex >= count) + { + return; + } + GLFWmonitor* monitor = monitors[monitorIndex]; + if (nullptr == monitor || _monitor == monitor) + { + return; + } + this->setFullscreen(monitor, w, h, refreshRate); +} + +void GLViewImpl::setFullscreen(GLFWmonitor* monitor, int w, int h, int refreshRate) +{ + _monitor = monitor; + + const GLFWvidmode* videoMode = glfwGetVideoMode(_monitor); + if (w == -1) + w = videoMode->width; + if (h == -1) + h = videoMode->height; + if (refreshRate == -1) + refreshRate = videoMode->refreshRate; + + glfwSetWindowMonitor(_mainWindow, _monitor, 0, 0, w, h, refreshRate); + + updateWindowSize(); +} + +void GLViewImpl::setWindowed(int width, int height) +{ + if (!this->isFullscreen()) + { + this->setFrameSize((float)width, (float)height); + } + else + { + const GLFWvidmode* videoMode = glfwGetVideoMode(_monitor); + int xpos = 0, ypos = 0; + glfwGetMonitorPos(_monitor, &xpos, &ypos); + xpos += (int)((videoMode->width - width) * 0.5f); + ypos += (int)((videoMode->height - height) * 0.5f); + _monitor = nullptr; + glfwSetWindowMonitor(_mainWindow, nullptr, xpos, ypos, width, height, GLFW_DONT_CARE); +#if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC) + // on mac window will sometimes lose title when windowed + glfwSetWindowTitle(_mainWindow, _viewName.c_str()); +#endif + + updateWindowSize(); + } +} + +void GLViewImpl::updateWindowSize() +{ + int w = 0, h = 0; + glfwGetFramebufferSize(_mainWindow, &w, &h); + int frameWidth = w / _frameZoomFactor; + int frameHeight = h / _frameZoomFactor; + setFrameSize(frameWidth, frameHeight); + updateDesignResolutionSize(); + Director::getInstance()->getEventDispatcher()->dispatchCustomEvent(GLViewImpl::EVENT_WINDOW_RESIZED, nullptr); +} + +int GLViewImpl::getMonitorCount() const +{ + int count = 0; + glfwGetMonitors(&count); + return count; +} + +Vec2 GLViewImpl::getMonitorSize() const +{ + GLFWmonitor* monitor = _monitor; + if (nullptr == monitor) + { + GLFWwindow* window = this->getWindow(); + monitor = glfwGetWindowMonitor(window); + } + if (nullptr == monitor) + { + monitor = glfwGetPrimaryMonitor(); + } + if (nullptr != monitor) + { + const GLFWvidmode* videoMode = glfwGetVideoMode(monitor); + Vec2 size = Vec2((float)videoMode->width, (float)videoMode->height); + return size; + } + return Vec2::ZERO; +} + +void GLViewImpl::updateFrameSize() +{ + if (_screenSize.width > 0 && _screenSize.height > 0) + { + int w = 0, h = 0; + glfwGetWindowSize(_mainWindow, &w, &h); + + int frameBufferW = 0, frameBufferH = 0; + glfwGetFramebufferSize(_mainWindow, &frameBufferW, &frameBufferH); + + if (frameBufferW == 2 * w && frameBufferH == 2 * h) + { + if (_isRetinaEnabled) + { + _retinaFactor = 1; + } + else + { + _retinaFactor = 2; + } + glfwSetWindowSize(_mainWindow, _screenSize.width / 2 * _retinaFactor * _frameZoomFactor, + _screenSize.height / 2 * _retinaFactor * _frameZoomFactor); + + _isInRetinaMonitor = true; + } + else + { + if (_isInRetinaMonitor) + { + _retinaFactor = 1; + } + glfwSetWindowSize(_mainWindow, (int)(_screenSize.width * _retinaFactor * _frameZoomFactor), + (int)(_screenSize.height * _retinaFactor * _frameZoomFactor)); + + _isInRetinaMonitor = false; + } + } +} + +void GLViewImpl::setFrameSize(float width, float height) +{ + GLView::setFrameSize(width, height); + updateFrameSize(); +} + +void GLViewImpl::setViewPortInPoints(float x, float y, float w, float h) +{ + Viewport vp; + vp.x = (int)(x * _scaleX * _retinaFactor * _frameZoomFactor + + _viewPortRect.origin.x * _retinaFactor * _frameZoomFactor); + vp.y = (int)(y * _scaleY * _retinaFactor * _frameZoomFactor + + _viewPortRect.origin.y * _retinaFactor * _frameZoomFactor); + vp.w = (unsigned int)(w * _scaleX * _retinaFactor * _frameZoomFactor); + vp.h = (unsigned int)(h * _scaleY * _retinaFactor * _frameZoomFactor); + Camera::setDefaultViewport(vp); +} + +void GLViewImpl::setScissorInPoints(float x, float y, float w, float h) +{ + auto x1 = (int)(x * _scaleX * _retinaFactor * _frameZoomFactor + + _viewPortRect.origin.x * _retinaFactor * _frameZoomFactor); + auto y1 = (int)(y * _scaleY * _retinaFactor * _frameZoomFactor + + _viewPortRect.origin.y * _retinaFactor * _frameZoomFactor); + auto width1 = (unsigned int)(w * _scaleX * _retinaFactor * _frameZoomFactor); + auto height1 = (unsigned int)(h * _scaleY * _retinaFactor * _frameZoomFactor); + auto renderer = Director::getInstance()->getRenderer(); + renderer->setScissorRect(x1, y1, width1, height1); +} + +ax::Rect GLViewImpl::getScissorRect() const +{ + auto renderer = Director::getInstance()->getRenderer(); + auto& rect = renderer->getScissorRect(); + + float x = (rect.x - _viewPortRect.origin.x * _retinaFactor * _frameZoomFactor) / + (_scaleX * _retinaFactor * _frameZoomFactor); + float y = (rect.y - _viewPortRect.origin.y * _retinaFactor * _frameZoomFactor) / + (_scaleY * _retinaFactor * _frameZoomFactor); + float w = rect.width / (_scaleX * _retinaFactor * _frameZoomFactor); + float h = rect.height / (_scaleY * _retinaFactor * _frameZoomFactor); + return ax::Rect(x, y, w, h); +} + +void GLViewImpl::onGLFWError(int errorID, const char* errorDesc) +{ + if (_mainWindow) + { + _glfwError = StringUtils::format("GLFWError #%d Happen, %s", errorID, errorDesc); + } + else + { + _glfwError.append(StringUtils::format("GLFWError #%d Happen, %s\n", errorID, errorDesc)); + } + AXLOGERROR("%s", _glfwError.c_str()); +} + +void GLViewImpl::onGLFWMouseCallBack(GLFWwindow* /*window*/, int button, int action, int /*modify*/) +{ + if (GLFW_MOUSE_BUTTON_LEFT == button) + { + if (GLFW_PRESS == action) + { + _captured = true; + if (this->getViewPortRect().equals(ax::Rect::ZERO) || + this->getViewPortRect().containsPoint(Vec2(_mouseX, _mouseY))) + { + intptr_t id = 0; + this->handleTouchesBegin(1, &id, &_mouseX, &_mouseY); + } + } + else if (GLFW_RELEASE == action) + { + if (_captured) + { + _captured = false; + intptr_t id = 0; + this->handleTouchesEnd(1, &id, &_mouseX, &_mouseY); + } + } + } + + // Because OpenGL and cocos2d-x uses different Y axis, we need to convert the coordinate here + float cursorX = (_mouseX - _viewPortRect.origin.x) / _scaleX; + float cursorY = (_viewPortRect.origin.y + _viewPortRect.size.height - _mouseY) / _scaleY; + + if (GLFW_PRESS == action) + { + EventMouse event(EventMouse::MouseEventType::MOUSE_DOWN); + event.setCursorPosition(cursorX, cursorY); + event.setMouseButton(static_cast(button)); + Director::getInstance()->getEventDispatcher()->dispatchEvent(&event); + } + else if (GLFW_RELEASE == action) + { + EventMouse event(EventMouse::MouseEventType::MOUSE_UP); + event.setCursorPosition(cursorX, cursorY); + event.setMouseButton(static_cast(button)); + Director::getInstance()->getEventDispatcher()->dispatchEvent(&event); + } +} + +void GLViewImpl::onGLFWMouseMoveCallBack(GLFWwindow* window, double x, double y) +{ + _mouseX = (float)x; + _mouseY = (float)y; + + _mouseX /= this->getFrameZoomFactor(); + _mouseY /= this->getFrameZoomFactor(); + + if (_isInRetinaMonitor) + { + if (_retinaFactor == 1) + { + _mouseX *= 2; + _mouseY *= 2; + } + } + + if (_captured) + { + intptr_t id = 0; + this->handleTouchesMove(1, &id, &_mouseX, &_mouseY); + } + + // Because OpenGL and cocos2d-x uses different Y axis, we need to convert the coordinate here + float cursorX = (_mouseX - _viewPortRect.origin.x) / _scaleX; + float cursorY = (_viewPortRect.origin.y + _viewPortRect.size.height - _mouseY) / _scaleY; + + EventMouse event(EventMouse::MouseEventType::MOUSE_MOVE); + // Set current button + if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) + { + event.setMouseButton(static_cast(GLFW_MOUSE_BUTTON_LEFT)); + } + else if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS) + { + event.setMouseButton(static_cast(GLFW_MOUSE_BUTTON_RIGHT)); + } + else if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS) + { + event.setMouseButton(static_cast(GLFW_MOUSE_BUTTON_MIDDLE)); + } + event.setCursorPosition(cursorX, cursorY); + Director::getInstance()->getEventDispatcher()->dispatchEvent(&event); +} + +void GLViewImpl::onGLFWMouseScrollCallback(GLFWwindow* /*window*/, double x, double y) +{ + EventMouse event(EventMouse::MouseEventType::MOUSE_SCROLL); + // Because OpenGL and cocos2d-x uses different Y axis, we need to convert the coordinate here + float cursorX = (_mouseX - _viewPortRect.origin.x) / _scaleX; + float cursorY = (_viewPortRect.origin.y + _viewPortRect.size.height - _mouseY) / _scaleY; + event.setScrollData((float)x, -(float)y); + event.setCursorPosition(cursorX, cursorY); + Director::getInstance()->getEventDispatcher()->dispatchEvent(&event); +} + +void GLViewImpl::onGLFWKeyCallback(GLFWwindow* /*window*/, int key, int /*scancode*/, int action, int /*mods*/) +{ + // x-studio spec, for repeat press key support. + EventKeyboard event(g_keyCodeMap[key], action); + auto dispatcher = Director::getInstance()->getEventDispatcher(); + dispatcher->dispatchEvent(&event); + + if (GLFW_RELEASE != action) + { + switch (g_keyCodeMap[key]) + { + case EventKeyboard::KeyCode::KEY_BACKSPACE: + IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward(); + break; + case EventKeyboard::KeyCode::KEY_HOME: + case EventKeyboard::KeyCode::KEY_KP_HOME: + case EventKeyboard::KeyCode::KEY_DELETE: + case EventKeyboard::KeyCode::KEY_KP_DELETE: + case EventKeyboard::KeyCode::KEY_END: + case EventKeyboard::KeyCode::KEY_LEFT_ARROW: + case EventKeyboard::KeyCode::KEY_RIGHT_ARROW: + case EventKeyboard::KeyCode::KEY_ESCAPE: + IMEDispatcher::sharedDispatcher()->dispatchControlKey(g_keyCodeMap[key]); + break; + default: + break; + } + } +} + +void GLViewImpl::onGLFWCharCallback(GLFWwindow* /*window*/, unsigned int charCode) +{ + std::string utf8String; + StringUtils::UTF32ToUTF8(std::u32string_view{(char32_t*)&charCode, (size_t)1}, utf8String); + static std::unordered_set controlUnicode = { + "\xEF\x9C\x80", // up + "\xEF\x9C\x81", // down + "\xEF\x9C\x82", // left + "\xEF\x9C\x83", // right + "\xEF\x9C\xA8", // delete + "\xEF\x9C\xA9", // home + "\xEF\x9C\xAB", // end + "\xEF\x9C\xAC", // pageup + "\xEF\x9C\xAD", // pagedown + "\xEF\x9C\xB9" // clear + }; + // Check for send control key + if (controlUnicode.find(utf8String) == controlUnicode.end()) + { + IMEDispatcher::sharedDispatcher()->dispatchInsertText(utf8String.c_str(), utf8String.size()); + } +} + +void GLViewImpl::onGLFWWindowPosCallback(GLFWwindow* /*window*/, int /*x*/, int /*y*/) +{ + Director::getInstance()->setViewport(); +} + +void GLViewImpl::onGLFWWindowSizeCallback(GLFWwindow* /*window*/, int w, int h) +{ + if (w && h && _resolutionPolicy != ResolutionPolicy::UNKNOWN) + { + /* Invoke `GLView::setFrameSize` to sync screen size immediately, + this->setFrameSize will invoke `glfwSetWindowSize` which is unnecessary. + */ + GLView::setFrameSize(w, h); + + /* + x-studio spec, fix view size incorrect when window size changed. + The original code behavior: + 1. first time enter full screen: w,h=1920,1080 + 2. second or later enter full screen: will trigger 2 times WindowSizeCallback + 1). w,h=976,679 + 2). w,h=1024,768 + + @remark: + 1. we should use glfwSetWindowMonitor to control the window size in full screen mode + @see also: updateWindowSize (call after enter/exit full screen mode) + */ + updateDesignResolutionSize(); + +#if defined(AX_USE_METAL) + // update metal attachment texture size. + int fbWidth, fbHeight; + glfwGetFramebufferSize(_mainWindow, &fbWidth, &fbHeight); + backend::UtilsMTL::resizeDefaultAttachmentTexture(fbWidth, fbHeight); +#endif + + Director::getInstance()->getEventDispatcher()->dispatchCustomEvent(GLViewImpl::EVENT_WINDOW_RESIZED, nullptr); + } +} + +void GLViewImpl::onGLFWWindowIconifyCallback(GLFWwindow* /*window*/, int iconified) +{ + if (iconified == GL_TRUE) + { + Application::getInstance()->applicationDidEnterBackground(); + } + else + { + Application::getInstance()->applicationWillEnterForeground(); + } +} + +void GLViewImpl::onGLFWWindowFocusCallback(GLFWwindow* /*window*/, int focused) +{ + if (focused == GL_TRUE) + { + Director::getInstance()->getEventDispatcher()->dispatchCustomEvent(GLViewImpl::EVENT_WINDOW_FOCUSED, nullptr); + } + else + { + Director::getInstance()->getEventDispatcher()->dispatchCustomEvent(GLViewImpl::EVENT_WINDOW_UNFOCUSED, nullptr); + } +} + +#if (AX_TARGET_PLATFORM != AX_PLATFORM_MAC) +static bool loadFboExtensions() +{ + const char* gl_extensions = (const char*)glGetString(GL_EXTENSIONS); + + // If the current opengl driver doesn't have framebuffers methods, check if an extension exists + if (glGenFramebuffers == nullptr) + { + log("OpenGL: glGenFramebuffers is nullptr, try to detect an extension"); + if (strstr(gl_extensions, "ARB_framebuffer_object")) + { + log("OpenGL: ARB_framebuffer_object is supported"); + + glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)glfwGetProcAddress("glIsRenderbuffer"); + glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)glfwGetProcAddress("glBindRenderbuffer"); + glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)glfwGetProcAddress("glDeleteRenderbuffers"); + glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)glfwGetProcAddress("glGenRenderbuffers"); + glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)glfwGetProcAddress("glRenderbufferStorage"); + glGetRenderbufferParameteriv = + (PFNGLGETRENDERBUFFERPARAMETERIVPROC)glfwGetProcAddress("glGetRenderbufferParameteriv"); + glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)glfwGetProcAddress("glIsFramebuffer"); + glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)glfwGetProcAddress("glBindFramebuffer"); + glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)glfwGetProcAddress("glDeleteFramebuffers"); + glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)glfwGetProcAddress("glGenFramebuffers"); + glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)glfwGetProcAddress("glCheckFramebufferStatus"); + glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)glfwGetProcAddress("glFramebufferTexture1D"); + glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)glfwGetProcAddress("glFramebufferTexture2D"); + glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)glfwGetProcAddress("glFramebufferTexture3D"); + glFramebufferRenderbuffer = + (PFNGLFRAMEBUFFERRENDERBUFFERPROC)glfwGetProcAddress("glFramebufferRenderbuffer"); + glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)glfwGetProcAddress( + "glGetFramebufferAttachmentParameteriv"); + glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)glfwGetProcAddress("glGenerateMipmap"); + } + else if (strstr(gl_extensions, "EXT_framebuffer_object")) + { + log("OpenGL: EXT_framebuffer_object is supported"); + glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)glfwGetProcAddress("glIsRenderbufferEXT"); + glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)glfwGetProcAddress("glBindRenderbufferEXT"); + glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)glfwGetProcAddress("glDeleteRenderbuffersEXT"); + glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)glfwGetProcAddress("glGenRenderbuffersEXT"); + glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)glfwGetProcAddress("glRenderbufferStorageEXT"); + glGetRenderbufferParameteriv = + (PFNGLGETRENDERBUFFERPARAMETERIVPROC)glfwGetProcAddress("glGetRenderbufferParameterivEXT"); + glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)glfwGetProcAddress("glIsFramebufferEXT"); + glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)glfwGetProcAddress("glBindFramebufferEXT"); + glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)glfwGetProcAddress("glDeleteFramebuffersEXT"); + glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)glfwGetProcAddress("glGenFramebuffersEXT"); + glCheckFramebufferStatus = + (PFNGLCHECKFRAMEBUFFERSTATUSPROC)glfwGetProcAddress("glCheckFramebufferStatusEXT"); + glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)glfwGetProcAddress("glFramebufferTexture1DEXT"); + glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)glfwGetProcAddress("glFramebufferTexture2DEXT"); + glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)glfwGetProcAddress("glFramebufferTexture3DEXT"); + glFramebufferRenderbuffer = + (PFNGLFRAMEBUFFERRENDERBUFFERPROC)glfwGetProcAddress("glFramebufferRenderbufferEXT"); + glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)glfwGetProcAddress( + "glGetFramebufferAttachmentParameterivEXT"); + glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)glfwGetProcAddress("glGenerateMipmapEXT"); + } + else if (strstr(gl_extensions, "GL_ANGLE_framebuffer_blit")) + { + log("OpenGL: GL_ANGLE_framebuffer_object is supported"); + + glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)glfwGetProcAddress("glIsRenderbufferOES"); + glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)glfwGetProcAddress("glBindRenderbufferOES"); + glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)glfwGetProcAddress("glDeleteRenderbuffersOES"); + glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)glfwGetProcAddress("glGenRenderbuffersOES"); + glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)glfwGetProcAddress("glRenderbufferStorageOES"); + // glGetRenderbufferParameteriv = + // (PFNGLGETRENDERBUFFERPARAMETERIVPROC)glfwGetProcAddress("glGetRenderbufferParameterivOES"); + glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)glfwGetProcAddress("glIsFramebufferOES"); + glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)glfwGetProcAddress("glBindFramebufferOES"); + glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)glfwGetProcAddress("glDeleteFramebuffersOES"); + glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)glfwGetProcAddress("glGenFramebuffersOES"); + glCheckFramebufferStatus = + (PFNGLCHECKFRAMEBUFFERSTATUSPROC)glfwGetProcAddress("glCheckFramebufferStatusOES"); + glFramebufferRenderbuffer = + (PFNGLFRAMEBUFFERRENDERBUFFERPROC)glfwGetProcAddress("glFramebufferRenderbufferOES"); + glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)glfwGetProcAddress("glFramebufferTexture2DOES"); + glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)glfwGetProcAddress( + "glGetFramebufferAttachmentParameterivOES"); + glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)glfwGetProcAddress("glGenerateMipmapOES"); + } + else + { + log("OpenGL: No framebuffers extension is supported"); + log("OpenGL: Any call to Fbo will crash!"); + return false; + } + } + return true; +} + +// helper +bool GLViewImpl::loadGL() +{ +# if (AX_TARGET_PLATFORM != AX_PLATFORM_MAC) + + // glad: load all OpenGL function pointers + // --------------------------------------- +# if !defined(AX_USE_GLES) + if (!gladLoadGL(glfwGetProcAddress)) + { + log("glad: Failed to Load GL"); + return false; + } +# else + if (!gladLoadGLES2(glfwGetProcAddress)) + { + log("glad: Failed to Load GLES2"); + return false; + } +# endif + + loadFboExtensions(); + +# endif // (AX_TARGET_PLATFORM != AX_PLATFORM_MAC) + + return true; +} + +#endif + +NS_AX_END // end of namespace ax; diff --git a/core/platform/emscripten/GLViewImpl-emscripten.h b/core/platform/emscripten/GLViewImpl-emscripten.h new file mode 100644 index 000000000000..eb490e9f4d84 --- /dev/null +++ b/core/platform/emscripten/GLViewImpl-emscripten.h @@ -0,0 +1,186 @@ +/**************************************************************************** +Copyright (c) 2010-2012 cocos2d-x.org +Copyright (c) 2013-2016 Chukong Technologies Inc. +Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + +https://axmolengine.github.io/ + +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. +****************************************************************************/ +#pragma once +#include "platform/GL.h" +#include "base/Ref.h" +#include "platform/Common.h" +#include "platform/GLView.h" +#include + +NS_AX_BEGIN + +class GLFWEventHandler; +class AX_DLL GLViewImpl : public GLView +{ + friend class GLFWEventHandler; + +public: + static GLViewImpl* create(std::string_view viewName); + static GLViewImpl* create(std::string_view viewName, bool resizable); + static GLViewImpl* createWithRect(std::string_view viewName, + const Rect& size, + float frameZoomFactor = 1.0f, + bool resizable = false); + static GLViewImpl* createWithFullScreen(std::string_view viewName); + static GLViewImpl* createWithFullScreen(std::string_view viewName, + const GLFWvidmode& videoMode, + GLFWmonitor* monitor); + + /* + *frameZoomFactor for frame. This method is for debugging big resolution (e.g.new ipad) app on desktop. + */ + + // void resize(int width, int height); + + float getFrameZoomFactor() const override; + // void centerWindow(); + + virtual void setViewPortInPoints(float x, float y, float w, float h) override; + virtual void setScissorInPoints(float x, float y, float w, float h) override; + virtual Rect getScissorRect() const override; + + bool windowShouldClose() override; + void pollEvents() override; + GLFWwindow* getWindow() const { return _mainWindow; } + + bool isFullscreen() const; + + /* Sets primary monitor full screen with default w*h(refresh rate) */ + void setFullscreen(); + /* Sets primary monitor full screen with w*h(refresh rate) */ + void setFullscreen(int w, int h, int refreshRate); + + /* Sets monitor full screen with default w*h(refresh rate) */ + void setFullscreen(int monitorIndex); + /// + /// Sets monitor full screen with w*h(refresh rate) + /// + /// the 0 based index of monitor + /// the width of hardware resolution in full screen, -1 use default value + /// the height of hardware resolution in full screen, -1 use default value + /// the display refresh rate, usually 60, -1 use default value + void setFullscreen(int monitorIndex, int w, int h, int refreshRate); + + /* for internal use */ + void setFullscreen(GLFWmonitor* monitor, int w, int h, int refreshRate); + void setWindowed(int width, int height); + + int getMonitorCount() const; + Vec2 getMonitorSize() const; + + /* override functions */ + virtual bool isOpenGLReady() override; + virtual void end() override; + virtual void swapBuffers() override; + virtual void setFrameSize(float width, float height) override; + virtual void setIMEKeyboardState(bool bOpen) override; + +#if AX_ICON_SET_SUPPORT + virtual void setIcon(std::string_view filename) const override; + virtual void setIcon(const std::vector& filelist) const override; + virtual void setDefaultIcon() const override; +#endif /* AX_ICON_SET_SUPPORT */ + + /* + * Set zoom factor for frame. This method is for debugging big resolution (e.g.new ipad) app on desktop. + */ + void setFrameZoomFactor(float zoomFactor) override; + /** + * Hide or Show the mouse cursor if there is one. + */ + virtual void setCursorVisible(bool isVisible) override; + /** Retina support is disabled by default + * @note This method is only available on Mac. + */ + void enableRetina(bool enabled); + /** Check whether retina display is enabled. */ + bool isRetinaEnabled() const { return _isRetinaEnabled; }; + + /** Get retina factor */ + int getRetinaFactor() const override { return _retinaFactor; } + +#if (AX_TARGET_PLATFORM == AX_PLATFORM_WIN32) + HWND getWin32Window() override; +#endif /* (AX_TARGET_PLATFORM == AX_PLATFORM_WIN32) */ + +#if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC) + void* getCocoaWindow() override; + void* getNSGLContext() override; // stevetranby: added +#endif // #if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC) + +protected: + GLViewImpl(bool initglfw = true); + virtual ~GLViewImpl(); + + bool initWithRect(std::string_view viewName, const Rect& rect, float frameZoomFactor, bool resizable); + bool initWithFullScreen(std::string_view viewName); + bool initWithFullscreen(std::string_view viewname, const GLFWvidmode& videoMode, GLFWmonitor* monitor); +#if (AX_TARGET_PLATFORM != AX_PLATFORM_MAC) // Windows, Linux: use glad to loadGL + bool loadGL(); +#endif + /* update frame layout when enter/exit full screen mode */ + void updateWindowSize(); + + void updateFrameSize(); + + // GLFW callbacks + void onGLFWError(int errorID, const char* errorDesc); + void onGLFWMouseCallBack(GLFWwindow* window, int button, int action, int modify); + void onGLFWMouseMoveCallBack(GLFWwindow* window, double x, double y); + void onGLFWMouseScrollCallback(GLFWwindow* window, double x, double y); + void onGLFWKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); + void onGLFWCharCallback(GLFWwindow* window, unsigned int character); + void onGLFWWindowPosCallback(GLFWwindow* windows, int x, int y); + void onGLFWWindowSizeCallback(GLFWwindow* window, int width, int height); + void onGLFWWindowIconifyCallback(GLFWwindow* window, int iconified); + void onGLFWWindowFocusCallback(GLFWwindow* window, int focused); + + bool _captured; + bool _isInRetinaMonitor; + bool _isRetinaEnabled; + int _retinaFactor; // Should be 1 or 2 + + float _frameZoomFactor; + + GLFWwindow* _mainWindow; + GLFWmonitor* _monitor; + + std::string _glfwError; + + float _mouseX; + float _mouseY; + +public: + // View will trigger an event when window is resized, gains or loses focus + static const std::string EVENT_WINDOW_RESIZED; + static const std::string EVENT_WINDOW_FOCUSED; + static const std::string EVENT_WINDOW_UNFOCUSED; + +private: + AX_DISALLOW_COPY_AND_ASSIGN(GLViewImpl); +}; + +NS_AX_END // end of namespace cocos2d diff --git a/core/platform/emscripten/PlatformDefine-emscripten.h b/core/platform/emscripten/PlatformDefine-emscripten.h new file mode 100644 index 000000000000..d8b222d198f0 --- /dev/null +++ b/core/platform/emscripten/PlatformDefine-emscripten.h @@ -0,0 +1,43 @@ +/**************************************************************************** +Copyright (c) 2011 Laschweinski +Copyright (c) 2013-2016 Chukong Technologies Inc. +Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + +http://www.cocos2d-x.org + +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. +****************************************************************************/ +#pragma once + +#include + +#define AX_DLL + +#include +#define AX_ASSERT(cond) assert(cond) +#define AX_UNUSED_PARAM(unusedparam) (void)unusedparam + +/* Define NULL pointer value */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif diff --git a/core/platform/emscripten/StdC-emscripten.h b/core/platform/emscripten/StdC-emscripten.h new file mode 100644 index 000000000000..1faa113e04ed --- /dev/null +++ b/core/platform/emscripten/StdC-emscripten.h @@ -0,0 +1,49 @@ +/**************************************************************************** +Copyright (c) 2010 cocos2d-x.org + +http://www.cocos2d-x.org + +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. +****************************************************************************/ + +#ifndef __AX_STD_C_H__ +#define __AX_STD_C_H__ + +#include "platform/PlatformConfig.h" +#include "platform/PlatformMacros.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MIN +#define MIN(x,y) (((x) > (y)) ? (y) : (x)) +#endif // MIN + +#ifndef MAX +#define MAX(x,y) (((x) < (y)) ? (y) : (x)) +#endif // MAX + +#endif // __AX_STD_C_H__ + diff --git a/core/platform/emscripten/devtools-emscripten.cpp b/core/platform/emscripten/devtools-emscripten.cpp new file mode 100644 index 000000000000..f12538017fc9 --- /dev/null +++ b/core/platform/emscripten/devtools-emscripten.cpp @@ -0,0 +1,70 @@ +#include "platform/PlatformConfig.h" +#if AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN + +#include "devtools-emscripten.h" +#include +#include "base/UTF8.h" + +using namespace std; +using namespace cocos2d; + +DevToolsImpl::DevToolsImpl() +{ + _director = Director::getInstance(); + _scheduler = _director->getScheduler(); + _tick = 0; +} + +void DevToolsImpl::update(float /*dt*/) +{ + // tick for 2 frames becuase delta time of the 1st frame after resume is forced to 0 + _tick ++; + if (_tick >= 2) + { + _director->pause(); + _scheduler->unscheduleUpdate(this); + } +} + +void DevToolsImpl::step() +{ + _director->resume(); + _tick = 0; + _scheduler->scheduleUpdate(this, 0, false); +} + +void DevToolsImpl::pause() +{ + _director->pause(); +} + +void DevToolsImpl::resume() +{ + _director->resume(); +} + +DevToolsImpl* DevToolsImpl::getInstance() +{ + static DevToolsImpl instance; + return &instance; +} + +extern "C" +{ + void cocos_ccdirector_pause() + { + DevToolsImpl::getInstance()->pause(); + } + + void cocos_ccdirector_resume() + { + DevToolsImpl::getInstance()->resume(); + } + + void cocos_ccdirector_step() + { + DevToolsImpl::getInstance()->step(); + } +} + +#endif // AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN diff --git a/core/platform/emscripten/devtools-emscripten.h b/core/platform/emscripten/devtools-emscripten.h new file mode 100644 index 000000000000..0ad97453b2a3 --- /dev/null +++ b/core/platform/emscripten/devtools-emscripten.h @@ -0,0 +1,34 @@ +#ifndef __DEVTOOLS_EMSCRIPTEN_H__ +#define __DEVTOOLS_EMSCRIPTEN_H__ + +#include "platform/PlatformConfig.h" +#if AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN + +#include "base/CCScheduler.h" +#include "base/CCDirector.h" + +using namespace cocos2d; + +class DevToolsImpl +{ +public: + DevToolsImpl(); + + void update(float /*dt*/); + + void step(); + + void pause(); + + void resume(); + + static DevToolsImpl* getInstance(); + +private: + unsigned int _tick; + Scheduler* _scheduler; + Director* _director; +}; + +#endif // AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN +#endif // __DEVTOOLS_EMSCRIPTEN_H__ \ No newline at end of file diff --git a/core/renderer/CMakeLists.txt b/core/renderer/CMakeLists.txt index cac1964d0aa9..5ccd67ad3b69 100644 --- a/core/renderer/CMakeLists.txt +++ b/core/renderer/CMakeLists.txt @@ -82,7 +82,7 @@ set(_AX_RENDERER_SRC renderer/backend/RenderPassDescriptor.cpp ) -if(ANDROID OR WINDOWS OR LINUX OR AX_USE_ANGLE) +if(ANDROID OR WINDOWS OR LINUX OR EMSCRIPTEN OR AX_USE_ANGLE) list(APPEND _AX_RENDERER_HEADER renderer/backend/opengl/BufferGL.h diff --git a/core/ui/CMakeLists.txt b/core/ui/CMakeLists.txt index faf92ae99c63..c65f972c966b 100644 --- a/core/ui/CMakeLists.txt +++ b/core/ui/CMakeLists.txt @@ -103,6 +103,10 @@ elseif(LINUX) set(_AX_UI_SPECIFIC_HEADER ui/UIMediaPlayer.h ${_AX_UI_SPECIFIC_HEADER}) set(_AX_UI_SPECIFIC_SRC ui/UIMediaPlayer.cpp ${_AX_UI_SPECIFIC_SRC}) endif() +elseif(EMSCRIPTEN) + set(_AX_UI_SPECIFIC_SRC + ui/UIEditBox/UIEditBoxImpl-stub.cpp + ) elseif(ANDROID) set(_AX_UI_SPECIFIC_HEADER ui/UIWebView/UIWebView.h diff --git a/extensions/ImGui/imgui_impl_ax.cpp b/extensions/ImGui/imgui_impl_ax.cpp index 0cbef698e628..ebd65946f916 100644 --- a/extensions/ImGui/imgui_impl_ax.cpp +++ b/extensions/ImGui/imgui_impl_ax.cpp @@ -388,7 +388,7 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); const char* key_name = glfwGetKeyName(key, scancode); glfwSetErrorCallback(prev_error_callback); -#if (GLFW_VERSION_COMBINED >= 3300) // Eat errors (see #5908) +#if (GLFW_VERSION_COMBINED >= 3300) && !defined(__EMSCRIPTEN__) // Eat errors (see #5908) (void)glfwGetError(NULL); #endif if (key_name && key_name[0] != 0 && key_name[1] == 0) @@ -589,7 +589,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); #endif glfwSetErrorCallback(prev_error_callback); -#if (GLFW_VERSION_COMBINED >= 3300) // Eat errors (see #5785) +#if (GLFW_VERSION_COMBINED >= 3300) && !defined(__EMSCRIPTEN__) // Eat errors (see #5785) (void)glfwGetError(nullptr); #endif @@ -762,7 +762,7 @@ static void ImGui_ImplGlfw_UpdateGamepads() return; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; -#if GLFW_HAS_GAMEPAD_API +#if GLFW_HAS_GAMEPAD_API && !defined(__EMSCRIPTEN__) GLFWgamepadstate gamepad; if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) return; @@ -1165,7 +1165,7 @@ static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*) //-------------------------------------------------------------------------------------------------------- // Avoid including so we can build without it -#if GLFW_HAS_VULKAN +#if GLFW_HAS_VULKAN && !defined(__EMSCRIPTEN__) #ifndef VULKAN_H_ #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; #if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) @@ -1211,7 +1211,7 @@ static void ImGui_ImplGlfw_InitPlatformInterface() #if GLFW_HAS_WINDOW_ALPHA platform_io.Platform_SetWindowAlpha = ImGui_ImplGlfw_SetWindowAlpha; #endif -#if GLFW_HAS_VULKAN +#if GLFW_HAS_VULKAN && !defined(__EMSCRIPTEN__) platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface; #endif diff --git a/extensions/scripting/lua-bindings/auto/axlua_audioengine_auto.cpp b/extensions/scripting/lua-bindings/auto/axlua_audioengine_auto.cpp index ee7e8d83c831..fddf653e6e93 100644 --- a/extensions/scripting/lua-bindings/auto/axlua_audioengine_auto.cpp +++ b/extensions/scripting/lua-bindings/auto/axlua_audioengine_auto.cpp @@ -1,5 +1,5 @@ #include "scripting/lua-bindings/auto/axlua_audioengine_auto.hpp" -#if AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_IOS || AX_TARGET_PLATFORM == AX_PLATFORM_MAC || defined(_WIN32) || AX_TARGET_PLATFORM == AX_PLATFORM_LINUX +#if AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_IOS || AX_TARGET_PLATFORM == AX_PLATFORM_MAC || defined(_WIN32) || AX_TARGET_PLATFORM == AX_PLATFORM_LINUX || AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN #include "audio/AudioEngine.h" #include "scripting/lua-bindings/manual/tolua_fix.h" #include "scripting/lua-bindings/manual/LuaBasicConversions.h" diff --git a/extensions/scripting/lua-bindings/auto/axlua_audioengine_auto.hpp b/extensions/scripting/lua-bindings/auto/axlua_audioengine_auto.hpp index e2452f761576..472209a05315 100644 --- a/extensions/scripting/lua-bindings/auto/axlua_audioengine_auto.hpp +++ b/extensions/scripting/lua-bindings/auto/axlua_audioengine_auto.hpp @@ -1,5 +1,5 @@ #include "base/Config.h" -#if AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_IOS || AX_TARGET_PLATFORM == AX_PLATFORM_MAC || defined(_WIN32) || AX_TARGET_PLATFORM == AX_PLATFORM_LINUX +#if AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_IOS || AX_TARGET_PLATFORM == AX_PLATFORM_MAC || defined(_WIN32) || AX_TARGET_PLATFORM == AX_PLATFORM_LINUX || AX_TARGET_PLATFORM == AX_PLATFORM_EMSCRIPTEN #ifndef __ax_audioengine_h__ #define __ax_audioengine_h__ diff --git a/extensions/scripting/lua-bindings/manual/cocostudio/CustomGUIReader.cpp b/extensions/scripting/lua-bindings/manual/cocostudio/CustomGUIReader.cpp index 498dc7abfb15..fe79375f5bbb 100644 --- a/extensions/scripting/lua-bindings/manual/cocostudio/CustomGUIReader.cpp +++ b/extensions/scripting/lua-bindings/manual/cocostudio/CustomGUIReader.cpp @@ -22,8 +22,8 @@ THE SOFTWARE. ****************************************************************************/ -#include "scripting/lua-bindings/manual/cocostudio/CustomGUIReader.h" #include "scripting/lua-bindings/manual/LuaEngine.h" +#include "scripting/lua-bindings/manual/cocostudio/CustomGUIReader.h" #include "rapidjson/writer.h" #include "rapidjson/stringbuffer.h" diff --git a/templates/cpp-template-default/CMakeLists.txt b/templates/cpp-template-default/CMakeLists.txt index bab7390a28be..c776d667841e 100644 --- a/templates/cpp-template-default/CMakeLists.txt +++ b/templates/cpp-template-default/CMakeLists.txt @@ -94,6 +94,11 @@ elseif(LINUX) proj.linux/main.cpp ) list(APPEND GAME_SOURCE ${common_content_files}) +elseif(EMSCRIPTEN) + list(APPEND GAME_SOURCE + proj.emscripten/main.cpp + ) + list(APPEND GAME_SOURCE ${common_content_files}) elseif(WINDOWS) if(NOT WINRT) list(APPEND GAME_HEADER @@ -227,3 +232,29 @@ endif() if (NOT DEFINED BUILD_ENGINE_DONE) ax_uwp_set_all_targets_deploy_min_version() endif() + +set(GAME_RES_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/Content") + +if(EMSCRIPTEN) + set(CMAKE_C_FLAGS "-s FORCE_FILESYSTEM=1 -s FETCH=1 -s USE_GLFW=3 -s VERBOSE=1 -s USE_LIBJPEG=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_FREETYPE=1") + set(CMAKE_CXX_FLAGS "-s FORCE_FILESYSTEM=1 -s FETCH=1 -s USE_GLFW=3 -s VERBOSE=1 -s USE_LIBJPEG=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_FREETYPE=1") +endif() + +if(EMSCRIPTEN) + set(CMAKE_EXECUTABLE_SUFFIX ".html") + target_link_options(${APP_NAME} PRIVATE + "-sEXPORTED_FUNCTIONS=[_main]" + "-sEXPORTED_RUNTIME_METHODS=[ccall,cwrap]" + ) + set(EMSCRIPTEN_LINK_FLAGS "-lidbfs.js -s MIN_WEBGL_VERSION=2 -s MAX_WEBGL_VERSION=2 -s INITIAL_MEMORY=512MB --shell-file ${CMAKE_CURRENT_SOURCE_DIR}/index.html --use-preload-cache") + # Disable wasm, generate js build + # string(APPEND EMSCRIPTEN_LINK_FLAGS " -s WASM=0") + # string(APPEND EMSCRIPTEN_LINK_FLAGS " -s SEPARATE_DWARF_URL=https://xxx:8080/axmolwasm/axmolwasm/build/HelloLua.debug.wasm") + # string(APPEND EMSCRIPTEN_LINK_FLAGS " -gseparate-dwarf=HelloLua.debug.wasm") + + foreach(FOLDER IN LISTS GAME_RES_FOLDER) + string(APPEND EMSCRIPTEN_LINK_FLAGS " --preload-file ${FOLDER}/@/") + endforeach() + + set_target_properties(${APP_NAME} PROPERTIES LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS}") +endif() \ No newline at end of file diff --git a/templates/cpp-template-default/index.html b/templates/cpp-template-default/index.html new file mode 100644 index 000000000000..dc5d71b2d949 --- /dev/null +++ b/templates/cpp-template-default/index.html @@ -0,0 +1,150 @@ + + + + + + Emscripten-Generated Code + + + +
+
Downloading...
+
+ +
+ +
+
+ Resize canvas + Lock/hide mouse pointer +     + + | + + + +
+ +
+ +
+ + + + {{{ SCRIPT }}} + + diff --git a/templates/cpp-template-default/proj.emscripten/main.cpp b/templates/cpp-template-default/proj.emscripten/main.cpp new file mode 100644 index 000000000000..b265379f11bc --- /dev/null +++ b/templates/cpp-template-default/proj.emscripten/main.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + + https://axmolengine.github.io/ + + 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. + ****************************************************************************/ + +#include "AppDelegate.h" +#include "cocos2d.h" + +#include +#include +#include +#include + +USING_NS_AX; + +int main(int argc, char** argv) +{ + // create the application instance + AppDelegate app; + return Application::getInstance()->run(); +} diff --git a/templates/lua-template-default/CMakeLists.txt b/templates/lua-template-default/CMakeLists.txt index 5c1c16e40b07..2d3cdfeb81a1 100644 --- a/templates/lua-template-default/CMakeLists.txt +++ b/templates/lua-template-default/CMakeLists.txt @@ -107,6 +107,11 @@ elseif(LINUX) proj.linux/main.cpp ) list(APPEND GAME_SOURCE ${common_content_files}) +elseif(EMSCRIPTEN) + list(APPEND GAME_SOURCE + proj.emscripten/main.cpp + ) + list(APPEND GAME_SOURCE ${common_content_files}) elseif(WINDOWS) if(NOT WINRT) list(APPEND GAME_HEADER @@ -253,3 +258,29 @@ endif() if (NOT DEFINED BUILD_ENGINE_DONE) ax_uwp_set_all_targets_deploy_min_version() endif() + +set(GAME_RES_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/Content") + +if(EMSCRIPTEN) + set(CMAKE_C_FLAGS "-s FORCE_FILESYSTEM=1 -s FETCH=1 -s USE_GLFW=3 -s VERBOSE=1 -s USE_LIBJPEG=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_FREETYPE=1") + set(CMAKE_CXX_FLAGS "-s FORCE_FILESYSTEM=1 -s FETCH=1 -s USE_GLFW=3 -s VERBOSE=1 -s USE_LIBJPEG=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_FREETYPE=1") +endif() + +if(EMSCRIPTEN) + set(CMAKE_EXECUTABLE_SUFFIX ".html") + target_link_options(${APP_NAME} PRIVATE + "-sEXPORTED_FUNCTIONS=[_main]" + "-sEXPORTED_RUNTIME_METHODS=[ccall,cwrap]" + ) + set(EMSCRIPTEN_LINK_FLAGS "-lidbfs.js -s MIN_WEBGL_VERSION=2 -s MAX_WEBGL_VERSION=2 -s INITIAL_MEMORY=512MB --shell-file ${CMAKE_CURRENT_SOURCE_DIR}/index.html --use-preload-cache") + # Disable wasm, generate js build + # string(APPEND EMSCRIPTEN_LINK_FLAGS " -s WASM=0") + # string(APPEND EMSCRIPTEN_LINK_FLAGS " -s SEPARATE_DWARF_URL=https://xxx:8080/axmolwasm/axmolwasm/build/HelloLua.debug.wasm") + # string(APPEND EMSCRIPTEN_LINK_FLAGS " -gseparate-dwarf=HelloLua.debug.wasm") + + foreach(FOLDER IN LISTS GAME_RES_FOLDER) + string(APPEND EMSCRIPTEN_LINK_FLAGS " --preload-file ${FOLDER}/@/") + endforeach() + + set_target_properties(${APP_NAME} PROPERTIES LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS}") +endif() \ No newline at end of file diff --git a/templates/lua-template-default/index.html b/templates/lua-template-default/index.html new file mode 100644 index 000000000000..dc5d71b2d949 --- /dev/null +++ b/templates/lua-template-default/index.html @@ -0,0 +1,150 @@ + + + + + + Emscripten-Generated Code + + + +
+
Downloading...
+
+ +
+ +
+
+ Resize canvas + Lock/hide mouse pointer +     + + | + + + +
+ +
+ +
+ + + + {{{ SCRIPT }}} + + diff --git a/templates/lua-template-default/proj.emscripten/main.cpp b/templates/lua-template-default/proj.emscripten/main.cpp new file mode 100644 index 000000000000..b265379f11bc --- /dev/null +++ b/templates/lua-template-default/proj.emscripten/main.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + + https://axmolengine.github.io/ + + 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. + ****************************************************************************/ + +#include "AppDelegate.h" +#include "cocos2d.h" + +#include +#include +#include +#include + +USING_NS_AX; + +int main(int argc, char** argv) +{ + // create the application instance + AppDelegate app; + return Application::getInstance()->run(); +} diff --git a/tests/cpp-tests/CMakeLists.txt b/tests/cpp-tests/CMakeLists.txt index 68db3d55c0f2..1728b8c8da71 100644 --- a/tests/cpp-tests/CMakeLists.txt +++ b/tests/cpp-tests/CMakeLists.txt @@ -62,6 +62,11 @@ elseif(LINUX) proj.linux/main.cpp ) list(APPEND GAME_SOURCE ${common_content_files}) +elseif(EMSCRIPTEN) + list(APPEND GAME_SOURCE + proj.emscripten/main.cpp + ) + list(APPEND GAME_SOURCE ${common_content_files}) elseif(WINDOWS) if(NOT WINRT) list(APPEND GAME_HEADER @@ -610,3 +615,27 @@ endif() if (NOT DEFINED BUILD_ENGINE_DONE) ax_uwp_set_all_targets_deploy_min_version() endif() + +set(GAME_RES_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/Content") + +if(EMSCRIPTEN) + set(CMAKE_C_FLAGS "-s FORCE_FILESYSTEM=1 -s FETCH=1 -s USE_GLFW=3 -s VERBOSE=1 -s USE_LIBJPEG=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_FREETYPE=1") + set(CMAKE_CXX_FLAGS "-s FORCE_FILESYSTEM=1 -s FETCH=1 -s USE_GLFW=3 -s VERBOSE=1 -s USE_LIBJPEG=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_FREETYPE=1") +endif() + +if(EMSCRIPTEN) + set(CMAKE_EXECUTABLE_SUFFIX ".html") + target_link_options(${APP_NAME} PRIVATE + "-sEXPORTED_FUNCTIONS=[_main]" + "-sEXPORTED_RUNTIME_METHODS=[ccall,cwrap]" + ) + set(EMSCRIPTEN_LINK_FLAGS "-lidbfs.js -s MIN_WEBGL_VERSION=2 -s MAX_WEBGL_VERSION=2 -s INITIAL_MEMORY=512MB --shell-file ${CMAKE_CURRENT_SOURCE_DIR}/index.html --use-preload-cache") + # Disable wasm, generate js build + # string(APPEND EMSCRIPTEN_LINK_FLAGS " -s WASM=0") + + foreach(FOLDER IN LISTS GAME_RES_FOLDER) + string(APPEND EMSCRIPTEN_LINK_FLAGS " --preload-file ${FOLDER}/@/") + endforeach() + + set_target_properties(${APP_NAME} PROPERTIES LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS}") +endif() diff --git a/tests/cpp-tests/Source/CurlTest/CurlTest.cpp b/tests/cpp-tests/Source/CurlTest/CurlTest.cpp index a9c3d5bb3bcd..edee52deb84f 100644 --- a/tests/cpp-tests/Source/CurlTest/CurlTest.cpp +++ b/tests/cpp-tests/Source/CurlTest/CurlTest.cpp @@ -22,6 +22,8 @@ THE SOFTWARE. ****************************************************************************/ +#if (AX_TARGET_PLATFORM != AX_PLATFORM_EMSCRIPTEN) + #include "platform/PlatformConfig.h" #include "CurlTest.h" #include "stdio.h" @@ -125,3 +127,5 @@ CurlTest::~CurlTest() { _label->release(); } + +#endif \ No newline at end of file diff --git a/tests/cpp-tests/Source/CurlTest/CurlTest.h b/tests/cpp-tests/Source/CurlTest/CurlTest.h index dc537d85bf0e..2015bdd5fc41 100644 --- a/tests/cpp-tests/Source/CurlTest/CurlTest.h +++ b/tests/cpp-tests/Source/CurlTest/CurlTest.h @@ -25,6 +25,8 @@ #ifndef _CURL_TEST_H_ #define _CURL_TEST_H_ +#if (AX_TARGET_PLATFORM != AX_PLATFORM_EMSCRIPTEN) + #include "axmol.h" #include "../BaseTest.h" @@ -44,4 +46,6 @@ class CurlTest : public TestCase ax::Label* _label; }; +#endif + #endif // _CURL_TEST_H_ diff --git a/tests/cpp-tests/Source/FileUtilsTest/FileUtilsTest.cpp b/tests/cpp-tests/Source/FileUtilsTest/FileUtilsTest.cpp index 194b84d7fa0b..ab60928ff673 100644 --- a/tests/cpp-tests/Source/FileUtilsTest/FileUtilsTest.cpp +++ b/tests/cpp-tests/Source/FileUtilsTest/FileUtilsTest.cpp @@ -63,7 +63,7 @@ void TestSearchPath::onEnter() std::string writablePath = sharedFileUtils->getWritablePath(); std::string fileName = writablePath + "external.txt"; char szBuf[100] = "Hello Cocos2d-x!"; - FILE* fp = fopen(fileName.c_str(), "wb"); + auto fp = fopen(fileName.c_str(), "wb"); if (fp) { size_t ret = fwrite(szBuf, 1, strlen(szBuf), fp); @@ -258,7 +258,7 @@ void TestFileFuncs::onEnter() std::string content = "Test string content to put into created file"; std::string msg; - FILE* out = fopen(filepath.c_str(), "w"); + auto out = fopen(filepath.c_str(), "w"); fputs(content.c_str(), out); fclose(out); @@ -1002,7 +1002,7 @@ void TestFileFuncsAsync::onEnter() std::string content = "Test string content to put into created file"; std::string msg; - FILE* out = fopen(filepath.c_str(), "w"); + auto out = fopen(filepath.c_str(), "w"); fputs(content.c_str(), out); fclose(out); diff --git a/tests/cpp-tests/Source/ImGuiTest/ImGuiTest.cpp b/tests/cpp-tests/Source/ImGuiTest/ImGuiTest.cpp index a65d979a1c84..a8d2c4b31cc4 100644 --- a/tests/cpp-tests/Source/ImGuiTest/ImGuiTest.cpp +++ b/tests/cpp-tests/Source/ImGuiTest/ImGuiTest.cpp @@ -6,7 +6,7 @@ USING_NS_AX; USING_NS_AX_EXT; -#if defined(AX_PLATFORM_PC) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID) +#if defined(AX_PLATFORM_PC) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID) || defined(__EMSCRIPTEN__) static bool show_test_window = true; static bool show_another_window = true; diff --git a/tests/cpp-tests/Source/ImGuiTest/ImGuiTest.h b/tests/cpp-tests/Source/ImGuiTest/ImGuiTest.h index 4aea46080123..57341dc1cae1 100644 --- a/tests/cpp-tests/Source/ImGuiTest/ImGuiTest.h +++ b/tests/cpp-tests/Source/ImGuiTest/ImGuiTest.h @@ -30,7 +30,7 @@ #include "axmol.h" #include "../BaseTest.h" -#if defined(AX_PLATFORM_PC) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID) +#if defined(AX_PLATFORM_PC) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID) || defined(__EMSCRIPTEN__) DEFINE_TEST_SUITE(ImGuiTests); diff --git a/tests/cpp-tests/Source/NewAudioEngineTest/NewAudioEngineTest.cpp b/tests/cpp-tests/Source/NewAudioEngineTest/NewAudioEngineTest.cpp index e984ed7c4a42..b4d7e1c80fe0 100644 --- a/tests/cpp-tests/Source/NewAudioEngineTest/NewAudioEngineTest.cpp +++ b/tests/cpp-tests/Source/NewAudioEngineTest/NewAudioEngineTest.cpp @@ -1128,7 +1128,7 @@ void AudioPlayFileInWritablePath::onEnter() if (!fileUtils->isFileExist(saveFilePath)) { Data data = fileUtils->getDataFromFile(musicFile); - FILE* fp = fopen(saveFilePath.c_str(), "wb"); + auto fp = fopen(saveFilePath.c_str(), "wb"); if (fp != nullptr) { fwrite(data.getBytes(), data.getSize(), 1, fp); diff --git a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/CocosGUIScene.cpp b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/CocosGUIScene.cpp index 64484ffcb474..e552844daff6 100644 --- a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/CocosGUIScene.cpp +++ b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/CocosGUIScene.cpp @@ -62,7 +62,7 @@ GUIDynamicCreateTests::GUIDynamicCreateTests() { -#if AX_TARGET_PLATFORM != AX_PLATFORM_LINUX || defined(AX_ENABLE_VLC_MEDIA) +#if (AX_TARGET_PLATFORM != AX_PLATFORM_LINUX && AX_TARGET_PLATFORM != AX_PLATFORM_EMSCRIPTEN) || defined(AX_ENABLE_VLC_MEDIA) addTest("VideoPlayer Test", []() { return new VideoPlayerTests; }); #endif #if (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_IOS) && \ diff --git a/tests/cpp-tests/Source/controller.cpp b/tests/cpp-tests/Source/controller.cpp index f192999b7ac9..285c59918f13 100644 --- a/tests/cpp-tests/Source/controller.cpp +++ b/tests/cpp-tests/Source/controller.cpp @@ -48,7 +48,7 @@ class RootTests : public TestList #endif // addTest("Node: Scene3D", [](){return new Scene3DTests(); }); -#if defined(AX_PLATFORM_PC) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID) +#if defined(AX_PLATFORM_PC) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID) || defined(__EMSCRIPTEN__) addTest("ImGui", []() { return new ImGuiTests(); }); #endif addTest("Texture2D", []() { return new Texture2DTests(); }); @@ -70,7 +70,9 @@ class RootTests : public TestList addTest("Click and Move", []() { return new ClickAndMoveTest(); }); addTest("Configuration", []() { return new ConfigurationTests(); }); addTest("Console", []() { return new ConsoleTests(); }); +#if !defined(AX_PLATFORM_EMSCRIPTEN) addTest("Curl", []() { return new CurlTests(); }); +#endif addTest("Current Language", []() { return new CurrentLanguageTests(); }); addTest("Network Test", []() { return new NetworkTests(); }); addTest("EventDispatcher", []() { return new EventDispatcherTests(); }); diff --git a/tests/cpp-tests/Source/tests.h b/tests/cpp-tests/Source/tests.h index 87306a3eb7ef..cfeaf1684d15 100644 --- a/tests/cpp-tests/Source/tests.h +++ b/tests/cpp-tests/Source/tests.h @@ -123,7 +123,7 @@ #include "ZwoptexTest/ZwoptexTest.h" #include "SpriteFrameCacheTest/SpriteFrameCacheTest.h" #include "ZipTest/ZipTests.h" -#if defined(AX_PLATFORM_PC) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID) +#if defined(AX_PLATFORM_PC) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID) || defined(__EMSCRIPTEN__) # include "ImGuiTest/ImGuiTest.h" #endif #endif diff --git a/tests/cpp-tests/index.html b/tests/cpp-tests/index.html new file mode 100644 index 000000000000..dc5d71b2d949 --- /dev/null +++ b/tests/cpp-tests/index.html @@ -0,0 +1,150 @@ + + + + + + Emscripten-Generated Code + + + +
+
Downloading...
+
+ +
+ +
+
+ Resize canvas + Lock/hide mouse pointer +     + + | + + + +
+ +
+ +
+ + + + {{{ SCRIPT }}} + + diff --git a/tests/cpp-tests/proj.emscripten/main.cpp b/tests/cpp-tests/proj.emscripten/main.cpp new file mode 100644 index 000000000000..b265379f11bc --- /dev/null +++ b/tests/cpp-tests/proj.emscripten/main.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + + https://axmolengine.github.io/ + + 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. + ****************************************************************************/ + +#include "AppDelegate.h" +#include "cocos2d.h" + +#include +#include +#include +#include + +USING_NS_AX; + +int main(int argc, char** argv) +{ + // create the application instance + AppDelegate app; + return Application::getInstance()->run(); +} diff --git a/tests/fairygui-tests/CMakeLists.txt b/tests/fairygui-tests/CMakeLists.txt index ae85a52b7d3c..f7f8d30ddfd9 100644 --- a/tests/fairygui-tests/CMakeLists.txt +++ b/tests/fairygui-tests/CMakeLists.txt @@ -74,6 +74,11 @@ elseif(LINUX) proj.linux/main.cpp ) list(APPEND GAME_SOURCE ${common_content_files}) +elseif(EMSCRIPTEN) + list(APPEND GAME_SOURCE + proj.emscripten/main.cpp + ) + list(APPEND GAME_SOURCE ${common_content_files}) elseif(WINDOWS) if(NOT WINRT) list(APPEND GAME_HEADER @@ -190,3 +195,27 @@ endif() if (NOT DEFINED BUILD_ENGINE_DONE) ax_uwp_set_all_targets_deploy_min_version() endif() + +set(GAME_RES_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/Content") + +if(EMSCRIPTEN) + set(CMAKE_C_FLAGS "-s FORCE_FILESYSTEM=1 -s FETCH=1 -s USE_GLFW=3 -s VERBOSE=1 -s USE_LIBJPEG=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_FREETYPE=1") + set(CMAKE_CXX_FLAGS "-s FORCE_FILESYSTEM=1 -s FETCH=1 -s USE_GLFW=3 -s VERBOSE=1 -s USE_LIBJPEG=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_FREETYPE=1") +endif() + +if(EMSCRIPTEN) + set(CMAKE_EXECUTABLE_SUFFIX ".html") + target_link_options(${APP_NAME} PRIVATE + "-sEXPORTED_FUNCTIONS=[_main]" + "-sEXPORTED_RUNTIME_METHODS=[ccall,cwrap]" + ) + set(EMSCRIPTEN_LINK_FLAGS "-lidbfs.js -s MIN_WEBGL_VERSION=2 -s MAX_WEBGL_VERSION=2 -s INITIAL_MEMORY=512MB --shell-file ${CMAKE_CURRENT_SOURCE_DIR}/index.html --use-preload-cache") + # Disable wasm, generate js build + # string(APPEND EMSCRIPTEN_LINK_FLAGS " -s WASM=0") + + foreach(FOLDER IN LISTS GAME_RES_FOLDER) + string(APPEND EMSCRIPTEN_LINK_FLAGS " --preload-file ${FOLDER}/@/") + endforeach() + + set_target_properties(${APP_NAME} PROPERTIES LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS}") +endif() \ No newline at end of file diff --git a/tests/fairygui-tests/index.html b/tests/fairygui-tests/index.html new file mode 100644 index 000000000000..dc5d71b2d949 --- /dev/null +++ b/tests/fairygui-tests/index.html @@ -0,0 +1,150 @@ + + + + + + Emscripten-Generated Code + + + +
+
Downloading...
+
+ +
+ +
+
+ Resize canvas + Lock/hide mouse pointer +     + + | + + + +
+ +
+ +
+ + + + {{{ SCRIPT }}} + + diff --git a/tests/fairygui-tests/proj.emscripten/main.cpp b/tests/fairygui-tests/proj.emscripten/main.cpp new file mode 100644 index 000000000000..b265379f11bc --- /dev/null +++ b/tests/fairygui-tests/proj.emscripten/main.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + + https://axmolengine.github.io/ + + 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. + ****************************************************************************/ + +#include "AppDelegate.h" +#include "cocos2d.h" + +#include +#include +#include +#include + +USING_NS_AX; + +int main(int argc, char** argv) +{ + // create the application instance + AppDelegate app; + return Application::getInstance()->run(); +} diff --git a/tests/live2d-tests/CMakeLists.txt b/tests/live2d-tests/CMakeLists.txt index b64fcb323837..bc380c232798 100644 --- a/tests/live2d-tests/CMakeLists.txt +++ b/tests/live2d-tests/CMakeLists.txt @@ -74,6 +74,11 @@ elseif(LINUX) proj.linux/main.cpp ) list(APPEND GAME_SOURCE ${common_content_files}) +elseif(EMSCRIPTEN) + list(APPEND GAME_SOURCE + proj.emscripten/main.cpp + ) + list(APPEND GAME_SOURCE ${common_content_files}) elseif(WINDOWS) if(NOT WINRT) list(APPEND GAME_HEADER diff --git a/tests/live2d-tests/index.html b/tests/live2d-tests/index.html new file mode 100644 index 000000000000..dc5d71b2d949 --- /dev/null +++ b/tests/live2d-tests/index.html @@ -0,0 +1,150 @@ + + + + + + Emscripten-Generated Code + + + +
+
Downloading...
+
+ +
+ +
+
+ Resize canvas + Lock/hide mouse pointer +     + + | + + + +
+ +
+ +
+ + + + {{{ SCRIPT }}} + + diff --git a/tests/live2d-tests/proj.emscripten/main.cpp b/tests/live2d-tests/proj.emscripten/main.cpp new file mode 100644 index 000000000000..b265379f11bc --- /dev/null +++ b/tests/live2d-tests/proj.emscripten/main.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + + https://axmolengine.github.io/ + + 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. + ****************************************************************************/ + +#include "AppDelegate.h" +#include "cocos2d.h" + +#include +#include +#include +#include + +USING_NS_AX; + +int main(int argc, char** argv) +{ + // create the application instance + AppDelegate app; + return Application::getInstance()->run(); +} diff --git a/tests/lua-tests/CMakeLists.txt b/tests/lua-tests/CMakeLists.txt index 11fd2dbe5513..7be1ef48ff16 100644 --- a/tests/lua-tests/CMakeLists.txt +++ b/tests/lua-tests/CMakeLists.txt @@ -74,6 +74,11 @@ elseif(LINUX) proj.linux/main.cpp ) list(APPEND GAME_SOURCE ${common_content_files}) +elseif(EMSCRIPTEN) + list(APPEND GAME_SOURCE + proj.emscripten/main.cpp + ) + list(APPEND GAME_SOURCE ${common_content_files}) elseif(WINDOWS) if(NOT WINRT) list(APPEND GAME_HEADER @@ -234,3 +239,27 @@ endif() if (NOT DEFINED BUILD_ENGINE_DONE) ax_uwp_set_all_targets_deploy_min_version() endif() + +set(GAME_RES_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/Content") + +if(EMSCRIPTEN) + set(CMAKE_C_FLAGS "-s FORCE_FILESYSTEM=1 -s FETCH=1 -s USE_GLFW=3 -s VERBOSE=1 -s USE_LIBJPEG=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_FREETYPE=1") + set(CMAKE_CXX_FLAGS "-s FORCE_FILESYSTEM=1 -s FETCH=1 -s USE_GLFW=3 -s VERBOSE=1 -s USE_LIBJPEG=1 -s USE_LIBPNG=1 -s USE_ZLIB=1 -s USE_FREETYPE=1") +endif() + +if(EMSCRIPTEN) + set(CMAKE_EXECUTABLE_SUFFIX ".html") + target_link_options(${APP_NAME} PRIVATE + "-sEXPORTED_FUNCTIONS=[_main]" + "-sEXPORTED_RUNTIME_METHODS=[ccall,cwrap]" + ) + set(EMSCRIPTEN_LINK_FLAGS "-lidbfs.js -s MIN_WEBGL_VERSION=2 -s MAX_WEBGL_VERSION=2 -s INITIAL_MEMORY=512MB --shell-file ${CMAKE_CURRENT_SOURCE_DIR}/index.html --use-preload-cache") + # Disable wasm, generate js build + # string(APPEND EMSCRIPTEN_LINK_FLAGS " -s WASM=0") + + foreach(FOLDER IN LISTS GAME_RES_FOLDER) + string(APPEND EMSCRIPTEN_LINK_FLAGS " --preload-file ${FOLDER}/@/") + endforeach() + + set_target_properties(${APP_NAME} PROPERTIES LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS}") +endif() \ No newline at end of file diff --git a/tests/lua-tests/index.html b/tests/lua-tests/index.html new file mode 100644 index 000000000000..dc5d71b2d949 --- /dev/null +++ b/tests/lua-tests/index.html @@ -0,0 +1,150 @@ + + + + + + Emscripten-Generated Code + + + +
+
Downloading...
+
+ +
+ +
+
+ Resize canvas + Lock/hide mouse pointer +     + + | + + + +
+ +
+ +
+ + + + {{{ SCRIPT }}} + + diff --git a/tests/lua-tests/proj.emscripten/main.cpp b/tests/lua-tests/proj.emscripten/main.cpp new file mode 100644 index 000000000000..b265379f11bc --- /dev/null +++ b/tests/lua-tests/proj.emscripten/main.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + + https://axmolengine.github.io/ + + 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. + ****************************************************************************/ + +#include "AppDelegate.h" +#include "cocos2d.h" + +#include +#include +#include +#include + +USING_NS_AX; + +int main(int argc, char** argv) +{ + // create the application instance + AppDelegate app; + return Application::getInstance()->run(); +} diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index ccff04cc15a2..3aab71746b88 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -184,6 +184,7 @@ if(ANDROID) add_subdirectory(android-specific/cpufeatures) endif() +if(NOT EMSCRIPTEN) add_subdirectory(zlib) target_link_libraries(thirdparty dep_zlib @@ -201,6 +202,7 @@ configure_target_outdir(png) set(PNG_PNG_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/png" CACHE STRING "png include dir" FORCE) set(PNG_LIBRARY "png" CACHE STRING "png include dir" FORCE) +endif(NOT EMSCRIPTEN) if(AX_WITH_BOX2D) set(BOX2D_BUILD_UNIT_TESTS OFF CACHE BOOL "Build the Box2D unit tests" FORCE) @@ -234,7 +236,7 @@ if(AX_WITH_CHIPMUNK) target_link_libraries(thirdparty chipmunk) configure_target_outdir(chipmunk) endif(AX_WITH_CHIPMUNK) -if(AX_WITH_FREETYPE) +if(AX_WITH_FREETYPE AND NOT EMSCRIPTEN) if (NOT LINUX) set(FT_WITH_ZLIB ON CACHE BOOL "Use system zlib instead of internal library." FORCE) set(DISABLE_FORCE_DEBUG_POSTFIX ON CACHE BOOL "" FORCE) @@ -250,7 +252,7 @@ if(AX_WITH_FREETYPE) endif() endif() target_include_directories(thirdparty PUBLIC "freetype/include") -endif(AX_WITH_FREETYPE) +endif(AX_WITH_FREETYPE AND NOT EMSCRIPTEN) if(AX_WITH_RECAST) add_subdirectory(recast) target_link_libraries(thirdparty recast) @@ -261,10 +263,14 @@ if(AX_WITH_BULLET) target_link_libraries(thirdparty bullet) configure_target_outdir(bullet) endif(AX_WITH_BULLET) + +if(NOT EMSCRIPTEN) if(AX_WITH_JPEG AND NOT WINRT) add_subdirectory(jpeg-turbo) target_link_libraries(thirdparty dep_jpeg-turbo) endif() +endif(NOT EMSCRIPTEN) + if(AX_WITH_OPENSSL) add_subdirectory(openssl) if(ANDROID OR LINUX) @@ -275,11 +281,13 @@ if(AX_WITH_OPENSSL) endif() target_compile_definitions(thirdparty PUBLIC OPENSSL_SUPPRESS_DEPRECATED=1) endif(AX_WITH_OPENSSL) +if(NOT EMSCRIPTEN) if(AX_WITH_WEBP) add_subdirectory(webp) target_link_libraries(thirdparty webp) configure_target_outdir(webp) endif(AX_WITH_WEBP) +endif(NOT EMSCRIPTEN) if(AX_WITH_PUGIXML) add_subdirectory(pugixml) target_link_libraries(thirdparty pugixml) @@ -338,7 +346,7 @@ if(AX_ENABLE_EXT_LUA) target_compile_definitions(lua-cjson PRIVATE USING_LUAJIT=1) endif() endif() - +if(NOT EMSCRIPTEN) if(AX_WITH_CURL) add_subdirectory(curl) if(ANDROID OR LINUX) @@ -346,7 +354,7 @@ if(AX_WITH_CURL) endif() target_link_libraries(thirdparty libcurl) endif(AX_WITH_CURL) - +endif(NOT EMSCRIPTEN) # The openal-soft(LGPL 2.1) if(AX_USE_ALSOFT) set(ALSOFT_DLOPEN OFF CACHE BOOL "Check for the dlopen API for loading optional libs" FORCE) @@ -389,7 +397,7 @@ add_subdirectory(ogg) target_link_libraries(thirdparty ogg) configure_target_outdir(ogg) -if(WINDOWS OR LINUX OR (ANDROID AND AX_USE_GLAD)) +if(WINDOWS OR LINUX OR EMSCRIPTEN OR (ANDROID AND AX_USE_GLAD)) add_subdirectory(glad) target_link_libraries(thirdparty glad) configure_target_outdir(glad) diff --git a/thirdparty/astcenc/CMakeLists.txt b/thirdparty/astcenc/CMakeLists.txt index 235979da8b56..66316617957f 100644 --- a/thirdparty/astcenc/CMakeLists.txt +++ b/thirdparty/astcenc/CMakeLists.txt @@ -81,6 +81,11 @@ if (NOT DEFINED ASTC_ISA_SIMD) set(ASTC_ISA_SIMD "none") endif() + # disable simd when emscripten + if(EMSCRIPTEN) + set(ASTC_ISA_SIMD "none") + endif(EMSCRIPTEN) + message(AUTHOR_WARNING "ASTC_ISA_SIMD=${ASTC_ISA_SIMD}") endif() diff --git a/thirdparty/openal/CMakeLists.txt b/thirdparty/openal/CMakeLists.txt index 34fd33122305..58ecf3c4c560 100644 --- a/thirdparty/openal/CMakeLists.txt +++ b/thirdparty/openal/CMakeLists.txt @@ -572,7 +572,7 @@ check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) check_symbol_exists(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) check_symbol_exists(proc_pidpath libproc.h HAVE_PROC_PIDPATH) -if(NOT WIN32) +if(NOT WIN32 AND NOT EMSCRIPTEN) # We need pthreads outside of Windows, for semaphores. It's also used to # set the priority and name of threads, when possible. check_include_file(pthread.h HAVE_PTHREAD_H) diff --git a/thirdparty/openal/core/helpers.cpp b/thirdparty/openal/core/helpers.cpp index 2eccc50f603b..0bc586d42d6b 100644 --- a/thirdparty/openal/core/helpers.cpp +++ b/thirdparty/openal/core/helpers.cpp @@ -437,7 +437,7 @@ namespace { bool SetRTPriorityPthread(int prio [[maybe_unused]]) { int err{ENOTSUP}; -#if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__) +#if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__) && !defined(__EMSCRIPTEN__) /* Get the min and max priority for SCHED_RR. Limit the max priority to * half, for now, to ensure the thread can't take the highest priority and * go rogue. diff --git a/thirdparty/openssl/include/emscripten/openssl/configuration.h b/thirdparty/openssl/include/emscripten/openssl/configuration.h new file mode 100644 index 000000000000..e093e27eeb41 --- /dev/null +++ b/thirdparty/openssl/include/emscripten/openssl/configuration.h @@ -0,0 +1,161 @@ +/* + * WARNING: do not edit! + * Generated by configdata.pm from Configurations/common0.tmpl, Configurations/unix-Makefile.tmpl + * via Makefile.in + * + * Copyright 2016-2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OPENSSL_CONFIGURATION_H +# define OPENSSL_CONFIGURATION_H +# pragma once + +# ifdef __cplusplus +extern "C" { +# endif + +# ifdef OPENSSL_ALGORITHM_DEFINES +# error OPENSSL_ALGORITHM_DEFINES no longer supported +# endif + +/* + * OpenSSL was configured with the following options: + */ + +# define OPENSSL_CONFIGURED_API 30000 +# ifndef OPENSSL_RAND_SEED_OS +# define OPENSSL_RAND_SEED_OS +# endif +# ifndef OPENSSL_NO_ACVP_TESTS +# define OPENSSL_NO_ACVP_TESTS +# endif +# ifndef OPENSSL_NO_AFALGENG +# define OPENSSL_NO_AFALGENG +# endif +# ifndef OPENSSL_NO_ASAN +# define OPENSSL_NO_ASAN +# endif +# ifndef OPENSSL_NO_ASM +# define OPENSSL_NO_ASM +# endif +# ifndef OPENSSL_NO_CAPIENG +# define OPENSSL_NO_CAPIENG +# endif +# ifndef OPENSSL_NO_CRYPTO_MDEBUG +# define OPENSSL_NO_CRYPTO_MDEBUG +# endif +# ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE +# define OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE +# endif +# ifndef OPENSSL_NO_DEVCRYPTOENG +# define OPENSSL_NO_DEVCRYPTOENG +# endif +# ifndef OPENSSL_NO_DSO +# define OPENSSL_NO_DSO +# endif +# ifndef OPENSSL_NO_DTLS +# define OPENSSL_NO_DTLS +# endif +# ifndef OPENSSL_NO_DTLS1 +# define OPENSSL_NO_DTLS1 +# endif +# ifndef OPENSSL_NO_DTLS1_2 +# define OPENSSL_NO_DTLS1_2 +# endif +# ifndef OPENSSL_NO_EC_NISTP_64_GCC_128 +# define OPENSSL_NO_EC_NISTP_64_GCC_128 +# endif +# ifndef OPENSSL_NO_EGD +# define OPENSSL_NO_EGD +# endif +# ifndef OPENSSL_NO_ENGINE +# define OPENSSL_NO_ENGINE +# endif +# ifndef OPENSSL_NO_EXTERNAL_TESTS +# define OPENSSL_NO_EXTERNAL_TESTS +# endif +# ifndef OPENSSL_NO_FIPS_SECURITYCHECKS +# define OPENSSL_NO_FIPS_SECURITYCHECKS +# endif +# ifndef OPENSSL_NO_FUZZ_AFL +# define OPENSSL_NO_FUZZ_AFL +# endif +# ifndef OPENSSL_NO_FUZZ_LIBFUZZER +# define OPENSSL_NO_FUZZ_LIBFUZZER +# endif +# ifndef OPENSSL_NO_KTLS +# define OPENSSL_NO_KTLS +# endif +# ifndef OPENSSL_NO_LOADERENG +# define OPENSSL_NO_LOADERENG +# endif +# ifndef OPENSSL_NO_MD2 +# define OPENSSL_NO_MD2 +# endif +# ifndef OPENSSL_NO_MSAN +# define OPENSSL_NO_MSAN +# endif +# ifndef OPENSSL_NO_PADLOCKENG +# define OPENSSL_NO_PADLOCKENG +# endif +# ifndef OPENSSL_NO_RC5 +# define OPENSSL_NO_RC5 +# endif +# ifndef OPENSSL_NO_SCTP +# define OPENSSL_NO_SCTP +# endif +# ifndef OPENSSL_NO_SSL3 +# define OPENSSL_NO_SSL3 +# endif +# ifndef OPENSSL_NO_SSL3_METHOD +# define OPENSSL_NO_SSL3_METHOD +# endif +# ifndef OPENSSL_NO_TRACE +# define OPENSSL_NO_TRACE +# endif +# ifndef OPENSSL_NO_UBSAN +# define OPENSSL_NO_UBSAN +# endif +# ifndef OPENSSL_NO_UI_CONSOLE +# define OPENSSL_NO_UI_CONSOLE +# endif +# ifndef OPENSSL_NO_UNIT_TEST +# define OPENSSL_NO_UNIT_TEST +# endif +# ifndef OPENSSL_NO_UPLINK +# define OPENSSL_NO_UPLINK +# endif +# ifndef OPENSSL_NO_WEAK_SSL_CIPHERS +# define OPENSSL_NO_WEAK_SSL_CIPHERS +# endif +# ifndef OPENSSL_NO_DYNAMIC_ENGINE +# define OPENSSL_NO_DYNAMIC_ENGINE +# endif + + +/* Generate 80386 code? */ +# undef I386_ONLY + +/* + * The following are cipher-specific, but are part of the public API. + */ +# if !defined(OPENSSL_SYS_UEFI) +# undef BN_LLONG +/* Only one for the following should be defined */ +# undef SIXTY_FOUR_BIT_LONG +# define SIXTY_FOUR_BIT +# undef THIRTY_TWO_BIT +# endif + +# define RC4_INT unsigned int + +# ifdef __cplusplus +} +# endif + +#endif /* OPENSSL_CONFIGURATION_H */ diff --git a/thirdparty/openssl/include/openssl/configuration.h b/thirdparty/openssl/include/openssl/configuration.h index 0d84b16597bb..5a6fe1ab602d 100644 --- a/thirdparty/openssl/include/openssl/configuration.h +++ b/thirdparty/openssl/include/openssl/configuration.h @@ -39,6 +39,8 @@ #elif defined(__linux__) # include "linux/openssl/configuration.h" +#elif defined(__EMSCRIPTEN__) +# include "emscripten/openssl/configuration.h" #else # error "unsupported platform" #endif diff --git a/thirdparty/openssl/prebuilt/emscripten/libcrypto.a b/thirdparty/openssl/prebuilt/emscripten/libcrypto.a new file mode 100644 index 000000000000..0a91ca673a3c Binary files /dev/null and b/thirdparty/openssl/prebuilt/emscripten/libcrypto.a differ diff --git a/thirdparty/openssl/prebuilt/emscripten/libssl.a b/thirdparty/openssl/prebuilt/emscripten/libssl.a new file mode 100644 index 000000000000..0ba3e53d0618 Binary files /dev/null and b/thirdparty/openssl/prebuilt/emscripten/libssl.a differ diff --git a/thirdparty/unzip/CMakeLists.txt b/thirdparty/unzip/CMakeLists.txt index c5fd0fc0d58a..c39b785b859e 100644 --- a/thirdparty/unzip/CMakeLists.txt +++ b/thirdparty/unzip/CMakeLists.txt @@ -19,7 +19,10 @@ target_include_directories(${target_name} PUBLIC .) if(MACOSX OR ANDROID OR WINDOWS) get_target_property(zlib_header dep_zlib INTERFACE_INCLUDE_DIRECTORIES) - target_include_directories(${target_name} PRIVATE ${zlib_header} -) + target_include_directories(${target_name} PRIVATE ${zlib_header}) endif() +if(NOT NOCRYPT) + get_target_property(openssl_header OpenSSL::SSL INTERFACE_INCLUDE_DIRECTORIES) + target_include_directories(${target_name} PRIVATE ${openssl_header}) +endif() \ No newline at end of file diff --git a/thirdparty/unzip/crypt.c b/thirdparty/unzip/crypt.c index 414c081e8b01..59d3165f711e 100644 --- a/thirdparty/unzip/crypt.c +++ b/thirdparty/unzip/crypt.c @@ -43,8 +43,8 @@ Microsoft may remove this API in future releases. #endif #include "zlib.h" - #include "crypt.h" +#include /***************************************************************************/ @@ -115,7 +115,8 @@ int cryptrand(unsigned char *buf, unsigned int len) return rlen; #else - arc4random_buf(buf, len); + + RAND_bytes(buf, len); return len; #endif } diff --git a/thirdparty/yasio/bindings/yasio_axlua.cpp b/thirdparty/yasio/bindings/yasio_axlua.cpp index a4c3996c79aa..1b38f64a6678 100644 --- a/thirdparty/yasio/bindings/yasio_axlua.cpp +++ b/thirdparty/yasio/bindings/yasio_axlua.cpp @@ -25,6 +25,8 @@ 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. */ + +#include "sol/sol.hpp" #include "yasio/bindings/yasio_axlua.hpp" #include "yasio/bindings/lyasio.hpp" #include "yasio/object_pool.hpp" @@ -118,12 +120,6 @@ YASIO_LUA_API void clear() #if YASIO__HAS_CXX14 -# if YASIO__HAS_CXX17 -# include "sol/sol.hpp" -# else -# include "sol2/sol.hpp" -# endif - extern "C" { struct lua_State; YASIO_LUA_API int luaopen_yasio_axlua(lua_State* L) diff --git a/thirdparty/yasio/endian_portable.hpp b/thirdparty/yasio/endian_portable.hpp index 76ea2460099c..dfd65f10ffb9 100644 --- a/thirdparty/yasio/endian_portable.hpp +++ b/thirdparty/yasio/endian_portable.hpp @@ -32,6 +32,16 @@ SOFTWARE. #include #include +#ifdef EMSCRIPTEN +/* + * Basic system type definitions, taken from the BSD file sys/types.h. + */ +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; +#endif + #ifdef _WIN32 # if !defined(WIN32_LEAN_AND_MEAN) # define WIN32_LEAN_AND_MEAN diff --git a/thirdparty/yasio/string_view.hpp b/thirdparty/yasio/string_view.hpp index 350e4d1d7b4f..8b8ae83cafa6 100644 --- a/thirdparty/yasio/string_view.hpp +++ b/thirdparty/yasio/string_view.hpp @@ -1404,19 +1404,19 @@ inline namespace literals { inline namespace string_view_literals { -inline cxx17::string_view operator"" _sv(const char* _Str, size_t _Len) +inline cxx17::string_view operator""_sv(const char* _Str, size_t _Len) { return cxx17::string_view(_Str, _Len); } -inline cxx17::wstring_view operator"" _sv(const wchar_t* _Str, size_t _Len) +inline cxx17::wstring_view operator""_sv(const wchar_t* _Str, size_t _Len) { return cxx17::wstring_view(_Str, _Len); } -inline cxx17::u16string_view operator"" _sv(const char16_t* _Str, size_t _Len) +inline cxx17::u16string_view operator""_sv(const char16_t* _Str, size_t _Len) { return cxx17::u16string_view(_Str, _Len); } -inline cxx17::u32string_view operator"" _sv(const char32_t* _Str, size_t _Len) +inline cxx17::u32string_view operator""_sv(const char32_t* _Str, size_t _Len) { return cxx17::u32string_view(_Str, _Len); } diff --git a/tools/ci/gendocs.ps1 b/tools/ci/gendocs.ps1 index 44adda6b0413..64a8539f836a 100644 --- a/tools/ci/gendocs.ps1 +++ b/tools/ci/gendocs.ps1 @@ -77,6 +77,9 @@ $site_dist = Join-Path $site_src 'dist' mkdirs $site_dist +$docsOut = Join-Path $docsRoot 'out' +mkdirs $docsOut + $store_cwd = (Get-Location).Path Set-Location $site_src @@ -132,3 +135,8 @@ Copy-Item './style.css' "$site_dist/assets/css/style.css" Copy-Item './index.html' "$site_dist/index.html" Set-Location $store_cwd + +# build html targets to docs +./build.ps1 -p wasm + +Copy-Item 'build_wasm/bin/'