diff --git a/BuildWasm.sh b/BuildWasm.sh new file mode 100644 index 0000000000..e5f6c5f68a --- /dev/null +++ b/BuildWasm.sh @@ -0,0 +1,231 @@ +#!/bin/bash + +SOURCE_DIR=$PWD +OUTPUT_DIR="build_wasm" +CLEAR_CACHE=0 +ENABLE_EXAMPLES="ON" +ENABLE_TESTS="ON" +ENABLE_PTHREADS="OFF" +BUILD_TYPE="Release" +PROJECT_ONLY=0 +VERBOSE=0 +GENERATOR="CodeBlocks - Unix Makefiles" + +# Check whether we are on a Linux distribution or MSYS on Windows +print_help() +{ + echo "USAGE:" + echo " BuildWasm.sh OPTIONS* [OUTPUT_DIR]" + echo "OPTIONS:" + echo " -c, --clear-cache ......... Clear CMake cache and rebuild" + echo " -h, --help ................ Print this help documentation and exit" + echo " -d, --debug ............... Configure Debug build (default is Release)" + echo " -p, --project-only [=G] ... Build project with CMake generator (default is CodeBlocks)" + echo " --no-examples ............. Exclude example projects" + echo " --no-tests ................ Exclude test projects" + echo " --pthreads ................ Enable pthreads (limits browser availability)" + echo "NOTES:" + echo " Default output directory is '$OUTPUT_DIR'" +} + +# Parse arguments +for ARG in "$@"; do + if [ "$ARG" = "-h" ] || [ "$ARG" = "--help" ]; then + print_help + exit 0 + elif [ "$ARG" = "-c" ] || [ "$ARG" = "--clear-cache" ]; then + CLEAR_CACHE=1 + elif [ "$ARG" = "-d" ] || [ "$ARG" = "--debug" ]; then + BUILD_TYPE="Debug" + elif [ "$ARG" = "-p" ] || [ "$ARG" = "--project-only" ]; then + PROJECT_ONLY=1 + elif [[ "$ARG" == -p=* ]]; then + PROJECT_ONLY=1 + GENERATOR="${ARG:3}" + elif [[ "$ARG" == --project-only=* ]]; then + PROJECT_ONLY=1 + GENERATOR="${ARG:15}" + elif [ "$ARG" = "-v" ] || [ "$ARG" = "--verbose" ]; then + VERBOSE=1 + elif [ "$ARG" = "--null" ]; then + ENABLE_NULL="ON" + elif [ "$ARG" = "--vulkan" ]; then + ENABLE_VULKAN="ON" + elif [ "$ARG" = "--no-examples" ]; then + ENABLE_EXAMPLES="OFF" + elif [ "$ARG" = "--no-tests" ]; then + ENABLE_TESTS="OFF" + elif [ "$ARG" = "--pthreads" ]; then + ENABLE_PTHREADS="ON" + else + OUTPUT_DIR="$ARG" + fi +done + +# Ensure we are inside the repository folder +if [ ! -f "CMakeLists.txt" ]; then + echo "Error: File not found: CMakeLists.txt" + exit 1 +fi + +# Make output build folder +if [ $CLEAR_CACHE = 1 ] && [ -d "$OUTPUT_DIR" ]; then + rm -rf "$OUTPUT_DIR" +fi + +if [ ! -d "$OUTPUT_DIR" ]; then + mkdir "$OUTPUT_DIR" +fi + +# Checkout external depenencies +GAUSSIAN_LIB_DIR="GaussianLib/include" + +if [ -f "$SOURCE_DIR/external/$GAUSSIAN_LIB_DIR/Gauss/Gauss.h" ]; then + GAUSSIAN_LIB_DIR=$(realpath "$SOURCE_DIR/external/$GAUSSIAN_LIB_DIR") +else + if [ ! -d "$OUTPUT_DIR/$GAUSSIAN_LIB_DIR" ]; then + (cd "$OUTPUT_DIR" && git clone https://github.com/LukasBanana/GaussianLib.git) + fi + GAUSSIAN_LIB_DIR=$(realpath "$OUTPUT_DIR/$GAUSSIAN_LIB_DIR") +fi + +# Print additional information if in verbose mode +if [ $VERBOSE -eq 1 ]; then + echo "GAUSSIAN_LIB_DIR=$GAUSSIAN_LIB_DIR" + if [ $PROJECT_ONLY -eq 0 ]; then + echo "BUILD_TYPE=$BUILD_TYPE" + else + echo "GENERATOR=$GENERATOR" + fi +fi + +# Find Emscripten SDK +if [ -z "$EMSDK" ]; then + echo "Error: Missing EMSDK environment variable. Run 'source /emsdk_env.sh' to fix it." + exit 1 +fi + +EMSCRIPTEN_CMAKE_TOOLCHAIN="$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake" +EMSCRIPTEN_FILE_PACKAGER="$EMSDK/upstream/emscripten/tools/file_packager" + +if [ ! -f "$EMSCRIPTEN_CMAKE_TOOLCHAIN" ]; then + echo "Error: Could not find file $EMSCRIPTEN_CMAKE_TOOLCHAIN" + exit 1 +fi + +# Build into output directory (this syntax requried CMake 3.13+) +OPTIONS=( + -DCMAKE_TOOLCHAIN_FILE="$EMSCRIPTEN_CMAKE_TOOLCHAIN" + -DLLGL_BUILD_RENDERER_WEBGL=ON + -DLLGL_GL_ENABLE_OPENGL2X=OFF + -DLLGL_BUILD_RENDERER_NULL=OFF + -DLLGL_BUILD_RENDERER_VULKAN=OFF + -DLLGL_BUILD_RENDERER_DIRECT3D11=OFF + -DLLGL_BUILD_RENDERER_DIRECT3D12=OFF + -DLLGL_BUILD_EXAMPLES=$ENABLE_EXAMPLES + -DLLGL_BUILD_TESTS=$ENABLE_TESTS + -DLLGL_BUILD_STATIC_LIB=ON + -DLLGL_ENABLE_EMSCRIPTEN_PTHREADS=$ENABLE_PTHREADS + -DGaussLib_INCLUDE_DIR:STRING="$GAUSSIAN_LIB_DIR" + -S "$SOURCE_DIR" + -B "$OUTPUT_DIR" +) + +if [ $PROJECT_ONLY -eq 0 ]; then + cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ${OPTIONS[@]} + cmake --build "$OUTPUT_DIR" -- -j 20 +else + cmake ${OPTIONS[@]} -G "$GENERATOR" +fi + +# Generate HTML pages +generate_html5_page() +{ + CURRENT_PROJECT=$1 + + echo "Generate HTML5 page: $CURRENT_PROJECT" + + # Get source folder + ASSET_SOURCE_DIR="$SOURCE_DIR/examples/Media" + PROJECT_SOURCE_DIR="$SOURCE_DIR/examples/Cpp" + if [[ "$CURRENT_PROJECT" == *D ]]; then + PROJECT_SOURCE_DIR="$PROJECT_SOURCE_DIR/${CURRENT_PROJECT:0:-1}" + else + PROJECT_SOURCE_DIR="$PROJECT_SOURCE_DIR/$CURRENT_PROJECT" + fi + + # Get destination folder + HTML5_ROOT="${OUTPUT_DIR}/html5/Example_$CURRENT_PROJECT" + BIN_ROOT=${OUTPUT_DIR}/build + + # Create folder structure + mkdir -p "$HTML5_ROOT" + BASE_FILENAME="Example_$CURRENT_PROJECT" + cp "$SOURCE_DIR/examples/Cpp/ExampleBase/Wasm/index.html" "$HTML5_ROOT/index.html" + cp "$BIN_ROOT/$BASE_FILENAME.js" "$HTML5_ROOT/$BASE_FILENAME.js" + cp "$BIN_ROOT/$BASE_FILENAME.wasm" "$HTML5_ROOT/$BASE_FILENAME.wasm" + if [ $ENABLE_PTHREADS = "ON" ]; then + cp "$BIN_ROOT/$BASE_FILENAME.worker.js" "$HTML5_ROOT/$BASE_FILENAME.worker.js" + fi + + # Replace meta data + sed -i "s/LLGL_EXAMPLE_NAME/${CURRENT_PROJECT}/" "$HTML5_ROOT/index.html" + sed -i "s/LLGL_EXAMPLE_PROJECT/Example_${CURRENT_PROJECT}/" "$HTML5_ROOT/index.html" + + # Find all required assets in Android.assets.txt file of respective project directory and copy them into app folder + ASSET_DIR="$HTML5_ROOT/assets" + mkdir -p "$ASSET_DIR" + + ASSET_LIST_FILE="$PROJECT_SOURCE_DIR/Android.assets.txt" + if [ -f "$ASSET_LIST_FILE" ]; then + # Read Android.asset.txt file line-by-line into array and make sure '\r' character is not present (on Win32 platform) + readarray -t ASSET_FILTERS < <(tr -d '\r' < "$ASSET_LIST_FILE") + ASSET_FILES=() + for FILTER in ${ASSET_FILTERS[@]}; do + for FILE in $ASSET_SOURCE_DIR/$FILTER; do + ASSET_FILES+=( "$FILE" ) + done + done + + # Copy all asset file into destination folder + for FILE in ${ASSET_FILES[@]}; do + if [ $VERBOSE -eq 1 ]; then + echo "Copy asset: $(basename $FILE)" + fi + cp "$FILE" "$ASSET_DIR/$(basename $FILE)" + done + fi + + # Find all shaders and copy them into app folder + for FILE in $PROJECT_SOURCE_DIR/*.vert \ + $PROJECT_SOURCE_DIR/*.frag; do + if [ -f "$FILE" ]; then + if [ $VERBOSE -eq 1 ]; then + echo "Copy shader: $(basename $FILE)" + fi + cp "$FILE" "$ASSET_DIR/$(basename $FILE)" + fi + done + + # Package assets into .data.js file with Emscripten packager tool + (cd "$HTML5_ROOT" && "$EMSCRIPTEN_FILE_PACKAGER" "$BASE_FILENAME.data" --preload assets "--js-output=$BASE_FILENAME.data.js" >/dev/null 2>&1) +} + +if [ $PROJECT_ONLY -eq 0 ] && [ $ENABLE_EXAMPLES == "ON" ]; then + + BIN_FILE_BASE="${OUTPUT_DIR}/build/Example_" + BIN_FILE_BASE_LEN=${#BIN_FILE_BASE} + + if [ $BUILD_TYPE = "Debug" ]; then + EXAMPLE_BIN_FILES=(${BIN_FILE_BASE}*D.wasm) + else + EXAMPLE_BIN_FILES=(${BIN_FILE_BASE}*.wasm) + fi + + for BIN_FILE in ${EXAMPLE_BIN_FILES[@]}; do + BIN_FILE_LEN=${#BIN_FILE} + PROJECT_NAME=${BIN_FILE:BIN_FILE_BASE_LEN:BIN_FILE_LEN-BIN_FILE_BASE_LEN-5} + generate_html5_page $PROJECT_NAME + done + +fi diff --git a/CMakeLists.txt b/CMakeLists.txt index ebb4953aaf..b9a18a5feb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ endif() set(LLGL_UWP_PLATFORM OFF) set(LLGL_IOS_PLATFORM OFF) set(LLGL_ANDROID_PLATFORM OFF) +set(LLGL_WASM_PLATFORM OFF) if(NOT DEFINED LLGL_TARGET_PLATFORM) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Android") @@ -33,6 +34,9 @@ if(NOT DEFINED LLGL_TARGET_PLATFORM) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "WindowsStore") set(LLGL_TARGET_PLATFORM "${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}") set(LLGL_UWP_PLATFORM ON) + elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Emscripten") + set(LLGL_TARGET_PLATFORM "WebAssembly") + set(LLGL_WASM_PLATFORM ON) elseif(WIN32) if(LLGL_BUILD_64BIT) set(LLGL_TARGET_PLATFORM "Win64") @@ -91,7 +95,7 @@ set( BACKEND_INCLUDE_DIR "${PROJECT_INCLUDE_DIR}/LLGL/Backend" ) # === Macros === -if(LLGL_IOS_PLATFORM OR LLGL_ANDROID_PLATFORM) +if(LLGL_IOS_PLATFORM OR LLGL_ANDROID_PLATFORM OR LLGL_WASM_PLATFORM) set(LLGL_MOBILE_PLATFORM ON) else() set(LLGL_MOBILE_PLATFORM OFF) @@ -380,6 +384,7 @@ endif() set(SUMMARY_LIBRARY_TYPE "Unknown") set(SUMMARY_TARGET_ARCH "Unknown") +set(SUMMARY_FLAGS "") # === Options === @@ -408,7 +413,10 @@ option(LLGL_BUILD_EXAMPLES "Include example projects" OFF) option(LLGL_BUILD_RENDERER_NULL "Include Null renderer project" ON) if(NOT LLGL_UWP_PLATFORM) - if(LLGL_MOBILE_PLATFORM) + if(EMSCRIPTEN) + option(LLGL_BUILD_RENDERER_WEBGL "Include WebGL renderer project" ON) + option(LLGL_ENABLE_EMSCRIPTEN_PTHREADS "Build for Wasm platform with pthreads (USE_PTHREADS). This limits browser availability!" OFF) + elseif(LLGL_MOBILE_PLATFORM) option(LLGL_BUILD_RENDERER_OPENGLES3 "Include OpenGLES 3 renderer project" ON) else() option(LLGL_BUILD_RENDERER_OPENGL "Include OpenGL renderer project" ON) @@ -483,6 +491,22 @@ else() set(SUMMARY_TARGET_ARCH "x86") endif() +if(EMSCRIPTEN) + # When USE_PTHREADS is enabled, HTML5 pages cannot be opened in Chrome unless launched with '--enable-features=SharedArrayBuffer' + if(LLGL_ENABLE_EMSCRIPTEN_PTHREADS) + add_compile_options("SHELL:-s USE_PTHREADS") + add_link_options("SHELL:-s USE_PTHREADS") + set(SUMMARY_FLAGS ${SUMMARY_FLAGS} "pthreads") + endif() + + # TODO: Emscripten file system pulls in a large amount of code. Consider limiting file system support to examples and tests. + add_link_options("SHELL:-s FORCE_FILESYSTEM") # for examples + + # LLGL needs at least WebGL 2.0 + add_link_options("SHELL:-s MIN_WEBGL_VERSION=2") + add_link_options("SHELL:-s MAX_WEBGL_VERSION=2") +endif() + # === Global files === @@ -522,7 +546,10 @@ if(LLGL_ENABLE_DEBUG_LAYER) find_source_files(FilesRendererDbgTexture CXX "${PROJECT_SOURCE_DIR}/sources/Renderer/DebugLayer/Texture") endif() -if(WIN32) +if(EMSCRIPTEN) + find_source_files(FilesPlatform CXX "${PROJECT_SOURCE_DIR}/sources/Platform/Wasm") + find_source_files(FilesIncludePlatform CXX "${PROJECT_INCLUDE_DIR}/LLGL/Platform/Wasm") +elseif(WIN32) if(LLGL_UWP_PLATFORM) find_source_files(FilesPlatform CXX "${PROJECT_SOURCE_DIR}/sources/Platform/UWP") find_source_files(FilesIncludePlatform CXX "${PROJECT_INCLUDE_DIR}/LLGL/Platform/UWP") @@ -679,6 +706,8 @@ elseif(APPLE) if(LLGL_MACOS_ENABLE_COREVIDEO) target_link_libraries(LLGL "-framework CoreVideo") endif() +elseif(EMSCRIPTEN) + target_link_libraries(LLGL embind) elseif(UNIX) target_link_libraries(LLGL X11 pthread Xrandr) #elseif(LLGL_UWP_PLATFORM) @@ -697,7 +726,7 @@ if(LLGL_BUILD_RENDERER_NULL) add_subdirectory(sources/Renderer/Null) endif() -if(LLGL_BUILD_RENDERER_OPENGL OR LLGL_BUILD_RENDERER_OPENGLES3) +if(LLGL_BUILD_RENDERER_OPENGL OR LLGL_BUILD_RENDERER_OPENGLES3 OR LLGL_BUILD_RENDERER_WEBGL) add_subdirectory(sources/Renderer/OpenGL) endif() @@ -787,6 +816,10 @@ if(LLGL_BUILD_RENDERER_OPENGLES3) message(STATUS "Build Renderer: ${LLGL_GL_ENABLE_OPENGLES}") endif() +if(LLGL_BUILD_RENDERER_WEBGL) + message(STATUS "Build Renderer: WebGL") +endif() + if(LLGL_BUILD_RENDERER_VULKAN) message(STATUS "Build Renderer: Vulkan") endif() @@ -823,5 +856,9 @@ if(LLGL_VK_ENABLE_SPIRV_REFLECT) message(STATUS "Including Submodule: SPIRV-Headers") endif() +if(NOT "${SUMMARY_FLAGS}" STREQUAL "") + message(STATUS "Options: ${SUMMARY_FLAGS}") +endif() + message(STATUS "~~~~~~~~~~~~~~~~~~~~~") diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000000..b0e5371e38 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,35 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "emscripten", + "generator": "Ninja Multi-Config", + "toolchainFile": "/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake", + "binaryDir": "${sourceDir}/../llgl-build", + "architecture": { + "strategy": "external", + "value": "x86" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Linux" ] } + } + } + ], + "buildPresets": [ + { + "name": "Release", + "configurePreset": "emscripten", + "configuration": "Release" + }, + { + "name": "Debug", + "configurePreset": "emscripten", + "configuration": "Debug" + }, + { + "name": "RelWithDebInfo", + "configurePreset": "emscripten", + "configuration": "RelWithDebInfo" + } + ] +} diff --git a/README.md b/README.md index 4542fb8378..e3d60b3664 100644 --- a/README.md +++ b/README.md @@ -30,14 +30,15 @@ with Introduction, Hello Triangle Tutorial, and Extensibility Example with [GLFW ## Platform Support -| Platform | CI | D3D12 | D3D11 | Vulkan | GL/GLES3 | Metal | +| Platform | CI | D3D12 | D3D11 | Vulkan | GL/GLES/WebGL | Metal | |----------|:--:|:-----:|:-----:|:------:|:--------:|:-----:| | Windows |

[![MSVC16+ CI](https://github.com/LukasBanana/LLGL/actions/workflows/ci_windows.yml/badge.svg)](https://github.com/LukasBanana/LLGL/actions/workflows/ci_windows.yml)

[![MSVC14 CI](https://ci.appveyor.com/api/projects/status/j09x8n07u3byfky0?svg=true)](https://ci.appveyor.com/project/LukasBanana/llgl)

| :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | N/A | -| UWP | [![UWP CI](https://github.com/LukasBanana/LLGL/actions/workflows/ci_uwp.yml/badge.svg)](https://github.com/LukasBanana/LLGL/actions/workflows/ci_uwp.yml) | :heavy_check_mark: | :heavy_check_mark: | N/A | N/A | N/A | +| UWP | [![UWP CI](https://github.com/LukasBanana/LLGL/actions/workflows/ci_uwp.yml/badge.svg)](https://github.com/LukasBanana/LLGL/actions/workflows/ci_uwp.yml) | :heavy_check_mark: | :heavy_check_mark: | N/A | N/A | N/A | | GNU/Linux | [![GNU/Linux CI](https://github.com/LukasBanana/LLGL/actions/workflows/ci_linux.yml/badge.svg)](https://github.com/LukasBanana/LLGL/actions/workflows/ci_linux.yml) | N/A | N/A | :heavy_check_mark: | :heavy_check_mark: | N/A | | macOS | [![macOS CI](https://github.com/LukasBanana/LLGL/actions/workflows/ci_macos.yml/badge.svg)](https://github.com/LukasBanana/LLGL/actions/workflows/ci_macos.yml) | N/A | N/A | N/A | :heavy_check_mark: | :heavy_check_mark: | | iOS | [![iOS CI](https://github.com/LukasBanana/LLGL/actions/workflows/ci_ios.yml/badge.svg)](https://github.com/LukasBanana/LLGL/actions/workflows/ci_ios.yml) | N/A | N/A | N/A | :heavy_check_mark: | :heavy_check_mark: | | Android | [![Android CI](https://github.com/LukasBanana/LLGL/actions/workflows/ci_android.yml/badge.svg)](https://github.com/LukasBanana/LLGL/actions/workflows/ci_android.yml) | N/A | N/A | :heavy_check_mark: | :heavy_check_mark: | N/A | +| Wasm | | N/A | N/A | N/A | :heavy_check_mark: | N/A | ## Build Notes diff --git a/cmake/FindOpenGLES3.cmake b/cmake/FindOpenGLES3.cmake index 87621d2e31..a40779fcf6 100644 --- a/cmake/FindOpenGLES3.cmake +++ b/cmake/FindOpenGLES3.cmake @@ -206,6 +206,15 @@ ELSE (WIN32) findpkg_framework(OpenGLES) set(OPENGLES_gl_LIBRARY "-framework OpenGLES") + ELSEIF(EMSCRIPTEN) + + find_package(OpenGL REQUIRED) + #message(STATUS "OPENGL_INCLUDE_DIR: " ${OPENGL_INCLUDE_DIR}) + #message(STATUS "OPENGL_LIBRARIES: " ${OPENGL_LIBRARIES}) + + SET(OPENGLES_INCLUDE_DIR ${OPENGL_INCLUDE_DIR}) + SET(OPENGLES_gl_LIBRARY ${OPENGL_LIBRARIES}) + ELSE(APPLE) FIND_PATH(OPENGLES_INCLUDE_DIR GLES3/gl3.h diff --git a/docu/Icons/uwp.svg b/docu/Icons/uwp.svg new file mode 100644 index 0000000000..9005f28de6 --- /dev/null +++ b/docu/Icons/uwp.svg @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/docu/Icons/uwp.svg.license.txt b/docu/Icons/uwp.svg.license.txt new file mode 100644 index 0000000000..9ab63821d7 --- /dev/null +++ b/docu/Icons/uwp.svg.license.txt @@ -0,0 +1,9 @@ +License: + CC BY 3.0 (https://creativecommons.org/licenses/by/3.0/) + +Attribution: + Icon created by [ViconsDesign] from https://www.iconfinder.com/ViconsDesign + +Weblink: + https://www.iconfinder.com/ViconsDesign + https://www.iconfinder.com/icons/7422390/microsoft_office_windows_window_icon \ No newline at end of file diff --git a/docu/Icons/wasm.svg b/docu/Icons/wasm.svg new file mode 100644 index 0000000000..fef0021b3a --- /dev/null +++ b/docu/Icons/wasm.svg @@ -0,0 +1 @@ +web-assembly-icon-black \ No newline at end of file diff --git a/docu/Icons/wasm.svg.license.txt b/docu/Icons/wasm.svg.license.txt new file mode 100644 index 0000000000..98fc3bed9b --- /dev/null +++ b/docu/Icons/wasm.svg.license.txt @@ -0,0 +1,5 @@ +License: + CC0-1.0 license + +Weblink: + https://github.com/carlosbaraza/web-assembly-logo/blob/master/dist/icon/web-assembly-icon-black.svg \ No newline at end of file diff --git a/docu/README.md b/docu/README.md index 556bc3f741..8779f87104 100644 --- a/docu/README.md +++ b/docu/README.md @@ -63,3 +63,22 @@ $ pacman -S mingw-w64-x86_64-freeglut The build script `BuildMsys2.sh` can be used to automate the build process. Use `$ ./BuildMsys2.sh -h` in a command prompt for more details. + +## WebAssembly (Wasm) + +LLGL can be build for the web using [Emscripten](https://emscripten.org/), [WebAssembly](https://webassembly.org/), and [WebGL 2](https://www.khronos.org/webgl/). +This is in an **experimental** state and requires [Google Chrome](https://www.google.com/chrome/) to be launched with the following arguments to enable web assembly: +``` +chrome --js-flags=--experimental-wasm-threads --enable-features=WebAssembly,SharedArrayBuffer +``` +When building on Windows, it is recommended to use the [Windows Subsystem for Linux (WSL)](https://ubuntu.com/desktop/wsl) to run the *BuildWasm.sh* script. +The generated example projects can be tested by running a local web server from the output folder, for instance with [Node.js](https://nodejs.org/) or Python `http.server` module: +``` +$ python -m http.server +``` +Then run your browser of choice at URL http://localhost:8000/. + +

+ Screenshot missing: HTML5 Example Showcase +

+ diff --git a/docu/Showcase/Showcase_Build_Wasm.png b/docu/Showcase/Showcase_Build_Wasm.png new file mode 100644 index 0000000000..6a99ee8912 Binary files /dev/null and b/docu/Showcase/Showcase_Build_Wasm.png differ diff --git a/examples/Cpp/ExampleBase/ExampleBase.cpp b/examples/Cpp/ExampleBase/ExampleBase.cpp index 4fce55ad66..36af10d139 100644 --- a/examples/Cpp/ExampleBase/ExampleBase.cpp +++ b/examples/Cpp/ExampleBase/ExampleBase.cpp @@ -28,9 +28,13 @@ #endif #include -#ifdef LLGL_OS_ANDROID +#if defined LLGL_OS_ANDROID # include "Android/AppUtils.h" # include +#elif defined LLGL_OS_WASM +# include +# include +# include #endif #define IMMEDIATE_SUBMIT_CMDBUFFER 0 @@ -152,6 +156,8 @@ static constexpr const char* GetDefaultRendererModule() return "Metal"; #elif defined LLGL_OS_ANDROID return "OpenGLES3"; + #elif defined LLGL_OS_WASM + return "WebGL"; #else return "OpenGL"; #endif @@ -375,16 +381,27 @@ void ExampleBase::SetAndroidApp(android_app* androidApp) #endif +void ExampleBase::MainLoopWrapper(void* args) +{ + ExampleBase* exampleBase = reinterpret_cast(args); + exampleBase->MainLoop(); +} + void ExampleBase::Run() { - bool showTimeRecords = false; - bool fullscreen = false; - const LLGL::Extent2D initialResolution = swapChain->GetResolution(); + initialResolution_ = swapChain->GetResolution(); #ifndef LLGL_MOBILE_PLATFORM LLGL::Window& window = LLGL::CastTo(swapChain->GetSurface()); #endif + #ifdef LLGL_OS_WASM + + // Receives a function to call and some user data to provide it. + emscripten_set_main_loop_arg(ExampleBase::MainLoopWrapper, this, 0, 1); + + #else + while (LLGL::Surface::ProcessEvents() && !input.KeyDown(LLGL::Key::Escape)) { #ifndef LLGL_MOBILE_PLATFORM @@ -405,57 +422,10 @@ void ExampleBase::Run() ANativeActivity_finish(ExampleBase::androidApp_->activity); #endif - // Update profiler (if debugging is enabled) - if (debuggerObj_) - { - LLGL::FrameProfile frameProfile; - debuggerObj_->FlushProfile(&frameProfile); - - if (showTimeRecords) - { - LLGL::Log::Printf( - "\n" - "FRAME TIME RECORDS:\n" - "-------------------\n" - ); - for (const LLGL::ProfileTimeRecord& rec : frameProfile.timeRecords) - LLGL::Log::Printf("%s: %" PRIu64 " ns\n", rec.annotation, rec.elapsedTime); - - debuggerObj_->SetTimeRecording(false); - showTimeRecords = false; - } - else if (input.KeyDown(LLGL::Key::F1)) - { - debuggerObj_->SetTimeRecording(true); - showTimeRecords = true; - } - } - - // Check to switch to fullscreen - if (input.KeyDown(LLGL::Key::F5)) - { - if (LLGL::Display* display = swapChain->GetSurface().FindResidentDisplay()) - { - fullscreen = !fullscreen; - if (fullscreen) - swapChain->ResizeBuffers(display->GetDisplayMode().resolution, LLGL::ResizeBuffersFlags::FullscreenMode); - else - swapChain->ResizeBuffers(initialResolution, LLGL::ResizeBuffersFlags::WindowedMode); - } - } - - // Draw current frame - #ifdef LLGL_OS_MACOS - @autoreleasepool - { - DrawFrame(); - } - #else - DrawFrame(); - #endif - - input.Reset(); + MainLoop(); } + + #endif // /LLGL_OS_WASM } void ExampleBase::DrawFrame() @@ -652,6 +622,74 @@ void ExampleBase::OnResize(const LLGL::Extent2D& resolution) // dummy } +void ExampleBase::MainLoop() +{ + #ifdef LLGL_OS_WASM + + // Clear GL state when rendering with WebGL + commands->Begin(); + { + LLGL::OpenGL::NativeCommand cmd; + cmd.type = LLGL::OpenGL::NativeCommandType::ClearCache; + commands->DoNativeCommand(&cmd, sizeof(cmd)); + } + commands->End(); + commandQueue->Submit(*commands); + + #endif + + // Update profiler (if debugging is enabled) + if (debuggerObj_) + { + LLGL::FrameProfile frameProfile; + debuggerObj_->FlushProfile(&frameProfile); + + if (showTimeRecords_) + { + LLGL::Log::Printf( + "\n" + "FRAME TIME RECORDS:\n" + "-------------------\n" + ); + for (const LLGL::ProfileTimeRecord& rec : frameProfile.timeRecords) + LLGL::Log::Printf("%s: %" PRIu64 " ns\n", rec.annotation, rec.elapsedTime); + + debuggerObj_->SetTimeRecording(false); + showTimeRecords_ = false; + } + else if (input.KeyDown(LLGL::Key::F1)) + { + debuggerObj_->SetTimeRecording(true); + showTimeRecords_ = true; + } + } + + // Check to switch to fullscreen + if (input.KeyDown(LLGL::Key::F5)) + { + if (LLGL::Display* display = swapChain->GetSurface().FindResidentDisplay()) + { + fullscreen_ = !fullscreen_; + if (fullscreen_) + swapChain->ResizeBuffers(display->GetDisplayMode().resolution, LLGL::ResizeBuffersFlags::FullscreenMode); + else + swapChain->ResizeBuffers(initialResolution_, LLGL::ResizeBuffersFlags::WindowedMode); + } + } + + // Draw current frame + #ifdef LLGL_OS_MACOS + @autoreleasepool + { + DrawFrame(); + } + #else + DrawFrame(); + #endif + + input.Reset(); +} + //private LLGL::Shader* ExampleBase::LoadShaderInternal( const ShaderDescWrapper& shaderDesc, @@ -663,6 +701,12 @@ LLGL::Shader* ExampleBase::LoadShaderInternal( { LLGL::Log::Printf("load shader: %s\n", shaderDesc.filename.c_str()); + #ifdef LLGL_OS_WASM + const std::string filename = "assets/" + shaderDesc.filename; + #else + const std::string filename = shaderDesc.filename; + #endif + std::vector shaders; std::vector vertexInputAttribs; @@ -677,7 +721,7 @@ LLGL::Shader* ExampleBase::LoadShaderInternal( } // Create shader - LLGL::ShaderDescriptor deviceShaderDesc = LLGL::ShaderDescFromFile(shaderDesc.type, shaderDesc.filename.c_str(), shaderDesc.entryPoint.c_str(), shaderDesc.profile.c_str()); + LLGL::ShaderDescriptor deviceShaderDesc = LLGL::ShaderDescFromFile(shaderDesc.type, filename.c_str(), shaderDesc.entryPoint.c_str(), shaderDesc.profile.c_str()); { deviceShaderDesc.debugName = shaderDesc.entryPoint.c_str(); @@ -941,7 +985,7 @@ bool ExampleBase::IsOpenGL() const return ( renderer->GetRendererID() == LLGL::RendererID::OpenGL || - renderer->GetRendererID() == LLGL::RendererID::OpenGLES3 + renderer->GetRendererID() == LLGL::RendererID::OpenGLES ); } diff --git a/examples/Cpp/ExampleBase/ExampleBase.h b/examples/Cpp/ExampleBase/ExampleBase.h index 432198f271..7b9abcafcb 100644 --- a/examples/Cpp/ExampleBase/ExampleBase.h +++ b/examples/Cpp/ExampleBase/ExampleBase.h @@ -156,6 +156,10 @@ class ExampleBase std::uint32_t samples_ = 1; + LLGL::Extent2D initialResolution_; + bool showTimeRecords_ = false; + bool fullscreen_ = false; + protected: friend class ResizeEventHandler; @@ -196,6 +200,11 @@ class ExampleBase private: + static void MainLoopWrapper(void* args); + + // Internal main loop. This is called manually on most platforms. With WebAssembly, it's passed to the browser glue code. + void MainLoop(); + // Internal function to load a shader. LLGL::Shader* LoadShaderInternal( const ShaderDescWrapper& shaderDesc, diff --git a/examples/Cpp/ExampleBase/FileUtils.cpp b/examples/Cpp/ExampleBase/FileUtils.cpp index 751a4ab31b..5ff6c59b50 100644 --- a/examples/Cpp/ExampleBase/FileUtils.cpp +++ b/examples/Cpp/ExampleBase/FileUtils.cpp @@ -75,6 +75,11 @@ static std::string FindAssetFilename(const std::string& name) // Handle filename resolution with AAssetManager on Android return name; + #elif defined LLGL_OS_WASM + + // Read asset files for examples from asset folder + return "assets/" + name; + #else // Return input name if it's a valid filename diff --git a/examples/Cpp/ExampleBase/Wasm/index.html b/examples/Cpp/ExampleBase/Wasm/index.html new file mode 100644 index 0000000000..5e24eacf57 --- /dev/null +++ b/examples/Cpp/ExampleBase/Wasm/index.html @@ -0,0 +1,49 @@ + + + + + + LLGL - LLGL_EXAMPLE_NAME + + + + +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/examples/Cpp/ShadowMapping/Example.cpp b/examples/Cpp/ShadowMapping/Example.cpp index 7bc6ebb106..872ed564c7 100644 --- a/examples/Cpp/ShadowMapping/Example.cpp +++ b/examples/Cpp/ShadowMapping/Example.cpp @@ -164,7 +164,7 @@ class Example_ShadowMapping : public ExampleBase LLGL::SamplerDescriptor shadowSamplerDesc; { // Clamp-to-border sampler address mode requires GLES 3.2, so use standard clamp mode in case hardware only supports GLES 3.0 - if (renderer->GetRendererID() == LLGL::RendererID::OpenGLES3) + if (renderer->GetRendererID() == LLGL::RendererID::OpenGLES) { shadowSamplerDesc.addressModeU = LLGL::SamplerAddressMode::Clamp; shadowSamplerDesc.addressModeV = LLGL::SamplerAddressMode::Clamp; diff --git a/include/LLGL/Backend/OpenGL/NativeHandle.h b/include/LLGL/Backend/OpenGL/NativeHandle.h index cb9582599d..8934f41273 100644 --- a/include/LLGL/Backend/OpenGL/NativeHandle.h +++ b/include/LLGL/Backend/OpenGL/NativeHandle.h @@ -21,6 +21,8 @@ # include #elif defined(LLGL_OS_ANDROID) # include +#elif defined(LLGL_OS_WASM) +# include #endif diff --git a/include/LLGL/Backend/OpenGL/Wasm/WasmNativeHandle.h b/include/LLGL/Backend/OpenGL/Wasm/WasmNativeHandle.h new file mode 100644 index 0000000000..a706e4510c --- /dev/null +++ b/include/LLGL/Backend/OpenGL/Wasm/WasmNativeHandle.h @@ -0,0 +1,42 @@ +/* + * WasmNativeHandle.h (OpenGL) + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#ifndef LLGL_OPENGL_WASM_NATIVE_HANDLE_H +#define LLGL_OPENGL_WASM_NATIVE_HANDLE_H + + +#include + + +namespace LLGL +{ + +namespace OpenGL +{ + + +/** +\brief Emscripten native handle structure for the OpenGL render system. +\see RenderSystem::GetNativeHandle +\see RenderSystemDescriptor::nativeHandle +*/ +struct RenderSystemNativeHandle +{ + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context; +}; + + +} // /namespace OpenGL + +} // /namespace LLGL + + +#endif + + + +// ================================================================================ diff --git a/include/LLGL/Platform/NativeHandle.h b/include/LLGL/Platform/NativeHandle.h index 8564869d75..d4a35a42c7 100644 --- a/include/LLGL/Platform/NativeHandle.h +++ b/include/LLGL/Platform/NativeHandle.h @@ -19,6 +19,8 @@ # include #elif defined(LLGL_OS_LINUX) # include +#elif defined(LLGL_OS_WASM) +# include #elif defined(LLGL_OS_IOS) # include #elif defined(LLGL_OS_ANDROID) diff --git a/include/LLGL/Platform/Platform.h b/include/LLGL/Platform/Platform.h index 30a4989d91..372ddea99b 100644 --- a/include/LLGL/Platform/Platform.h +++ b/include/LLGL/Platform/Platform.h @@ -30,6 +30,8 @@ see https://sourceforge.net/p/predef/wiki/OperatingSystems/ # endif #elif defined __ANDROID__ || defined ANDROID # define LLGL_OS_ANDROID +#elif defined EMSCRIPTEN +# define LLGL_OS_WASM #elif defined __linux__ # define LLGL_OS_LINUX #endif diff --git a/include/LLGL/Platform/Wasm/WasmNativeHandle.h b/include/LLGL/Platform/Wasm/WasmNativeHandle.h new file mode 100644 index 0000000000..2e784a83cd --- /dev/null +++ b/include/LLGL/Platform/Wasm/WasmNativeHandle.h @@ -0,0 +1,34 @@ +/* + * WasmNativeHandle.h + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#ifndef LLGL_WASM_NATIVE_HANDLE_H +#define LLGL_WASM_NATIVE_HANDLE_H + + +#include + + +namespace LLGL +{ + + +//! Emscripten native handle structure. +struct NativeHandle +{ + //! CSS selector of canvas object. + emscripten::val canvas; +}; + + +} // /namespace LLGL + + +#endif + + + +// ================================================================================ diff --git a/include/LLGL/RenderSystemFlags.h b/include/LLGL/RenderSystemFlags.h index d06392ab20..91e2e3bd80 100644 --- a/include/LLGL/RenderSystemFlags.h +++ b/include/LLGL/RenderSystemFlags.h @@ -240,9 +240,9 @@ struct RendererID static constexpr int Null = 0x00000001; //!< ID number for a Null renderer. This renderer does not render anything but provides the same interface for debugging purposes. static constexpr int OpenGL = 0x00000002; //!< ID number for an OpenGL renderer. - static constexpr int OpenGLES1 = 0x00000003; //!< ID number for an OpenGL ES 1 renderer. - static constexpr int OpenGLES2 = 0x00000004; //!< ID number for an OpenGL ES 2 renderer. - static constexpr int OpenGLES3 = 0x00000005; //!< ID number for an OpenGL ES 3 renderer. + static constexpr int OpenGLES = 0x00000003; //!< ID number for an OpenGL ES renderer. + static constexpr int WebGL = 0x00000004; //!< ID number for a WebGL renderer. + static constexpr int WebGPU = 0x00000005; //!< ID number for a WebGPU renderer. static constexpr int Direct3D9 = 0x00000006; //!< ID number for a Direct3D 9 renderer. static constexpr int Direct3D10 = 0x00000007; //!< ID number for a Direct3D 10 renderer. static constexpr int Direct3D11 = 0x00000008; //!< ID number for a Direct3D 11 renderer. @@ -250,6 +250,15 @@ struct RendererID static constexpr int Vulkan = 0x0000000A; //!< ID number for a Vulkan renderer. static constexpr int Metal = 0x0000000B; //!< ID number for a Metal renderer. + LLGL_DEPRECATED("LLGL::RendererID::OpenGLES1 is deprecated since 0.04b; Use LLGL::RendererID::OpenGLES instead!", "OpenGLES") + static constexpr int OpenGLES1 = RendererID::OpenGLES; + + LLGL_DEPRECATED("LLGL::RendererID::OpenGLES2 is deprecated since 0.04b; Use LLGL::RendererID::OpenGLES instead!", "OpenGLES") + static constexpr int OpenGLES2 = RendererID::OpenGLES; + + LLGL_DEPRECATED("LLGL::RendererID::OpenGLES3 is deprecated since 0.04b; Use LLGL::RendererID::OpenGLES instead!", "OpenGLES") + static constexpr int OpenGLES3 = RendererID::OpenGLES; + static constexpr int Reserved = 0x000000FF; //!< Highest ID number for reserved future renderers. Value is 0x000000ff. }; diff --git a/include/LLGL/RendererConfiguration.h b/include/LLGL/RendererConfiguration.h index 0290d0dbae..9f1f08381e 100644 --- a/include/LLGL/RendererConfiguration.h +++ b/include/LLGL/RendererConfiguration.h @@ -35,7 +35,8 @@ enum class OpenGLContextProfile /** \brief OpenGL ES profile. - \note Only supported on: Android and iOS. + \remarks This profile is used for both OpenGL ES and WebGL since WebGL shaders also refer to the ES profile. + \note Only supported on: Android, iOS, and WebAssembly. */ ESProfile, @@ -43,7 +44,7 @@ enum class OpenGLContextProfile \brief Default GL profile. \remarks This is equivalent to CoreProfile for OpenGL and ESProfile for OpenGLES. */ - #if defined(LLGL_OS_ANDROID) || defined(LLGL_OS_IOS) + #if defined(LLGL_OS_ANDROID) || defined(LLGL_OS_IOS) || defined(LLGL_OS_WASM) DefaultProfile = ESProfile, #else DefaultProfile = CoreProfile, diff --git a/sources/Platform/Debug.h b/sources/Platform/Debug.h index c1ff5262c3..2eface2e14 100644 --- a/sources/Platform/Debug.h +++ b/sources/Platform/Debug.h @@ -22,6 +22,8 @@ # include "MacOS/MacOSDebug.h" # elif defined(LLGL_OS_LINUX) # include "Linux/LinuxDebug.h" +# elif defined(LLGL_OS_WASM) +# include "Wasm/WasmDebug.h" # elif defined(LLGL_OS_IOS) # include "IOS/IOSDebug.h" # elif defined(LLGL_OS_ANDROID) diff --git a/sources/Platform/Wasm/WasmCanvas.cpp b/sources/Platform/Wasm/WasmCanvas.cpp new file mode 100644 index 0000000000..3c7f22cd61 --- /dev/null +++ b/sources/Platform/Wasm/WasmCanvas.cpp @@ -0,0 +1,288 @@ +/* + * WasmCanvas.cpp + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#include "WasmCanvas.h" +#include "WasmKeyCodes.h" +#include +#include +#include "../../Core/CoreUtils.h" + +#include + + +namespace LLGL +{ + + +/* + * Surface class + */ + +bool Surface::ProcessEvents() +{ + return true; // dummy - handled by web browser +} + + +/* + * Canvas class + */ + +std::unique_ptr Canvas::Create(const CanvasDescriptor& desc) +{ + return MakeUnique(desc); +} + + +/* + * WasmCanvas class + */ + +WasmCanvas::WasmCanvas(const CanvasDescriptor& desc) +{ + CreateEmscriptenCanvas(desc); +} + +bool WasmCanvas::GetNativeHandle(void* nativeHandle, std::size_t nativeHandleSize) +{ + if (nativeHandle != nullptr && nativeHandleSize == sizeof(NativeHandle)) + { + auto* handle = reinterpret_cast(nativeHandle); + handle->canvas = canvas_; + return true; + } + return false; +} + +Extent2D WasmCanvas::GetContentSize() const +{ + int width = 0, height = 0; + emscripten_get_canvas_element_size("#canvas", &width, &height); + return Extent2D + { + static_cast(width), + static_cast(height) + }; +} + +void WasmCanvas::SetTitle(const UTF8String& title) +{ + emscripten_set_window_title(title.c_str()); +} + +UTF8String WasmCanvas::GetTitle() const +{ + return emscripten_get_window_title(); +} + + +/* + * ======= Private: ======= + */ + +/*static const char* EmscriptenResultToString(EMSCRIPTEN_RESULT result) +{ + switch (result) + { + case EMSCRIPTEN_RESULT_SUCCESS: return "EMSCRIPTEN_RESULT_SUCCESS"; + case EMSCRIPTEN_RESULT_DEFERRED: return "EMSCRIPTEN_RESULT_DEFERRED"; + case EMSCRIPTEN_RESULT_NOT_SUPPORTED: return "EMSCRIPTEN_RESULT_NOT_SUPPORTED"; + case EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED: return "EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED"; + case EMSCRIPTEN_RESULT_INVALID_TARGET: return "EMSCRIPTEN_RESULT_INVALID_TARGET"; + case EMSCRIPTEN_RESULT_UNKNOWN_TARGET: return "EMSCRIPTEN_RESULT_UNKNOWN_TARGET"; + case EMSCRIPTEN_RESULT_INVALID_PARAM: return "EMSCRIPTEN_RESULT_INVALID_PARAM"; + case EMSCRIPTEN_RESULT_FAILED: return "EMSCRIPTEN_RESULT_FAILED"; + case EMSCRIPTEN_RESULT_NO_DATA: return "EMSCRIPTEN_RESULT_NO_DATA"; + } + return "Unknown EMSCRIPTEN_RESULT!"; +}*/ + +/*static int EmscriptenKeyeventToCharcode(int eventType, const EmscriptenKeyboardEvent* event) +{ + // Only KeyPress events carry a charCode. For KeyDown and KeyUp events, these don't seem to be present yet, until later when the KeyDown + // is transformed to KeyPress. Sometimes it can be useful to already at KeyDown time to know what the charCode of the resulting + // KeyPress will be. The following attempts to do this: + if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && event->which) return event->which; + if (event->charCode) return event->charCode; + if (strlen(event->key) == 1) return (int)event->key[0]; + if (event->which) return event->which; + return event->keyCode; +}*/ + +const char* WasmCanvas::OnBeforeUnloadCallback(int eventType, const void* /*reserved*/, void* userData) +{ + WasmCanvas* canvas = reinterpret_cast(userData); + canvas->PostDestroy(); + return nullptr; // no string to be displayed to the user +} + +EM_BOOL WasmCanvas::OnCanvasResizeCallback(int eventType, const EmscriptenUiEvent* event, void* userData) +{ + if (eventType == EMSCRIPTEN_EVENT_RESIZE) + { + /* Resize this canvas as the event comes from the window and we must update the canvas resolution */ + emscripten_set_canvas_element_size("#canvas", event->windowInnerWidth, event->windowInnerHeight); + + /* Send resize event to event listeners */ + WasmCanvas* canvas = reinterpret_cast(userData); + const Extent2D clientAreaSize + { + static_cast(event->windowInnerWidth), + static_cast(event->windowInnerHeight) + }; + canvas->PostResize(clientAreaSize); + return EM_TRUE; + } + return EM_FALSE; +} + +EM_BOOL WasmCanvas::OnKeyCallback(int eventType, const EmscriptenKeyboardEvent* event, void* userData) +{ + WasmCanvas* canvas = reinterpret_cast(userData); + + Key key = MapEmscriptenKeyCode(event->code); + + if (eventType == 2) + canvas->PostKeyDown(key); + else if (eventType == 3) + canvas->PostKeyUp(key); + + return EM_TRUE; +} + +static Key EmscriptenMouseButtonToKeyCode(unsigned short button) +{ + switch (button) + { + case 0: return Key::LButton; + case 1: return Key::MButton; + case 2: return Key::RButton; + default: return Key::Any; + } +} + +static EventAction EmscriptenMouseEventToAction(int eventType) +{ + switch (eventType) + { + case EMSCRIPTEN_EVENT_MOUSEENTER: return EventAction::Began; + case EMSCRIPTEN_EVENT_MOUSEMOVE: return EventAction::Changed; + case EMSCRIPTEN_EVENT_MOUSELEAVE: return EventAction::Ended; + default: return EventAction::Ended; // fallback + } +} + +EM_BOOL WasmCanvas::OnMouseCallback(int eventType, const EmscriptenMouseEvent* event, void* userData) +{ + WasmCanvas* canvas = reinterpret_cast(userData); + + switch (eventType) + { + case EMSCRIPTEN_EVENT_MOUSEDOWN: + { + canvas->PostKeyDown(EmscriptenMouseButtonToKeyCode(event->button)); + } + return EM_TRUE; + + case EMSCRIPTEN_EVENT_MOUSEUP: + { + canvas->PostKeyUp(EmscriptenMouseButtonToKeyCode(event->button)); + } + return EM_TRUE; + + case EMSCRIPTEN_EVENT_CLICK: + case EMSCRIPTEN_EVENT_DBLCLICK: + { + const Offset2D position + { + static_cast(event->clientX), + static_cast(event->clientY) + }; + canvas->PostTapGesture(position, 1); + } + return EM_TRUE; + + case EMSCRIPTEN_EVENT_MOUSEENTER: + case EMSCRIPTEN_EVENT_MOUSEMOVE: + case EMSCRIPTEN_EVENT_MOUSELEAVE: + { + const Offset2D position + { + static_cast(event->clientX), + static_cast(event->clientY) + }; + const float motionX = static_cast(event->movementX); + const float motionY = static_cast(event->movementY); + canvas->PostPanGesture(position, 1, motionX, motionY, EmscriptenMouseEventToAction(eventType)); + } + return EM_TRUE; + + default: + break; + } + + return EM_FALSE; +} + +EM_BOOL WasmCanvas::OnWheelCallback(int eventType, const EmscriptenWheelEvent* event, void* userData) +{ + //TODO + return EM_TRUE; +} + +void WasmCanvas::CreateEmscriptenCanvas(const CanvasDescriptor& desc) +{ + /* Find canvas handle*/ + emscripten::val config = emscripten::val::module_property("config"); + emscripten::val document = emscripten::val::global("document"); + + //if (config.isUndefined() || config.isNull()) + // return; + + //if (!config.hasOwnProperty("canvas_selector")) + // return; + + std::string canvasSelector = "#canvas";//config["canvas_selector"].as(); + canvas_ = document["body"].call("querySelector", canvasSelector); + + EM_ASM({ + console.log(Emval.toValue($0)); + }, canvas_.as_handle()); + + //emscripten::val console = emscripten::val::global("console"); + //console.call("log", canvas); + + /* Set title and show canvas (if enabled) */ + SetTitle(desc.title); + + /* Set callbacks */ + EMSCRIPTEN_RESULT ret; + ret = emscripten_set_beforeunload_callback(this, WasmCanvas::OnBeforeUnloadCallback); + ret = emscripten_set_resize_callback (EMSCRIPTEN_EVENT_TARGET_WINDOW, this, EM_TRUE, WasmCanvas::OnCanvasResizeCallback); + ret = emscripten_set_keydown_callback (/*canvasSelector.c_str()*/EMSCRIPTEN_EVENT_TARGET_WINDOW, this, EM_TRUE, WasmCanvas::OnKeyCallback); + ret = emscripten_set_keyup_callback (/*canvasSelector.c_str()*/EMSCRIPTEN_EVENT_TARGET_WINDOW, this, EM_TRUE, WasmCanvas::OnKeyCallback); + ret = emscripten_set_click_callback (/*canvasSelector.c_str()*/EMSCRIPTEN_EVENT_TARGET_WINDOW, this, EM_TRUE, WasmCanvas::OnMouseCallback); + ret = emscripten_set_dblclick_callback (/*canvasSelector.c_str()*/EMSCRIPTEN_EVENT_TARGET_WINDOW, this, EM_TRUE, WasmCanvas::OnMouseCallback); + ret = emscripten_set_mousedown_callback (/*canvasSelector.c_str()*/EMSCRIPTEN_EVENT_TARGET_WINDOW, this, EM_TRUE, WasmCanvas::OnMouseCallback); + ret = emscripten_set_mouseup_callback (/*canvasSelector.c_str()*/EMSCRIPTEN_EVENT_TARGET_WINDOW, this, EM_TRUE, WasmCanvas::OnMouseCallback); + ret = emscripten_set_mousemove_callback (/*canvasSelector.c_str()*/EMSCRIPTEN_EVENT_TARGET_WINDOW, this, EM_TRUE, WasmCanvas::OnMouseCallback); + ret = emscripten_set_mouseenter_callback(/*canvasSelector.c_str()*/EMSCRIPTEN_EVENT_TARGET_WINDOW, this, EM_TRUE, WasmCanvas::OnMouseCallback); + ret = emscripten_set_mouseleave_callback(/*canvasSelector.c_str()*/EMSCRIPTEN_EVENT_TARGET_WINDOW, this, EM_TRUE, WasmCanvas::OnMouseCallback); + ret = emscripten_set_wheel_callback (/*canvasSelector.c_str()*/EMSCRIPTEN_EVENT_TARGET_WINDOW, this, EM_TRUE, WasmCanvas::OnWheelCallback); + + /* Resize canvas to initial CSS size */ + double w = 0, h = 0; + emscripten_get_element_css_size(canvasSelector.c_str(), &w, &h); + emscripten_set_canvas_element_size(canvasSelector.c_str(), static_cast(w), static_cast(h)); +} + + +} // /namespace LLGL + + + +// ================================================================================ diff --git a/sources/Platform/Wasm/WasmCanvas.h b/sources/Platform/Wasm/WasmCanvas.h new file mode 100644 index 0000000000..bcc94a4025 --- /dev/null +++ b/sources/Platform/Wasm/WasmCanvas.h @@ -0,0 +1,66 @@ +/* + * WasmCanvas.h + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#ifndef LLGL_WASM_CANVAS_H +#define LLGL_WASM_CANVAS_H + + +#include +#include +#include +#include +#include +#include + + +namespace LLGL +{ + + +class WasmCanvas : public Canvas +{ + + public: + + WasmCanvas(const CanvasDescriptor& desc); + + bool GetNativeHandle(void* nativeHandle, std::size_t nativeHandleSize) override; + + Extent2D GetContentSize() const override; + + void SetTitle(const UTF8String& title) override; + UTF8String GetTitle() const override; + + private: + + void CreateEmscriptenCanvas(const CanvasDescriptor& desc); + + private: + + static const char* OnBeforeUnloadCallback(int eventType, const void* reserved, void* userData); + static EM_BOOL OnCanvasResizeCallback(int eventType, const EmscriptenUiEvent* event, void *userData); + + static EM_BOOL OnKeyCallback(int eventType, const EmscriptenKeyboardEvent* event, void *userData); + + static EM_BOOL OnMouseCallback(int eventType, const EmscriptenMouseEvent* event, void *userData); + static EM_BOOL OnWheelCallback(int eventType, const EmscriptenWheelEvent* event, void *userData); + + private: + + emscripten::val canvas_; + +}; + + +} // /namespace LLGL + + +#endif + + + +// ================================================================================ diff --git a/sources/Platform/Wasm/WasmDebug.cpp b/sources/Platform/Wasm/WasmDebug.cpp new file mode 100644 index 0000000000..56c3df3535 --- /dev/null +++ b/sources/Platform/Wasm/WasmDebug.cpp @@ -0,0 +1,31 @@ +/* + * WasmDebug.cpp + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#include "../Debug.h" +#include + + +namespace LLGL +{ + + +LLGL_EXPORT void DebugPuts(const char* text) +{ + ::fprintf(stderr, "%s\n", text); +} + +LLGL_EXPORT UTF8String DebugStackTrace(unsigned firstStackFrame, unsigned maxNumStackFrames) +{ + return {}; //TODO +} + + +} // /namespace LLGL + + + +// ================================================================================ diff --git a/sources/Platform/Wasm/WasmDebug.h b/sources/Platform/Wasm/WasmDebug.h new file mode 100644 index 0000000000..4f087d665c --- /dev/null +++ b/sources/Platform/Wasm/WasmDebug.h @@ -0,0 +1,25 @@ +/* + * WasmDebug.h + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#ifndef LLGL_WASM_DEBUG_H +#define LLGL_WASM_DEBUG_H + + +#include + +#ifdef SIGTRAP +# define LLGL_DEBUG_BREAK() raise(SIGTRAP) +#else +# define LLGL_DEBUG_BREAK() +#endif + + +#endif + + + +// ================================================================================ diff --git a/sources/Platform/Wasm/WasmDisplay.cpp b/sources/Platform/Wasm/WasmDisplay.cpp new file mode 100644 index 0000000000..903d18a6d0 --- /dev/null +++ b/sources/Platform/Wasm/WasmDisplay.cpp @@ -0,0 +1,123 @@ +/* + * WasmDisplay.cpp + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#include "WasmDisplay.h" +#include "../../Core/CoreUtils.h" +#include + + +namespace LLGL +{ + + +/* + * Display class + */ + +std::size_t Display::Count() +{ + return 0; +} + +Display* const * Display::GetList() +{ + static Display* const displayList[] = { Display::GetPrimary(), nullptr }; + return displayList; +} + +Display* Display::Get(std::size_t index) +{ + return (index == 0 ? Display::GetPrimary() : nullptr); +} + +Display* Display::GetPrimary() +{ + static WasmDisplay primary; + return &primary; +} + +bool Display::ShowCursor(bool /*show*/) +{ + return false; +} + +bool Display::IsCursorShown() +{ + return true; +} + +bool Display::SetCursorPosition(const Offset2D& /*position*/) +{ + return false; // dummy +} + +Offset2D Display::GetCursorPosition() +{ + Offset2D rootPosition = { 0, 0 }; + //Offset2D childPosition = { 0, 0 }; + return rootPosition; +} + + +/* + * WasmDisplay class + */ + +bool WasmDisplay::IsPrimary() const +{ + return true; +} + +UTF8String WasmDisplay::GetDeviceName() const +{ + return UTF8String{ "device name" }; +} + +Offset2D WasmDisplay::GetOffset() const +{ + return Offset2D{}; +} + +float WasmDisplay::GetScale() const +{ + return 1.0f; // dummy +} + +bool WasmDisplay::ResetDisplayMode() +{ + return false; // dummy +} + +bool WasmDisplay::SetDisplayMode(const DisplayMode& /*displayMode*/) +{ + return false; // dummy +} + +DisplayMode WasmDisplay::GetDisplayMode() const +{ + DisplayMode displayMode; + { + int width = 0, height = 0; + emscripten_get_screen_size(&width, &height); + displayMode.resolution.width = static_cast(width); + displayMode.resolution.height = static_cast(height); + displayMode.refreshRate = 60; // default to 60 Hz + } + return displayMode; +} + +std::vector WasmDisplay::GetSupportedDisplayModes() const +{ + return { GetDisplayMode() }; +} + + +} // /namespace LLGL + + + +// ================================================================================ diff --git a/sources/Platform/Wasm/WasmDisplay.h b/sources/Platform/Wasm/WasmDisplay.h new file mode 100644 index 0000000000..4a189039ce --- /dev/null +++ b/sources/Platform/Wasm/WasmDisplay.h @@ -0,0 +1,49 @@ +/* + * WasmDisplay.h + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#ifndef LLGL_WASM_DISPLAY_H +#define LLGL_WASM_DISPLAY_H + + +#include + + +namespace LLGL +{ + + +class WasmDisplay : public Display +{ + + public: + + WasmDisplay() = default; + + bool IsPrimary() const override; + + UTF8String GetDeviceName() const override; + + Offset2D GetOffset() const override; + float GetScale() const override; + + bool ResetDisplayMode() override; + bool SetDisplayMode(const DisplayMode& displayMode) override; + DisplayMode GetDisplayMode() const override; + + std::vector GetSupportedDisplayModes() const override; + +}; + + +} // /namespace LLGL + + +#endif + + + +// ================================================================================ diff --git a/sources/Platform/Wasm/WasmKeyCodes.cpp b/sources/Platform/Wasm/WasmKeyCodes.cpp new file mode 100644 index 0000000000..1a4cdc468d --- /dev/null +++ b/sources/Platform/Wasm/WasmKeyCodes.cpp @@ -0,0 +1,203 @@ +/* + * WasmKeyCodes.cpp + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#include "WasmKeyCodes.h" +#include +#include +#include + + +namespace LLGL +{ + + +#define KEYPAIR(KEYSYM, KEY) { KEYSYM, Key::KEY } + +static std::map GenerateEmscriptenKeyCodeMap() +{ + return + { + KEYPAIR(DOM_PK_ESCAPE , Escape), /* "Escape" */ + + KEYPAIR(DOM_PK_0 , D0), /* "Digit0" */ + KEYPAIR(DOM_PK_1 , D1), /* "Digit1" */ + KEYPAIR(DOM_PK_2 , D2), /* "Digit2" */ + KEYPAIR(DOM_PK_3 , D3), /* "Digit3" */ + KEYPAIR(DOM_PK_4 , D4), /* "Digit4" */ + KEYPAIR(DOM_PK_5 , D5), /* "Digit5" */ + KEYPAIR(DOM_PK_6 , D6), /* "Digit6" */ + KEYPAIR(DOM_PK_7 , D7), /* "Digit7" */ + KEYPAIR(DOM_PK_8 , D8), /* "Digit8" */ + KEYPAIR(DOM_PK_9 , D9), /* "Digit9" */ + + KEYPAIR(DOM_PK_MINUS , Minus), /* "Minus" */ + KEYPAIR(DOM_PK_EQUAL , Any), /* "Equal" */ + KEYPAIR(DOM_PK_BACKSPACE , Back), /* "Backspace" */ + KEYPAIR(DOM_PK_TAB , Tab), /* "Tab" */ + + KEYPAIR(DOM_PK_Q , Q), /* "KeyQ" */ + KEYPAIR(DOM_PK_W , W), /* "KeyW" */ + KEYPAIR(DOM_PK_E , E), /* "KeyE" */ + KEYPAIR(DOM_PK_R , R), /* "KeyR" */ + KEYPAIR(DOM_PK_T , T), /* "KeyT" */ + KEYPAIR(DOM_PK_Y , Y), /* "KeyY" */ + KEYPAIR(DOM_PK_U , U), /* "KeyU" */ + KEYPAIR(DOM_PK_I , I), /* "KeyI" */ + KEYPAIR(DOM_PK_O , O), /* "KeyO" */ + KEYPAIR(DOM_PK_P , P), /* "KeyP" */ + //KEYPAIR(DOM_PK_BRACKET_LEFT , Any), /* "BracketLeft" */ + //KEYPAIR(DOM_PK_BRACKET_RIGHT , Any), /* "BracketRight" */ + KEYPAIR(DOM_PK_ENTER , Return), /* "Enter" */ + KEYPAIR(DOM_PK_CONTROL_LEFT , LControl), /* "ControlLeft" */ + KEYPAIR(DOM_PK_A , A), /* "KeyA" */ + KEYPAIR(DOM_PK_S , S), /* "KeyS" */ + KEYPAIR(DOM_PK_D , D), /* "KeyD" */ + KEYPAIR(DOM_PK_F , F), /* "KeyF" */ + KEYPAIR(DOM_PK_G , G), /* "KeyG" */ + KEYPAIR(DOM_PK_H , H), /* "KeyH" */ + KEYPAIR(DOM_PK_J , J), /* "KeyJ" */ + KEYPAIR(DOM_PK_K , K), /* "KeyK" */ + KEYPAIR(DOM_PK_L , L), /* "KeyL" */ + //KEYPAIR(DOM_PK_SEMICOLON , Any), /* "Semicolon" */ + //KEYPAIR(DOM_PK_QUOTE , Any), /* "Quote" */ + //KEYPAIR(DOM_PK_BACKQUOTE , Any), /* "Backquote" */ + KEYPAIR(DOM_PK_SHIFT_LEFT , LShift), /* "ShiftLeft" */ + //KEYPAIR(DOM_PK_BACKSLASH , Any), /* "Backslash" */ + KEYPAIR(DOM_PK_Z , Z), /* "KeyZ" */ + KEYPAIR(DOM_PK_X , X), /* "KeyX" */ + KEYPAIR(DOM_PK_C , C), /* "KeyC" */ + KEYPAIR(DOM_PK_V , V), /* "KeyV" */ + KEYPAIR(DOM_PK_B , B), /* "KeyB" */ + KEYPAIR(DOM_PK_N , N), /* "KeyN" */ + KEYPAIR(DOM_PK_M , M), /* "KeyM" */ + KEYPAIR(DOM_PK_COMMA , Comma), /* "Comma" */ + KEYPAIR(DOM_PK_PERIOD , Period), /* "Period" */ + //KEYPAIR(DOM_PK_SLASH , Any), /* "Slash" */ + KEYPAIR(DOM_PK_SHIFT_RIGHT , RShift), /* "ShiftRight" */ + KEYPAIR(DOM_PK_NUMPAD_MULTIPLY , KeypadMultiply), /* "NumpadMultiply" */ + KEYPAIR(DOM_PK_ALT_LEFT , LMenu), /* "AltLeft" */ + KEYPAIR(DOM_PK_SPACE , Space), /* "Space" */ + KEYPAIR(DOM_PK_CAPS_LOCK , Capital), /* "CapsLock" */ + KEYPAIR(DOM_PK_F1 , F1), /* "F1" */ + KEYPAIR(DOM_PK_F2 , F2), /* "F2" */ + KEYPAIR(DOM_PK_F3 , F3), /* "F3" */ + KEYPAIR(DOM_PK_F4 , F4), /* "F4" */ + KEYPAIR(DOM_PK_F5 , F5), /* "F5" */ + KEYPAIR(DOM_PK_F6 , F6), /* "F6" */ + KEYPAIR(DOM_PK_F7 , F7), /* "F7" */ + KEYPAIR(DOM_PK_F8 , F8), /* "F8" */ + KEYPAIR(DOM_PK_F9 , F9), /* "F9" */ + KEYPAIR(DOM_PK_F10 , F10), /* "F10" */ + + KEYPAIR(DOM_PK_PAUSE , Pause), /* "Pause" */ + KEYPAIR(DOM_PK_SCROLL_LOCK , ScrollLock), /* "ScrollLock" */ + KEYPAIR(DOM_PK_NUMPAD_7 , Keypad7), /* "Numpad7" */ + KEYPAIR(DOM_PK_NUMPAD_8 , Keypad8), /* "Numpad8" */ + KEYPAIR(DOM_PK_NUMPAD_9 , Keypad9), /* "Numpad9" */ + KEYPAIR(DOM_PK_NUMPAD_SUBTRACT , KeypadMinus), /* "NumpadSubtract" */ + KEYPAIR(DOM_PK_NUMPAD_4 , Keypad4), /* "Numpad4" */ + KEYPAIR(DOM_PK_NUMPAD_5 , Keypad5), /* "Numpad5" */ + KEYPAIR(DOM_PK_NUMPAD_6 , Keypad6), /* "Numpad6" */ + KEYPAIR(DOM_PK_NUMPAD_ADD , KeypadPlus), /* "NumpadAdd" */ + KEYPAIR(DOM_PK_NUMPAD_1 , Keypad1), /* "Numpad1" */ + KEYPAIR(DOM_PK_NUMPAD_2 , Keypad2), /* "Numpad2" */ + KEYPAIR(DOM_PK_NUMPAD_3 , Keypad3), /* "Numpad3" */ + KEYPAIR(DOM_PK_NUMPAD_0 , Keypad0), /* "Numpad0" */ + KEYPAIR(DOM_PK_NUMPAD_DECIMAL , KeypadDecimal), /* "NumpadDecimal" */ + KEYPAIR(DOM_PK_PRINT_SCREEN , Print), /* "PrintScreen" */ + //KEYPAIR(DOM_PK_INTL_BACKSLASH , Any), /* "IntlBackslash" */ + KEYPAIR(DOM_PK_F11 , F11), /* "F11" */ + KEYPAIR(DOM_PK_F12 , F12), /* "F12" */ + //KEYPAIR(DOM_PK_NUMPAD_EQUAL , Any), /* "NumpadEqual" */ + KEYPAIR(DOM_PK_F13 , F13), /* "F13" */ + KEYPAIR(DOM_PK_F14 , F14), /* "F14" */ + KEYPAIR(DOM_PK_F15 , F15), /* "F15" */ + KEYPAIR(DOM_PK_F16 , F16), /* "F16" */ + KEYPAIR(DOM_PK_F17 , F17), /* "F17" */ + KEYPAIR(DOM_PK_F18 , F18), /* "F18" */ + KEYPAIR(DOM_PK_F19 , F19), /* "F19" */ + KEYPAIR(DOM_PK_F20 , F20), /* "F20" */ + KEYPAIR(DOM_PK_F21 , F21), /* "F21" */ + KEYPAIR(DOM_PK_F22 , F22), /* "F22" */ + KEYPAIR(DOM_PK_F23 , F23), /* "F23" */ + //KEYPAIR(DOM_PK_KANA_MODE , Any), /* "KanaMode" */ + //KEYPAIR(DOM_PK_LANG_2 , Any), /* "Lang2" */ + //KEYPAIR(DOM_PK_LANG_1 , Any), /* "Lang1" */ + //KEYPAIR(DOM_PK_INTL_RO , Any), /* "IntlRo" */ + KEYPAIR(DOM_PK_F24 , F24), /* "F24" */ + //KEYPAIR(DOM_PK_CONVERT , Any), /* "Convert" */ + //KEYPAIR(DOM_PK_NON_CONVERT , Any), /* "NonConvert" */ + //KEYPAIR(DOM_PK_INTL_YEN , Any), /* "IntlYen" */ + KEYPAIR(DOM_PK_NUMPAD_COMMA , KeypadDecimal), /* "NumpadComma" */ + //KEYPAIR(DOM_PK_PASTE , Any), /* "Paste" */ + KEYPAIR(DOM_PK_MEDIA_TRACK_PREVIOUS , MediaPrevTrack), /* "MediaTrackPrevious" */ + //KEYPAIR(DOM_PK_CUT , Any), /* "Cut" */ + //KEYPAIR(DOM_PK_COPY , Any), /* "Copy" */ + KEYPAIR(DOM_PK_MEDIA_TRACK_NEXT , MediaNextTrack), /* "MediaTrackNext" */ + + KEYPAIR(DOM_PK_NUMPAD_ENTER , Return), /* "NumpadEnter" */ + KEYPAIR(DOM_PK_CONTROL_RIGHT , RControl), /* "ControlRight" */ + KEYPAIR(DOM_PK_AUDIO_VOLUME_MUTE , VolumeMute), /* "VolumeMute" */ + KEYPAIR(DOM_PK_LAUNCH_APP_2 , LaunchApp2), /* "LaunchApp2" */ + KEYPAIR(DOM_PK_MEDIA_PLAY_PAUSE , MediaPlayPause), /* "MediaPlayPause" */ + KEYPAIR(DOM_PK_MEDIA_STOP , MediaStop), /* "MediaStop" */ + //KEYPAIR(DOM_PK_EJECT , Any), /* "Eject" */ + KEYPAIR(DOM_PK_AUDIO_VOLUME_DOWN , VolumeDown), /* "VolumeDown" */ + KEYPAIR(DOM_PK_AUDIO_VOLUME_UP , VolumeUp), /* "VolumeUp" */ + KEYPAIR(DOM_PK_BROWSER_HOME , BrowserHome), /* "BrowserHome" */ + KEYPAIR(DOM_PK_NUMPAD_DIVIDE , KeypadDivide), /* "NumpadDivide" */ + KEYPAIR(DOM_PK_ALT_RIGHT , RMenu), /* "AltRight" */ + KEYPAIR(DOM_PK_HELP , Help), /* "Help" */ + KEYPAIR(DOM_PK_NUM_LOCK , NumLock), /* "NumLock" */ + KEYPAIR(DOM_PK_HOME , Home), /* "Home" */ + KEYPAIR(DOM_PK_ARROW_UP , Up), /* "ArrowUp" */ + KEYPAIR(DOM_PK_PAGE_UP , PageUp), /* "PageUp" */ + KEYPAIR(DOM_PK_ARROW_LEFT , Left), /* "ArrowLeft" */ + KEYPAIR(DOM_PK_ARROW_RIGHT , Right), /* "ArrowRight" */ + KEYPAIR(DOM_PK_END , End), /* "End" */ + KEYPAIR(DOM_PK_ARROW_DOWN , Down), /* "ArrowDown" */ + KEYPAIR(DOM_PK_PAGE_DOWN , PageDown), /* "PageDown" */ + KEYPAIR(DOM_PK_INSERT , Insert), /* "Insert" */ + KEYPAIR(DOM_PK_DELETE , Delete), /* "Delete" */ + KEYPAIR(DOM_PK_META_LEFT , LWin), /* "MetaLeft" */ + //KEYPAIR(DOM_PK_OS_LEFT , Any), /* "OSLeft" */ + KEYPAIR(DOM_PK_META_RIGHT , RWin), /* "MetaRight" */ + //KEYPAIR(DOM_PK_OS_RIGHT , Any), /* "OSRight" */ + KEYPAIR(DOM_PK_CONTEXT_MENU , Apps), /* "ContextMenu" */ + //KEYPAIR(DOM_PK_POWER , Any), /* "Power" */ + KEYPAIR(DOM_PK_BROWSER_SEARCH , BrowserSearch), /* "BrowserSearch" */ + KEYPAIR(DOM_PK_BROWSER_FAVORITES , BrowserFavorits), /* "BrowserFavorites" */ + KEYPAIR(DOM_PK_BROWSER_REFRESH , BrowserRefresh), /* "BrowserRefresh" */ + KEYPAIR(DOM_PK_BROWSER_STOP , BrowserStop), /* "BrowserStop" */ + KEYPAIR(DOM_PK_BROWSER_FORWARD , BrowserForward), /* "BrowserForward" */ + KEYPAIR(DOM_PK_BROWSER_BACK , BrowserBack), /* "BrowserBack" */ + KEYPAIR(DOM_PK_LAUNCH_APP_1 , LaunchApp1), /* "LaunchApp1" */ + KEYPAIR(DOM_PK_LAUNCH_MAIL , LaunchMail), /* "LaunchMail" */ + //KEYPAIR(DOM_PK_LAUNCH_MEDIA_PLAYER , Any), /* "LaunchMediaPlayer" */ + KEYPAIR(DOM_PK_MEDIA_SELECT , LaunchMediaSelect), /* "MediaSelect" */ + }; +}; + +static std::map g_emscriptenWindowKeyCodeMap = GenerateEmscriptenKeyCodeMap(); + +#undef KEYPAIR + + +Key MapEmscriptenKeyCode(const char* keyEvent) +{ + int keyCode = emscripten_compute_dom_pk_code(keyEvent); + auto it = g_emscriptenWindowKeyCodeMap.find(keyCode); + return (it != g_emscriptenWindowKeyCodeMap.end() ? it->second : Key::Pause); +} + + + +} // /namespace LLGL + + + +// ================================================================================ diff --git a/sources/Platform/Wasm/WasmKeyCodes.h b/sources/Platform/Wasm/WasmKeyCodes.h new file mode 100644 index 0000000000..ed717c1167 --- /dev/null +++ b/sources/Platform/Wasm/WasmKeyCodes.h @@ -0,0 +1,29 @@ +/* + * WasmKeyCodes.h + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#ifndef LLGL_WASM_KEY_CODES_H +#define LLGL_WASM_KEY_CODES_H + + +#include + + +namespace LLGL +{ + + +Key MapEmscriptenKeyCode(const char* keyEvent); + + +} // /namespace LLGL + + +#endif + + + +// ================================================================================ diff --git a/sources/Platform/Wasm/WasmModule.cpp b/sources/Platform/Wasm/WasmModule.cpp new file mode 100644 index 0000000000..3fcbb2ac0e --- /dev/null +++ b/sources/Platform/Wasm/WasmModule.cpp @@ -0,0 +1,112 @@ +/* + * WasmModule.cpp + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#include "WasmModule.h" +#include "../../Core/CoreUtils.h" +#include "../../Core/Exception.h" +#include +#include +#include +#include +#include + + +namespace LLGL +{ + + +// Returns absolute path of program instance +static std::string GetProgramPath() +{ + /* Get filename of running program */ + char buf[1024] = { 0 }; + ssize_t bufSize = readlink("/proc/self/exe", buf, sizeof(buf)); + if (bufSize == -1) + { + int errorCode = errno; + LLGL_TRAP("readlink(/proc/self/exe) failed: errno=%d (%s)", errorCode, strerror(errorCode)); + } + + /* Get path from program */ + std::string path = buf; + + std::size_t pathEnd = path.find_last_of('/'); + if (pathEnd != std::string::npos) + path.resize(pathEnd + 1); + + return path; +} + +std::string Module::GetModuleFilename(const char* moduleName) +{ + /* Extend module name to Linux shared library name (SO) */ + std::string s = GetProgramPath(); + s += "libLLGL_"; + s += moduleName; + #ifdef LLGL_DEBUG + s += "D"; + #endif + s += ".so"; + return s; +} + +bool Module::IsAvailable(const char* moduleFilename) +{ + /* Check if Linux shared library can be loaded properly */ + if (void* handle = dlopen(moduleFilename, RTLD_LAZY)) + { + dlclose(handle); + return true; + } + return false; +} + +std::unique_ptr Module::Load(const char* moduleFilename, Report* report) +{ + std::unique_ptr module = MakeUnique(moduleFilename, report); + return (module->IsValid() ? std::move(module) : nullptr); +} + +WasmModule::WasmModule(const char* moduleFilename, Report* report) +{ + /* Open Linux shared library */ + handle_ = dlopen(moduleFilename, RTLD_LAZY); + + /* Check if loading has failed */ + if (!handle_ && report != nullptr) + { + /* Append error message from most recent call to 'dlopen' */ + std::string appendix; + if (const char* err = dlerror()) + { + appendix += "; "; + appendix += err; + } + + /* Throw error message */ + report->Errorf("failed to load shared library (SO): \"%s\"%s\n", moduleFilename, appendix.c_str()); + } +} + +WasmModule::~WasmModule() +{ + if (handle_ != nullptr) + dlclose(handle_); +} + +void* WasmModule::LoadProcedure(const char* procedureName) +{ + /* Get procedure address from library module and return it as raw-pointer */ + return dlsym(handle_, procedureName); +} + + +} // /namespace LLGL + + + +// ================================================================================ diff --git a/sources/Platform/Wasm/WasmModule.h b/sources/Platform/Wasm/WasmModule.h new file mode 100644 index 0000000000..2fa5093f14 --- /dev/null +++ b/sources/Platform/Wasm/WasmModule.h @@ -0,0 +1,50 @@ +/* + * WasmModule.h + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#ifndef LLGL_WASM_MODULE_H +#define LLGL_WASM_MODULE_H + + +#include "../Module.h" + + +namespace LLGL +{ + + +class WasmModule : public Module +{ + + public: + + WasmModule(const char* moduleFilename, Report* report = nullptr); + ~WasmModule(); + + void* LoadProcedure(const char* procedureName) override; + + public: + + inline bool IsValid() const + { + return (handle_ != nullptr); + } + + private: + + void* handle_ = nullptr; + +}; + + +} // /namespace LLGL + + +#endif + + + +// ================================================================================ diff --git a/sources/Platform/Wasm/WasmPath.cpp b/sources/Platform/Wasm/WasmPath.cpp new file mode 100644 index 0000000000..dd0ea6fdf5 --- /dev/null +++ b/sources/Platform/Wasm/WasmPath.cpp @@ -0,0 +1,42 @@ +/* + * WasmPath.cpp + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#include "../Path.h" +#include + + +namespace LLGL +{ + +namespace Path +{ + + +LLGL_EXPORT char GetSeparator() +{ + return '/'; +} + +LLGL_EXPORT UTF8String GetWorkingDir() +{ + char path[PATH_MAX] = { 0 }; + return UTF8String{ ::getcwd(path, sizeof(path)) }; +} + +LLGL_EXPORT UTF8String GetAbsolutePath(const UTF8String& filename) +{ + return Combine(GetWorkingDir(), filename); +} + + +} // /nameapace Path + +} // /namespace LLGL + + + +// ================================================================================ diff --git a/sources/Platform/Wasm/WasmTimer.cpp b/sources/Platform/Wasm/WasmTimer.cpp new file mode 100644 index 0000000000..0142f4a811 --- /dev/null +++ b/sources/Platform/Wasm/WasmTimer.cpp @@ -0,0 +1,45 @@ +/* + * WasmTimer.cpp + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#include +#include + + +namespace LLGL +{ + +namespace Timer +{ + + +static const std::uint64_t g_nsecFrequency = 1000000000ull; + +LLGL_EXPORT std::uint64_t Frequency() +{ + return g_nsecFrequency; +} + +static std::uint64_t MonotonicTimeToUInt64(const timespec& t) +{ + return (static_cast(t.tv_sec) * g_nsecFrequency + static_cast(t.tv_nsec)); +} + +LLGL_EXPORT std::uint64_t Tick() +{ + timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return MonotonicTimeToUInt64(t); +} + + +} // /namespace Timer + +} // /namespace LLGL + + + +// ================================================================================ diff --git a/sources/Renderer/OpenGL/Buffer/GLBuffer.cpp b/sources/Renderer/OpenGL/Buffer/GLBuffer.cpp index eca74c3dae..20512c49f4 100644 --- a/sources/Renderer/OpenGL/Buffer/GLBuffer.cpp +++ b/sources/Renderer/OpenGL/Buffer/GLBuffer.cpp @@ -307,7 +307,7 @@ void GLBuffer::UnmapBuffer() #endif // /GL_ARB_direct_state_access { GLStateManager::Get().BindGLBuffer(*this); - glUnmapBuffer(GetGLTarget()); + GLProfile::UnmapBuffer(GetGLTarget()); } } diff --git a/sources/Renderer/OpenGL/CMakeLists.txt b/sources/Renderer/OpenGL/CMakeLists.txt index 61f5f23b44..1447f1658f 100644 --- a/sources/Renderer/OpenGL/CMakeLists.txt +++ b/sources/Renderer/OpenGL/CMakeLists.txt @@ -62,6 +62,7 @@ find_source_files(FilesRendererGLShader CXX ${PROJECT_SOURCE_DIR}/Shader find_source_files(FilesRendererGLTexture CXX ${PROJECT_SOURCE_DIR}/Texture) find_source_files(FilesRendererGLCoreProfile CXX ${PROJECT_SOURCE_DIR}/GLCoreProfile) find_source_files(FilesRendererGLESProfile CXX ${PROJECT_SOURCE_DIR}/GLESProfile) +find_source_files(FilesRendererWebGLProfile CXX ${PROJECT_SOURCE_DIR}/WebGLProfile) find_source_files(FilesIncludeGL INC ${BACKEND_INCLUDE_DIR}/OpenGL) # Remove selected files if GL2X is disabled @@ -98,44 +99,15 @@ elseif(UNIX) if(LLGL_ANDROID_PLATFORM) find_source_files(FilesRendererGLPlatform CXX ${PROJECT_SOURCE_DIR}/Platform/Android) find_source_files(FilesIncludeGLPlatform INC ${BACKEND_INCLUDE_DIR}/OpenGL/Android) + elseif(EMSCRIPTEN) + find_source_files(FilesRendererGLPlatform CXX ${PROJECT_SOURCE_DIR}/Platform/Wasm) + find_source_files(FilesIncludeGLPlatform INC ${BACKEND_INCLUDE_DIR}/OpenGL/Wasm) else() find_source_files(FilesRendererGLPlatform CXX ${PROJECT_SOURCE_DIR}/Platform/Linux) find_source_files(FilesIncludeGLPlatform INC ${BACKEND_INCLUDE_DIR}/OpenGL/Linux) endif() endif() -set( - FilesGL - ${FilesRendererGL} - ${FilesRendererGLBuffer} - ${FilesRendererGLCommand} - ${FilesRendererGLExt} - ${FilesRendererGLPlatform} - ${FilesRendererGLPlatformBase} - ${FilesRendererGLRenderState} - ${FilesRendererGLShader} - ${FilesRendererGLTexture} - ${FilesRendererGLCoreProfile} - ${FilesIncludeGL} - ${FilesIncludeGLPlatform} -) - -set( - FilesGLES3 - ${FilesRendererGL} - ${FilesRendererGLBuffer} - ${FilesRendererGLCommand} - ${FilesRendererGLExt} - ${FilesRendererGLPlatform} - ${FilesRendererGLPlatformBase} - ${FilesRendererGLRenderState} - ${FilesRendererGLES3Shader} - ${FilesRendererGLTexture} - ${FilesRendererGLESProfile} - ${FilesIncludeGL} - ${FilesIncludeGLPlatform} -) - # === Source group folders === @@ -149,6 +121,7 @@ source_group("OpenGL\\Shader" FILES ${FilesRendererGLShader}) source_group("OpenGL\\Texture" FILES ${FilesRendererGLTexture}) source_group("OpenGL\\GLCoreProfile" FILES ${FilesRendererGLCoreProfile}) source_group("OpenGL\\GLESProfile" FILES ${FilesRendererGLESProfile}) +source_group("OpenGL\\WebGLProfile" FILES ${FilesRendererWebGLProfile}) source_group("Include\\Platform" FILES ${FilesIncludeGL} ${FilesIncludeGLPlatform}) @@ -162,12 +135,57 @@ endif() # === Projects === +if(LLGL_BUILD_RENDERER_WEBGL) + # WebGL Renderer + set( + FilesWebGL + ${FilesRendererGL} + ${FilesRendererGLBuffer} + ${FilesRendererGLCommand} + ${FilesRendererGLExt} + ${FilesRendererGLPlatform} + ${FilesRendererGLPlatformBase} + ${FilesRendererGLRenderState} + ${FilesRendererGLES3Shader} + ${FilesRendererGLTexture} + ${FilesRendererWebGLProfile} + ${FilesIncludeGL} + ${FilesIncludeGLPlatform} + ) + + include("${EXTERNAL_MODULE_DIR}/FindOpenGLES3.cmake") + if(OPENGLES_FOUND) + include_directories(${OPENGLES_INCLUDE_DIR}) + add_llgl_module(LLGL_WebGL LLGL_BUILD_RENDERER_WEBGL "${FilesWebGL}") + target_link_libraries(LLGL_WebGL LLGL ${OPENGLES_LIBRARIES}) + ADD_PROJECT_DEFINE(LLGL_WebGL LLGL_WEBGL) + else() + message(FATAL_ERROR "LLGL_BUILD_RENDERER_WEBGL failed: missing OpenGLES libraries") + endif() +endif() + if(LLGL_BUILD_RENDERER_OPENGLES3) # OpenGLES Renderer + set( + FilesGLES3 + ${FilesRendererGL} + ${FilesRendererGLBuffer} + ${FilesRendererGLCommand} + ${FilesRendererGLExt} + ${FilesRendererGLPlatform} + ${FilesRendererGLPlatformBase} + ${FilesRendererGLRenderState} + ${FilesRendererGLES3Shader} + ${FilesRendererGLTexture} + ${FilesRendererGLESProfile} + ${FilesIncludeGL} + ${FilesIncludeGLPlatform} + ) + include("${EXTERNAL_MODULE_DIR}/FindOpenGLES3.cmake") if(OPENGLES_FOUND) include_directories(${OPENGLES_INCLUDE_DIR}) - + add_llgl_module(LLGL_OpenGLES3 LLGL_BUILD_RENDERER_OPENGLES3 "${FilesGLES3}") if(APPLE) @@ -185,15 +203,31 @@ endif() if(LLGL_BUILD_RENDERER_OPENGL) # OpenGL Renderer + set( + FilesGL + ${FilesRendererGL} + ${FilesRendererGLBuffer} + ${FilesRendererGLCommand} + ${FilesRendererGLExt} + ${FilesRendererGLPlatform} + ${FilesRendererGLPlatformBase} + ${FilesRendererGLRenderState} + ${FilesRendererGLShader} + ${FilesRendererGLTexture} + ${FilesRendererGLCoreProfile} + ${FilesIncludeGL} + ${FilesIncludeGLPlatform} + ) + set(OpenGL_GL_PREFERENCE GLVND) find_package(OpenGL REQUIRED) - if(OpenGL_FOUND) + if(OPENGL_FOUND) include_directories(${OPENGL_INCLUDE_DIR}) add_llgl_module(LLGL_OpenGL LLGL_BUILD_RENDERER_OPENGL "${FilesGL}") - target_link_libraries(LLGL_OpenGL LLGL ${OPENGL_LIBRARIES}) - + target_link_libraries(LLGL_OpenGL LLGL ${OPENGL_LIBRARIES}) + if(APPLE) ADD_PROJECT_DEFINE(LLGL_OpenGL GL_SILENCE_DEPRECATION) endif() diff --git a/sources/Renderer/OpenGL/Ext/GLExtensions.h b/sources/Renderer/OpenGL/Ext/GLExtensions.h index a4bf1f8304..5944c5827e 100644 --- a/sources/Renderer/OpenGL/Ext/GLExtensions.h +++ b/sources/Renderer/OpenGL/Ext/GLExtensions.h @@ -9,10 +9,10 @@ #define LLGL_GL_EXTENSIONS_H -#ifdef LLGL_OPENGLES3 -# include "../GLESProfile/GLESExtensions.h" -#else +#ifdef LLGL_OPENGL # include "../GLCoreProfile/GLCoreExtensions.h" +#else +# include "../GLESProfile/GLESExtensions.h" #endif diff --git a/sources/Renderer/OpenGL/GLCore.cpp b/sources/Renderer/OpenGL/GLCore.cpp index 703cc57f47..a0ef25902e 100644 --- a/sources/Renderer/OpenGL/GLCore.cpp +++ b/sources/Renderer/OpenGL/GLCore.cpp @@ -177,10 +177,12 @@ int GLGetVersion() [[noreturn]] void ErrUnsupportedGLProc(const char* name) { - #ifdef LLGL_OPENGLES3 + #if defined(LLGL_OPENGL) + LLGL_TRAP("illegal use of unsupported OpenGL procedure: %s", name); + #elif defined(LLGL_OS_WASM) LLGL_TRAP("illegal use of unsupported OpenGLES procedure: %s", name); #else - LLGL_TRAP("illegal use of unsupported OpenGL procedure: %s", name); + LLGL_TRAP("illegal use of unsupported WebGL procedure: %s", name); #endif } diff --git a/sources/Renderer/OpenGL/GLCoreProfile/GLCoreProfile.cpp b/sources/Renderer/OpenGL/GLCoreProfile/GLCoreProfile.cpp index de20ea9422..a3b2b557af 100644 --- a/sources/Renderer/OpenGL/GLCoreProfile/GLCoreProfile.cpp +++ b/sources/Renderer/OpenGL/GLCoreProfile/GLCoreProfile.cpp @@ -98,6 +98,11 @@ void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr /*length*/, GLbi return reinterpret_cast(ptr + offset); } +void UnmapBuffer(GLenum target) +{ + glUnmapBuffer(target); +} + void DrawBuffer(GLenum buf) { glDrawBuffer(buf); diff --git a/sources/Renderer/OpenGL/GLESProfile/GLESExtensionsDecl.inl b/sources/Renderer/OpenGL/GLESProfile/GLESExtensionsDecl.inl index b3404d8da6..cebf84d219 100644 --- a/sources/Renderer/OpenGL/GLESProfile/GLESExtensionsDecl.inl +++ b/sources/Renderer/OpenGL/GLESProfile/GLESExtensionsDecl.inl @@ -8,7 +8,7 @@ // THIS FILE MUST NOT HAVE A HEADER GUARD -#ifndef __APPLE__ +#if !GL_GLEXT_PROTOTYPES #ifndef DECL_GLPROC #error Missing definition of macro DECL_GLPROC(PFNTYPE, NAME, RTYPE, ARGS) diff --git a/sources/Renderer/OpenGL/GLESProfile/GLESProfile.cpp b/sources/Renderer/OpenGL/GLESProfile/GLESProfile.cpp index 3bdc302f7d..d977698e2b 100644 --- a/sources/Renderer/OpenGL/GLESProfile/GLESProfile.cpp +++ b/sources/Renderer/OpenGL/GLESProfile/GLESProfile.cpp @@ -106,6 +106,11 @@ void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfie return glMapBufferRange(target, offset, length, access); } +void UnmapBuffer(GLenum target) +{ + glUnmapBuffer(target); +} + void DrawBuffer(GLenum buf) { glDrawBuffers(1, &buf); diff --git a/sources/Renderer/OpenGL/GLModuleInterface.cpp b/sources/Renderer/OpenGL/GLModuleInterface.cpp index 329d6dc281..5c69b70963 100644 --- a/sources/Renderer/OpenGL/GLModuleInterface.cpp +++ b/sources/Renderer/OpenGL/GLModuleInterface.cpp @@ -14,8 +14,10 @@ namespace LLGL { -#ifdef LLGL_OPENGLES3 +#if defined(LLGL_OPENGLES3) # define ModuleOpenGL ModuleOpenGLES3 +#elif defined(LLGL_WEBGL) +# define ModuleOpenGL ModuleWebGL #endif namespace ModuleOpenGL diff --git a/sources/Renderer/OpenGL/GLProfile.h b/sources/Renderer/OpenGL/GLProfile.h index 803e53d118..ebbe6a56b5 100644 --- a/sources/Renderer/OpenGL/GLProfile.h +++ b/sources/Renderer/OpenGL/GLProfile.h @@ -11,8 +11,12 @@ #if defined LLGL_OPENGL # include "GLCoreProfile/GLCoreProfileTypes.h" -#elif defined LLGL_OPENGLES3 +#elif defined LLGL_OPEGNLES3 # include "GLESProfile/GLESProfileTypes.h" +#elif defined LLGL_WEBGL +# include "WebGLProfile/WebGLProfileTypes.h" +#else +# error Unknwon OpenGL backend #endif @@ -24,7 +28,7 @@ namespace GLProfile { -// Returns the renderer ID number, e.g. RendererID::OpenGL or RendererID::OpenGLES3. +// Returns the renderer ID number, e.g. RendererID::OpenGL or RendererID::OpenGLES. int GetRendererID(); // Returns the renderer module name, e.g. "OpenGL" or "OpenGLES3". @@ -63,6 +67,9 @@ void* MapBuffer(GLenum target, GLenum access); // Wrapper for glMapBufferRange; uses glMapBuffer for GL. void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +// Wrapper for glUnmapBuffer. Not supported in WebGL. +void UnmapBuffer(GLenum target); + // Wrapper for glDrawBuffer; uses glDrawBuffers for GLES. void DrawBuffer(GLenum buf); diff --git a/sources/Renderer/OpenGL/GLSwapChain.cpp b/sources/Renderer/OpenGL/GLSwapChain.cpp index 4aebe50ae1..f56eb43ad8 100644 --- a/sources/Renderer/OpenGL/GLSwapChain.cpp +++ b/sources/Renderer/OpenGL/GLSwapChain.cpp @@ -30,7 +30,7 @@ namespace LLGL static GLint GetFramebufferHeight(const Extent2D& resolution) { - #ifndef LLGL_OPENGL + #if !defined(LLGL_OPENGL) && !defined(LLGL_OS_WASM) /* For GLES, the framebuffer height is determined by the display height */ if (LLGL::Display* display = LLGL::Display::GetPrimary()) { diff --git a/sources/Renderer/OpenGL/GLTypes.cpp b/sources/Renderer/OpenGL/GLTypes.cpp index 400bf82ae2..f8791b96cc 100644 --- a/sources/Renderer/OpenGL/GLTypes.cpp +++ b/sources/Renderer/OpenGL/GLTypes.cpp @@ -34,7 +34,11 @@ GLenum MapOrZero(const Format format) case Format::Undefined: return 0; /* --- Alpha channel color formats --- */ + #ifdef LLGL_WEBGL + case Format::A8UNorm: return GL_ALPHA; + #else case Format::A8UNorm: return GL_R8; // texture swizzle + #endif /* --- Red channel color formats --- */ case Format::R8UNorm: return GL_R8; @@ -119,11 +123,13 @@ GLenum MapOrZero(const Format format) case Format::RGBA64Float: return 0; /* --- BGRA color formats --- */ + #ifndef LLGL_WEBGL // WebGL does not support texture swizzling case Format::BGRA8UNorm: return GL_RGBA8; // texture swizzle case Format::BGRA8UNorm_sRGB: return GL_SRGB8_ALPHA8; // texture swizzle case Format::BGRA8SNorm: return GL_RGBA8_SNORM; // texture swizzle case Format::BGRA8UInt: return GL_RGBA8UI; // texture swizzle case Format::BGRA8SInt: return GL_RGBA8I; // texture swizzle + #endif #ifdef LLGL_OPENGL /* --- Packed formats --- */ @@ -249,7 +255,7 @@ GLenum Map(const TextureSwizzle textureSwizzle) GLenum Map(const Format textureFormat) { - if (auto result = MapOrZero(textureFormat)) + if (GLenum result = MapOrZero(textureFormat)) return result; LLGL_TRAP_GL_MAP(Format, textureFormat); } @@ -258,7 +264,11 @@ static GLenum MapImageFormat(const ImageFormat imageFormat) { switch (imageFormat) { + #ifdef LLGL_WEBGL + case ImageFormat::Alpha: return GL_ALPHA; + #else case ImageFormat::Alpha: return GL_RED; // texture swizzle + #endif case ImageFormat::R: return GL_RED; case ImageFormat::RG: return GL_RG; case ImageFormat::RGB: return GL_RGB; @@ -288,7 +298,11 @@ static GLenum MapIntegerImageFormat(const ImageFormat imageFormat) { switch (imageFormat) { + #ifdef LLGL_WEBGL + case ImageFormat::Alpha: break; // WebGL does not support texture swizzling, only GL_ALPHA but it's not an integer format + #else case ImageFormat::Alpha: return GL_RED_INTEGER; // texture swizzle + #endif case ImageFormat::R: return GL_RED_INTEGER; case ImageFormat::RG: return GL_RG_INTEGER; case ImageFormat::RGB: return GL_RGB_INTEGER; @@ -616,59 +630,7 @@ GLenum ToPrimitiveMode(const PrimitiveTopology primitiveTopology) UniformType UnmapUniformType(const GLenum uniformType) { - #ifdef LLGL_OPENGLES3 - - switch (uniformType) - { - /* ----- Scalars/Vectors ----- */ - case GL_FLOAT: return UniformType::Float1; - case GL_FLOAT_VEC2: return UniformType::Float2; - case GL_FLOAT_VEC3: return UniformType::Float3; - case GL_FLOAT_VEC4: return UniformType::Float4; - case GL_INT: return UniformType::Int1; - case GL_INT_VEC2: return UniformType::Int2; - case GL_INT_VEC3: return UniformType::Int3; - case GL_INT_VEC4: return UniformType::Int4; - case GL_UNSIGNED_INT: return UniformType::UInt1; - case GL_UNSIGNED_INT_VEC2: return UniformType::UInt2; - case GL_UNSIGNED_INT_VEC3: return UniformType::UInt3; - case GL_UNSIGNED_INT_VEC4: return UniformType::UInt4; - case GL_BOOL: return UniformType::Bool1; - case GL_BOOL_VEC2: return UniformType::Bool2; - case GL_BOOL_VEC3: return UniformType::Bool3; - case GL_BOOL_VEC4: return UniformType::Bool4; - - /* ----- Matrices ----- */ - case GL_FLOAT_MAT2: return UniformType::Float2x2; - case GL_FLOAT_MAT3: return UniformType::Float3x3; - case GL_FLOAT_MAT4: return UniformType::Float4x4; - case GL_FLOAT_MAT2x3: return UniformType::Float2x3; - case GL_FLOAT_MAT2x4: return UniformType::Float2x4; - case GL_FLOAT_MAT3x2: return UniformType::Float3x2; - case GL_FLOAT_MAT3x4: return UniformType::Float3x4; - case GL_FLOAT_MAT4x2: return UniformType::Float4x2; - case GL_FLOAT_MAT4x3: return UniformType::Float4x3; - - /* ----- Samplers ----- */ - case GL_SAMPLER_2D: - case GL_SAMPLER_3D: - case GL_SAMPLER_CUBE: - case GL_SAMPLER_2D_SHADOW: - case GL_SAMPLER_2D_ARRAY: - case GL_SAMPLER_2D_ARRAY_SHADOW: - case GL_SAMPLER_CUBE_SHADOW: - case GL_INT_SAMPLER_2D: - case GL_INT_SAMPLER_3D: - case GL_INT_SAMPLER_CUBE: - case GL_INT_SAMPLER_2D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D: - case GL_UNSIGNED_INT_SAMPLER_3D: - case GL_UNSIGNED_INT_SAMPLER_CUBE: - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - return UniformType::Sampler; - } - - #else + #ifdef LLGL_OPENGL switch (uniformType) { @@ -793,7 +755,59 @@ UniformType UnmapUniformType(const GLenum uniformType) #endif // /__APPLE__ } - #endif // /LLGL_OPENGLES3 + #else // LLGL_OPENGL + + switch (uniformType) + { + /* ----- Scalars/Vectors ----- */ + case GL_FLOAT: return UniformType::Float1; + case GL_FLOAT_VEC2: return UniformType::Float2; + case GL_FLOAT_VEC3: return UniformType::Float3; + case GL_FLOAT_VEC4: return UniformType::Float4; + case GL_INT: return UniformType::Int1; + case GL_INT_VEC2: return UniformType::Int2; + case GL_INT_VEC3: return UniformType::Int3; + case GL_INT_VEC4: return UniformType::Int4; + case GL_UNSIGNED_INT: return UniformType::UInt1; + case GL_UNSIGNED_INT_VEC2: return UniformType::UInt2; + case GL_UNSIGNED_INT_VEC3: return UniformType::UInt3; + case GL_UNSIGNED_INT_VEC4: return UniformType::UInt4; + case GL_BOOL: return UniformType::Bool1; + case GL_BOOL_VEC2: return UniformType::Bool2; + case GL_BOOL_VEC3: return UniformType::Bool3; + case GL_BOOL_VEC4: return UniformType::Bool4; + + /* ----- Matrices ----- */ + case GL_FLOAT_MAT2: return UniformType::Float2x2; + case GL_FLOAT_MAT3: return UniformType::Float3x3; + case GL_FLOAT_MAT4: return UniformType::Float4x4; + case GL_FLOAT_MAT2x3: return UniformType::Float2x3; + case GL_FLOAT_MAT2x4: return UniformType::Float2x4; + case GL_FLOAT_MAT3x2: return UniformType::Float3x2; + case GL_FLOAT_MAT3x4: return UniformType::Float3x4; + case GL_FLOAT_MAT4x2: return UniformType::Float4x2; + case GL_FLOAT_MAT4x3: return UniformType::Float4x3; + + /* ----- Samplers ----- */ + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_CUBE_SHADOW: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + return UniformType::Sampler; + } + + #endif // /LLGL_OPENGL return UniformType::Undefined; } diff --git a/sources/Renderer/OpenGL/OpenGL.h b/sources/Renderer/OpenGL/OpenGL.h index 6a27dd0743..da0ace137f 100644 --- a/sources/Renderer/OpenGL/OpenGL.h +++ b/sources/Renderer/OpenGL/OpenGL.h @@ -13,14 +13,18 @@ # include "GLCoreProfile/OpenGLCore.h" #elif defined LLGL_OPENGLES3 # include "GLESProfile/OpenGLES.h" +#elif defined LLGL_WEBGL +# include "WebGLProfile/WebGL.h" +#else +# error Unknown OpenGL backend #endif -#if GL_ARB_draw_indirect || GL_ES_VERSION_3_1 +#if (GL_ARB_draw_indirect || GL_ES_VERSION_3_1) && !defined(LLGL_WEBGL) # define LLGL_GLEXT_DRAW_INDIRECT #endif -#if GL_ARB_draw_elements_base_vertex || GL_ES_VERSION_3_2 +#if (GL_ARB_draw_elements_base_vertex || GL_ES_VERSION_3_2) && !defined(LLGL_WEBGL) # define LLGL_GLEXT_DRAW_ELEMENTS_BASE_VERTEX #endif @@ -32,11 +36,11 @@ # define LLGL_GLEXT_MULTI_DRAW_INDIRECT #endif -#if GL_ARB_compute_shader || GL_ES_VERSION_3_1 +#if (GL_ARB_compute_shader || GL_ES_VERSION_3_1) && !defined(LLGL_WEBGL) # define LLGL_GLEXT_COMPUTE_SHADER #endif -#if GL_KHR_debug || GL_ES_VERSION_3_2 +#if (GL_KHR_debug || GL_ES_VERSION_3_2) && !defined(LLGL_WEBGL) # define LLGL_GLEXT_DEBUG 1 #endif @@ -49,27 +53,27 @@ # define LLGL_GLEXT_TRANSFORM_FEEDBACK #endif -#if GL_EXT_draw_buffers2 || GL_ES_VERSION_3_2 +#if (GL_EXT_draw_buffers2 || GL_ES_VERSION_3_2) && !defined(LLGL_WEBGL) # define LLGL_GLEXT_DRAW_BUFFERS2 #endif -#if GL_ARB_draw_buffers_blend || GL_ES_VERSION_3_2 +#if (GL_ARB_draw_buffers_blend || GL_ES_VERSION_3_2) && !defined(LLGL_WEBGL) # define LLGL_GLEXT_DRAW_BUFFERS_BLEND #endif -#if GL_ARB_tessellation_shader || GL_ES_VERSION_3_2 +#if (GL_ARB_tessellation_shader || GL_ES_VERSION_3_2) && !defined(LLGL_WEBGL) # define LLGL_GLEXT_TESSELLATION_SHADER #endif -#if GL_ARB_shader_storage_buffer_object || GL_ES_VERSION_3_1 +#if (GL_ARB_shader_storage_buffer_object || GL_ES_VERSION_3_1) && !defined(LLGL_WEBGL) # define LLGL_GLEXT_SHADER_STORAGE_BUFFER_OBJECT #endif -#if GL_ARB_program_interface_query || GL_ES_VERSION_3_1 +#if (GL_ARB_program_interface_query || GL_ES_VERSION_3_1) && !defined(LLGL_WEBGL) # define LLGL_GLEXT_PROGRAM_INTERFACE_QUERY #endif -#if defined LLGL_OPENGL || GL_ES_VERSION_3_1 +#if (defined(LLGL_OPENGL) || GL_ES_VERSION_3_1) && !defined(LLGL_WEBGL) # define LLGL_GLEXT_GET_TEX_LEVEL_PARAMETER #endif @@ -81,7 +85,7 @@ # define LLGL_SAMPLER_BORDER_COLOR #endif -#if GL_ARB_shader_image_load_store || GL_ES_VERSION_3_1 +#if (GL_ARB_shader_image_load_store || GL_ES_VERSION_3_1) && !defined(LLGL_WEBGL) # define LLGL_GLEXT_MEMORY_BARRIERS #endif diff --git a/sources/Renderer/OpenGL/Platform/Wasm/WasmGLContext.cpp b/sources/Renderer/OpenGL/Platform/Wasm/WasmGLContext.cpp new file mode 100644 index 0000000000..bfbdb81efd --- /dev/null +++ b/sources/Renderer/OpenGL/Platform/Wasm/WasmGLContext.cpp @@ -0,0 +1,136 @@ +/* + * WasmGLContext.cpp + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#include "WasmGLContext.h" +#include "../../../CheckedCast.h" +#include "../../../StaticAssertions.h" +#include "../../../../Core/CoreUtils.h" +#include "../../../../Core/Exception.h" +#include +#include + + +namespace LLGL +{ + + +/* + * GLContext class + */ + +LLGL_ASSERT_STDLAYOUT_STRUCT( OpenGL::RenderSystemNativeHandle ); + +std::unique_ptr GLContext::Create( + const GLPixelFormat& pixelFormat, + const RendererConfigurationOpenGL& profile, + Surface& surface, + GLContext* sharedContext, + const ArrayView& /*customNativeHandle*/) +{ + WasmGLContext* sharedContextEGL = (sharedContext != nullptr ? LLGL_CAST(WasmGLContext*, sharedContext) : nullptr); + return MakeUnique(pixelFormat, profile, surface, sharedContextEGL); +} + + +/* + * WasmGLContext class + */ + +WasmGLContext::WasmGLContext( + const GLPixelFormat& pixelFormat, + const RendererConfigurationOpenGL& profile, + Surface& surface, + WasmGLContext* sharedContext) +{ + CreateContext(pixelFormat, profile, sharedContext); +} + +WasmGLContext::~WasmGLContext() +{ + DeleteContext(); +} + +int WasmGLContext::GetSamples() const +{ + int samples_ = 4; + return samples_; +} + +bool WasmGLContext::GetNativeHandle(void* nativeHandle, std::size_t nativeHandleSize) const +{ + if (nativeHandle != nullptr && nativeHandleSize == sizeof(OpenGL::RenderSystemNativeHandle)) + { + auto* nativeHandleGL = reinterpret_cast(nativeHandle); + nativeHandleGL->context = context_; + return true; + } + return false; +} + + +/* + * ======= Private: ======= + */ + +bool WasmGLContext::SetSwapInterval(int /*interval*/) +{ + return false; // dummy +} + +static void GetWebGLVersionFromConfig(EmscriptenWebGLContextAttributes& attrs, const RendererConfigurationOpenGL& cfg) +{ + if (cfg.majorVersion == 0 && cfg.minorVersion == 0) + { + /* WebGL 2.0 is requested by default */ + attrs.majorVersion = 2; + attrs.minorVersion = 0; + } + else + { + /* Request custom WebGL version (can only be 1.0 or 2.0) */ + attrs.majorVersion = cfg.majorVersion; + attrs.minorVersion = cfg.minorVersion; + } +} + +void WasmGLContext::CreateContext(const GLPixelFormat& pixelFormat, const RendererConfigurationOpenGL& profile, WasmGLContext* sharedContext) +{ + EmscriptenWebGLContextAttributes attrs = {}; + emscripten_webgl_init_context_attributes(&attrs); + + GetWebGLVersionFromConfig(attrs, profile); + attrs.alpha = true;//(pixelFormat.colorBits > 24); + attrs.depth = true;//(pixelFormat.depthBits > 0); + attrs.stencil = true;//(pixelFormat.stencilBits > 0); + attrs.antialias = true;//(pixelFormat.samples > 1); + attrs.premultipliedAlpha = true; + attrs.preserveDrawingBuffer = false; + attrs.explicitSwapControl = 0; + attrs.failIfMajorPerformanceCaveat = false; + attrs.enableExtensionsByDefault = true; + attrs.powerPreference = EM_WEBGL_POWER_PREFERENCE_DEFAULT; + + //TODO: determine canvas ID + context_ = emscripten_webgl_create_context("#canvas", &attrs); + + if (!context_) + LLGL_TRAP("emscripten_webgl_create_context failed"); + + EMSCRIPTEN_RESULT res = emscripten_webgl_make_context_current(context_); +} + +void WasmGLContext::DeleteContext() +{ + //destroy context_ +} + + +} // /namespace LLGL + + + +// ================================================================================ diff --git a/sources/Renderer/OpenGL/Platform/Wasm/WasmGLContext.h b/sources/Renderer/OpenGL/Platform/Wasm/WasmGLContext.h new file mode 100644 index 0000000000..7133a9ab28 --- /dev/null +++ b/sources/Renderer/OpenGL/Platform/Wasm/WasmGLContext.h @@ -0,0 +1,72 @@ +/* + * WasmGLContext.h + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#ifndef LLGL_WASM_CONTEXT_H +#define LLGL_WASM_CONTEXT_H + + +#include "../GLContext.h" +#include "../../OpenGL.h" +#include +#include + +namespace LLGL +{ + + +// Implementation of the interface for Android and wrapper for a native EGL context. +class WasmGLContext : public GLContext +{ + + public: + + WasmGLContext( + const GLPixelFormat& pixelFormat, + const RendererConfigurationOpenGL& profile, + Surface& surface, + WasmGLContext* sharedContext + ); + ~WasmGLContext(); + + int GetSamples() const override; + + bool GetNativeHandle(void* nativeHandle, std::size_t nativeHandleSize) const override; + + public: + + // Returns the native WebGL context. + inline EMSCRIPTEN_WEBGL_CONTEXT_HANDLE GetWebGLContext() const + { + return context_; + } + + private: + + bool SetSwapInterval(int interval) override; + + void CreateContext( + const GLPixelFormat& pixelFormat, + const RendererConfigurationOpenGL& profile, + WasmGLContext* sharedContext + ); + void DeleteContext(); + + private: + + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context_ = 0; + +}; + + +} // /namespace LLGL + + +#endif + + + +// ================================================================================ diff --git a/sources/Renderer/OpenGL/Platform/Wasm/WasmGLSwapChainContext.cpp b/sources/Renderer/OpenGL/Platform/Wasm/WasmGLSwapChainContext.cpp new file mode 100644 index 0000000000..759b2c320a --- /dev/null +++ b/sources/Renderer/OpenGL/Platform/Wasm/WasmGLSwapChainContext.cpp @@ -0,0 +1,88 @@ +/* + * WasmGLSwapChainContext.cpp + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#include "WasmGLSwapChainContext.h" +#include "../../../../Core/CoreUtils.h" +#include "../../../../Core/Assertion.h" +#include + + +namespace LLGL +{ + + +/* + * GLSwapChainContext class + */ + +std::unique_ptr GLSwapChainContext::Create(GLContext& context, Surface& surface) +{ + return MakeUnique(static_cast(context), surface); +} + +bool GLSwapChainContext::MakeCurrentUnchecked(GLSwapChainContext* context) +{ + return WasmGLSwapChainContext::MakeCurrentEGLContext(static_cast(context)); +} + + +/* + * WasmGLSwapChainContext class + */ + +WasmGLSwapChainContext::WasmGLSwapChainContext(WasmGLContext& context, Surface& surface) : + GLSwapChainContext { context }, + context_ { context.GetWebGLContext() } +{ + /* Get native surface handle */ + //NativeHandle nativeHandle; + //surface.GetNativeHandle(&nativeHandle, sizeof(nativeHandle)); + + if (!context_) + LLGL_TRAP("GetWebGLContext failed"); +} + +WasmGLSwapChainContext::~WasmGLSwapChainContext() +{ + //eglDestroySurface(display_, surface_); +} + +bool WasmGLSwapChainContext::HasDrawable() const +{ + return true; // dummy +} + +bool WasmGLSwapChainContext::SwapBuffers() +{ + // do nothing + return true; +} + +void WasmGLSwapChainContext::Resize(const Extent2D& resolution) +{ + // do nothing (WebGL context does not need to be resized) +} + +bool WasmGLSwapChainContext::MakeCurrentEGLContext(WasmGLSwapChainContext* context) +{ + EMSCRIPTEN_RESULT res = emscripten_webgl_make_context_current(context->context_); + LLGL_ASSERT(res == EMSCRIPTEN_RESULT_SUCCESS, "emscripten_webgl_make_context_current() failed"); + + //LLGL_ASSERT(emscripten_webgl_get_current_context() == context->GetGLContext()); + + //int width = 0, height = 0, fs = 0; + //emscripten_webgl_get_drawing_buffer_size(context->context_, &width, &height); + + return true; +} + + +} // /namespace LLGL + + + +// ================================================================================ diff --git a/sources/Renderer/OpenGL/Platform/Wasm/WasmGLSwapChainContext.h b/sources/Renderer/OpenGL/Platform/Wasm/WasmGLSwapChainContext.h new file mode 100644 index 0000000000..c7f675a0cf --- /dev/null +++ b/sources/Renderer/OpenGL/Platform/Wasm/WasmGLSwapChainContext.h @@ -0,0 +1,53 @@ +/* + * WasmGLSwapChainContext.h + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#ifndef LLGL_WASM_GL_SWAP_CHAIN_CONTEXT_H +#define LLGL_WASM_GL_SWAP_CHAIN_CONTEXT_H + + +#include "../GLSwapChainContext.h" +#include "../../OpenGL.h" +#include "WasmGLContext.h" + +namespace LLGL +{ + + +class Surface; +class WasmGLContext; + +class WasmGLSwapChainContext final : public GLSwapChainContext +{ + + public: + + WasmGLSwapChainContext(WasmGLContext& context, Surface& surface); + ~WasmGLSwapChainContext(); + + bool HasDrawable() const override; + bool SwapBuffers() override; + void Resize(const Extent2D& resolution) override; + + public: + + static bool MakeCurrentEGLContext(WasmGLSwapChainContext* context); + + private: + + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context_ = 0; + +}; + + +} // /namespace LLGL + + +#endif + + + +// ================================================================================ diff --git a/sources/Renderer/OpenGL/Shader/GLShaderProgram.cpp b/sources/Renderer/OpenGL/Shader/GLShaderProgram.cpp index 863ac0245d..1ef54c6c23 100644 --- a/sources/Renderer/OpenGL/Shader/GLShaderProgram.cpp +++ b/sources/Renderer/OpenGL/Shader/GLShaderProgram.cpp @@ -916,10 +916,10 @@ void GLShaderProgram::BuildProgramBinary( if (orderedShaders.fragmentShader == nullptr) { const GLchar* nullFragmentShaderSource = - #ifdef LLGL_OPENGLES3 - "#version 300 es\n" - #else + #ifdef LLGL_OPENGL "#version 330 core\n" + #else + "#version 300 es\n" #endif "void main() {}\n" ; diff --git a/sources/Renderer/OpenGL/Texture/GLTexture.cpp b/sources/Renderer/OpenGL/Texture/GLTexture.cpp index 0b1fc84353..988f8f8624 100644 --- a/sources/Renderer/OpenGL/Texture/GLTexture.cpp +++ b/sources/Renderer/OpenGL/Texture/GLTexture.cpp @@ -66,6 +66,9 @@ static bool IsRenderbufferSufficient(const TextureDescriptor& desc) // Maps the specified format to a swizzle format, or identity swizzle if texture swizzling is not necessary static GLSwizzleFormat MapToGLSwizzleFormat(const Format format) { + #ifdef LLGL_WEBGL + return GLSwizzleFormat::RGBA; // WebGL does not support texture swizzling + #else const auto& formatDesc = GetFormatAttribs(format); if (formatDesc.format == ImageFormat::Alpha) return GLSwizzleFormat::Alpha; @@ -73,6 +76,7 @@ static GLSwizzleFormat MapToGLSwizzleFormat(const Format format) return GLSwizzleFormat::BGRA; else return GLSwizzleFormat::RGBA; + #endif } GLTexture::GLTexture(const TextureDescriptor& desc) : @@ -112,7 +116,7 @@ GLTexture::GLTexture(const TextureDescriptor& desc) : } } - #ifdef LLGL_OPENGLES3 + #ifndef LLGL_GLEXT_GET_TEX_LEVEL_PARAMETER /* Store additional parameters for GLES */ extent_[0] = static_cast(desc.extent.width); extent_[1] = static_cast(desc.extent.height); diff --git a/sources/Renderer/OpenGL/Texture/GLTexture.h b/sources/Renderer/OpenGL/Texture/GLTexture.h index 42cce0640c..efbc0c8dd5 100644 --- a/sources/Renderer/OpenGL/Texture/GLTexture.h +++ b/sources/Renderer/OpenGL/Texture/GLTexture.h @@ -163,7 +163,7 @@ class GLTexture final : public Texture const bool isRenderbuffer_ = false; const GLSwizzleFormat swizzleFormat_ = GLSwizzleFormat::RGBA; // Identity texture swizzle by default - #ifdef LLGL_OPENGLES3 + #ifndef LLGL_GLEXT_GET_TEX_LEVEL_PARAMETER GLint extent_[3] = {}; GLint samples_ = 1; #endif diff --git a/sources/Renderer/OpenGL/WebGLProfile/WebGL.h b/sources/Renderer/OpenGL/WebGLProfile/WebGL.h new file mode 100644 index 0000000000..266b7e3933 --- /dev/null +++ b/sources/Renderer/OpenGL/WebGLProfile/WebGL.h @@ -0,0 +1,28 @@ +/* + * WebGL.h + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#ifndef LLGL_WEBGL_H +#define LLGL_WEBGL_H + + +#include + +#if defined(LLGL_OS_WASM) +# define GL_GLEXT_PROTOTYPES 1 +# define EGL_EGLEXT_PROTOTYPES 1 +# include +# include +#else +# error Unsupported platform for WebGL +#endif + + +#endif + + + +// ================================================================================ diff --git a/sources/Renderer/OpenGL/WebGLProfile/WebGLExtensionLoader.cpp b/sources/Renderer/OpenGL/WebGLProfile/WebGLExtensionLoader.cpp new file mode 100644 index 0000000000..2221bd65f4 --- /dev/null +++ b/sources/Renderer/OpenGL/WebGLProfile/WebGLExtensionLoader.cpp @@ -0,0 +1,145 @@ +/* + * WebGLExtensionLoader.cpp + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#include "../Ext/GLExtensionLoader.h" +#include "../Ext/GLExtensionRegistry.h" +#include "WebGL.h" +#include "../GLCore.h" +#if defined(LLGL_OS_WASM) +# include +#endif +#include + + +namespace LLGL +{ + + +// Global member to store if the extension have already been loaded +static bool g_WebGLExtensionsLoaded = false; +static std::set g_supportedWebGLExtensions; +static std::set g_loadedWebGLExtensions; + +static void EnableWebGLExtension(GLExt ext, const char* name) +{ + RegisterExtension(ext); + g_supportedWebGLExtensions.insert(name); //TODO: find better way to determine supported GLES extensions + g_loadedWebGLExtensions.insert(name); +} + +bool LoadSupportedOpenGLExtensions(bool isCoreProfile, bool abortOnFailure) +{ + /* Only load GL extensions once */ + if (g_WebGLExtensionsLoaded) + return true; + + #define ENABLE_GLEXT(NAME) \ + EnableWebGLExtension(GLExt::NAME, "GL_" #NAME) + + const int version = GLGetVersion(); + + ENABLE_GLEXT(ARB_clear_buffer_object); + ENABLE_GLEXT(ARB_clear_texture); + ENABLE_GLEXT(ARB_buffer_storage); + ENABLE_GLEXT(ARB_copy_buffer); + ENABLE_GLEXT(ARB_draw_buffers); + ENABLE_GLEXT(ARB_draw_buffers_blend); + ENABLE_GLEXT(ARB_draw_elements_base_vertex); + ENABLE_GLEXT(ARB_draw_instanced); + ENABLE_GLEXT(ARB_draw_indirect); + ENABLE_GLEXT(ARB_framebuffer_object); + ENABLE_GLEXT(ARB_geometry_shader4); // no procedures + ENABLE_GLEXT(ARB_instanced_arrays); + ENABLE_GLEXT(ARB_internalformat_query); + ENABLE_GLEXT(ARB_internalformat_query2); + ENABLE_GLEXT(ARB_multitexture); + ENABLE_GLEXT(ARB_multi_draw_indirect); + ENABLE_GLEXT(ARB_occlusion_query); + ENABLE_GLEXT(ARB_pipeline_statistics_query); + ENABLE_GLEXT(ARB_polygon_offset_clamp); + ENABLE_GLEXT(ARB_sampler_objects); + ENABLE_GLEXT(ARB_seamless_cubemap_per_texture); + ENABLE_GLEXT(ARB_shader_image_load_store); + ENABLE_GLEXT(ARB_shader_objects); + ENABLE_GLEXT(ARB_shader_objects_21); + ENABLE_GLEXT(ARB_sync); + ENABLE_GLEXT(ARB_texture_compression); + ENABLE_GLEXT(ARB_texture_cube_map); // no procedures + ENABLE_GLEXT(ARB_texture_cube_map_array); // no procedures + ENABLE_GLEXT(ARB_texture_multisample); + ENABLE_GLEXT(ARB_texture_storage); + ENABLE_GLEXT(ARB_texture_storage_multisample); + ENABLE_GLEXT(ARB_timer_query); + ENABLE_GLEXT(ARB_transform_feedback3); + ENABLE_GLEXT(ARB_uniform_buffer_object); + ENABLE_GLEXT(ARB_vertex_array_object); + ENABLE_GLEXT(ARB_vertex_buffer_object); + ENABLE_GLEXT(ARB_vertex_shader); + ENABLE_GLEXT(ARB_viewport_array); + ENABLE_GLEXT(ARB_ES2_compatibility); + ENABLE_GLEXT(ARB_compatibility); + ENABLE_GLEXT(ARB_map_buffer_range); + + ENABLE_GLEXT(EXT_blend_color); + ENABLE_GLEXT(EXT_blend_equation_separate); + ENABLE_GLEXT(EXT_blend_func_separate); + ENABLE_GLEXT(EXT_blend_minmax); + ENABLE_GLEXT(EXT_copy_texture); + ENABLE_GLEXT(EXT_draw_buffers2); + ENABLE_GLEXT(EXT_gpu_shader4); + ENABLE_GLEXT(EXT_stencil_two_side); + ENABLE_GLEXT(EXT_texture3D); + ENABLE_GLEXT(EXT_texture_array); + ENABLE_GLEXT(EXT_transform_feedback); + + if (version >= 300) + { + ENABLE_GLEXT(ARB_ES3_compatibility); + ENABLE_GLEXT(ARB_get_program_binary); + ENABLE_GLEXT(ARB_shader_objects_30); + } + + if (version >= 310) + { + ENABLE_GLEXT(ARB_shader_storage_buffer_object); + ENABLE_GLEXT(ARB_program_interface_query); + ENABLE_GLEXT(ARB_compute_shader); + ENABLE_GLEXT(ARB_framebuffer_no_attachments); + } + + if (version >= 320) + { + ENABLE_GLEXT(ARB_tessellation_shader); + ENABLE_GLEXT(ARB_copy_image); + } + + #undef ENABLE_GLEXT + + return true; +} + +bool AreOpenGLExtensionsLoaded() +{ + return g_WebGLExtensionsLoaded; +} + +const std::set& GetSupportedOpenGLExtensions() +{ + return g_supportedWebGLExtensions; +} + +const std::set& GetLoadedOpenGLExtensions() +{ + return g_loadedWebGLExtensions; +} + + +} // /namespace LLGL + + + +// ================================================================================ diff --git a/sources/Renderer/OpenGL/WebGLProfile/WebGLProfile.cpp b/sources/Renderer/OpenGL/WebGLProfile/WebGLProfile.cpp new file mode 100644 index 0000000000..a3a669b5e6 --- /dev/null +++ b/sources/Renderer/OpenGL/WebGLProfile/WebGLProfile.cpp @@ -0,0 +1,123 @@ +/* + * WebGLProfile.cpp + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#include "../GLProfile.h" +#include "../Ext/GLExtensions.h" +#include +#include + + +namespace LLGL +{ + +namespace GLProfile +{ + + +int GetRendererID() +{ + return RendererID::WebGL; +} + +const char* GetModuleName() +{ + return "WebGL"; +} + +const char* GetRendererName() +{ + return "WebGL"; +} + +const char* GetAPIName() +{ + return "WebGL"; +} + +const char* GetShadingLanguageName() +{ + return "ESSL"; +} + +GLint GetMaxViewports() +{ + return 1; +} + +void GetTexParameterInternalFormat(GLenum target, GLint* params) +{ + //TODO... +} + +void GetInternalformativ(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufsize, GLint* params) +{ + //TODO +} + +void DepthRange(GLclamp_t nearVal, GLclamp_t farVal) +{ + glDepthRangef(nearVal, farVal); +} + +void ClearDepth(GLclamp_t depth) +{ + glClearDepthf(depth); +} + +void GetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void* data) +{ + // dummy +} + +void* MapBuffer(GLenum target, GLenum access) +{ + return nullptr; // dummy +} + +void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) +{ + return nullptr; // dummy +} + +void UnmapBuffer(GLenum target) +{ + // dummy +} + +void DrawBuffer(GLenum buf) +{ + glDrawBuffers(1, &buf); +} + +void FramebufferTexture1D(GLenum /*target*/, GLenum /*attachment*/, GLenum /*textarget*/, GLuint /*texture*/, GLint /*level*/) +{ + // dummy +} + +void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +{ + glFramebufferTexture2D(target, attachment, textarget, texture, level); +} + +void FramebufferTexture3D(GLenum target, GLenum attachment, GLenum /*textarget*/, GLuint texture, GLint level, GLint layer) +{ + glFramebufferTextureLayer(target, attachment, texture, level, layer); +} + +void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) +{ + glFramebufferTextureLayer(target, attachment, texture, level, layer); +} + + +} // /namespace GLProfile + +} // /namespace LLGL + + + +// ================================================================================ diff --git a/sources/Renderer/OpenGL/WebGLProfile/WebGLProfileCaps.cpp b/sources/Renderer/OpenGL/WebGLProfile/WebGLProfileCaps.cpp new file mode 100644 index 0000000000..6b84464afc --- /dev/null +++ b/sources/Renderer/OpenGL/WebGLProfile/WebGLProfileCaps.cpp @@ -0,0 +1,241 @@ +/* + * WebGLProfileCaps.cpp + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#include "../GLRenderingCaps.h" +#include "../GLProfile.h" +#include "../GLTypes.h" +#include "../Ext/GLExtensions.h" +#include "../Ext/GLExtensionRegistry.h" +#include "../../../Core/CoreUtils.h" +#include +#include + + +namespace LLGL +{ + + +static std::int32_t GLGetInt(GLenum param) +{ + GLint attr = 0; + glGetIntegerv(param, &attr); + return attr; +} + +static std::uint32_t GLGetUInt(GLenum param) +{ + return static_cast(GLGetInt(param)); +}; + +static std::uint32_t GLGetUIntIndexed(GLenum param, GLuint index) +{ + GLint attr = 0; + if (HasExtension(GLExt::EXT_draw_buffers2)) + glGetIntegeri_v(param, index, &attr); + return static_cast(attr); +}; + +static float GLGetFloat(GLenum param) +{ + GLfloat attr = 0.0f; + glGetFloatv(param, &attr); + return attr; +} + +// Returns the GLES version in the ESSL version format (e.g. 200 for GLES 2.0, 320 for GLES 3.2) +static GLint GetGLESVersion() +{ + GLint major = 0, minor = 0; + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MINOR_VERSION, &minor); + return (major * 100 + minor * 10); +} + +static std::vector GLQueryShadingLanguages(GLint version) +{ + std::vector languages; + + /* Add supported GLSL versions */ + languages.push_back(ShadingLanguage::ESSL); + + if (version >= 200) { languages.push_back(ShadingLanguage::ESSL_100); } + if (version >= 300) { languages.push_back(ShadingLanguage::ESSL_300); } + if (version >= 310) { languages.push_back(ShadingLanguage::ESSL_310); } + if (version >= 320) { languages.push_back(ShadingLanguage::ESSL_320); } + + return languages; +} + +//TODO +static std::vector GetDefaultSupportedGLTextureFormats() +{ + return + { + Format::A8UNorm, + Format::R8UNorm, Format::R8SNorm, Format::R8UInt, Format::R8SInt, + Format::R16UNorm, Format::R16SNorm, Format::R16UInt, Format::R16SInt, Format::R16Float, + Format::R32UInt, Format::R32SInt, Format::R32Float, + Format::RG8UNorm, Format::RG8SNorm, Format::RG8UInt, Format::RG8SInt, + Format::RG16UNorm, Format::RG16SNorm, Format::RG16UInt, Format::RG16SInt, Format::RG16Float, + Format::RG32UInt, Format::RG32SInt, Format::RG32Float, + Format::RGB8UNorm, Format::RGB8SNorm, Format::RGB8UInt, Format::RGB8SInt, + Format::RGB16UNorm, Format::RGB16SNorm, Format::RGB16UInt, Format::RGB16SInt, Format::RGB16Float, + Format::RGB32UInt, Format::RGB32SInt, Format::RGB32Float, + Format::RGBA8UNorm, Format::RGBA8SNorm, Format::RGBA8UInt, Format::RGBA8SInt, + Format::RGBA16UNorm, Format::RGBA16SNorm, Format::RGBA16UInt, Format::RGBA16SInt, Format::RGBA16Float, + Format::RGBA32UInt, Format::RGBA32SInt, Format::RGBA32Float, + Format::BGRA8UNorm, Format::BGRA8UNorm_sRGB, Format::BGRA8SNorm, Format::BGRA8UInt, Format::BGRA8SInt, + Format::D16UNorm, Format::D32Float, Format::D24UNormS8UInt, Format::D32FloatS8X24UInt, + }; +} + +static void GLGetRenderingAttribs(RenderingCapabilities& caps, GLint version) +{ + /* Set fixed states for this renderer */ + caps.screenOrigin = ScreenOrigin::LowerLeft; + caps.clippingRange = ClippingRange::MinusOneToOne; + caps.shadingLanguages = GLQueryShadingLanguages(version); +} + +static void GLGetSupportedTextureFormats(std::vector& textureFormats) +{ + textureFormats = GetDefaultSupportedGLTextureFormats(); + + RemoveAllFromListIf( + textureFormats, + [](Format format) -> bool + { + if (auto internalformat = GLTypes::MapOrZero(format)) + { + GLint supported = 0; + GLProfile::GetInternalformativ(GL_TEXTURE_2D, internalformat, GL_INTERNALFORMAT_SUPPORTED, 1, &supported); + return (supported == GL_FALSE); + } + return true; + } + ); + + const auto numCompressedTexFormats = GLGetUInt(GL_NUM_COMPRESSED_TEXTURE_FORMATS); + + std::vector compressedTexFormats(numCompressedTexFormats); + glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, compressedTexFormats.data()); + + for (GLint internalFormat : compressedTexFormats) + { + const Format format = GLTypes::UnmapFormat(internalFormat); + if (format != Format::Undefined) + textureFormats.push_back(format); + } +} + +static void GLGetSupportedFeatures(RenderingFeatures& features, GLint version) +{ + /* Query all boolean capabilies by their respective OpenGL extension */ + features.hasRenderTargets = true; // GLES 2.0 + features.has3DTextures = true; // GLES 2.0 + features.hasCubeTextures = true; // GLES 2.0 + features.hasArrayTextures = true; // GLES 2.0 + features.hasCubeArrayTextures = (version >= 320); // GLES 3.2 + features.hasMultiSampleTextures = (version >= 310); // GLES 3.1 + features.hasTextureViews = false; + features.hasTextureViewSwizzle = false; + features.hasBufferViews = (version >= 300); // GLES 3.0 + features.hasConstantBuffers = (version >= 300); // GLES 3.0 + features.hasStorageBuffers = (version >= 300); // GLES 3.0 + features.hasGeometryShaders = (version >= 320); // GLES 3.2 + features.hasTessellationShaders = (version >= 320); // GLES 3.2 + features.hasTessellatorStage = (version >= 320); // GLES 3.2 + features.hasComputeShaders = (version >= 310); // GLES 3.1 + features.hasInstancing = (version >= 300); // GLES 3.0 + features.hasOffsetInstancing = false; + features.hasIndirectDrawing = (version >= 310); // GLES 3.1 + features.hasViewportArrays = false; + features.hasConservativeRasterization = false; + features.hasStreamOutputs = (version >= 300); // GLES 3.0 + features.hasLogicOp = false; + features.hasPipelineCaching = (version >= 300); // GLES 3.0 + features.hasPipelineStatistics = false; + features.hasRenderCondition = false; +} + +static void GLGetFeatureLimits(RenderingLimits& limits, GLint version) +{ + /* Determine minimal line width range for both aliased and smooth lines */ + GLfloat aliasedLineRange[2]; + glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, aliasedLineRange); + + //limits.lineWidthRange[0] = ??? + //limits.lineWidthRange[1] = ??? + + /* Query integral attributes */ + limits.maxTextureArrayLayers = GLGetUInt(GL_MAX_ARRAY_TEXTURE_LAYERS); + limits.maxColorAttachments = GLGetUInt(GL_MAX_DRAW_BUFFERS); + //limits.maxPatchVertices = ??? + //limits.maxAnisotropy = ??? + + #ifdef GL_ES_VERSION_3_1 + limits.maxComputeShaderWorkGroups[0] = GLGetUIntIndexed(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0); + limits.maxComputeShaderWorkGroups[1] = GLGetUIntIndexed(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1); + limits.maxComputeShaderWorkGroups[2] = GLGetUIntIndexed(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2); + limits.maxComputeShaderWorkGroupSize[0] = GLGetUIntIndexed(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0); + limits.maxComputeShaderWorkGroupSize[1] = GLGetUIntIndexed(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1); + limits.maxComputeShaderWorkGroupSize[2] = GLGetUIntIndexed(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2); + #endif + + limits.minConstantBufferAlignment = GLGetUInt(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); + + #ifdef GL_ES_VERSION_3_1 + limits.minSampledBufferAlignment = GLGetUInt(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); + limits.minStorageBufferAlignment = limits.minSampledBufferAlignment; // Use SSBO for both sampled and storage buffers + #endif + + /* Query viewport limits */ + limits.maxViewports = 1; + + GLint maxViewportDims[2]; + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, maxViewportDims); + limits.maxViewportSize[0] = static_cast(maxViewportDims[0]); + limits.maxViewportSize[1] = static_cast(maxViewportDims[1]); + + /* Determine maximum buffer size to maximum value for (used in 'glBufferData') */ + limits.maxBufferSize = static_cast(std::numeric_limits::max()); + limits.maxConstantBufferSize = static_cast(GLGetUInt(GL_MAX_UNIFORM_BLOCK_SIZE)); + + /* Presume that at least one stream-output is supported */ + limits.maxStreamOutputs = 1u; + + #ifdef GL_ES_VERSION_3_2 + /* Determine tessellation limits */ + limits.maxTessFactor = GLGetUInt(GL_MAX_TESS_GEN_LEVEL); + #endif +} + +static void GLGetTextureLimits(const RenderingFeatures& features, RenderingLimits& limits, GLint version) +{ + /* No proxy textures in GLES, so rely on glGet*() functions */ + limits.max1DTextureSize = GLGetUInt(GL_MAX_TEXTURE_SIZE); + limits.max2DTextureSize = limits.max1DTextureSize; + limits.max3DTextureSize = GLGetUInt(GL_MAX_3D_TEXTURE_SIZE); + limits.maxCubeTextureSize = GLGetUInt(GL_MAX_CUBE_MAP_TEXTURE_SIZE); +} + +void GLQueryRenderingCaps(RenderingCapabilities& caps) +{ + const GLint version = GetGLESVersion(); + GLGetRenderingAttribs(caps, version); + GLGetSupportedTextureFormats(caps.textureFormats); + GLGetSupportedFeatures(caps.features, version); + GLGetFeatureLimits(caps.limits, version); + GLGetTextureLimits(caps.features, caps.limits, version); +} + + +} // /namespace LLGL + + + +// ================================================================================ diff --git a/sources/Renderer/OpenGL/WebGLProfile/WebGLProfileTypes.h b/sources/Renderer/OpenGL/WebGLProfile/WebGLProfileTypes.h new file mode 100644 index 0000000000..69026e105c --- /dev/null +++ b/sources/Renderer/OpenGL/WebGLProfile/WebGLProfileTypes.h @@ -0,0 +1,112 @@ +/* + * WebGLProfileTypyes.h + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#ifndef LLGL_WEBGL_PROFILE_TYPES_H +#define LLGL_WEBGL_PROFILE_TYPES_H + + +#include "WebGL.h" + + +namespace LLGL +{ + + +#ifndef GL_READ_ONLY +#define GL_READ_ONLY 0x88B8 // for wrappers only +#endif + +#ifndef GL_WRITE_ONLY +#define GL_WRITE_ONLY 0x88B9 // for wrappers only +#endif + +#ifndef GL_READ_WRITE +#define GL_READ_WRITE 0x88BA // for wrappers only +#endif + +#ifndef GL_LOWER_LEFT +#define GL_LOWER_LEFT 0x8CA1 // for wrappers only +#endif + +#ifndef GL_UPPER_LEFT +#define GL_UPPER_LEFT 0x8CA2 // for wrappers only +#endif + +#ifndef GL_TEXTURE_1D +#define GL_TEXTURE_1D 0x0DE0 // for wrappers only +#endif + +#ifndef GL_TEXTURE_1D_ARRAY +#define GL_TEXTURE_1D_ARRAY 0x8C18 // for wrappers only +#endif + +#ifndef GL_TEXTURE_RECTANGLE +#define GL_TEXTURE_RECTANGLE 0x84F5 // for wrappers only +#endif + +#ifndef GL_TEXTURE_CUBE_MAP_ARRAY +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 // GLES 3.2 +#endif + +#ifndef GL_TEXTURE_BUFFER +#define GL_TEXTURE_BUFFER 0x8C2A // GLES 3.2 +#endif + +#ifndef GL_TEXTURE_2D_MULTISAMPLE +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 // GLES 3.1 +#endif + +#ifndef GL_TEXTURE_2D_MULTISAMPLE_ARRAY +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 // GLES 3.1 +#endif + +#ifndef GL_PROXY_TEXTURE_3D +#define GL_PROXY_TEXTURE_3D 0x8070 // for wrappers only +#endif + +#ifndef GL_ATOMIC_COUNTER_BUFFER +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 // GLES 3.2 +#endif + +#ifndef GL_DISPATCH_INDIRECT_BUFFER +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE // GLES 3.2 +#endif + +#ifndef GL_DRAW_INDIRECT_BUFFER +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F // GLES 3.2 +#endif + +#ifndef GL_QUERY_BUFFER +#define GL_QUERY_BUFFER 0x9192 // for wrappers only +#endif + +#ifndef GL_SHADER_STORAGE_BUFFER +#define GL_SHADER_STORAGE_BUFFER 0x90D2 // GLES 3.2 +#endif + +#ifndef GL_STENCIL_INDEX +#define GL_STENCIL_INDEX GL_STENCIL_INDEX8 // indirection +#endif + +#ifndef GL_INTERNALFORMAT_SUPPORTED +#define GL_INTERNALFORMAT_SUPPORTED 0x826F // for wrappers only +#endif + + + +// Used for glDepthRangef +typedef GLfloat GLclamp_t; + + +} // /namespace LLGL + + +#endif + + + +// ================================================================================ diff --git a/sources/Renderer/StaticModuleInterface.cpp b/sources/Renderer/StaticModuleInterface.cpp index 33838f6108..ce5e46296a 100644 --- a/sources/Renderer/StaticModuleInterface.cpp +++ b/sources/Renderer/StaticModuleInterface.cpp @@ -39,6 +39,10 @@ LLGL_DECLARE_STATIC_MODULE_INTERFACE(OpenGL); LLGL_DECLARE_STATIC_MODULE_INTERFACE(OpenGLES3); #endif +#ifdef LLGL_BUILD_RENDERER_WEBGL +LLGL_DECLARE_STATIC_MODULE_INTERFACE(WebGL); +#endif + #ifdef LLGL_BUILD_RENDERER_VULKAN LLGL_DECLARE_STATIC_MODULE_INTERFACE(Vulkan); #endif @@ -73,6 +77,9 @@ std::vector GetStaticModules() #ifdef LLGL_BUILD_RENDERER_OPENGLES3 ModuleOpenGLES3::GetModuleName(), #endif + #ifdef LLGL_BUILD_RENDERER_WEBGL + ModuleWebGL::GetModuleName(), + #endif #ifdef LLGL_BUILD_RENDERER_VULKAN ModuleVulkan::GetModuleName(), #endif @@ -106,6 +113,10 @@ const char* GetRendererName(const std::string& moduleName) LLGL_GET_RENDERER_NAME(ModuleOpenGLES3); #endif + #ifdef LLGL_BUILD_RENDERER_WEBGL + LLGL_GET_RENDERER_NAME(ModuleWebGL); + #endif + #ifdef LLGL_BUILD_RENDERER_VULKAN LLGL_GET_RENDERER_NAME(ModuleVulkan); #endif @@ -145,6 +156,10 @@ int GetRendererID(const std::string& moduleName) LLGL_GET_RENDERER_ID(ModuleOpenGLES3); #endif + #ifdef LLGL_BUILD_RENDERER_WEBGL + LLGL_GET_RENDERER_ID(ModuleWebGL); + #endif + #ifdef LLGL_BUILD_RENDERER_VULKAN LLGL_GET_RENDERER_ID(ModuleVulkan); #endif @@ -184,6 +199,10 @@ RenderSystem* AllocRenderSystem(const RenderSystemDescriptor& renderSystemDesc) LLGL_ALLOC_RENDER_SYSTEM(ModuleOpenGLES3); #endif + #ifdef LLGL_BUILD_RENDERER_WEBGL + LLGL_ALLOC_RENDER_SYSTEM(ModuleWebGL); + #endif + #ifdef LLGL_BUILD_RENDERER_VULKAN LLGL_ALLOC_RENDER_SYSTEM(ModuleVulkan); #endif